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 depends on HAS_MCUX_ENET
select NOCACHE_MEMORY if HAS_MCUX_CACHE select NOCACHE_MEMORY if HAS_MCUX_CACHE
select ARM_MPU if CPU_CORTEX_M7 select ARM_MPU if CPU_CORTEX_M7
select NET_POWER_MANAGEMENT if DEVICE_POWER_MANAGEMENT
help help
Enable MCUX Ethernet driver. Note, this driver performs one shot PHY Enable MCUX Ethernet driver. Note, this driver performs one shot PHY
setup. There is no support for PHY disconnect, reconnect or 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_enet.h"
#include "fsl_phy.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_B0 0x00
#define FREESCALE_OUI_B1 0x04 #define FREESCALE_OUI_B1 0x04
@ -50,6 +54,8 @@ enum eth_mcux_phy_state {
eth_mcux_phy_state_closing eth_mcux_phy_state_closing
}; };
static void eth_mcux_init(struct device *dev);
static const char * static const char *
phy_state_name(enum eth_mcux_phy_state state) __attribute__((unused)); phy_state_name(enum eth_mcux_phy_state state) __attribute__((unused));
@ -91,6 +97,11 @@ struct eth_context {
* used for anything. * used for anything.
*/ */
struct net_if *iface; 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; enet_handle_t enet_handle;
#if defined(CONFIG_PTP_CLOCK_MCUX) #if defined(CONFIG_PTP_CLOCK_MCUX)
struct device *ptp_clock; 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) static u8_t __aligned(ENET_BUFF_ALIGNMENT)
tx_buffer[CONFIG_ETH_MCUX_TX_BUFFERS][ETH_MCUX_BUFFER_SIZE]; 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, static void eth_mcux_decode_duplex_and_speed(u32_t status,
phy_duplex_t *p_phy_duplex, phy_duplex_t *p_phy_duplex,
phy_speed_t *p_phy_speed) phy_speed_t *p_phy_speed)
@ -846,7 +911,7 @@ static void generate_eth1_unique_mac(u8_t *mac_addr)
} }
#endif #endif
static int eth_init(struct device *dev) static void eth_mcux_init(struct device *dev)
{ {
struct eth_context *context = dev->driver_data; struct eth_context *context = dev->driver_data;
enet_config_t enet_config; 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 }; u8_t mdns_multicast[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFB };
#endif #endif
#if defined(CONFIG_PTP_CLOCK_MCUX) context->phy_state = eth_mcux_phy_state_initial;
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);
sys_clock = CLOCK_GetFreq(kCLOCK_CoreSysClk); 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_TxFrameInterrupt;
enet_config.interrupt |= kENET_MiiInterrupt; enet_config.interrupt |= kENET_MiiInterrupt;
#ifdef CONFIG_ETH_MCUX_PROMISCUOUS_MODE if (IS_ENABLED(CONFIG_ETH_MCUX_PROMISCUOUS_MODE)) {
enet_config.macSpecialConfig |= kENET_ControlPromiscuousEnable; 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 defined(CONFIG_NET_VLAN) if (IS_ENABLED(CONFIG_NET_VLAN)) {
enet_config.macSpecialConfig |= kENET_ControlVLANTagEnable; enet_config.macSpecialConfig |= kENET_ControlVLANTagEnable;
#endif }
#if defined(CONFIG_ETH_MCUX_HW_ACCELERATION) if (IS_ENABLED(CONFIG_ETH_MCUX_HW_ACCELERATION)) {
enet_config.txAccelerConfig |= enet_config.txAccelerConfig |=
kENET_TxAccelIpCheckEnabled | kENET_TxAccelProtoCheckEnabled; kENET_TxAccelIpCheckEnabled |
enet_config.rxAccelerConfig |= kENET_TxAccelProtoCheckEnabled;
kENET_RxAccelIpCheckEnabled | kENET_RxAccelProtoCheckEnabled; enet_config.rxAccelerConfig |=
#endif kENET_RxAccelIpCheckEnabled |
kENET_RxAccelProtoCheckEnabled;
}
ENET_Init(context->base, ENET_Init(context->base,
&context->enet_handle, &context->enet_handle,
@ -920,6 +967,7 @@ static int eth_init(struct device *dev)
context->mac_addr, context->mac_addr,
sys_clock); sys_clock);
#if defined(CONFIG_PTP_CLOCK_MCUX) #if defined(CONFIG_PTP_CLOCK_MCUX)
ENET_AddMulticastGroup(context->base, ptp_multicast); ENET_AddMulticastGroup(context->base, ptp_multicast);
@ -935,6 +983,7 @@ static int eth_init(struct device *dev)
ENET_Ptp1588Configure(context->base, &context->enet_handle, ENET_Ptp1588Configure(context->base, &context->enet_handle,
&context->ptp_config); &context->ptp_config);
#endif #endif
#if defined(CONFIG_MDNS_RESPONDER) || defined(CONFIG_MDNS_RESOLVER) #if defined(CONFIG_MDNS_RESPONDER) || defined(CONFIG_MDNS_RESOLVER)
ENET_AddMulticastGroup(context->base, mdns_multicast); ENET_AddMulticastGroup(context->base, mdns_multicast);
#endif #endif
@ -944,15 +993,48 @@ static int eth_init(struct device *dev)
/* handle PHY setup after SMI initialization */ /* handle PHY setup after SMI initialization */
eth_mcux_phy_setup(context); 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); ENET_SetCallback(&context->enet_handle, eth_callback, dev);
eth_mcux_phy_start(context); 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; return 0;
} }
@ -1110,6 +1192,10 @@ static void eth_0_config_func(void);
static struct eth_context eth_0_context = { static struct eth_context eth_0_context = {
.base = ENET, .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, .config_func = eth_0_config_func,
.phy_addr = 0U, .phy_addr = 0U,
.phy_duplex = kPHY_FullDuplex, .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, 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); CONFIG_ETH_INIT_PRIORITY, &api_funcs, NET_ETH_MTU);
static void eth_0_config_func(void) 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 = { static struct eth_context eth_1_context = {
.base = ENET2, .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, .config_func = eth_1_config_func,
.phy_addr = 0U, .phy_addr = 0U,
.phy_duplex = kPHY_FullDuplex, .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, 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); CONFIG_ETH_INIT_PRIORITY, &api_funcs, NET_ETH_MTU);
static void eth_1_config_func(void) static void eth_1_config_func(void)