rt-thread/bsp/frdm-k64f/board/drv_emac.c

451 lines
13 KiB
C

/*
* File : emac_drv.c
* i.MX6 EMAC Ethernet driver
* COPYRIGHT (C) 2015, Shanghai Real-Thread Electronic Technology Co.,Ltd
*
* Change Logs:
* Date Author Notes
* 2015-07-15 Bernard The first version
*/
#include <board.h>
#include <rtthread.h>
#include "drv_emac.h"
#if defined(RT_USING_LWIP)
#include <finsh.h>
#include <stdint.h>
#include <netif/ethernetif.h>
#include <lwip/opt.h>
#include "MK64F12.h"
#include "fsl_port.h"
#include "fsl_enet.h"
#include "fsl_phy.h"
//#define DRV_EMAC_DEBUG
//#define DRV_EMAC_RX_DUMP
//#define DRV_EMAC_TX_DUMP
#ifdef DRV_EMAC_DEBUG
#define DEBUG_PRINTF(...) rt_kprintf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...)
#endif
#define MAX_ADDR_LEN 6
#define ENET_RX_RING_LEN (16)
#define ENET_TX_RING_LEN (8)
#define K64_EMAC_DEVICE(eth) (struct emac_device*)(eth)
#define ENET_ALIGN(x) \
((unsigned int)((x) + ((ENET_BUFF_ALIGNMENT)-1)) & (unsigned int)(~(unsigned int)((ENET_BUFF_ALIGNMENT)-1)))
#define ENET_RXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN)
#define ENET_TXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN)
#define ENET_ETH_MAX_FLEN (1522) // recommended size for a VLAN frame
struct emac_device
{
/* inherit from Ethernet device */
struct eth_device parent;
ALIGN(64) enet_rx_bd_struct_t RxBuffDescrip[ENET_RX_RING_LEN];
ALIGN(64) enet_tx_bd_struct_t TxBuffDescrip[ENET_TX_RING_LEN];
ALIGN(64) uint8_t RxDataBuff[ENET_RX_RING_LEN * ENET_ALIGN(ENET_RXBUFF_SIZE)];
ALIGN(64) uint8_t TxDataBuff[ENET_TX_RING_LEN * ENET_ALIGN(ENET_TXBUFF_SIZE)];
enet_handle_t enet_handle;
rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* MAC address */
struct rt_semaphore tx_wait;
};
static struct emac_device _emac;
static void setup_k64_io_enet(void)
{
port_pin_config_t configENET = {0};
#ifndef FEATURE_UVISOR
/* Disable MPU only when uVisor is not around. */
SYSMPU->CESR &= ~SYSMPU_CESR_VLD_MASK;
#endif/*FEATURE_UVISOR*/
/* Affects PORTC_PCR16 register */
PORT_SetPinMux(PORTC, 16u, kPORT_MuxAlt4);
/* Affects PORTC_PCR17 register */
PORT_SetPinMux(PORTC, 17u, kPORT_MuxAlt4);
/* Affects PORTC_PCR18 register */
PORT_SetPinMux(PORTC, 18u, kPORT_MuxAlt4);
/* Affects PORTC_PCR19 register */
PORT_SetPinMux(PORTC, 19u, kPORT_MuxAlt4);
/* Affects PORTB_PCR1 register */
PORT_SetPinMux(PORTB, 1u, kPORT_MuxAlt4);
configENET.openDrainEnable = kPORT_OpenDrainEnable;
configENET.mux = kPORT_MuxAlt4;
configENET.pullSelect = kPORT_PullUp;
/* Ungate the port clock */
CLOCK_EnableClock(kCLOCK_PortA);
/* Affects PORTB_PCR0 register */
PORT_SetPinConfig(PORTB, 0u, &configENET);
/* Affects PORTA_PCR13 register */
PORT_SetPinMux(PORTA, 13u, kPORT_MuxAlt4);
/* Affects PORTA_PCR12 register */
PORT_SetPinMux(PORTA, 12u, kPORT_MuxAlt4);
/* Affects PORTA_PCR14 register */
PORT_SetPinMux(PORTA, 14u, kPORT_MuxAlt4);
/* Affects PORTA_PCR5 register */
PORT_SetPinMux(PORTA, 5u, kPORT_MuxAlt4);
/* Affects PORTA_PCR16 register */
PORT_SetPinMux(PORTA, 16u, kPORT_MuxAlt4);
/* Affects PORTA_PCR17 register */
PORT_SetPinMux(PORTA, 17u, kPORT_MuxAlt4);
/* Affects PORTA_PCR15 register */
PORT_SetPinMux(PORTA, 15u, kPORT_MuxAlt4);
/* Affects PORTA_PCR28 register */
PORT_SetPinMux(PORTA, 28u, kPORT_MuxAlt4);
}
static void setup_enet_clock_init(void)
{
CLOCK_EnableClock(kCLOCK_PortC);
CLOCK_EnableClock(kCLOCK_PortB);
/* Select the Ethernet timestamp clock source */
CLOCK_SetEnetTime0Clock(0x2);
}
static void enet_mac_rx_isr(struct emac_device* emac)
{
rt_err_t result;
result = eth_device_ready(&(_emac.parent));
if( result != RT_EOK )
{
DEBUG_PRINTF("RX err =%d\n", result );
}
}
static void enet_mac_tx_isr(struct emac_device* emac)
{
rt_sem_release(&emac->tx_wait);
}
static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param)
{
struct emac_device* emac = param;
switch (event)
{
case kENET_RxEvent:
enet_mac_rx_isr(emac);
break;
case kENET_TxEvent:
enet_mac_tx_isr(emac);
break;
default:
break;
}
}
static rt_err_t k64_emac_init(rt_device_t dev)
{
struct emac_device* emac = K64_EMAC_DEVICE(dev);
enet_handle_t * enet_handle = &emac->enet_handle;
bool link = false;
uint32_t phyAddr = 0;
phy_speed_t phy_speed;
phy_duplex_t phy_duplex;
uint32_t sysClock;
enet_buffer_config_t buffCfg;
enet_config_t config;
/* initialize config according to emac device */
setup_enet_clock_init();
/* enable iomux and clock */
setup_k64_io_enet();
/* prepare the buffer configuration. */
buffCfg.rxBdNumber = ENET_RX_RING_LEN; /* Receive buffer descriptor number. */
buffCfg.txBdNumber = ENET_TX_RING_LEN; /* Transmit buffer descriptor number. */
buffCfg.rxBuffSizeAlign = ENET_ALIGN(ENET_RXBUFF_SIZE); /* Aligned receive data buffer size. */
buffCfg.txBuffSizeAlign = ENET_ALIGN(ENET_TXBUFF_SIZE); /* Aligned transmit data buffer size. */
buffCfg.rxBdStartAddrAlign = emac->RxBuffDescrip; /* Aligned receive buffer descriptor start address. */
buffCfg.txBdStartAddrAlign = emac->TxBuffDescrip; /* Aligned transmit buffer descriptor start address. */
buffCfg.rxBufferAlign = emac->RxDataBuff; /* Receive data buffer start address. */
buffCfg.txBufferAlign = emac->TxDataBuff; /* Transmit data buffer start address. */
sysClock = CLOCK_GetFreq(kCLOCK_CoreSysClk);
DEBUG_PRINTF("sysClock: %d\n", sysClock);
ENET_GetDefaultConfig(&config);
PHY_Init(ENET, 0, CLOCK_GetFreq(kCLOCK_CoreSysClk));
if (PHY_GetLinkStatus(ENET, phyAddr, &link) == kStatus_Success)
{
if (link)
{
DEBUG_PRINTF("phy link up\n");
/* Get link information from PHY */
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &phy_speed, &phy_duplex);
/* Change the MII speed and duplex for actual link status. */
config.miiSpeed = (enet_mii_speed_t)phy_speed;
config.miiDuplex = (enet_mii_duplex_t)phy_duplex;
config.interrupt = kENET_RxFrameInterrupt | kENET_TxFrameInterrupt;
}
else
{
DEBUG_PRINTF("phy link down\n");
}
config.rxMaxFrameLen = ENET_ETH_MAX_FLEN;
config.macSpecialConfig = kENET_ControlFlowControlEnable;
config.txAccelerConfig = 0;
config.rxAccelerConfig = kENET_RxAccelMacCheckEnabled;
ENET_Init(ENET, enet_handle, &config, &buffCfg, emac->dev_addr, sysClock);
ENET_SetCallback(enet_handle, ethernet_callback, emac);
ENET_ActiveRead(ENET);
}
else
{
DEBUG_PRINTF("read phy failed\n");
}
return RT_EOK;
}
static rt_err_t k64_emac_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t k64_emac_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_size_t k64_emac_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
rt_set_errno(-RT_ENOSYS);
return 0;
}
static rt_size_t k64_emac_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
rt_set_errno(-RT_ENOSYS);
return 0;
}
static rt_err_t k64_emac_control(rt_device_t dev, int cmd, void *args)
{
struct emac_device *emac;
DEBUG_PRINTF("k64_emac_control\n");
emac = K64_EMAC_DEVICE(dev);
RT_ASSERT(emac != RT_NULL);
switch(cmd)
{
case NIOCTL_GADDR:
/* get MAC address */
if(args) rt_memcpy(args, emac->dev_addr, 6);
else return -RT_ERROR;
break;
default :
break;
}
return RT_EOK;
}
static rt_err_t k64_emac_tx(rt_device_t dev, struct pbuf* p)
{
rt_err_t result = RT_EOK;
struct emac_device *emac = K64_EMAC_DEVICE(dev);
enet_handle_t * enet_handle = &emac->enet_handle;
RT_ASSERT(p != NULL);
DEBUG_PRINTF("k64_emac_tx: %d\n", p->len);
emac = K64_EMAC_DEVICE(dev);
RT_ASSERT(emac != RT_NULL);
#ifdef DRV_EMAC_RX_DUMP
{
int i;
uint8_t * buf;
buf = (uint8_t *)p->payload;
for (i = 0; i < p->len; i++)
{
DEBUG_PRINTF("%02X ", buf[i]);
if (i % 16 == 15)
DEBUG_PRINTF("\n");
}
DEBUG_PRINTF("\n");
}
#endif
do
{
result = ENET_SendFrame(ENET, enet_handle, p->payload, p->len);
if (result == kStatus_ENET_TxFrameBusy)
{
rt_sem_take(&emac->tx_wait, RT_WAITING_FOREVER);
}
} while (result == kStatus_ENET_TxFrameBusy);
return RT_EOK;
}
struct pbuf *k64_emac_rx(rt_device_t dev)
{
uint32_t length = 0;
status_t status;
enet_data_error_stats_t eErrStatic;
struct pbuf* p = RT_NULL;
struct emac_device *emac = K64_EMAC_DEVICE(dev);
enet_handle_t * enet_handle = &emac->enet_handle;
RT_ASSERT(emac != RT_NULL);
DEBUG_PRINTF("k64_emac_rx\n");
/* Get the Frame size */
status = ENET_GetRxFrameSize(enet_handle, &length);
if (status == kStatus_ENET_RxFrameError)
{
/* Update the received buffer when error happened. */
/* Get the error information of the received g_frame. */
ENET_GetRxErrBeforeReadFrame(enet_handle, &eErrStatic);
/* update the receive buffer. */
ENET_ReadFrame(ENET, enet_handle, NULL, 0);
DEBUG_PRINTF("receive frame faild\n");
return p;
}
/* Call ENET_ReadFrame when there is a received frame. */
if (length != 0)
{
/* Received valid frame. Deliver the rx buffer with the size equal to length. */
p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
}
if (p != NULL)
{
status = ENET_ReadFrame(ENET, enet_handle, p->payload, length);
if (status == kStatus_Success)
{
#ifdef DRV_EMAC_RX_DUMP
uint8_t *buf;
int i;
DEBUG_PRINTF(" A frame received. the length:%d\n", p->len);
buf = (uint8_t *)p->payload;
for (i = 0; i < p->len; i++)
{
DEBUG_PRINTF("%02X ", buf[i]);
if (i % 16 == 15)
DEBUG_PRINTF("\n");
}
DEBUG_PRINTF("\n");
#endif
}
else
{
DEBUG_PRINTF(" A frame read failed\n");
pbuf_free(p);
}
}
return p;
}
int drv_emac_hw_init(void)
{
/* use the test MAC address */
_emac.dev_addr[0] = 0x00;
_emac.dev_addr[1] = 0x04;
_emac.dev_addr[2] = 0x9f;
_emac.dev_addr[3] = 0xc4;
_emac.dev_addr[4] = 0x44;
_emac.dev_addr[5] = 0x22;
_emac.parent.parent.init = k64_emac_init;
_emac.parent.parent.open = k64_emac_open;
_emac.parent.parent.close = k64_emac_close;
_emac.parent.parent.read = k64_emac_read;
_emac.parent.parent.write = k64_emac_write;
_emac.parent.parent.control = k64_emac_control;
_emac.parent.parent.user_data = RT_NULL;
_emac.parent.eth_rx = k64_emac_rx;
_emac.parent.eth_tx = k64_emac_tx;
/* init tx semaphore */
rt_sem_init(&_emac.tx_wait, "tx_wait", ENET_TX_RING_LEN - 1, RT_IPC_FLAG_FIFO);
/* register ETH device */
eth_device_init(&(_emac.parent), "e0");
return 0;
}
INIT_DEVICE_EXPORT(drv_emac_hw_init);
#ifdef DRV_EMAC_DEBUG
long k64_dump_tx_bd(void)
{
int i;
enet_tx_bd_struct_t *txbd = _emac.TxBuffDescrip;
for (i = 0; i < ENET_RX_RING_LEN; i++)
{
DEBUG_PRINTF("status: %04X, length: %04X, data: %08X\n", txbd[i].control, txbd[i].length, (uint32_t)txbd[i].buffer);
}
return 0;
}
FINSH_FUNCTION_EXPORT(k64_dump_tx_bd, dump all receive buffer descriptor);
MSH_CMD_EXPORT(k64_dump_tx_bd, dump all receive buffer descriptor);
long k64_dump_rx_bd(void)
{
int i;
enet_rx_bd_struct_t *rxbd = _emac.RxBuffDescrip;
for (i = 0; i < ENET_RX_RING_LEN; i++)
{
DEBUG_PRINTF("bd:%08X, ", (void *)&rxbd[i]);
//rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, (void *)&rxbd[i], sizeof(enet_rx_bd_struct_t));
DEBUG_PRINTF("status:%04X, length:%04X, data:%08X ", rxbd[i].control, rxbd[i].length, (uint32_t)rxbd[i].buffer);
#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE
DEBUG_PRINTF("ce:%04X/%04X/%04X ", rxbd[i].controlExtend0, rxbd[i].controlExtend1, rxbd[i].controlExtend2);
DEBUG_PRINTF("crc:%04X, len:%04X, type:%04X, ts:%04X", rxbd[i].payloadCheckSum, rxbd[i].headerLength, rxbd[i].protocolTyte, rxbd[i].timestamp);
#endif
DEBUG_PRINTF("\n");
}
return 0;
}
FINSH_FUNCTION_EXPORT(k64_dump_rx_bd, dump all receive buffer descriptor);
MSH_CMD_EXPORT(k64_dump_rx_bd, dump all receive buffer descriptor);
#endif
#endif