/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-03-09 Carl the first version */ #include "board.h" #include #include "lwipopts.h" #include "ft_parameters.h" #include "ft_gmac.h" #include "ft_cache.h" #include "ft_gmac_hw.h" #include "ft_status.h" #include "ft_io.h" #include "drv_eth.h" #ifdef BSP_USING_GMAC #define LOG_TAG "drv.gmac" #include #define MAX_ADDR_LEN 6 #define LINK_THREAD_STACK_LENGTH 0x400 struct drv_gmac { struct eth_device parent; /* inherit from ethernet device */ Ft_Gmac_t Gmac; /* Gmac driver */ #ifndef PHY_USING_INTERRUPT_MODE rt_timer_t poll_link_timer; #endif rt_uint8_t *rx_buffer; /* Buffer for RxDesc */ rt_uint8_t *tx_buffer; /* Buffer for TxDesc */ uint32_t eth_speed; /* eth_speed */ uint32_t eth_mode; /* ETH_Duplex_Mode */ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* MAC address */ struct rt_event link_event; struct rt_thread _link_thread; rt_uint8_t _link_thread_stack[LINK_THREAD_STACK_LENGTH]; rt_thread_t _debug_tid; }; static void rt_ft2004_status_check(void *Args, u32 MacPhyStatus); // #if defined(RAW_DATA_PRINT) #if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP) #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen) { unsigned char *buf = (unsigned char *)ptr; int i, j; for (i = 0; i < buflen; i += 16) { rt_kprintf("%08X: ", i); for (j = 0; j < 16; j++) if (i + j < buflen) rt_kprintf("%02X ", buf[i + j]); else rt_kprintf(" "); rt_kprintf(" "); for (j = 0; j < 16; j++) if (i + j < buflen) rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); rt_kprintf("\n"); } } #endif #endif /** * @name: rt_gmacmem_create * @msg: Initialize the Gmac TX/Rx Describe Memory 。 * @param {*} * @return {*} */ static void rt_gmacmem_create(struct drv_gmac *pOsGmac) { pOsGmac->rx_buffer = rt_calloc(1, RX_DESCNUM * GMAC_MAX_PACKET_SIZE); if (pOsGmac->rx_buffer == NULL) { LOG_E("rx_buffer Malloc is error "); RT_ASSERT(0) } pOsGmac->tx_buffer = rt_calloc(1, TX_DESCNUM * GMAC_MAX_PACKET_SIZE); if (pOsGmac->tx_buffer == NULL) { LOG_E("tx_buffer Malloc is error "); RT_ASSERT(0) } pOsGmac->Gmac.TxDesc = rt_calloc(1, TX_DESCNUM * sizeof(FGmac_DmaDesc_t)); if (pOsGmac->Gmac.TxDesc == NULL) { LOG_E("TxDesc Malloc is error "); RT_ASSERT(0) } pOsGmac->Gmac.RxDesc = rt_calloc(1, RX_DESCNUM * sizeof(FGmac_DmaDesc_t) + 128); if (pOsGmac->Gmac.RxDesc == NULL) { LOG_E("RxDesc Malloc is error "); RT_ASSERT(0) } #define ROUND_UP(x, align) (((long)(x) + ((long)align - 1)) & \ ~((long)align - 1)) pOsGmac->Gmac.RxDesc = (FGmac_DmaDesc_t *)ROUND_UP(pOsGmac->Gmac.RxDesc, 128); LOG_D("RxDesc fit after addr %x ", pOsGmac->Gmac.RxDesc); } static void rt_gmacmem_free(struct drv_gmac *pOsGmac) { if (pOsGmac->rx_buffer) { rt_free(pOsGmac->rx_buffer); } if (pOsGmac->tx_buffer) { rt_free(pOsGmac->tx_buffer); } if (pOsGmac->Gmac.RxDesc) { rt_free(pOsGmac->Gmac.RxDesc); } if (pOsGmac->Gmac.TxDesc) { rt_free(pOsGmac->Gmac.TxDesc); } } static void rt_hw_gmac_isr(int irqno, void *param) { FGmac_IntrHandler(param); } static void rt_hw_gmac_recv_isr(void *Args) { struct drv_gmac *pOsMac; rt_err_t result = 0; if (RT_NULL == Args) { LOG_E("Args is NULL"); return; } pOsMac = (struct drv_gmac *)Args; result = eth_device_ready(&(pOsMac->parent)); if (result != RT_EOK) { LOG_I("RxCpltCallback err = %d", result); } } static rt_err_t rt_ft2004_gmac_start(struct drv_gmac *pOsMac) { Ft_Gmac_t *pGmac; pGmac = &pOsMac->Gmac; if (FST_SUCCESS != Ft_Gmac_HwInitialize(pGmac)) { return -RT_ERROR; } FGmac_SetHandler(pGmac, FT_GMAC_RX_COMPLETE_CB_ID, rt_hw_gmac_recv_isr, pOsMac); FGmac_SetHandler(pGmac, FT_GMAC_MAC_PHY_STATUS_CB_ID, rt_ft2004_status_check, pOsMac); /* Initialize Rx Description list : ring Mode */ FGmac_DmaRxDescRingInit(pGmac, pGmac->RxDesc, pOsMac->rx_buffer, GMAC_MAX_PACKET_SIZE, RX_DESCNUM); /* Initialize Tx Description list : ring Mode */ FGmac_DmaTxDescRingInit(pGmac, pGmac->TxDesc, pOsMac->tx_buffer, GMAC_MAX_PACKET_SIZE, TX_DESCNUM); Ft_Gmac_Start(pGmac); /* Gmac interrupt init */ rt_hw_interrupt_install(pGmac->Config.IRQ_NUM, rt_hw_gmac_isr, pGmac, "Gmac"); rt_hw_interrupt_umask(pGmac->Config.IRQ_NUM); return RT_EOK; } void rt_ft2004_gmac_stop(struct drv_gmac *pOsMac) { Ft_Gmac_t *pGmac; pGmac = &pOsMac->Gmac; Ft_Gmac_Stop(pGmac); } /* GMAC initialization function */ static rt_err_t rt_ft2004_gmac_init(rt_device_t dev) { struct drv_gmac *pOsMac; struct eth_device *pGmacParent; FGmac_Config_t *pConfig; pGmacParent = rt_container_of(dev, struct eth_device, parent); if (NULL == pGmacParent) { return -RT_ENOMEM; } pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent); if (NULL == pOsMac) { return -RT_ENOMEM; } pConfig = Ft_Gmac_LookupConfig(pOsMac->Gmac.Config.InstanceId); if (NULL == pConfig) { return -RT_ENOMEM; } Ft_Gmac_UseDefaultMacAddr(&pOsMac->Gmac, pOsMac->Gmac.Config.MacAddr); if (FST_SUCCESS != Ft_GmacCfgInitialize(&pOsMac->Gmac, pConfig)) { return -RT_ERROR; } return rt_ft2004_gmac_start(pOsMac); } static rt_err_t rt_ft2004_gmac_open(rt_device_t dev, rt_uint16_t oflag) { LOG_D("gmac open"); return RT_EOK; } static rt_err_t rt_ft2004_gmac_close(rt_device_t dev) { LOG_D("gmac close"); return RT_EOK; } static rt_size_t rt_ft2004_gmac_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { LOG_D("gmac read"); rt_set_errno(-RT_ENOSYS); return 0; } static rt_size_t rt_ft2004_gmac_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { LOG_D("gmac write"); rt_set_errno(-RT_ENOSYS); return 0; } static rt_err_t rt_ft2004_gmac_control(rt_device_t dev, int cmd, void *args) { struct drv_gmac *pOsMac; struct eth_device *pGmacParent; pGmacParent = rt_container_of(dev, struct eth_device, parent); if (NULL == pGmacParent) { return -RT_ENOMEM; } pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent); if (NULL == pOsMac) { return -RT_ENOMEM; } switch (cmd) { case NIOCTL_GADDR: /* get mac address */ if (args) rt_memcpy(args, pOsMac->dev_addr, 6); else return -RT_ERROR; break; default: break; } return RT_EOK; } rt_err_t rt_ft2004_gmac_tx(rt_device_t dev, struct pbuf *p) { struct drv_gmac *pOsMac; Ft_Gmac_t *pGmac; struct eth_device *pGmacParent; err_t errval; struct pbuf *q; u8 *Buffer = NULL; volatile FGmac_DmaDesc_t *DmaTxDesc; u32 FrameLength = 0; u32 BufferOffset = 0; u32 BytesLeftToCopy = 0; u32 PayLoadOffset = 0; pGmacParent = rt_container_of(dev, struct eth_device, parent); if (NULL == pGmacParent) { return -RT_ENOMEM; } pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent); if (NULL == pOsMac) { return -RT_ENOMEM; } pGmac = &pOsMac->Gmac; DmaTxDesc = &pGmac->TxDesc[pGmac->TxDescRingData.DescBufIndex]; Buffer = (u8 *)DmaTxDesc->Buffer1Addr; if (Buffer == NULL) { LOG_E("Buffer is NULL \r\n"); RT_ASSERT(0) } #if RT_LWIP_ETH_PAD_SIZE pbuf_header(p, -RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */ #endif for (q = p; q != NULL; q = q->next) { /* Is this buffer available? If not, goto error */ if ((DmaTxDesc->Status & DMA_TDES0_OWN) != 0) { errval = ERR_USE; LOG_E("error errval = ERR_USE; \r\n"); goto error; } /* Get bytes in current lwIP buffer */ BytesLeftToCopy = q->len; PayLoadOffset = 0; /* Check if the length of data to copy is bigger than Tx buffer size*/ while ((BytesLeftToCopy + BufferOffset) > GMAC_MAX_PACKET_SIZE) { /* Copy data to Tx buffer*/ memcpy((u8 *)((u8 *)Buffer + BufferOffset), (u8 *)((u8 *)q->payload + PayLoadOffset), (GMAC_MAX_PACKET_SIZE - BufferOffset)); FCache_cpuDcacheClean((rt_uint32_t *)DmaTxDesc->Buffer1Addr, GMAC_MAX_PACKET_SIZE); GMAC_INC_DESC(pGmac->TxDescRingData.DescBufIndex, pGmac->TxDescRingData.DescMaxNumber); /* Point to next descriptor */ DmaTxDesc = &pGmac->TxDesc[pGmac->TxDescRingData.DescBufIndex]; /* Check if the Bufferis available */ if ((DmaTxDesc->Status & DMA_TDES0_OWN) != (u32)0) { errval = ERR_USE; LOG_E("Check if the Bufferis available \r\n"); goto error; } Buffer = (u8 *)(DmaTxDesc->Buffer1Addr); BytesLeftToCopy = BytesLeftToCopy - (GMAC_MAX_PACKET_SIZE - BufferOffset); PayLoadOffset = PayLoadOffset + (GMAC_MAX_PACKET_SIZE - BufferOffset); FrameLength = FrameLength + (GMAC_MAX_PACKET_SIZE - BufferOffset); BufferOffset = 0; if (Buffer == NULL) { LOG_E(" error Buffer is 0 \r\n"); RT_ASSERT(0) } } /* Copy the remaining bytes */ memcpy((u8 *)((u8 *)Buffer + BufferOffset), (u8 *)((u8 *)q->payload + PayLoadOffset), BytesLeftToCopy); BufferOffset = BufferOffset + BytesLeftToCopy; FrameLength = FrameLength + BytesLeftToCopy; } /* 指向下一个位置 */ FCache_cpuDcacheClean((rt_uint32_t *)DmaTxDesc->Buffer1Addr, GMAC_MAX_PACKET_SIZE); GMAC_INC_DESC(pGmac->TxDescRingData.DescBufIndex, pGmac->TxDescRingData.DescMaxNumber); #if RT_LWIP_ETH_PAD_SIZE pbuf_header(p, RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */ #endif #ifdef ETH_TX_DUMP dump_hex(Buffer, p->tot_len); #endif FGmac_TransmitframeRingPoll(pGmac, FrameLength); error: FGmac_SetTransmitUnderflow(pGmac); return errval; } struct pbuf *rt_ft2004_gmac_rx(rt_device_t dev) { struct drv_gmac *pOsMac; Ft_Gmac_t *pGmac; struct eth_device *pGmacParent; struct pbuf *p = NULL; struct pbuf *q = NULL; u16 Length = 0; u8 *Buffer; volatile FGmac_DmaDesc_t *DmaRxDesc; u32 BufferOffset = 0; u32 PayLoadOffset = 0; u32 BytesLeftToCopy = 0; u32 DescBufIndex; /* For Current Desc buffer buf position */ pGmacParent = rt_container_of(dev, struct eth_device, parent); if (NULL == pGmacParent) { return RT_NULL; } pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent); if (NULL == pOsMac) { return RT_NULL; } pGmac = &pOsMac->Gmac; /* get received frame */ if (FST_SUCCESS != FGmac_RingGetReceivedFrame_IT(pGmac)) { return NULL; } DescBufIndex = pGmac->RxDescRingData.DescBufIndex; Length = (pGmac->RxDesc[DescBufIndex].Status & DMA_RDES0_FRAME_LEN_MASK) >> DMA_RDES0_FRAME_LEN_SHIFT; Buffer = (u8 *)pGmac->RxDesc[DescBufIndex].Buffer1Addr; #if RT_LWIP_ETH_PAD_SIZE Length += RT_LWIP_ETH_PAD_SIZE; /* allow room for Ethernet padding */ #endif if (Length > 0) { /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ p = pbuf_alloc(PBUF_RAW, Length, PBUF_POOL); } #ifdef ETH_RX_DUMP dump_hex(Buffer, (u32)Length); #endif if (p != NULL) { #if RT_LWIP_ETH_PAD_SIZE pbuf_header(p, -RT_LWIP_ETH_PAD_SIZE); /* drop the padding word */ #endif DmaRxDesc = &pGmac->RxDesc[DescBufIndex]; BufferOffset = 0; for (q = p; q != NULL; q = q->next) { BytesLeftToCopy = q->len; PayLoadOffset = 0; /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ while ((BytesLeftToCopy + BufferOffset) > GMAC_MAX_PACKET_SIZE) { /* Copy data to pbuf */ memcpy((u8 *)((u8 *)q->payload + PayLoadOffset), (u8 *)((u8 *)Buffer + BufferOffset), (GMAC_MAX_PACKET_SIZE - BufferOffset)); /* Point to next descriptor */ GMAC_INC_DESC(DescBufIndex, pGmac->RxDescRingData.DescMaxNumber); if (DescBufIndex == pGmac->RxDescRingData.DescIndex) { break; } DmaRxDesc = &pGmac->RxDesc[DescBufIndex]; Buffer = (u8 *)(DmaRxDesc->Buffer1Addr); BytesLeftToCopy = BytesLeftToCopy - (GMAC_MAX_PACKET_SIZE - BufferOffset); PayLoadOffset = PayLoadOffset + (GMAC_MAX_PACKET_SIZE - BufferOffset); BufferOffset = 0; } /* Copy remaining data in pbuf */ memcpy((u8 *)((u8 *)q->payload + PayLoadOffset), (u8 *)((u8 *)Buffer + BufferOffset), BytesLeftToCopy); BufferOffset = BufferOffset + BytesLeftToCopy; } #if RT_LWIP_ETH_PAD_SIZE pbuf_header(p, RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */ #endif } /* Release descriptors to DMA */ /* Point to first descriptor */ DmaRxDesc = &pGmac->RxDesc[DescBufIndex]; /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ for (DescBufIndex = pGmac->RxDescRingData.DescBufIndex; DescBufIndex != pGmac->RxDescRingData.DescIndex; GMAC_INC_DESC(DescBufIndex, pGmac->RxDescRingData.DescMaxNumber)) { FCache_cpuDcacheInvalidate((rt_uint32_t *)pGmac->RxDesc[DescBufIndex].Buffer1Addr, GMAC_MAX_PACKET_SIZE); DmaRxDesc->Status |= DMA_RDES0_OWN; DmaRxDesc = &pGmac->RxDesc[DescBufIndex]; } /* Sync index */ pGmac->RxDescRingData.DescBufIndex = pGmac->RxDescRingData.DescIndex; FGmac_ResumeTransmissionReception(pGmac); return p; } static void rt_ft2004_status_check(void *Args, u32 MacPhyStatus) { struct drv_gmac *pOsMac; pOsMac = (struct drv_gmac *)Args; if (MacPhyStatus & 0x8) { rt_event_send(&pOsMac->link_event, FT_NETIF_LINKUP); } else { rt_event_send(&pOsMac->link_event, FT_NETIF_DOWN); } } static void ethernet_link_thread(void *Args) { struct drv_gmac *pOsMac; rt_uint32_t status; u32 LastStatus = FT_NETIF_DOWN; u32 Flg; if (RT_NULL == Args) { return; } pOsMac = (struct drv_gmac *)Args; while (1) { status = 0; if (rt_event_recv(&pOsMac->link_event, FT_NETIF_LINKUP | FT_NETIF_DOWN, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, &status) != RT_EOK) { LOG_E("wait completed timeout"); continue; } if (status & FT_NETIF_DOWN) { eth_device_linkchange(&pOsMac->parent, RT_FALSE); LastStatus = FT_NETIF_DOWN; } else if (status & FT_NETIF_LINKUP) { Flg = (LastStatus == FT_NETIF_LINKUP) ? 0 : 1; LastStatus = FT_NETIF_LINKUP; } else { LOG_I(" EventGroup is error \r\n"); RT_ASSERT(0) } if (Flg) { Flg = 0; // eth_device_linkchange(&pOsMac->parent, RT_FALSE); LOG_I(" Start Linkup \r\n"); rt_ft2004_gmac_stop(pOsMac); rt_ft2004_gmac_start(pOsMac); LOG_I(" HardWare is ok \r\n"); if (LastStatus == FT_NETIF_LINKUP) { rt_thread_mdelay(5000); eth_device_linkchange(&pOsMac->parent, RT_TRUE); } } } } #ifdef BSP_USING_GMAC0 struct drv_gmac os_drv_gmac0; static char *os_drv_gmac0_name = "gmac0"; #endif #ifdef BSP_USING_GMAC1 struct drv_gmac os_drv_gmac1; static char *os_drv_gmac1_name = "gmac1"; #endif static int rt_hw_gmac_init(struct drv_gmac *pOsMac, const char *name) { rt_err_t state = RT_EOK; // rt_thread_t tid; rt_gmacmem_free(pOsMac); rt_gmacmem_create(pOsMac); pOsMac->eth_speed = GMAC_SPEED_1000M; pOsMac->eth_mode = GMAC_MODE_FULLDUPLEX; pOsMac->parent.parent.init = rt_ft2004_gmac_init; pOsMac->parent.parent.open = rt_ft2004_gmac_open; pOsMac->parent.parent.close = rt_ft2004_gmac_close; pOsMac->parent.parent.read = rt_ft2004_gmac_read; pOsMac->parent.parent.write = rt_ft2004_gmac_write; pOsMac->parent.parent.control = rt_ft2004_gmac_control; pOsMac->parent.parent.user_data = RT_NULL; pOsMac->parent.eth_rx = rt_ft2004_gmac_rx; pOsMac->parent.eth_tx = rt_ft2004_gmac_tx; Ft_Gmac_UseDefaultMacAddr(&pOsMac->Gmac, pOsMac->dev_addr); state = rt_event_init(&pOsMac->link_event, name, RT_IPC_FLAG_FIFO); LOG_I("rt_event_init is ok \r\n"); if (RT_EOK != state) { rt_kprintf("init gmac0 event failed.\n"); return -RT_ERROR; } /* register eth device */ state = eth_device_init(&(pOsMac->parent), name); if (RT_EOK != state) { LOG_E("gmac device init faild: %d", state); return -RT_ERROR; } state = rt_thread_init(&pOsMac->_link_thread, name, ethernet_link_thread, pOsMac, &pOsMac->_link_thread_stack[0], sizeof(pOsMac->_link_thread_stack), 10, 2); if (RT_EOK == state) { rt_thread_startup(&pOsMac->_link_thread); } else { LOG_E("rt_thread_init is error"); return -RT_ERROR; } return RT_EOK; } static int rt_hw_ft2004_eth_init(void) { rt_err_t state = RT_EOK; #ifdef BSP_USING_GMAC0 os_drv_gmac0.Gmac.Config.InstanceId = 0; state = rt_hw_gmac_init(&os_drv_gmac0, os_drv_gmac0_name); if (RT_EOK != state) { goto __exit; } #endif #ifdef BSP_USING_GMAC1 os_drv_gmac1.Gmac.Config.InstanceId = 1; state = rt_hw_gmac_init(&os_drv_gmac1, os_drv_gmac1_name); if (RT_EOK != state) { goto __exit; } #endif __exit: return state; } INIT_DEVICE_EXPORT(rt_hw_ft2004_eth_init); #endif