diff --git a/arch/arm/soc/nxp_kinetis/k6x/Kconfig.defconfig.mk64f12 b/arch/arm/soc/nxp_kinetis/k6x/Kconfig.defconfig.mk64f12 index d78d28c7628..82307e8c30d 100644 --- a/arch/arm/soc/nxp_kinetis/k6x/Kconfig.defconfig.mk64f12 +++ b/arch/arm/soc/nxp_kinetis/k6x/Kconfig.defconfig.mk64f12 @@ -209,4 +209,14 @@ endif # SPI_K64 endif # SPI +if ETHERNET + +config ETH_KSDK + def_bool y + +config ETH_KSDK_0 + def_bool y + +endif # ETHERNET + endif # SOC_MK64F12 diff --git a/arch/arm/soc/nxp_kinetis/k6x/soc.c b/arch/arm/soc/nxp_kinetis/k6x/soc.c index 061b8542a80..ecb86b44344 100644 --- a/arch/arm/soc/nxp_kinetis/k6x/soc.c +++ b/arch/arm/soc/nxp_kinetis/k6x/soc.c @@ -41,6 +41,8 @@ #define ER32KSEL_RTC (2) #define ER32KSEL_LPO1KHZ (3) +#define TIMESRC_OSCERCLK (2) + /* * K64F Flash configuration fields * These 16 bytes, which must be loaded to address 0x400, include default @@ -139,6 +141,10 @@ static ALWAYS_INLINE void clkInit(void) CONFIG_MCG_FCRDIV); CLOCK_SetSimConfig(&simConfig); + +#if CONFIG_ETH_KSDK + CLOCK_SetEnetTime0Clock(TIMESRC_OSCERCLK); +#endif } /** diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index f21249a125b..0a61bf6e3f1 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -93,3 +93,4 @@ config ETH_DW_0_IRQ_PRI endif # ETH_DW source "drivers/ethernet/Kconfig.enc28j60" +source "drivers/ethernet/Kconfig.ksdk" diff --git a/drivers/ethernet/Kconfig.ksdk b/drivers/ethernet/Kconfig.ksdk new file mode 100644 index 00000000000..0f2f34fc514 --- /dev/null +++ b/drivers/ethernet/Kconfig.ksdk @@ -0,0 +1,113 @@ +# Kconfig - ETH_KSDK Ethernet driver configuration options + +# Copyright (c) 2016 ARM Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +menuconfig ETH_KSDK + bool + prompt "KSDK Ethernet driver" + depends on ETHERNET && HAS_KSDK + default n + help + Enable KSDK Ethernet driver. Note, this driver performs one shot PHY + setup. There is no support for PHY disconnect, reconnect or + configuration change. + +if ETH_KSDK +config ETH_KSDK_RX_BUFFERS + int "Number of KSDK RX buffers" + depends on ETH_KSDK + default 2 + range 1 16 + help + Set the number of RX buffers provided to the KSDK driver. + +config ETH_KSDK_TX_BUFFERS + int "Number of KSDK TX buffers" + depends on ETH_KSDK + default 2 + range 1 16 + help + Set the number of TX buffers provided to the KSDK driver. + +config ETH_KSDK_0 + bool "KSDK Ethernet port 0" + default n + help + Include port 0 driver + +config ETH_KSDK_0_NAME + string "Driver name" + depends on ETH_KSDK_0 + default "ETH_0" + +config ETH_KSDK_0_IRQ_PRI + int "Controller interrupt priority" + depends on ETH_KSDK_0 + default 0 + help + IRQ priority + +config ETH_KSDK_0_RANDOM_MAC + bool "Random MAC address" + depends on ETH_KSDK_0 && RANDOM_GENERATOR + default y + help + Generate a random MAC address dynamically. + +if ETH_KSDK_0 && ! ETH_KSDK_0_RANDOM_MAC + +config ETH_KSDK_0_MAC0 + hex "MAC Address Byte 0" + default 0 + range 0 ff + help + This is byte 0 of the MAC address. + +config ETH_KSDK_0_MAC1 + hex "MAC Address Byte 1" + default 0 + range 0 ff + help + This is the byte 1 of the MAC address. + +config ETH_KSDK_0_MAC2 + hex "MAC Address Byte 2" + default 0 + range 0 ff + help + This is the byte 2 of the MAC address. + +config ETH_KSDK_0_MAC3 + hex "MAC Address Byte 3" + default 0 + range 0 ff + help + This is the byte 3 of the MAC address. + +config ETH_KSDK_0_MAC4 + hex "MAC Address Byte 4" + default 0 + range 0 ff + help + This is the byte 4 of the MAC address. + +config ETH_KSDK_0_MAC5 + hex "MAC Address Byte 5" + default 0 + range 0 ff + help + This is the byte 5 of the MAC address. +endif +endif diff --git a/drivers/ethernet/Makefile b/drivers/ethernet/Makefile index a3360f7baf0..07753f4e907 100644 --- a/drivers/ethernet/Makefile +++ b/drivers/ethernet/Makefile @@ -6,3 +6,4 @@ ccflags-y += -I${srctree} obj-$(CONFIG_ETH_DW) += eth_dw.o obj-$(CONFIG_ETH_ENC28J60) += eth_enc28j60.o +obj-$(CONFIG_ETH_KSDK) += eth_ksdk.o diff --git a/drivers/ethernet/eth_ksdk.c b/drivers/ethernet/eth_ksdk.c new file mode 100644 index 00000000000..f8dca8fa085 --- /dev/null +++ b/drivers/ethernet/eth_ksdk.c @@ -0,0 +1,335 @@ +/* KSDK Ethernet Driver + * + * Copyright (c) 2016 ARM Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* The driver performs one shot PHY setup. There is no support for + * PHY disconnect, reconnect or configuration change. The PHY setup, + * implemented via KSDK contains polled code that can block the + * initialization thread for a few seconds. + * + * There is no statistics collection for either normal operation or + * error behaviour. + */ + +#include +#include +#include +#include +#include + +#include "fsl_enet.h" +#include "fsl_phy.h" +#include "fsl_port.h" + +#define SYS_LOG_DOMAIN "ETH KSDK" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ETHERNET_LEVEL +#include + +struct eth_context { + enet_handle_t enet_handle; + struct nano_sem tx_buf_sem; + uint8_t mac_addr[6]; +}; + +static int eth_net_tx(struct net_buf *); +static void eth_0_config_func(void); + +static enet_rx_bd_struct_t __aligned(ENET_BUFF_ALIGNMENT) +rx_buffer_desc[CONFIG_ETH_KSDK_TX_BUFFERS]; + +static enet_tx_bd_struct_t __aligned(ENET_BUFF_ALIGNMENT) +tx_buffer_desc[CONFIG_ETH_KSDK_TX_BUFFERS]; + +/* Use ENET_FRAME_MAX_VALNFRAMELEN for VLAN frame size + * Use ENET_FRAME_MAX_FRAMELEN for ethernet frame size + */ +#define ETH_KSDK_BUFFER_SIZE \ + ROUND_UP(ENET_FRAME_MAX_VALNFRAMELEN, ENET_BUFF_ALIGNMENT) + +static uint8_t __aligned(ENET_BUFF_ALIGNMENT) +rx_buffer[CONFIG_ETH_KSDK_RX_BUFFERS][ETH_KSDK_BUFFER_SIZE]; + +static uint8_t __aligned(ENET_BUFF_ALIGNMENT) +tx_buffer[CONFIG_ETH_KSDK_TX_BUFFERS][ETH_KSDK_BUFFER_SIZE]; + +static int eth_tx(struct device *iface, struct net_buf *buf) +{ + struct eth_context *context = iface->driver_data; + status_t status; + + nano_sem_take(&context->tx_buf_sem, TICKS_UNLIMITED); + + status = ENET_SendFrame(ENET, &context->enet_handle, uip_buf(buf), + uip_len(buf)); + if (status) { + SYS_LOG_ERR("ENET_SendFrame error: %d\n", status); + return 0; + } + return 1; +} + +static void eth_rx(struct device *iface) +{ + struct eth_context *context = iface->driver_data; + struct net_buf *buf; + uint32_t frame_length = 0; + status_t status; + + status = ENET_GetRxFrameSize(&context->enet_handle, &frame_length); + if (status) { + enet_data_error_stats_t error_stats; + + SYS_LOG_ERR("ENET_GetRxFrameSize return: %d", status); + + ENET_GetRxErrBeforeReadFrame(&context->enet_handle, + &error_stats); + /* Flush the current read buffer. This operation can + * only report failure if there is no frame to flush, + * which cannot happen in this context. + */ + status = ENET_ReadFrame(ENET, &context->enet_handle, NULL, 0); + assert(status == kStatus_Success); + return; + } + + buf = ip_buf_get_reserve_rx(0); + if (buf == NULL) { + /* We failed to get a receive buffer. We don't add + * any further logging here because the allocator + * issued a diagnostic when it failed to allocate. + * + * Flush the current read buffer. This operation can + * only report failure if there is no frame to flush, + * which cannot happen in this context. + */ + status = ENET_ReadFrame(ENET, &context->enet_handle, NULL, 0); + assert(status == kStatus_Success); + return; + } + + if (net_buf_tailroom(buf) < frame_length) { + SYS_LOG_ERR("frame too large\n"); + net_buf_unref(buf); + status = ENET_ReadFrame(ENET, &context->enet_handle, NULL, 0); + assert(status == kStatus_Success); + return; + } + + status = ENET_ReadFrame(ENET, &context->enet_handle, + net_buf_add(buf, frame_length), frame_length); + if (status) { + SYS_LOG_ERR("ENET_ReadFrame failed: %d\n", status); + net_buf_unref(buf); + return; + } + + uip_len(buf) = frame_length; + net_driver_ethernet_recv(buf); +} + +void eth_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, + void *param) +{ + struct device *iface = param; + struct eth_context *context = iface->driver_data; + + switch (event) { + case kENET_RxEvent: + eth_rx(iface); + break; + case kENET_TxEvent: + /* Free the TX buffer. */ + nano_sem_give(&context->tx_buf_sem); + break; + case kENET_ErrEvent: + /* Error event: BABR/BABT/EBERR/LC/RL/UN/PLR. */ + break; + case kENET_WakeUpEvent: + /* Wake up from sleep mode event. */ + break; +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + case kENET_TimeStampEvent: + /* Time stamp event. */ + break; + case kENET_TimeStampAvailEvent: + /* Time stamp available event. */ + break; +#endif + } +} + +#if defined(CONFIG_ETH_KSDK_0_RANDOM_MAC) +static void generate_mac(uint8_t *mac_addr) +{ + uint32_t entropy; + + entropy = sys_rand32_get(); + + /* Locally administered, unicast */ + mac_addr[0] = ((entropy >> 0) & 0xfc) | 0x02; + + mac_addr[1] = entropy >> 8; + mac_addr[2] = entropy >> 16; + mac_addr[3] = entropy >> 24; + + entropy = sys_rand32_get(); + + mac_addr[4] = entropy >> 0; + mac_addr[5] = entropy >> 8; +} +#endif + +static int eth_0_init(struct device *dev) +{ + struct eth_context *context = dev->driver_data; + enet_config_t enet_config; + uint32_t sys_clock; + const uint32_t phy_addr = 0x0; + bool link; + status_t status; + int result; + enet_buffer_config_t buffer_config = { + .rxBdNumber = CONFIG_ETH_KSDK_RX_BUFFERS, + .txBdNumber = CONFIG_ETH_KSDK_TX_BUFFERS, + .rxBuffSizeAlign = ETH_KSDK_BUFFER_SIZE, + .txBuffSizeAlign = ETH_KSDK_BUFFER_SIZE, + .rxBdStartAddrAlign = rx_buffer_desc, + .txBdStartAddrAlign = tx_buffer_desc, + .rxBufferAlign = rx_buffer[0], + .txBufferAlign = tx_buffer[0], + }; + + nano_sem_init(&context->tx_buf_sem); + for (int i = 0; i < CONFIG_ETH_KSDK_TX_BUFFERS; i++) { + nano_sem_give(&context->tx_buf_sem); + } + + sys_clock = CLOCK_GetFreq(kCLOCK_CoreSysClk); + + ENET_GetDefaultConfig(&enet_config); + enet_config.interrupt |= kENET_RxFrameInterrupt; + enet_config.interrupt |= kENET_TxFrameInterrupt; + + status = PHY_Init(ENET, phy_addr, sys_clock); + if (status) { + SYS_LOG_ERR("PHY_Init() failed: %d", status); + return 1; + } + + PHY_GetLinkStatus(ENET, phy_addr, &link); + if (link) { + phy_speed_t phy_speed; + phy_duplex_t phy_duplex; + + PHY_GetLinkSpeedDuplex(ENET, phy_addr, &phy_speed, &phy_duplex); + enet_config.miiSpeed = (enet_mii_speed_t) phy_speed; + enet_config.miiDuplex = (enet_mii_duplex_t) phy_duplex; + + SYS_LOG_INF("Enabled %dM %s-duplex mode.", + (phy_speed ? 100 : 10), + (phy_duplex ? "full" : "half")); + } else { + SYS_LOG_INF("Link down."); + } + +#if defined(CONFIG_ETH_KSDK_0_RANDOM_MAC) + generate_mac(context->mac_addr); +#endif + + ENET_Init(ENET, + &context->enet_handle, + &enet_config, + &buffer_config, + context->mac_addr, + sys_clock); + + SYS_LOG_DBG("MAC %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", + context->mac_addr[0], context->mac_addr[1], + context->mac_addr[2], context->mac_addr[3], + context->mac_addr[4], context->mac_addr[5]); + + result = net_set_mac(context->mac_addr, sizeof(context->mac_addr)); + if (result) { + return 1; + } + + ENET_SetCallback(&context->enet_handle, eth_callback, dev); + net_driver_ethernet_register_tx(eth_net_tx); + eth_0_config_func(); + ENET_ActiveRead(ENET); + return 0; +} + +static void eth_ksdk_rx_isr(void *p) +{ + struct device *dev = p; + struct eth_context *context = dev->driver_data; + + ENET_ReceiveIRQHandler(ENET, &context->enet_handle); +} + +static void eth_ksdk_tx_isr(void *p) +{ + struct device *dev = p; + struct eth_context *context = dev->driver_data; + + ENET_TransmitIRQHandler(ENET, &context->enet_handle); +} + +static void eth_ksdk_error_isr(void *p) +{ + struct device *dev = p; + struct eth_context *context = dev->driver_data; + + ENET_ErrorIRQHandler(ENET, &context->enet_handle); +} + +static struct eth_context eth_0_context = { +#if !defined(CONFIG_ETH_KSDK_0_RANDOM_MAC) + .mac_addr = { + CONFIG_ETH_KSDK_0_MAC0, + CONFIG_ETH_KSDK_0_MAC1, + CONFIG_ETH_KSDK_0_MAC2, + CONFIG_ETH_KSDK_0_MAC3, + CONFIG_ETH_KSDK_0_MAC4, + CONFIG_ETH_KSDK_0_MAC5 + } +#endif +}; + +DEVICE_INIT(eth_ksdk_0, CONFIG_ETH_KSDK_0_NAME, eth_0_init, + ð_0_context, NULL, + NANOKERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); + +static int eth_net_tx(struct net_buf *buf) +{ + return eth_tx(DEVICE_GET(eth_ksdk_0), buf); +} + +static void eth_0_config_func(void) +{ + IRQ_CONNECT(IRQ_ETH_RX, CONFIG_ETH_KSDK_0_IRQ_PRI, + eth_ksdk_rx_isr, DEVICE_GET(eth_ksdk_0), 0); + irq_enable(IRQ_ETH_RX); + + IRQ_CONNECT(IRQ_ETH_TX, CONFIG_ETH_KSDK_0_IRQ_PRI, + eth_ksdk_tx_isr, DEVICE_GET(eth_ksdk_0), 0); + irq_enable(IRQ_ETH_TX); + + IRQ_CONNECT(IRQ_ETH_ERR_MISC, CONFIG_ETH_KSDK_0_IRQ_PRI, + eth_ksdk_error_isr, DEVICE_GET(eth_ksdk_0), 0); + irq_enable(IRQ_ETH_ERR_MISC); +} diff --git a/drivers/pinmux/frdm_k64f/pinmux_board_frdm_k64f.c b/drivers/pinmux/frdm_k64f/pinmux_board_frdm_k64f.c index 1aaead64861..9a1b486e0e6 100644 --- a/drivers/pinmux/frdm_k64f/pinmux_board_frdm_k64f.c +++ b/drivers/pinmux/frdm_k64f/pinmux_board_frdm_k64f.c @@ -81,6 +81,26 @@ struct pin_config mux_config[] = { { K64_PIN_PTB11, K64_PINMUX_FUNC_ANALOG }, /* ADC1_SE15/Analog In 3 */ { K64_PIN_PTC11, K64_PINMUX_FUNC_ANALOG }, /* ADC1_SE7b/Analog In 4 */ { K64_PIN_PTC10, K64_PINMUX_FUNC_ANALOG }, /* ADC1_SE6b/Analog In 5 */ + +#if CONFIG_ETH_KSDK_0 + { K64_PIN_PTA5, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA12, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA13, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA14, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA15, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA16, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA17, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTA28, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTB0, (K64_PINMUX_FUNC_ETHERNET | + K64_PINMUX_OPEN_DRN_ENABLE | + K64_PINMUX_PULL_ENABLE | + K64_PINMUX_PULL_UP)}, + { K64_PIN_PTB1, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTC16, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTC17, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTC18, K64_PINMUX_FUNC_ETHERNET}, + { K64_PIN_PTC19, K64_PINMUX_FUNC_ETHERNET}, +#endif }; int fsl_frdm_k64f_pin_init(struct device *arg) diff --git a/drivers/pinmux/frdm_k64f/pinmux_k64.h b/drivers/pinmux/frdm_k64f/pinmux_k64.h index ca40c142517..f51373026f0 100644 --- a/drivers/pinmux/frdm_k64f/pinmux_k64.h +++ b/drivers/pinmux/frdm_k64f/pinmux_k64.h @@ -67,6 +67,7 @@ #define K64_PINMUX_FUNC_GPIO K64_PINMUX_ALT_1 #define K64_PINMUX_FUNC_DISABLED K64_PINMUX_ALT_0 #define K64_PINMUX_FUNC_ANALOG K64_PINMUX_ALT_0 +#define K64_PINMUX_FUNC_ETHERNET K64_PINMUX_ALT_4 /* * Pin drive strength configuration, for output: