/* * 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 #include #include "drv_emac.h" #if defined(RT_USING_LWIP) #include #include #include #include #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