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:
parent
aba61cc105
commit
db6f896c00
2 changed files with 132 additions and 41 deletions
|
@ -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
|
||||||
|
|
|
@ -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, ð_0_context, NULL,
|
ETH_MCUX_PM_FUNC, ð_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, ð_1_context, NULL,
|
ETH_MCUX_PM_FUNC, ð_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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue