451 lines
13 KiB
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
|