eth: Add KSDK ENET driver.

Provide a network driver wrapped around the KSDK ENET and PHY
drivers.

The driver performs one shot PHY setup.  There is no support for PHY
disconnect, reconnect or configuration change.  The PHY setup,
implement 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.

Origin: Original

Change-Id: Ia0f2e89a61348ed949976070353e823c178fcb24
Signed-off-by: Marcus Shawcroft <marcus.shawcroft@arm.com>
This commit is contained in:
Marcus Shawcroft 2016-06-16 17:33:47 +01:00 committed by Maureen Helm
commit 87077727bf
8 changed files with 487 additions and 0 deletions

View file

@ -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

View file

@ -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
}
/**

View file

@ -93,3 +93,4 @@ config ETH_DW_0_IRQ_PRI
endif # ETH_DW
source "drivers/ethernet/Kconfig.enc28j60"
source "drivers/ethernet/Kconfig.ksdk"

View file

@ -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

View file

@ -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

335
drivers/ethernet/eth_ksdk.c Normal file
View file

@ -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 <board.h>
#include <device.h>
#include <misc/util.h>
#include <nanokernel.h>
#include <net/ip/net_driver_ethernet.h>
#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 <misc/sys_log.h>
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,
&eth_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);
}

View file

@ -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)

View file

@ -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: