drivers/ethernet: Add support for power management into MCUX

Requesting the clock controller to switch on or off the device.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
Tomasz Bursztyka 2020-02-27 10:42:50 +01:00 committed by Jukka Rissanen
commit db6f896c00
2 changed files with 132 additions and 41 deletions

View file

@ -8,6 +8,7 @@ menuconfig ETH_MCUX
depends on HAS_MCUX_ENET
select NOCACHE_MEMORY if HAS_MCUX_CACHE
select ARM_MPU if CPU_CORTEX_M7
select NET_POWER_MANAGEMENT if DEVICE_POWER_MANAGEMENT
help
Enable MCUX Ethernet driver. Note, this driver performs one shot PHY
setup. There is no support for PHY disconnect, reconnect or

View file

@ -34,6 +34,10 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include "fsl_enet.h"
#include "fsl_phy.h"
#ifdef CONFIG_NET_POWER_MANAGEMENT
#include "fsl_clock.h"
#include <drivers/clock_control.h>
#endif
#define FREESCALE_OUI_B0 0x00
#define FREESCALE_OUI_B1 0x04
@ -50,6 +54,8 @@ enum eth_mcux_phy_state {
eth_mcux_phy_state_closing
};
static void eth_mcux_init(struct device *dev);
static const char *
phy_state_name(enum eth_mcux_phy_state state) __attribute__((unused));
@ -91,6 +97,11 @@ struct eth_context {
* used for anything.
*/
struct net_if *iface;
#ifdef CONFIG_NET_POWER_MANAGEMENT
const char *clock_name;
clock_ip_name_t clock;
struct device *clock_dev;
#endif
enet_handle_t enet_handle;
#if defined(CONFIG_PTP_CLOCK_MCUX)
struct device *ptp_clock;
@ -166,6 +177,60 @@ rx_buffer[CONFIG_ETH_MCUX_RX_BUFFERS][ETH_MCUX_BUFFER_SIZE];
static u8_t __aligned(ENET_BUFF_ALIGNMENT)
tx_buffer[CONFIG_ETH_MCUX_TX_BUFFERS][ETH_MCUX_BUFFER_SIZE];
#ifdef CONFIG_NET_POWER_MANAGEMENT
static void eth_mcux_phy_enter_reset(struct eth_context *context);
void eth_mcux_phy_stop(struct eth_context *context);
static int eth_mcux_device_pm_control(struct device *dev, u32_t command,
void *context, device_pm_cb cb, void *arg)
{
struct eth_context *eth_ctx = (struct eth_context *)dev->driver_data;
int ret = 0;
if (!eth_ctx->clock_dev) {
LOG_ERR("No CLOCK dev");
ret = -EIO;
goto out;
}
if (command == DEVICE_PM_SET_POWER_STATE) {
if (*(u32_t *)context == DEVICE_PM_SUSPEND_STATE) {
LOG_DBG("Suspending");
eth_mcux_phy_enter_reset(eth_ctx);
eth_mcux_phy_stop(eth_ctx);
ENET_Reset(eth_ctx->base);
ENET_Deinit(eth_ctx->base);
clock_control_off(eth_ctx->clock_dev,
(clock_control_subsys_t)eth_ctx->clock);
} else if (*(u32_t *)context == DEVICE_PM_ACTIVE_STATE) {
LOG_DBG("Resuming");
clock_control_on(eth_ctx->clock_dev,
(clock_control_subsys_t)eth_ctx->clock);
eth_mcux_init(dev);
net_if_resume(eth_ctx->iface);
}
} else {
return -EINVAL;
}
out:
if (cb) {
cb(dev, ret, context, arg);
}
return ret;
}
#define ETH_MCUX_PM_FUNC eth_mcux_device_pm_control
#else
#define ETH_MCUX_PM_FUNC device_pm_control_nop
#endif /* CONFIG_NET_POWER_MANAGEMENT */
static void eth_mcux_decode_duplex_and_speed(u32_t status,
phy_duplex_t *p_phy_duplex,
phy_speed_t *p_phy_speed)
@ -846,7 +911,7 @@ static void generate_eth1_unique_mac(u8_t *mac_addr)
}
#endif
static int eth_init(struct device *dev)
static void eth_mcux_init(struct device *dev)
{
struct eth_context *context = dev->driver_data;
enet_config_t enet_config;
@ -869,17 +934,7 @@ static int eth_init(struct device *dev)
u8_t mdns_multicast[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFB };
#endif
#if defined(CONFIG_PTP_CLOCK_MCUX)
ts_tx_rd = 0;
ts_tx_wr = 0;
(void)memset(ts_tx_pkt, 0, sizeof(ts_tx_pkt));
#endif
k_sem_init(&context->tx_buf_sem,
0, CONFIG_ETH_MCUX_TX_BUFFERS);
k_work_init(&context->phy_work, eth_mcux_phy_work);
k_delayed_work_init(&context->delayed_phy_work,
eth_mcux_delayed_phy_work);
context->phy_state = eth_mcux_phy_state_initial;
sys_clock = CLOCK_GetFreq(kCLOCK_CoreSysClk);
@ -888,30 +943,22 @@ static int eth_init(struct device *dev)
enet_config.interrupt |= kENET_TxFrameInterrupt;
enet_config.interrupt |= kENET_MiiInterrupt;
#ifdef CONFIG_ETH_MCUX_PROMISCUOUS_MODE
enet_config.macSpecialConfig |= kENET_ControlPromiscuousEnable;
#endif
/* Initialize/override OUI which may not be correct in
* devicetree.
*/
context->mac_addr[0] = FREESCALE_OUI_B0;
context->mac_addr[1] = FREESCALE_OUI_B1;
context->mac_addr[2] = FREESCALE_OUI_B2;
if (context->generate_mac) {
context->generate_mac(context->mac_addr);
if (IS_ENABLED(CONFIG_ETH_MCUX_PROMISCUOUS_MODE)) {
enet_config.macSpecialConfig |= kENET_ControlPromiscuousEnable;
}
#if defined(CONFIG_NET_VLAN)
enet_config.macSpecialConfig |= kENET_ControlVLANTagEnable;
#endif
if (IS_ENABLED(CONFIG_NET_VLAN)) {
enet_config.macSpecialConfig |= kENET_ControlVLANTagEnable;
}
#if defined(CONFIG_ETH_MCUX_HW_ACCELERATION)
enet_config.txAccelerConfig |=
kENET_TxAccelIpCheckEnabled | kENET_TxAccelProtoCheckEnabled;
enet_config.rxAccelerConfig |=
kENET_RxAccelIpCheckEnabled | kENET_RxAccelProtoCheckEnabled;
#endif
if (IS_ENABLED(CONFIG_ETH_MCUX_HW_ACCELERATION)) {
enet_config.txAccelerConfig |=
kENET_TxAccelIpCheckEnabled |
kENET_TxAccelProtoCheckEnabled;
enet_config.rxAccelerConfig |=
kENET_RxAccelIpCheckEnabled |
kENET_RxAccelProtoCheckEnabled;
}
ENET_Init(context->base,
&context->enet_handle,
@ -920,6 +967,7 @@ static int eth_init(struct device *dev)
context->mac_addr,
sys_clock);
#if defined(CONFIG_PTP_CLOCK_MCUX)
ENET_AddMulticastGroup(context->base, ptp_multicast);
@ -935,6 +983,7 @@ static int eth_init(struct device *dev)
ENET_Ptp1588Configure(context->base, &context->enet_handle,
&context->ptp_config);
#endif
#if defined(CONFIG_MDNS_RESPONDER) || defined(CONFIG_MDNS_RESOLVER)
ENET_AddMulticastGroup(context->base, mdns_multicast);
#endif
@ -944,15 +993,48 @@ static int eth_init(struct device *dev)
/* handle PHY setup after SMI initialization */
eth_mcux_phy_setup(context);
LOG_DBG("%s MAC %02x:%02x:%02x:%02x:%02x:%02x",
eth_name(context->base),
context->mac_addr[0], context->mac_addr[1],
context->mac_addr[2], context->mac_addr[3],
context->mac_addr[4], context->mac_addr[5]);
ENET_SetCallback(&context->enet_handle, eth_callback, dev);
eth_mcux_phy_start(context);
}
static int eth_init(struct device *dev)
{
struct eth_context *context = dev->driver_data;
#if defined(CONFIG_PTP_CLOCK_MCUX)
ts_tx_rd = 0;
ts_tx_wr = 0;
(void)memset(ts_tx_pkt, 0, sizeof(ts_tx_pkt));
#endif
#if defined(CONFIG_NET_POWER_MANAGEMENT)
context->clock_dev = device_get_binding(context->clock_name);
#endif
k_sem_init(&context->tx_buf_sem,
0, CONFIG_ETH_MCUX_TX_BUFFERS);
k_work_init(&context->phy_work, eth_mcux_phy_work);
k_delayed_work_init(&context->delayed_phy_work,
eth_mcux_delayed_phy_work);
/* Initialize/override OUI which may not be correct in
* devicetree.
*/
context->mac_addr[0] = FREESCALE_OUI_B0;
context->mac_addr[1] = FREESCALE_OUI_B1;
context->mac_addr[2] = FREESCALE_OUI_B2;
if (context->generate_mac) {
context->generate_mac(context->mac_addr);
}
eth_mcux_init(dev);
LOG_DBG("MAC %02x:%02x:%02x:%02x:%02x:%02x",
context->mac_addr[0], context->mac_addr[1],
context->mac_addr[2], context->mac_addr[3],
context->mac_addr[4], context->mac_addr[5]);
return 0;
}
@ -1110,6 +1192,10 @@ static void eth_0_config_func(void);
static struct eth_context eth_0_context = {
.base = ENET,
#if defined(CONFIG_NET_POWER_MANAGEMENT)
.clock_name = DT_INST_0_NXP_KINETIS_ETHERNET_CLOCK_CONTROLLER,
.clock = kCLOCK_Enet0,
#endif
.config_func = eth_0_config_func,
.phy_addr = 0U,
.phy_duplex = kPHY_FullDuplex,
@ -1127,7 +1213,7 @@ static struct eth_context eth_0_context = {
};
ETH_NET_DEVICE_INIT(eth_mcux_0, DT_ETH_MCUX_0_NAME, eth_init,
device_pm_control_nop, &eth_0_context, NULL,
ETH_MCUX_PM_FUNC, &eth_0_context, NULL,
CONFIG_ETH_INIT_PRIORITY, &api_funcs, NET_ETH_MTU);
static void eth_0_config_func(void)
@ -1168,6 +1254,10 @@ static void eth_1_config_func(void);
static struct eth_context eth_1_context = {
.base = ENET2,
#if defined(CONFIG_NET_POWER_MANAGEMENT)
.clock_name = DT_INST_1_NXP_KINETIS_ETHERNET_CLOCK_CONTROLLER,
.clock = kCLOCK_Enet2,
#endif
.config_func = eth_1_config_func,
.phy_addr = 0U,
.phy_duplex = kPHY_FullDuplex,
@ -1185,7 +1275,7 @@ static struct eth_context eth_1_context = {
};
ETH_NET_DEVICE_INIT(eth_mcux_1, DT_ETH_MCUX_1_NAME, eth_init,
device_pm_control_nop, &eth_1_context, NULL,
ETH_MCUX_PM_FUNC, &eth_1_context, NULL,
CONFIG_ETH_INIT_PRIORITY, &api_funcs, NET_ETH_MTU);
static void eth_1_config_func(void)