Browse Source

[bsp/cvitek]add eth driver

flyingcys 1 year ago
parent
commit
7890e2cb14

+ 1 - 0
bsp/cvitek/README.md

@@ -64,6 +64,7 @@ $ scons
 | timer | 支持 |  |
 | wdt | 支持 |  |
 | sdio | 支持 |  |
+| eth | 支持 |  |
 
 ## 支持开发板
 - milk-v duo: [https://milkv.io/duo](https://milkv.io/duo)

+ 87 - 8
bsp/cvitek/cv18xx_risc-v/.config

@@ -108,7 +108,7 @@ CONFIG_RT_USING_FINSH=y
 CONFIG_FINSH_USING_MSH=y
 CONFIG_FINSH_THREAD_NAME="tshell"
 CONFIG_FINSH_THREAD_PRIORITY=20
-CONFIG_FINSH_THREAD_STACK_SIZE=4096
+CONFIG_FINSH_THREAD_STACK_SIZE=8192
 CONFIG_FINSH_USING_HISTORY=y
 CONFIG_FINSH_HISTORY_LINES=5
 CONFIG_FINSH_USING_SYMTAB=y
@@ -196,9 +196,9 @@ CONFIG_RT_USING_RTC=y
 # CONFIG_RT_USING_ALARM is not set
 # CONFIG_RT_USING_SOFT_RTC is not set
 CONFIG_RT_USING_SDIO=y
-CONFIG_RT_SDIO_STACK_SIZE=4096
+CONFIG_RT_SDIO_STACK_SIZE=8192
 CONFIG_RT_SDIO_THREAD_PRIORITY=15
-CONFIG_RT_MMCSD_STACK_SIZE=4096
+CONFIG_RT_MMCSD_STACK_SIZE=8192
 CONFIG_RT_MMCSD_THREAD_PREORITY=22
 CONFIG_RT_MMCSD_MAX_PARTITION=16
 # CONFIG_RT_SDIO_DEBUG is not set
@@ -252,10 +252,10 @@ CONFIG_RT_USING_POSIX_FS=y
 CONFIG_RT_USING_POSIX_DEVIO=y
 CONFIG_RT_USING_POSIX_STDIO=y
 CONFIG_RT_USING_POSIX_POLL=y
-# CONFIG_RT_USING_POSIX_SELECT is not set
+CONFIG_RT_USING_POSIX_SELECT=y
 # CONFIG_RT_USING_POSIX_EVENTFD is not set
 # CONFIG_RT_USING_POSIX_TIMERFD is not set
-# CONFIG_RT_USING_POSIX_SOCKET is not set
+CONFIG_RT_USING_POSIX_SOCKET=y
 CONFIG_RT_USING_POSIX_TERMIOS=y
 # CONFIG_RT_USING_POSIX_AIO is not set
 # CONFIG_RT_USING_POSIX_MMAN is not set
@@ -284,9 +284,87 @@ CONFIG_RT_USING_POSIX_TIMER=y
 #
 # Network
 #
-# CONFIG_RT_USING_SAL is not set
-# CONFIG_RT_USING_NETDEV is not set
-# CONFIG_RT_USING_LWIP is not set
+CONFIG_RT_USING_SAL=y
+CONFIG_SAL_INTERNET_CHECK=y
+
+#
+# Docking with protocol stacks
+#
+CONFIG_SAL_USING_LWIP=y
+# CONFIG_SAL_USING_AT is not set
+# CONFIG_SAL_USING_TLS is not set
+# end of Docking with protocol stacks
+
+CONFIG_SAL_USING_POSIX=y
+CONFIG_RT_USING_NETDEV=y
+CONFIG_NETDEV_USING_IFCONFIG=y
+CONFIG_NETDEV_USING_PING=y
+CONFIG_NETDEV_USING_NETSTAT=y
+CONFIG_NETDEV_USING_AUTO_DEFAULT=y
+# CONFIG_NETDEV_USING_IPV6 is not set
+CONFIG_NETDEV_IPV4=1
+CONFIG_NETDEV_IPV6=0
+CONFIG_RT_USING_LWIP=y
+# CONFIG_RT_USING_LWIP_LOCAL_VERSION is not set
+# CONFIG_RT_USING_LWIP141 is not set
+# CONFIG_RT_USING_LWIP203 is not set
+CONFIG_RT_USING_LWIP212=y
+# CONFIG_RT_USING_LWIP_LATEST is not set
+CONFIG_RT_USING_LWIP_VER_NUM=0x20102
+# CONFIG_RT_USING_LWIP_IPV6 is not set
+CONFIG_RT_LWIP_MEM_ALIGNMENT=8
+CONFIG_RT_LWIP_IGMP=y
+CONFIG_RT_LWIP_ICMP=y
+# CONFIG_RT_LWIP_SNMP is not set
+CONFIG_RT_LWIP_DNS=y
+CONFIG_RT_LWIP_DHCP=y
+CONFIG_IP_SOF_BROADCAST=1
+CONFIG_IP_SOF_BROADCAST_RECV=1
+
+#
+# Static IPv4 Address
+#
+CONFIG_RT_LWIP_IPADDR="192.168.1.30"
+CONFIG_RT_LWIP_GWADDR="192.168.1.1"
+CONFIG_RT_LWIP_MSKADDR="255.255.255.0"
+# end of Static IPv4 Address
+
+CONFIG_RT_LWIP_UDP=y
+CONFIG_RT_LWIP_TCP=y
+CONFIG_RT_LWIP_RAW=y
+# CONFIG_RT_LWIP_PPP is not set
+CONFIG_RT_MEMP_NUM_NETCONN=8
+CONFIG_RT_LWIP_PBUF_NUM=16
+CONFIG_RT_LWIP_RAW_PCB_NUM=4
+CONFIG_RT_LWIP_UDP_PCB_NUM=4
+CONFIG_RT_LWIP_TCP_PCB_NUM=4
+CONFIG_RT_LWIP_TCP_SEG_NUM=40
+CONFIG_RT_LWIP_TCP_SND_BUF=8196
+CONFIG_RT_LWIP_TCP_WND=8196
+CONFIG_RT_LWIP_TCPTHREAD_PRIORITY=10
+CONFIG_RT_LWIP_TCPTHREAD_MBOX_SIZE=8
+CONFIG_RT_LWIP_TCPTHREAD_STACKSIZE=8192
+# CONFIG_LWIP_NO_RX_THREAD is not set
+# CONFIG_LWIP_NO_TX_THREAD is not set
+CONFIG_RT_LWIP_ETHTHREAD_PRIORITY=12
+CONFIG_RT_LWIP_ETHTHREAD_STACKSIZE=8192
+CONFIG_RT_LWIP_ETHTHREAD_MBOX_SIZE=8
+# CONFIG_RT_LWIP_REASSEMBLY_FRAG is not set
+CONFIG_LWIP_NETIF_STATUS_CALLBACK=1
+CONFIG_LWIP_NETIF_LINK_CALLBACK=1
+CONFIG_RT_LWIP_NETIF_NAMESIZE=6
+CONFIG_SO_REUSE=1
+CONFIG_LWIP_SO_RCVTIMEO=1
+CONFIG_LWIP_SO_SNDTIMEO=1
+CONFIG_LWIP_SO_RCVBUF=1
+CONFIG_LWIP_SO_LINGER=0
+# CONFIG_RT_LWIP_NETIF_LOOPBACK is not set
+CONFIG_LWIP_NETIF_LOOPBACK=0
+# CONFIG_RT_LWIP_STATS is not set
+# CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set
+CONFIG_RT_LWIP_USING_PING=y
+# CONFIG_LWIP_USING_DHCPD is not set
+# CONFIG_RT_LWIP_DEBUG is not set
 # CONFIG_RT_USING_AT is not set
 # end of Network
 
@@ -1205,6 +1283,7 @@ CONFIG_UART_IRQ_BASE=44
 # CONFIG_BSP_USING_SPI is not set
 # CONFIG_BSP_USING_PWM is not set
 # CONFIG_BSP_USING_SDH is not set
+# CONFIG_BSP_USING_ETH is not set
 # end of General Drivers Configuration
 
 CONFIG_BSP_USING_CV18XX=y

+ 7 - 1
bsp/cvitek/cv18xx_risc-v/board/Kconfig

@@ -73,5 +73,11 @@ menu "General Drivers Configuration"
         select RT_USING_DFS_ROMFS
         bool "Enable Secure Digital Host Controller"
         default n
-        
+
+    config BSP_USING_ETH
+        bool "Enable Ethernet"
+        default n
+        select RT_USING_LWIP
+        select RT_USING_POSIX_FS
+        select RT_USING_POSIX_SOCKET
 endmenu

+ 64 - 3
bsp/cvitek/cv18xx_risc-v/rtconfig.h

@@ -74,7 +74,7 @@
 #define FINSH_USING_MSH
 #define FINSH_THREAD_NAME "tshell"
 #define FINSH_THREAD_PRIORITY 20
-#define FINSH_THREAD_STACK_SIZE 4096
+#define FINSH_THREAD_STACK_SIZE 8192
 #define FINSH_USING_HISTORY
 #define FINSH_HISTORY_LINES 5
 #define FINSH_USING_SYMTAB
@@ -127,9 +127,9 @@
 #define RT_USING_RANDOM
 #define RT_USING_RTC
 #define RT_USING_SDIO
-#define RT_SDIO_STACK_SIZE 4096
+#define RT_SDIO_STACK_SIZE 8192
 #define RT_SDIO_THREAD_PRIORITY 15
-#define RT_MMCSD_STACK_SIZE 4096
+#define RT_MMCSD_STACK_SIZE 8192
 #define RT_MMCSD_THREAD_PREORITY 22
 #define RT_MMCSD_MAX_PARTITION 16
 #define RT_USING_PIN
@@ -159,6 +159,8 @@
 #define RT_USING_POSIX_DEVIO
 #define RT_USING_POSIX_STDIO
 #define RT_USING_POSIX_POLL
+#define RT_USING_POSIX_SELECT
+#define RT_USING_POSIX_SOCKET
 #define RT_USING_POSIX_TERMIOS
 #define RT_USING_POSIX_DELAY
 #define RT_USING_POSIX_CLOCK
@@ -175,6 +177,65 @@
 
 /* Network */
 
+#define RT_USING_SAL
+#define SAL_INTERNET_CHECK
+
+/* Docking with protocol stacks */
+
+#define SAL_USING_LWIP
+/* end of Docking with protocol stacks */
+#define SAL_USING_POSIX
+#define RT_USING_NETDEV
+#define NETDEV_USING_IFCONFIG
+#define NETDEV_USING_PING
+#define NETDEV_USING_NETSTAT
+#define NETDEV_USING_AUTO_DEFAULT
+#define NETDEV_IPV4 1
+#define NETDEV_IPV6 0
+#define RT_USING_LWIP
+#define RT_USING_LWIP212
+#define RT_USING_LWIP_VER_NUM 0x20102
+#define RT_LWIP_MEM_ALIGNMENT 8
+#define RT_LWIP_IGMP
+#define RT_LWIP_ICMP
+#define RT_LWIP_DNS
+#define RT_LWIP_DHCP
+#define IP_SOF_BROADCAST 1
+#define IP_SOF_BROADCAST_RECV 1
+
+/* Static IPv4 Address */
+
+#define RT_LWIP_IPADDR "192.168.1.30"
+#define RT_LWIP_GWADDR "192.168.1.1"
+#define RT_LWIP_MSKADDR "255.255.255.0"
+/* end of Static IPv4 Address */
+#define RT_LWIP_UDP
+#define RT_LWIP_TCP
+#define RT_LWIP_RAW
+#define RT_MEMP_NUM_NETCONN 8
+#define RT_LWIP_PBUF_NUM 16
+#define RT_LWIP_RAW_PCB_NUM 4
+#define RT_LWIP_UDP_PCB_NUM 4
+#define RT_LWIP_TCP_PCB_NUM 4
+#define RT_LWIP_TCP_SEG_NUM 40
+#define RT_LWIP_TCP_SND_BUF 8196
+#define RT_LWIP_TCP_WND 8196
+#define RT_LWIP_TCPTHREAD_PRIORITY 10
+#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8
+#define RT_LWIP_TCPTHREAD_STACKSIZE 8192
+#define RT_LWIP_ETHTHREAD_PRIORITY 12
+#define RT_LWIP_ETHTHREAD_STACKSIZE 8192
+#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8
+#define LWIP_NETIF_STATUS_CALLBACK 1
+#define LWIP_NETIF_LINK_CALLBACK 1
+#define RT_LWIP_NETIF_NAMESIZE 6
+#define SO_REUSE 1
+#define LWIP_SO_RCVTIMEO 1
+#define LWIP_SO_SNDTIMEO 1
+#define LWIP_SO_RCVBUF 1
+#define LWIP_SO_LINGER 0
+#define LWIP_NETIF_LOOPBACK 0
+#define RT_LWIP_USING_PING
 /* end of Network */
 
 /* Memory protection */

+ 10 - 0
bsp/cvitek/drivers/SConscript

@@ -41,6 +41,16 @@ if GetDepend('BSP_USING_SDH'):
     src += ['drv_sdhci.c', 'port/mnt.c']
     CPPPATH += [cwd + r'/libraries/sdif']
 
+
+if GetDepend('BSP_USING_ETH'):
+    src += Split('''
+        drv_eth.c
+        libraries/eth/dw_eth_mac.c
+        libraries/eth/cvi_eth_phy.c
+        libraries/eth/eth_phy_cvitek.c
+    ''')
+    CPPPATH += [cwd + r'/libraries/eth']
+
 CPPDEFINES += ['-DCONFIG_64BIT']
 
 if GetDepend('BSP_USING_RTC'):

+ 411 - 0
bsp/cvitek/drivers/drv_eth.c

@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2024/04/25     flyingcys    first version
+ */
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include "board.h"
+
+#define DBG_TAG     "drv.eth"
+#define DBG_LEVEL   DBG_INFO
+#include <rtdbg.h>
+
+#include <lwip/sys.h>
+#include <netif/ethernetif.h>
+
+#include "drv_eth.h"
+
+
+// #define ETH_TX_DUMP
+// #define ETH_RX_DUMP
+
+#define MAX_ADDR_LEN        6
+
+struct _dw_eth
+{
+    rt_uint32_t *base;
+    rt_uint32_t irq;
+
+    struct eth_device parent;               /* inherit from ethernet device */
+
+    rt_uint8_t dev_addr[MAX_ADDR_LEN];      /* interface address info, hw address */
+
+    struct rt_semaphore rx_sem;
+};
+static struct _dw_eth dw_eth_device = {0};
+
+#define GMAC_BUF_LEN (1500 + 20)
+static uint8_t g_mac_addr[6] = {0xf2, 0x42, 0x9f, 0xa5, 0x0a, 0x72};
+static uint8_t g_mac_phy_init_finish = 0;
+static eth_mac_handle_t g_mac_handle;
+static eth_phy_handle_t g_phy_handle;
+
+static uint8_t SendDataBuf[GMAC_BUF_LEN];
+static uint8_t RecvDataBuf[GMAC_BUF_LEN];
+
+static void cvi_ephy_id_init(void)
+{
+    // set rg_ephy_apb_rw_sel 0x0804@[0]=1/APB by using APB interface
+    mmio_write_32(0x03009804, 0x0001);
+
+    // Release 0x0800[0]=0/shutdown
+    mmio_write_32(0x03009800, 0x0900);
+
+    // Release 0x0800[2]=1/dig_rst_n, Let mii_reg can be accessabile
+    mmio_write_32(0x03009800, 0x0904);
+
+    // PHY_ID
+    mmio_write_32(0x03009008, 0x0043);
+    mmio_write_32(0x0300900c, 0x5649);
+
+    // switch to MDIO control by ETH_MAC
+    mmio_write_32(0x03009804, 0x0000);
+}
+
+static int cvi_eth_mac_phy_enable(uint32_t enable)
+{
+    eth_mac_addr_t addr;
+    int32_t ret;
+
+    if ((g_mac_phy_init_finish == 0) && enable)
+    {
+        /* startup mac */
+        ret = cvi_eth_mac_control(g_mac_handle, CSI_ETH_MAC_CONFIGURE, 1);
+        if (ret != 0)
+        {
+           LOG_E("Failed to control mac");
+            return -1;
+        }
+
+        /* Start up the PHY */
+        ret = cvi_eth_phy_power_control(g_phy_handle, CSI_ETH_POWER_FULL);
+        if (ret != 0)
+        {
+           LOG_E("Failed to control phy, ret:0x%d", ret);
+            return -1;
+        }
+    }
+
+    /* enable mac TX/RX */
+    ret = cvi_eth_mac_control(g_mac_handle, CSI_ETH_MAC_CONTROL_TX, enable ? 1 : 0);
+    if (ret != 0)
+    {
+       LOG_E("Failed to enable mac TX");
+       return ret;
+    }
+
+    ret = cvi_eth_mac_control(g_mac_handle, CSI_ETH_MAC_CONTROL_RX, enable ? 1 : 0);
+    if (ret != 0)
+    {
+       LOG_E("Failed to enable mac RX");
+       return ret;
+    }
+
+    /* set mac address */
+    memcpy(addr.b, g_mac_addr, sizeof(g_mac_addr));
+    ret = cvi_eth_mac_set_macaddr(g_mac_handle, &addr);
+    if (ret != 0)
+    {
+       LOG_E("Failed to set mac address");
+        return -1;
+    }
+
+    /* adjust mac link parameter */
+    ret = cvi_eth_mac_control(g_mac_handle, DRV_ETH_MAC_ADJUST_LINK, 1);
+    if (ret != 0)
+    {
+       LOG_E("Failed to adjust link");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int32_t fn_phy_read(uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
+{
+    return dw_eth_mac_phy_read(g_mac_handle, phy_addr, reg_addr, data);
+}
+
+static int32_t fn_phy_write(uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
+{
+    return dw_eth_mac_phy_write(g_mac_handle, phy_addr, reg_addr, data);
+}
+
+static void dw_gmac_handler_irq(int vector, void *param)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)param;
+    struct dw_gmac_dma_regs *dma_reg = mac_dev->priv->dma_regs_p;
+    uint32_t dma_status;
+    uint32_t event = 0;
+
+    /* no ephy or ephy link down */
+    if (!mac_dev->phy_dev || !mac_dev->phy_dev->link_state)
+        return;
+
+    /* read and clear dma interrupt */
+    dma_status = dma_reg->status;
+
+    /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
+    dma_reg->status = dma_status & 0x1ffff;
+
+    if (dma_status & CVI_DMA_STATUS_RI)
+    {
+        LOG_D("CVI_DMA_STATUS_RI");
+
+        /* a frame has been received */
+        eth_device_ready(&(dw_eth_device.parent));
+    }
+
+    if (dma_status & CVI_DMA_STATUS_TI)
+    {
+        LOG_D("CVI_DMA_STATUS_TI");
+    }
+
+    if (dma_status & CVI_DMA_STATUS_ERI)
+    {
+        LOG_D("CVI_DMA_STATUS_TI");
+    }
+}
+
+static rt_err_t rt_dw_eth_init(rt_device_t dev)
+{
+    struct _dw_eth *dw_eth;
+    struct eth_device *eth_dev;
+
+    RT_ASSERT(dev != RT_NULL);
+    eth_dev = rt_container_of(dev, struct eth_device, parent);
+    if (eth_dev == RT_NULL)
+        return -RT_ERROR;
+
+    dw_eth = rt_container_of(eth_dev, struct _dw_eth, parent);
+    if (dw_eth == RT_NULL)
+        return -RT_ERROR;
+
+    /* init phy id */
+    cvi_ephy_id_init();
+
+    /* initialize MAC & PHY */
+    g_mac_handle = cvi_eth_mac_init(dw_eth->base);
+    if (g_mac_handle == NULL)
+        return -RT_ERROR;
+
+    g_phy_handle = cvi_eth_phy_init(fn_phy_read, fn_phy_write);
+
+    dw_eth_mac_connect_phy(g_mac_handle, g_phy_handle);
+
+    if (cvi_eth_mac_phy_enable(1))
+    {
+       LOG_E("PHY MAC init fail");
+        return -RT_ERROR;
+    }
+
+    rt_hw_interrupt_install(dw_eth->irq, dw_gmac_handler_irq, g_mac_handle, "e0");
+    rt_hw_interrupt_umask(dw_eth->irq);
+
+    /* change device link status */
+    eth_device_linkchange(&(dw_eth_device.parent), RT_TRUE);
+
+    return RT_EOK;
+}
+
+static rt_err_t rt_dw_eth_control(rt_device_t dev, int cmd, void *args)
+{
+    switch (cmd)
+    {
+        case NIOCTL_GADDR:
+            if (args)
+                rt_memcpy(args, g_mac_addr, MAX_ADDR_LEN);
+        break;
+
+        default:
+
+        break;
+    }
+
+    return RT_EOK;
+}
+
+#if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
+static void packet_dump(const char *msg, const struct pbuf *p)
+{
+    const struct pbuf *q;
+    rt_uint32_t i, j;
+    rt_uint8_t *ptr;
+
+    rt_kprintf("%s %d byte\n", msg, p->tot_len);
+
+    i = 0;
+    for (q = p; q != RT_NULL; q = q->next)
+    {
+        ptr = q->payload;
+
+        for (j = 0; j < q->len; j++)
+        {
+            if ((i % 8) == 0)
+            {
+                rt_kprintf("  ");
+            }
+
+            if ((i % 16) == 0)
+            {
+                rt_kprintf("\r\n");
+            }
+            rt_kprintf("%02x ", *ptr);
+
+            i ++;
+            ptr ++;
+        }
+    }
+
+    rt_kprintf("\n\n");
+}
+#endif
+
+struct pbuf* rt_dw_eth_rx(rt_device_t dev)
+{
+    struct pbuf *p = NULL;
+    struct pbuf *q = NULL;
+    uint32_t i = 0;
+
+    int32_t len = cvi_eth_mac_read_frame(g_mac_handle, RecvDataBuf, GMAC_BUF_LEN);
+    if((len <= 0) || (len > GMAC_BUF_LEN))
+    {
+        return NULL;
+    }
+
+#if RT_LWIP_ETH_PAD_SIZE
+  len += RT_LWIP_ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+    if (p == NULL)
+    {
+        LOG_E("eth_rx: pbuf_alloc failed");
+        len = 0;
+        return NULL;
+    }
+
+#if RT_LWIP_ETH_PAD_SIZE
+    pbuf_header(p, -RT_LWIP_ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+    /* We iterate over the pbuf chain until we have read the entire
+     * packet into the pbuf. */
+    for (q = p; q != NULL; q = q->next)
+    {
+        /* Read enough bytes to fill this pbuf in the chain. The
+        * available data in the pbuf is given by the q->len
+        * variable.
+        * This does not necessarily have to be a memcpy, you can also preallocate
+        * pbufs for a DMA-enabled MAC and after receiving truncate it to the
+        * actually received size. In this case, ensure the tot_len member of the
+        * pbuf is the sum of the chained pbuf len members.
+        */
+        memcpy((u8_t*)q->payload, (u8_t*)&RecvDataBuf[i], q->len);
+        i = i + q->len;
+    }
+
+    if((i != p->tot_len) || (i > len))
+    {
+        return NULL;
+    }
+
+#ifdef ETH_RX_DUMP
+    packet_dump("RX dump", p);
+#endif /* ETH_RX_DUMP */
+
+    return p;
+
+}
+
+rt_err_t rt_dw_eth_tx(rt_device_t dev, struct pbuf* p)
+{
+    rt_err_t ret = RT_EOK;
+
+#ifdef ETH_TX_DUMP
+    packet_dump("send", p);
+#endif
+
+    struct pbuf *q;
+    uint32_t len = 0;
+
+
+#if RT_LWIP_ETH_PAD_SIZE
+    pbuf_header(p, -RT_LWIP_ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+    for (q = p; q != NULL; q = q->next)
+    {
+        /* Send the data from the pbuf to the interface, one pbuf at a
+        time. The size of the data in each pbuf is kept in the ->len
+        variable. */
+        MEMCPY((uint8_t *)&SendDataBuf[len], (uint8_t *)q->payload, q->len);
+        len = len + q->len;
+        if((len > GMAC_BUF_LEN) || (len > p->tot_len))
+        {
+            LOG_E("rt_dw_eth_tx: error, len=%d, tot_len=%d", len, p->tot_len);
+            return -RT_ERROR;
+        }
+    }
+
+    if(len == p->tot_len)
+    {
+        if (cvi_eth_mac_send_frame(g_mac_handle, SendDataBuf, len) < 0)
+            ret = -RT_ERROR;
+    }
+    else
+        ret = -RT_ERROR;
+
+#if RT_LWIP_ETH_PAD_SIZE
+    pbuf_header(p, RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+    return ret;
+}
+
+const static struct rt_device_ops dw_eth_ops =
+{
+    rt_dw_eth_init,
+    RT_NULL,
+    RT_NULL,
+    RT_NULL,
+    RT_NULL,
+    rt_dw_eth_control
+};
+
+static int rthw_eth_init(void)
+{
+    rt_err_t ret = RT_EOK;
+
+    dw_eth_device.base = (rt_uint32_t *)DW_MAC_BASE;
+    dw_eth_device.irq = DW_MAC_IRQ;
+
+    dw_eth_device.parent.parent.ops = &dw_eth_ops;
+
+    dw_eth_device.parent.eth_rx = rt_dw_eth_rx;
+    dw_eth_device.parent.eth_tx = rt_dw_eth_tx;
+
+    ret = rt_sem_init(&dw_eth_device.rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
+    if (ret != RT_EOK)
+    {
+        LOG_E("rt_sem_init failed: %d", ret);
+        return ret;
+    }
+
+    ret = eth_device_init(&dw_eth_device.parent, "e0");
+    if (ret != RT_EOK)
+    {
+        LOG_E("eth_device_init failed: %d", ret);
+        return ret;
+    }
+
+    return RT_EOK;
+}
+INIT_DEVICE_EXPORT(rthw_eth_init);

+ 20 - 0
bsp/cvitek/drivers/drv_eth.h

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2024/04/25     flyingcys    first version
+ */
+#ifndef __DRV_ETH_H__
+#define __DRV_ETH_H__
+
+#include "mmio.h"
+#include "dw_eth_mac.h"
+#include "cvi_eth_phy.h"
+
+#define DW_MAC_BASE                 0x04070000UL
+#define DW_MAC_IRQ                  31
+
+#endif /* __DRV_ETH_H__ */

+ 606 - 0
bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.c

@@ -0,0 +1,606 @@
+/*
+* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
+*/
+#include <rtthread.h>
+
+#include "cvi_eth_phy.h"
+
+#define CSI_ETH_AUTONEG_DISABLE         (0)     ///< Disable auto-negotiation
+#define CSI_ETH_AUTONEG_ENABLE          (1)     ///< Enable auto-negotiation
+
+#define CONFIG_ETH_PHY_NUM 2
+
+eth_phy_priv_t  phy_priv_list[CONFIG_ETH_PHY_NUM];
+
+extern eth_phy_dev_t cv181x_device;
+
+/* registered phy devices */
+static eth_phy_dev_t *const eth_phy_devices[] = {
+    &cv181x_device,
+    NULL /* Must be the last item */
+};
+
+int32_t eth_phy_read(eth_phy_priv_t *priv, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
+{
+    RT_ASSERT(priv);
+    RT_ASSERT(priv->phy_read);
+    return priv->phy_read(phy_addr, reg_addr, data);
+}
+
+int32_t eth_phy_write(eth_phy_priv_t *priv, uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
+{
+    RT_ASSERT(priv);
+    RT_ASSERT(priv->phy_write);
+    return priv->phy_write(phy_addr, reg_addr, data);
+}
+
+static eth_phy_dev_t *eth_get_phy_device(eth_phy_priv_t *priv, uint8_t phy_addr, uint32_t phy_id)
+{
+    eth_phy_dev_t *p = eth_phy_devices[0];
+    int32_t i = 0;
+
+    while (p != NULL)
+    {
+        if ((p->phy_id & p->mask) == (phy_id & p->mask))
+        {
+            p->phy_addr = phy_addr;
+            p->advertising = p->supported = p->features;
+            return p;
+        }
+
+        i ++;
+        p = eth_phy_devices[i];
+    }
+
+    return NULL;
+}
+
+static int32_t eth_read_phy_id(eth_phy_priv_t *priv, uint8_t phy_addr, uint32_t *phy_id)
+{
+    int32_t ret;
+    uint16_t data;
+    uint32_t id;
+
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_PHYSID1, &data);
+
+    if (ret != 0)
+    {
+        return ret;
+    }
+
+    id = data;
+    id = (id & 0xffff) << 16;
+
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_PHYSID2, &data);
+
+    if (ret != 0)
+    {
+        return ret;
+    }
+
+    id |= (data & 0xffff);
+
+    if (phy_id != NULL)
+    {
+        *phy_id = id;
+    }
+
+    return 0;
+}
+
+static eth_phy_dev_t * eth_get_phy_by_mask(eth_phy_priv_t *priv, uint32_t phy_mask, phy_if_mode_t interface)
+{
+    uint32_t phy_id = 0xffffffff;
+
+    while (phy_mask)
+    {
+        int32_t addr = ffs(phy_mask) - 1;
+        int32_t r = eth_read_phy_id(priv, addr, &phy_id);
+
+        /* If the PHY ID is mostly f's, we didn't find anything */
+        if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff)
+            return eth_get_phy_device(priv, addr, phy_id);
+
+        phy_mask &= ~(1 << addr);
+    }
+    return NULL;
+}
+
+static void eth_config(void)
+{
+    unsigned int val;
+
+    val = mmio_read_32(ETH_PHY_BASE) & ETH_PHY_INIT_MASK;
+    mmio_write_32(ETH_PHY_BASE, (val | ETH_PHY_SHUTDOWN) & ETH_PHY_RESET);
+    rt_thread_mdelay(1);
+    mmio_write_32(ETH_PHY_BASE, val & ETH_PHY_POWERUP & ETH_PHY_RESET);
+    rt_thread_mdelay(20);
+    mmio_write_32(ETH_PHY_BASE, (val & ETH_PHY_POWERUP) | ETH_PHY_RESET_N);
+    rt_thread_mdelay(1);
+}
+
+static eth_phy_dev_t *eth_connect_phy(eth_phy_priv_t *priv, uint32_t phy_mask, phy_if_mode_t interface)
+{
+    int32_t i;
+    eth_phy_dev_t *phydev = NULL;
+
+    /* config eth internal phy on ASIC board */
+    eth_config();
+
+#ifdef CONFIG_PHY_ADDR
+    phy_mask = 1 << CONFIG_PHY_ADDR;
+#endif
+
+    for (i = 0; i < 5; i++)
+    {
+        phydev = eth_get_phy_by_mask(priv, phy_mask, interface);
+        if (phydev)
+            return phydev;
+    }
+
+    rt_kprintf("\n PHY: ");
+    while (phy_mask)
+    {
+        int32_t addr = ffs(phy_mask) - 1;
+        rt_kprintf("%d ", addr);
+        phy_mask &= ~(1 << addr);
+    }
+    rt_kprintf("not found\n");
+
+    return NULL;
+}
+
+int32_t eth_phy_reset(eth_phy_handle_t handle)
+{
+    RT_ASSERT(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+    RT_ASSERT(dev->priv);
+
+    uint16_t data;
+    int32_t ret;
+    int32_t timeout = 600;                                  /* in ms */
+    eth_phy_priv_t *priv = dev->priv;
+    uint32_t phy_addr = dev->phy_addr;
+
+    /* Soft reset */
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_BMCR, &data);
+    if (ret != 0)
+    {
+        rt_kprintf("eth phy read failed\n");
+        return ret;
+    }
+
+    ret = eth_phy_write(priv, phy_addr, CVI_MII_BMCR, data | CVI_BMCR_RESET);
+    if (ret != 0)
+    {
+        rt_kprintf("eth phy write failed\n");
+        return ret;
+    }
+
+#ifdef CONFIG_PHY_RESET_DELAY
+    rt_hw_us_delay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */
+#endif
+    /*
+     * Wait up to 0.6s for the reset sequence to finish. According to
+     * IEEE 802.3, Section 2, Subsection 22.2.4.1.1 a PHY reset may take
+     * up to 0.5 s.
+     */
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_BMCR, &data);
+    while ((data & CVI_BMCR_RESET) && timeout--)
+    {
+        ret = eth_phy_read(priv, phy_addr, CVI_MII_BMCR, &data);
+
+        if (ret != 0) {
+            return ret;
+        }
+
+        rt_thread_mdelay(1);
+    }
+
+    if (data & CVI_BMCR_RESET)
+    {
+        rt_kprintf("eth phy reset timed out\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int32_t eth_phy_config(eth_phy_handle_t handle)
+{
+    RT_ASSERT(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+
+    if (dev->config)
+    {
+        return dev->config(handle);
+    }
+
+    return 0;
+}
+
+int32_t eth_phy_start(eth_phy_handle_t handle)
+{
+    RT_ASSERT(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+
+    if (dev->start)
+    {
+        return dev->start(handle);
+    }
+
+    return 0;
+}
+
+int32_t eth_phy_stop(eth_phy_handle_t handle)
+{
+    RT_ASSERT(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+
+    if (dev->start) {
+        return dev->stop(handle);
+    }
+
+    return 0;
+}
+
+int32_t cvi_eth_phy_power_control(eth_phy_handle_t handle, eth_power_state_t state)
+{
+    if (state == CSI_ETH_POWER_FULL)
+    {
+        return eth_phy_start(handle);
+    }
+    else if (state == CSI_ETH_POWER_OFF)
+    {
+        return eth_phy_stop(handle);
+    }
+
+    return 0;
+}
+
+int32_t genphy_update_link(eth_phy_dev_t *phy_dev)
+{
+    uint8_t phy_addr = phy_dev->phy_addr;
+    uint16_t mii_reg;
+    int32_t ret;
+
+    /*
+     * Wait if the link is up, and autonegotiation is in progress
+     * (ie - we're capable and it's not done)
+     */
+    ret = eth_phy_read(phy_dev->priv, phy_addr, CVI_MII_BMSR, &mii_reg);
+
+    if (ret != 0) {
+        return ret;
+    }
+
+    /*
+     * If we already saw the link up, and it hasn't gone down, then
+     * we don't need to wait for autoneg again
+     */
+    if (phy_dev->link_state && mii_reg & CVI_BMSR_LSTATUS)
+        return 0;
+
+    if ((phy_dev->priv->link_info.autoneg == CSI_ETH_AUTONEG_ENABLE) &&
+        !(mii_reg & CVI_BMSR_ANEGCOMPLETE)) {
+        int i = 0;
+
+        rt_kprintf("%s waiting for PHY auto negotiation to complete...\n",
+            phy_dev->name);
+        while (!(mii_reg & CVI_BMSR_ANEGCOMPLETE)) {
+            /*
+             * Timeout reached ?
+             */
+            if (i > CVI_PHY_ANEG_TIMEOUT) {
+                rt_kprintf("TIMEOUT!\n");
+                phy_dev->link_state = ETH_LINK_DOWN;
+                return -1;
+            }
+
+            // if ((i++ % 1000) == 0)
+            //  rt_kprintf(".");
+            i ++;
+
+            rt_hw_us_delay(1000);   /* 1 ms */
+
+            ret = eth_phy_read(phy_dev->priv, phy_addr, CVI_MII_BMSR, &mii_reg);
+            if (ret != 0) {
+                return ret;
+            }
+        }
+        rt_kprintf("auto negotiation Done!\n");
+        phy_dev->link_state = ETH_LINK_UP;
+    } else {
+
+        /* Read the link a second time to clear the latched state */
+        ret = eth_phy_read(phy_dev->priv, phy_addr, CVI_MII_BMSR, &mii_reg);
+
+        if (ret != 0) {
+            return ret;
+        }
+
+        if (mii_reg & CVI_BMSR_LSTATUS)
+            phy_dev->link_state = ETH_LINK_UP;
+        else
+            phy_dev->link_state = ETH_LINK_DOWN;
+    }
+
+    return 0;
+}
+
+int32_t eth_phy_update_link(eth_phy_handle_t handle)
+{
+    RT_ASSERT(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+
+    if (dev->update_link) {
+        return dev->update_link(handle);
+    } else {
+        return genphy_update_link(dev);
+    }
+}
+
+static int32_t genphy_config_advert(eth_phy_dev_t *phy_dev)
+{
+    RT_ASSERT(phy_dev->priv);
+
+    eth_phy_priv_t *priv = phy_dev->priv;
+    uint8_t phy_addr = phy_dev->phy_addr;
+    uint32_t advertise;
+    uint16_t oldadv, adv, bmsr;
+    int32_t changed = 0;
+    int32_t ret;
+
+    /* Only allow advertising what this PHY supports */
+    phy_dev->advertising &= phy_dev->supported;
+    advertise = phy_dev->advertising;
+
+    /* Setup standard advertisement */
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_ADVERTISE, &adv);
+    if (ret != 0) {
+        return ret;
+    }
+    oldadv = adv;
+
+    if (adv < 0)
+        return adv;
+
+    adv &= ~(CVI_ADVERTISE_ALL | CVI_ADVERTISE_100BASE4 | CVI_ADVERTISE_PAUSE_CAP |
+         CVI_ADVERTISE_PAUSE_ASYM);
+    if (advertise & CVI_ADVERTISED_10baseT_Half)
+        adv |= CVI_ADVERTISE_10HALF;
+    if (advertise & CVI_ADVERTISED_10baseT_Full)
+        adv |= CVI_ADVERTISE_10FULL;
+    if (advertise & CVI_ADVERTISED_100baseT_Half)
+        adv |= CVI_ADVERTISE_100HALF;
+    if (advertise & CVI_ADVERTISED_100baseT_Full)
+        adv |= CVI_ADVERTISE_100FULL;
+    if (advertise & CVI_ADVERTISED_Pause)
+        adv |= CVI_ADVERTISE_PAUSE_CAP;
+    if (advertise & CVI_ADVERTISED_Asym_Pause)
+        adv |= CVI_ADVERTISE_PAUSE_ASYM;
+    if (advertise & CVI_ADVERTISED_1000baseX_Half)
+        adv |= CVI_ADVERTISE_1000XHALF;
+    if (advertise & CVI_ADVERTISED_1000baseX_Full)
+        adv |= CVI_ADVERTISE_1000XFULL;
+
+    if (adv != oldadv) {
+        ret = eth_phy_write(priv, phy_addr, CVI_MII_ADVERTISE, adv);
+
+        if (ret != 0) {
+            return ret;
+        }
+        changed = 1;
+    }
+
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_BMSR, &bmsr);
+
+    if (ret != 0 || bmsr < 0) {
+        return ret;
+    }
+
+    /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
+     * 1000Mbits/sec capable PHYs shall have the CVI_BMSR_ESTATEN bit set to a
+     * logical 1.
+     */
+    if (!(bmsr & CVI_BMSR_ESTATEN))
+        return changed;
+
+    /* Configure gigabit if it's supported */
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_CTRL1000, &adv);
+
+    if (ret != 0 || adv < 0) {
+        return ret;
+    }
+
+    oldadv = adv;
+
+    adv &= ~(CVI_ADVERTISE_1000FULL | CVI_ADVERTISE_1000HALF);
+
+    if (phy_dev->supported & (CVI_SUPPORTED_1000baseT_Half |
+                CVI_SUPPORTED_1000baseT_Full)) {
+        if (advertise & CVI_SUPPORTED_1000baseT_Half)
+            adv |= CVI_ADVERTISE_1000HALF;
+        if (advertise & CVI_SUPPORTED_1000baseT_Full)
+            adv |= CVI_ADVERTISE_1000FULL;
+    }
+
+    if (adv != oldadv)
+        changed = 1;
+
+    ret = eth_phy_write(priv, phy_addr, CVI_MII_CTRL1000, adv);
+
+    if (ret != 0) {
+        return ret;
+    }
+
+    return changed;
+}
+
+static int32_t genphy_setup_forced(eth_phy_dev_t *phy_dev)
+{
+    RT_ASSERT(phy_dev->priv);
+
+    eth_phy_priv_t *priv = phy_dev->priv;
+    uint8_t phy_addr = phy_dev->phy_addr;
+    int32_t ctl = CVI_BMCR_ANRESTART;
+    int32_t ret;
+
+    if (CSI_ETH_SPEED_1G == priv->link_info.speed)
+        ctl |= CVI_BMCR_SPEED1000;
+    else if (CSI_ETH_SPEED_100M == priv->link_info.speed)
+        ctl |= CVI_BMCR_SPEED100;
+    else//CSI_ETH_SPEED_10M == priv->link_info.speed
+        ctl |= CVI_BMCR_SPEED100;
+
+    if (CSI_ETH_DUPLEX_FULL == priv->link_info.duplex)
+        ctl |= CVI_BMCR_FULLDPLX;
+
+    ret = eth_phy_write(priv, phy_addr, CVI_MII_BMCR, ctl);
+
+    return ret;
+}
+
+int genphy_restart_aneg(eth_phy_dev_t *phy_dev)
+{
+    int32_t ret;
+    uint16_t ctl;
+    ret = eth_phy_read(phy_dev->priv, phy_dev->phy_addr, CVI_MII_BMCR, &ctl);
+
+    if (ret != 0 || ctl < 0)
+        return ret;
+
+    ctl |= (CVI_BMCR_ANENABLE | CVI_BMCR_ANRESTART);
+
+    /* Don't isolate the PHY if we're negotiating */
+    ctl &= ~(CVI_BMCR_ISOLATE);
+
+    ret = eth_phy_write(phy_dev->priv, phy_dev->phy_addr, CVI_MII_BMCR, ctl);
+
+    return ret;
+}
+
+int32_t genphy_config_aneg(eth_phy_dev_t *phy_dev)
+{
+    RT_ASSERT(phy_dev->priv);
+
+    eth_phy_priv_t *priv = phy_dev->priv;
+    uint8_t phy_addr = phy_dev->phy_addr;
+    int32_t result;
+    uint16_t ctl;
+    int32_t ret;
+
+    if (CSI_ETH_AUTONEG_ENABLE != priv->link_info.autoneg)
+        return genphy_setup_forced(phy_dev);
+
+    result = genphy_config_advert(phy_dev);
+
+    if (result < 0) /* error */
+        return result;
+
+    if (result == 0) {
+        /* Advertisment hasn't changed, but maybe aneg was never on to
+         * begin with?  Or maybe phy was isolated? */
+        ret = eth_phy_read(priv, phy_addr, CVI_MII_BMCR, &ctl);
+        if (ret != 0 || ctl < 0)
+            return ret;
+
+        if (!(ctl & CVI_BMCR_ANENABLE) || (ctl & CVI_BMCR_ISOLATE))
+            result = 1; /* do restart aneg */
+    }
+
+    /* Only restart aneg if we are advertising something different
+     * than we were before.  */
+    if (result > 0)
+        result = genphy_restart_aneg(phy_dev);
+
+    return result;
+}
+
+int32_t genphy_config(eth_phy_dev_t *phy_dev)
+{
+    RT_ASSERT(phy_dev->priv);
+
+    eth_phy_priv_t *priv = phy_dev->priv;
+    uint8_t phy_addr = phy_dev->phy_addr;
+    int32_t ret;
+    uint16_t val;
+    uint32_t features;
+
+    features = (CVI_SUPPORTED_TP | CVI_SUPPORTED_MII
+            | CVI_SUPPORTED_AUI | CVI_SUPPORTED_FIBRE |
+            CVI_SUPPORTED_BNC);
+
+    /* Do we support autonegotiation? */
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_BMSR, &val);
+    if (ret != 0 || val < 0)
+        return ret;
+
+    if (val & CVI_BMSR_ANEGCAPABLE)
+        features |= CVI_SUPPORTED_Autoneg;
+
+    if (val & CVI_BMSR_100FULL)
+        features |= CVI_SUPPORTED_100baseT_Full;
+    if (val & CVI_BMSR_100HALF)
+        features |= CVI_SUPPORTED_100baseT_Half;
+    if (val & CVI_BMSR_10FULL)
+        features |= CVI_SUPPORTED_10baseT_Full;
+    if (val & CVI_BMSR_10HALF)
+        features |= CVI_SUPPORTED_10baseT_Half;
+
+    if (val & CVI_BMSR_ESTATEN) {
+        ret = eth_phy_read(priv, phy_addr, CVI_MII_ESTATUS, &val);
+        if (ret != 0 || val < 0)
+            return val;
+
+        if (val & CVI_ESTATUS_1000_TFULL)
+            features |= CVI_SUPPORTED_1000baseT_Full;
+        if (val & CVI_ESTATUS_1000_THALF)
+            features |= CVI_SUPPORTED_1000baseT_Half;
+        if (val & CVI_ESTATUS_1000_XFULL)
+            features |= CVI_SUPPORTED_1000baseX_Full;
+        if (val & CVI_ESTATUS_1000_XHALF)
+            features |= CVI_SUPPORTED_1000baseX_Half;
+    }
+
+    phy_dev->supported &= features;
+    phy_dev->advertising &= features;
+
+    genphy_config_aneg(phy_dev);
+
+    return 0;
+}
+
+eth_phy_handle_t cvi_eth_phy_init(csi_eth_phy_read_t  fn_read, csi_eth_phy_write_t fn_write)
+{
+    eth_phy_dev_t *phy_dev;
+    eth_phy_priv_t *priv;
+    uint32_t phy_mask = 0xffffffff;
+    phy_if_mode_t interface = 0;
+
+    RT_ASSERT(fn_read != RT_NULL);
+    RT_ASSERT(fn_write != RT_NULL);
+
+    priv = &phy_priv_list[0];
+
+    priv->phy_read = fn_read;
+    priv->phy_write = fn_write;
+    priv->link_info.autoneg = CSI_ETH_AUTONEG_ENABLE;
+
+    phy_dev = eth_connect_phy(priv, phy_mask, interface);
+    if (phy_dev == NULL) {
+        rt_kprintf("No phy device found!\n");
+        return;
+    }
+    rt_kprintf("connect phy id: 0x%X\n", phy_dev->phy_id);
+
+    phy_dev->priv = priv;
+
+    /* Reset PHY */
+    eth_phy_reset(phy_dev);
+
+    /* Config PHY */
+    eth_phy_config(phy_dev);
+
+    return phy_dev;
+}

+ 401 - 0
bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.h

@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2019-2020 AlibabaGroup Holding Limited
+ */
+/******************************************************************************
+ * @file     phy.h
+ * @brief    header file for generic PHY Driver
+ * @version  V1.0
+ * @date     21 March 2019
+ ******************************************************************************/
+#ifndef _ETH_PHY_H_
+#define _ETH_PHY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include "mmio.h"
+
+/**
+\brief Ethernet link speed
+*/
+#define CSI_ETH_SPEED_10M               (0)     ///< 10 Mbps link speed
+#define CSI_ETH_SPEED_100M              (1)     ///< 100 Mbps link speed
+#define CSI_ETH_SPEED_1G                (2)     ///< 1 Gpbs link speed
+
+/**
+\brief Ethernet duplex mode
+*/
+#define CSI_ETH_DUPLEX_HALF             (0)     ///< Half duplex link
+#define CSI_ETH_DUPLEX_FULL             (1)     ///< Full duplex link
+
+typedef void *eth_phy_handle_t;
+
+typedef enum eth_power_state
+{
+    CSI_ETH_POWER_OFF,                        ///< Power off: no operation possible
+    CSI_ETH_POWER_LOW,                        ///< Low Power mode: retain state, detect and signal wake-up events
+    CSI_ETH_POWER_FULL                        ///< Power on: full operation at maximum performance
+} eth_power_state_t;
+
+typedef int32_t (*csi_eth_phy_read_t)(uint8_t phy_addr, uint8_t reg_addr, uint16_t *data);   ///< Read Ethernet PHY Register.
+typedef int32_t (*csi_eth_phy_write_t)(uint8_t phy_addr, uint8_t reg_addr, uint16_t  data);  ///< Write Ethernet PHY Register.
+
+typedef volatile struct eth_link_info
+{
+    uint32_t speed              : 2;                ///< Link speed: 0= 10 MBit, 1= 100 MBit, 2= 1 GBit
+    uint32_t duplex             : 1;                ///< Duplex mode: 0= Half, 1= Full
+    uint32_t autoneg            : 1;                ///< Set the interface to Auto Negotiation mode of transmission parameters
+    uint32_t loopback           : 1;                ///< Set the interface into a Loop-back test mode
+    uint32_t isolation          : 1;                ///< Set to indicate electrical isolation of PHY interface from MII/RMII interface
+    uint32_t reserved           : 26;
+} eth_link_info_t;
+
+typedef struct
+{
+    csi_eth_phy_read_t  phy_read;
+    csi_eth_phy_write_t phy_write;
+    eth_link_info_t     link_info;
+} eth_phy_priv_t;
+
+typedef enum eth_link_state
+{
+    ETH_LINK_DOWN,                    ///< Link is down
+    ETH_LINK_UP                       ///< Link is up
+} eth_link_state_t;
+
+/* Basic mode control register */
+#define CVI_BMCR_RESV           (0x003f)
+#define CVI_BMCR_SPEED1000      (0x0040)
+#define CVI_BMCR_CTST           (0x0080)
+#define CVI_BMCR_FULLDPLX       (0x0100)
+#define CVI_BMCR_ANRESTART      (0x0200)
+#define CVI_BMCR_ISOLATE        (0x0400)
+#define CVI_BMCR_PDOWN          (0x0800)
+#define CVI_BMCR_ANENABLE       (0x1000)
+#define CVI_BMCR_SPEED100       (0x2000)
+#define CVI_BMCR_LOOPBACK       (0x4000)
+#define CVI_BMCR_RESET          (0x8000)
+#define BMCR_SPEED10            (0x0000)
+
+
+/* Generic MII registers */
+#define CVI_MII_BMCR            (0x00)
+#define CVI_MII_BMSR            (0x01)
+#define CVI_MII_PHYSID1         (0x02)
+#define CVI_MII_PHYSID2         (0x03)
+#define CVI_MII_ADVERTISE       (0x04)
+#define CVI_MII_LPA             (0x05)
+#define CVI_MII_EXPANSION       (0x06)
+#define CVI_MII_CTRL1000        (0x09)
+#define CVI_MII_STAT1000        (0x0a)
+#define MII_MMD_CTRL            (0x0d)
+#define MII_MMD_DATA            (0x0e)
+#define CVI_MII_ESTATUS         (0x0f)
+#define CVI_MII_DCOUNTER        (0x12)
+#define CVI_MII_FCSCOUNTER      (0x13)
+#define CVI_MII_NWAYTEST        (0x14)
+#define CVI_MII_RERRCOUNTER     (0x15)
+#define CVI_MII_SREVISION       (0x16)
+#define CVI_MII_RESV1           (0x17)
+#define CVI_MII_LBRERROR        (0x18)
+#define CVI_MII_PHYADDR         (0x19)
+#define CVI_MII_RESV2           (0x1a)
+#define CVI_MII_TPISTATUS       (0x1b)
+#define CVI_MII_NCONFIG         (0x1c)
+
+/* Advertisement control register. */
+#define CVI_ADVERTISE_CSMA          (0x0001)
+#define CVI_ADVERTISE_SLCT          (0x001f)
+#define CVI_ADVERTISE_10HALF        (0x0020)
+#define CVI_ADVERTISE_1000XFULL     (0x0020)
+#define CVI_ADVERTISE_10FULL        (0x0040)
+#define CVI_ADVERTISE_1000XHALF     (0x0040)
+#define CVI_ADVERTISE_100HALF       (0x0080)
+#define CVI_ADVERTISE_1000XPAUSE    (0x0080)
+#define CVI_ADVERTISE_100FULL       (0x0100)
+#define CVI_ADVERTISE_1000XPSE_ASYM (0x0100)
+#define CVI_ADVERTISE_100BASE4      (0x0200)
+#define CVI_ADVERTISE_PAUSE_CAP     (0x0400)
+#define CVI_ADVERTISE_PAUSE_ASYM    (0x0800)
+#define CVI_ADVERTISE_RESV          (0x1000)
+#define CVI_ADVERTISE_RFAULT        (0x2000)
+#define CVI_ADVERTISE_LPACK         (0x4000)
+#define CVI_ADVERTISE_NPAGE         (0x8000)
+
+/* Basic mode status register. */
+#define CVI_BMSR_ERCAP          (0x0001)
+#define CVI_BMSR_JCD            (0x0002)
+#define CVI_BMSR_LSTATUS        (0x0004)
+#define CVI_BMSR_ANEGCAPABLE    (0x0008)
+#define CVI_BMSR_RFAULT         (0x0010)
+#define CVI_BMSR_ANEGCOMPLETE   (0x0020)
+#define CVI_BMSR_RESV           (0x00c0)
+#define CVI_BMSR_ESTATEN        (0x0100)
+#define CVI_BMSR_100HALF2       (0x0200)
+#define CVI_BMSR_100FULL2       (0x0400)
+#define CVI_BMSR_10HALF         (0x0800)
+#define CVI_BMSR_10FULL         (0x1000)
+#define CVI_BMSR_100HALF        (0x2000)
+#define CVI_BMSR_100FULL        (0x4000)
+#define CVI_BMSR_100BASE4       (0x8000)
+
+#define CVI_ADVERTISE_FULL  (CVI_ADVERTISE_100FULL | CVI_ADVERTISE_10FULL | \
+                         CVI_ADVERTISE_CSMA)
+#define CVI_ADVERTISE_ALL   (CVI_ADVERTISE_10HALF | CVI_ADVERTISE_10FULL | \
+                         CVI_ADVERTISE_100HALF | CVI_ADVERTISE_100FULL)
+
+/* Link partner ability register. */
+#define CVI_LPA_SLCT            (0x001f)  /* Same as advertise selector */
+#define CVI_LPA_10HALF          (0x0020)  /* Can do 10mbps half-duplex */
+#define CVI_LPA_1000XFULL       (0x0020)  /* Can do 1000BASE-X full-duplex */
+#define CVI_LPA_10FULL          (0x0040)  /* Can do 10mbps full-duplex */
+#define CVI_LPA_1000XHALF       (0x0040)  /* Can do 1000BASE-X half-duplex */
+#define CVI_LPA_100HALF         (0x0080)  /* Can do 100mbps half-duplex */
+#define CVI_LPA_1000XPAUSE      (0x0080)  /* Can do 1000BASE-X pause */
+#define CVI_LPA_100FULL         (0x0100)  /* Can do 100mbps full-duplex */
+#define CVI_LPA_1000XPAUSE_ASYM (0x0100)  /* Can do 1000BASE-X pause asym */
+#define CVI_LPA_100BASE4        (0x0200)  /* Can do 100mbps 4k packets */
+#define CVI_LPA_PAUSE_CAP       (0x0400)  /* Can pause */
+#define CVI_LPA_PAUSE_ASYM      (0x0800)  /* Can pause asymetrically */
+#define CVI_LPA_RESV            (0x1000)  /* Unused */
+#define CVI_LPA_RFAULT          (0x2000)  /* Link partner faulted */
+#define CVI_LPA_LPACK           (0x4000)  /* Link partner acked us */
+#define CVI_LPA_NPAGE           (0x8000)  /* Next page bit */
+
+#define CVI_LPA_DUPLEX  (CVI_LPA_10FULL | CVI_LPA_100FULL)
+#define CVI_LPA_100     (CVI_LPA_100FULL | CVI_LPA_100HALF | CVI_LPA_100BASE4)
+
+/* Expansion register for auto-negotiation. */
+#define CVI_EXPANSION_NWAY          (0x0001)  /* Can do N-way auto-nego */
+#define CVI_EXPANSION_LCWP          (0x0002)  /* Got new RX page code word */
+#define CVI_EXPANSION_ENABLENPAGE   (0x0004)  /* This enables npage words */
+#define CVI_EXPANSION_NPCAPABLE     (0x0008)  /* Link partner supports npage */
+#define CVI_EXPANSION_MFAULTS       (0x0010)  /* Multiple faults detected */
+#define CVI_EXPANSION_RESV          (0xffe0)  /* Unused */
+
+#define CVI_ESTATUS_1000_XFULL  (0x8000)  /* Can do 1000BX Full */
+#define CVI_ESTATUS_1000_XHALF  (0x4000)  /* Can do 1000BX Half */
+#define CVI_ESTATUS_1000_TFULL  (0x2000)  /* Can do 1000BT Full */
+#define CVI_ESTATUS_1000_THALF  (0x1000)  /* Can do 1000BT Half */
+
+/* N-way test register. */
+#define CVI_NWAYTEST_RESV1      (0x00ff)  /* Unused */
+#define CVI_NWAYTEST_LOOPBACK   (0x0100)  /* Enable loopback for N-way */
+#define CVI_NWAYTEST_RESV2      (0xfe00)  /* Unused */
+
+/* 1000BASE-T Control register */
+#define CVI_ADVERTISE_1000FULL      0x0200  /* Advertise 1000BASE-T full duplex */
+#define CVI_ADVERTISE_1000HALF      0x0100  /* Advertise 1000BASE-T half duplex */
+#define CTL1000_AS_MASTER       0x0800
+#define CTL1000_ENABLE_MASTER   0x1000
+
+/* 1000BASE-T Status register */
+#define CVI_LPA_1000LOCALRXOK   0x2000  /* Link partner local receiver status */
+#define CVI_LPA_1000REMRXOK     0x1000  /* Link partner remote receiver status */
+#define CVI_LPA_1000FULL        0x0800  /* Link partner 1000BASE-T full duplex */
+#define CVI_LPA_1000HALF        0x0400  /* Link partner 1000BASE-T half duplex */
+
+/* Flow control flags */
+#define CVI_FLOW_CTRL_TX    0x01
+#define CVI_FLOW_CTRL_RX    0x02
+
+/* MMD Access Control register fields */
+#define CVI_MII_MMD_CTRL_DEVAD_MASK 0x1f    /* Mask MMD DEVAD*/
+#define CVI_MII_MMD_CTRL_ADDR       0x0000  /* Address */
+#define CVI_MII_MMD_CTRL_NOINCR     0x4000  /* no post increment */
+#define CVI_MII_MMD_CTRL_INCR_RDWT  0x8000  /* post increment on reads & writes */
+#define CVI_MII_MMD_CTRL_INCR_ON_WT 0xC000  /* post increment on writes only */
+
+
+/* Indicates what features are advertised by the interface. */
+#define CVI_ADVERTISED_10baseT_Half         (1 << 0)
+#define CVI_ADVERTISED_10baseT_Full         (1 << 1)
+#define CVI_ADVERTISED_100baseT_Half        (1 << 2)
+#define CVI_ADVERTISED_100baseT_Full        (1 << 3)
+#define CVI_ADVERTISED_1000baseT_Half       (1 << 4)
+#define CVI_ADVERTISED_1000baseT_Full       (1 << 5)
+#define CVI_ADVERTISED_Autoneg              (1 << 6)
+#define CVI_ADVERTISED_TP                   (1 << 7)
+#define CVI_ADVERTISED_AUI                  (1 << 8)
+#define CVI_ADVERTISED_MII                  (1 << 9)
+#define CVI_ADVERTISED_FIBRE                (1 << 10)
+#define CVI_ADVERTISED_BNC                  (1 << 11)
+#define CVI_ADVERTISED_10000baseT_Full      (1 << 12)
+#define CVI_ADVERTISED_Pause                (1 << 13)
+#define CVI_ADVERTISED_Asym_Pause           (1 << 14)
+#define CVI_ADVERTISED_2500baseX_Full       (1 << 15)
+#define CVI_ADVERTISED_Backplane            (1 << 16)
+#define CVI_ADVERTISED_1000baseKX_Full      (1 << 17)
+#define CVI_ADVERTISED_10000baseKX4_Full    (1 << 18)
+#define CVI_ADVERTISED_10000baseKR_Full     (1 << 19)
+#define CVI_ADVERTISED_10000baseR_FEC       (1 << 20)
+#define CVI_ADVERTISED_1000baseX_Half       (1 << 21)
+#define CVI_ADVERTISED_1000baseX_Full       (1 << 22)
+
+
+/* Indicates what features are supported by the interface. */
+#define CVI_SUPPORTED_10baseT_Half      (1 << 0)
+#define CVI_SUPPORTED_10baseT_Full      (1 << 1)
+#define CVI_SUPPORTED_100baseT_Half     (1 << 2)
+#define CVI_SUPPORTED_100baseT_Full     (1 << 3)
+#define CVI_SUPPORTED_1000baseT_Half    (1 << 4)
+#define CVI_SUPPORTED_1000baseT_Full    (1 << 5)
+#define CVI_SUPPORTED_Autoneg           (1 << 6)
+#define CVI_SUPPORTED_TP                (1 << 7)
+#define CVI_SUPPORTED_AUI               (1 << 8)
+#define CVI_SUPPORTED_MII               (1 << 9)
+#define CVI_SUPPORTED_FIBRE             (1 << 10)
+#define CVI_SUPPORTED_BNC               (1 << 11)
+#define CVI_SUPPORTED_10000baseT_Full   (1 << 12)
+#define CVI_SUPPORTED_Pause             (1 << 13)
+#define CVI_SUPPORTED_Asym_Pause        (1 << 14)
+#define CVI_SUPPORTED_2500baseX_Full    (1 << 15)
+#define CVI_SUPPORTED_Backplane         (1 << 16)
+#define CVI_SUPPORTED_1000baseKX_Full   (1 << 17)
+#define CVI_SUPPORTED_10000baseKX4_Full (1 << 18)
+#define CVI_SUPPORTED_10000baseKR_Full  (1 << 19)
+#define CVI_SUPPORTED_10000baseR_FEC    (1 << 20)
+#define CVI_SUPPORTED_1000baseX_Half    (1 << 21)
+#define CVI_SUPPORTED_1000baseX_Full    (1 << 22)
+
+/* PHY features */
+#define CVI_PHY_DEFAULT_FEATURES    (CVI_SUPPORTED_Autoneg | \
+                                 CVI_SUPPORTED_TP | \
+                                 CVI_SUPPORTED_MII)
+
+#define CVI_PHY_10BT_FEATURES   (CVI_SUPPORTED_10baseT_Half | \
+                             CVI_SUPPORTED_10baseT_Full)
+
+#define CVI_PHY_100BT_FEATURES  (CVI_SUPPORTED_100baseT_Half | \
+                             CVI_SUPPORTED_100baseT_Full)
+
+#define CVI_PHY_1000BT_FEATURES (CVI_SUPPORTED_1000baseT_Half | \
+                             CVI_SUPPORTED_1000baseT_Full)
+
+#define CVI_PHY_BASIC_FEATURES  (CVI_PHY_10BT_FEATURES | \
+                             CVI_PHY_100BT_FEATURES | \
+                             CVI_PHY_DEFAULT_FEATURES)
+
+#define CVI_PHY_GBIT_FEATURES   (CVI_PHY_BASIC_FEATURES | \
+                             CVI_PHY_1000BT_FEATURES)
+
+#define CVI_PHY_ANEG_TIMEOUT    5000    /* in ms */
+
+typedef enum {
+    LOOPBACK_XMII2MAC,
+    LOOPBACK_PCS2MAC,
+    LOOPBACK_PMA2MAC,
+    LOOPBACK_RMII2PHY,
+} phy_loopback_mode_t;
+
+/* phy interface mode */
+typedef enum {
+    PHY_IF_MODE_MII,
+    PHY_IF_MODE_GMII,
+    PHY_IF_MODE_SGMII,
+    PHY_IF_MODE_TBI,
+    PHY_IF_MODE_RMII,
+    PHY_IF_MODE_RGMII,
+    PHY_IF_MODE_RGMII_ID,
+    PHY_IF_MODE_RGMII_RXID,
+    PHY_IF_MODE_RGMII_TXID,
+    PHY_IF_MODE_RTBI,
+
+    PHY_IF_MODE_NONE,   /* Last One */
+    PHY_IF_MODE_COUNT,
+} phy_if_mode_t;
+
+typedef struct {
+    eth_phy_priv_t *priv;
+    eth_link_state_t link_state;
+
+    uint32_t supported;
+    uint32_t advertising;
+
+    /*
+     * platform specific
+     */
+    uint32_t phy_addr;
+    phy_if_mode_t interface;
+
+    /*
+     * driver specific
+     */
+    uint32_t phy_id;
+    uint32_t mask;
+    uint32_t features;
+    int8_t name[20];
+    /* config() should be called before calling start() */
+    int32_t (*config)(eth_phy_handle_t *phy_dev);
+    int32_t (*start)(eth_phy_handle_t *phy_dev);
+    int32_t (*stop)(eth_phy_handle_t *phy_dev);
+    int32_t (*loopback)(eth_phy_handle_t *phy_dev);
+    int32_t (*update_link)(eth_phy_handle_t *phy_dev);
+} eth_phy_dev_t;
+
+/* ethernet phy config */
+#define ETH_PHY_BASE 0x03009000
+#define ETH_PHY_INIT_MASK 0xFFFFFFF9
+#define ETH_PHY_SHUTDOWN (1 << 1)
+#define ETH_PHY_POWERUP 0xFFFFFFFD
+#define ETH_PHY_RESET 0xFFFFFFFB
+#define ETH_PHY_RESET_N (1 << 2)
+#define ETH_PHY_LED_LOW_ACTIVE (1 << 3)
+
+int generic_phy_config_aneg(eth_phy_dev_t *dev);
+int generic_phy_restart_aneg(eth_phy_dev_t *dev);
+int generic_phy_update_link(eth_phy_dev_t *dev);
+
+int32_t eth_phy_read(eth_phy_priv_t *priv, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data);
+int32_t eth_phy_write(eth_phy_priv_t *priv, uint8_t phy_addr, uint8_t reg_addr, uint16_t data);
+
+int32_t eth_phy_reset(eth_phy_handle_t handle);
+int32_t eth_phy_config(eth_phy_handle_t handle);
+int32_t eth_phy_start(eth_phy_handle_t handle);
+int32_t eth_phy_update_link(eth_phy_handle_t handle);
+
+int32_t genphy_config(eth_phy_dev_t *phy_dev);
+int32_t genphy_update_link(eth_phy_dev_t *phy_dev);
+
+/*
+ * ffs: find first bit set. This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+
+// static inline int32_t ffs(int32_t x)
+// {
+//  int32_t r = 1;
+
+//  if (!x)
+//      return 0;
+//  if (!(x & 0xffff)) {
+//      x >>= 16;
+//      r += 16;
+//  }
+//  if (!(x & 0xff)) {
+//      x >>= 8;
+//      r += 8;
+//  }
+//  if (!(x & 0xf)) {
+//      x >>= 4;
+//      r += 4;
+//  }
+//  if (!(x & 3)) {
+//      x >>= 2;
+//      r += 2;
+//  }
+//  if (!(x & 1)) {
+//      x >>= 1;
+//      r += 1;
+//  }
+//  return r;
+// }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ETH_PHY_H_ */

+ 717 - 0
bsp/cvitek/drivers/libraries/eth/dw_eth_mac.c

@@ -0,0 +1,717 @@
+/*
+* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
+*/
+#include <rtthread.h>
+
+#include "dw_eth_mac.h"
+#include "cache.h"
+
+#define roundup(x, y) (                 \
+{                           \
+    const typeof(y) __y = y;            \
+    (((x) + (__y - 1)) / __y) * __y;        \
+}                           \
+)
+
+#define CONFIG_GMAC_NUM 2
+
+static gmac_dev_t gmac_instance[CONFIG_GMAC_NUM];
+
+static int32_t designware_read_hwaddr(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_mac_regs *mac_reg = mac_dev->priv->mac_regs_p;
+    uint32_t macid_lo, macid_hi;
+    uint8_t mac_id[6] = {0};
+
+    macid_hi = mac_reg->macaddr0hi;
+    macid_lo = mac_reg->macaddr0lo;
+
+    mac_id[0] = macid_lo & 0xff;
+    mac_id[1] = (macid_lo >> 8) & 0xff;
+    mac_id[2] = (macid_lo >> 16) & 0xff;
+    mac_id[3] = (macid_lo >> 24) & 0xff;
+    mac_id[4] = macid_hi & 0xff;
+    mac_id[5] = (macid_hi >> 8) & 0xff;
+
+    memcpy(mac_dev->mac_addr, mac_id, sizeof(mac_id));
+
+    return 0;
+}
+
+static int32_t designware_write_hwaddr(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_mac_regs *mac_reg = mac_dev->priv->mac_regs_p;
+
+    uint32_t macid_lo, macid_hi;
+    uint8_t *mac_id = mac_dev->mac_addr;
+
+    macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) +
+           (mac_id[3] << 24);
+    macid_hi = mac_id[4] + (mac_id[5] << 8);
+
+    mac_reg->macaddr0hi = macid_hi;
+    mac_reg->macaddr0lo = macid_lo;
+
+    return 0;
+}
+
+static void tx_descs_init(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    struct dw_gmac_dma_regs *dma_reg = priv->dma_regs_p;
+    struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0];
+    char *txbuffs = &priv->txbuffs[0];
+    struct dmamacdescr *desc_p;
+    uint32_t idx;
+
+    for (idx = 0; idx < CVI_CONFIG_TX_DESCR_NUM; idx++) {
+        desc_p = &desc_table_p[idx];
+        desc_p->dmamac_addr = (unsigned long)&txbuffs[idx * CVI_CONFIG_ETH_BUFSIZE];
+        desc_p->dmamac_next = (unsigned long)&desc_table_p[idx + 1];
+
+#if defined(CONFIG_DW_ALTDESCRIPTOR)
+        desc_p->txrx_status &= ~(CVI_DESC_TXSTS_TXINT | CVI_DESC_TXSTS_TXLAST |
+                CVI_DESC_TXSTS_TXFIRST | CVI_DESC_TXSTS_TXCRCDIS |
+                CVI_DESC_TXSTS_TXCHECKINSCTRL |
+                CVI_DESC_TXSTS_TXRINGEND | CVI_DESC_TXSTS_TXPADDIS);
+
+        desc_p->txrx_status |= CVI_DESC_TXSTS_TXCHAIN;
+        desc_p->dmamac_cntl = 0;
+        desc_p->txrx_status &= ~(CVI_DESC_TXSTS_MSK | CVI_DESC_TXSTS_OWNBYDMA);
+#else
+        desc_p->dmamac_cntl = CVI_DESC_TXCTRL_TXCHAIN;
+        desc_p->txrx_status = 0;
+#endif
+    }
+
+    /* Correcting the last pointer of the chain */
+    desc_p->dmamac_next = (unsigned long)&desc_table_p[0];
+
+    /* Flush all Tx buffer descriptors at once */
+    rt_hw_cpu_dcache_clean((unsigned long)priv->tx_mac_descrtable, sizeof(priv->tx_mac_descrtable));
+
+    dma_reg->txdesclistaddr = (unsigned long)&desc_table_p[0];
+
+    priv->tx_currdescnum = 0;
+}
+
+static void rx_descs_init(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    struct dw_gmac_dma_regs *dma_reg = priv->dma_regs_p;
+    struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0];
+    char *rxbuffs = &priv->rxbuffs[0];
+    struct dmamacdescr *desc_p;
+    uint32_t idx;
+
+    /* Before passing buffers to GMAC we need to make sure zeros
+     * written there right after "priv" structure allocation were
+     * flushed into RAM.
+     * Otherwise there's a chance to get some of them flushed in RAM when
+     * GMAC is already pushing data to RAM via DMA. This way incoming from
+     * GMAC data will be corrupted. */
+    rt_hw_cpu_dcache_clean((unsigned long)rxbuffs, CVI_RX_TOTAL_BUFSIZE);
+
+    for (idx = 0; idx < CVI_CONFIG_RX_DESCR_NUM; idx++) {
+        desc_p = &desc_table_p[idx];
+        desc_p->dmamac_addr = (unsigned long)&rxbuffs[idx * CVI_CONFIG_ETH_BUFSIZE];
+        desc_p->dmamac_next = (unsigned long)&desc_table_p[idx + 1];
+
+        desc_p->dmamac_cntl =
+            (CVI_MAC_MAX_FRAME_SZ & CVI_DESC_RXCTRL_SIZE1MASK) |
+                      CVI_DESC_RXCTRL_RXCHAIN;
+
+        desc_p->txrx_status = CVI_DESC_RXSTS_OWNBYDMA;
+    }
+
+    /* Correcting the last pointer of the chain */
+    desc_p->dmamac_next = (unsigned long)&desc_table_p[0];
+
+    /* Flush all Rx buffer descriptors at once */
+    rt_hw_cpu_dcache_clean((unsigned long)priv->rx_mac_descrtable, sizeof(priv->rx_mac_descrtable));
+
+    dma_reg->rxdesclistaddr = (unsigned long)&desc_table_p[0];
+
+    priv->rx_currdescnum = 0;
+}
+
+static int32_t designware_adjust_link(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    struct dw_gmac_mac_regs *mac_reg = priv->mac_regs_p;
+    eth_link_info_t *link_info = &mac_dev->phy_dev->priv->link_info;
+    eth_link_state_t link_state = mac_dev->phy_dev->link_state;
+
+    uint32_t conf = mac_reg->conf | CVI_FRAMEBURSTENABLE | CVI_DISABLERXOWN;
+
+    if (!link_state) {
+        rt_kprintf("eth No link.\n");
+        return 0;
+    }
+
+    if (link_info->speed != CSI_ETH_SPEED_1G)
+        conf |= CVI_MII_PORTSELECT;
+    else
+        conf &= ~CVI_MII_PORTSELECT;
+
+    if (link_info->speed == CSI_ETH_SPEED_100M)
+        conf |= CVI_FES_100;
+
+    if (link_info->duplex)
+        conf |= CVI_FULLDPLXMODE;
+
+    mac_reg->conf = conf;
+
+    rt_kprintf("Speed: %s, duplex: %s\n",
+            (link_info->speed) ? "100M" : "10M",
+           (link_info->duplex) ? "full" : "half");
+
+    return 0;
+}
+
+static int32_t designware_eth_init(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_mac_regs *mac_reg = mac_dev->priv->mac_regs_p;
+    struct dw_gmac_dma_regs *dma_reg = mac_dev->priv->dma_regs_p;
+
+    uint32_t start;
+
+    dma_reg->busmode |= CVI_DMAMAC_SRST;
+
+    start = rt_tick_get_millisecond();
+    while (dma_reg->busmode & CVI_DMAMAC_SRST) {
+        if ((rt_tick_get_millisecond() - start) >= CVI_CONFIG_MACRESET_TIMEOUT) {
+            rt_kprintf("DMA reset timeout\n");
+            return -ETIMEDOUT;
+        }
+
+        rt_thread_mdelay(100);
+    };
+
+    /*
+     * Soft reset above clears HW address registers.
+     * So we have to set it here once again.
+     */
+    // designware_read_hwaddr(handle);
+    // designware_write_hwaddr(handle);
+
+    rx_descs_init(handle);
+    tx_descs_init(handle);
+
+    dma_reg->busmode = (CVI_FIXEDBURST | CVI_PRIORXTX_41 | CVI_DMA_PBL);
+
+    // mac_reg->framefilt = 0x10;
+    // mac_reg->flowcontrol = 0x8;
+    // dma_reg->wdtforri = 0xff;
+    // dma_reg->axibus = 0x0012100F;
+
+#ifndef CONFIG_DW_MAC_FORCE_THRESHOLD_MODE
+    dma_reg->opmode |= (CVI_FLUSHTXFIFO | CVI_STOREFORWARD);
+#else
+    dma_reg->opmode |= CVI_FLUSHTXFIFO;
+#endif
+
+    dma_reg->opmode |= (CVI_RXSTART | CVI_TXSTART);
+    dma_reg->opmode = 0x2202906;
+    dma_reg->busmode = 0x3900800;
+    mac_reg->conf = 0x41cc00;
+    dma_reg->intenable = 0x10040;
+
+#ifdef CONFIG_DW_AXI_BURST_LEN
+    dma_reg->axibus = (CONFIG_DW_AXI_BURST_LEN & 0x1FF >> 1);
+#endif
+
+    /* Start up the PHY */
+    /* adjust link */
+    return 0;
+}
+
+static int32_t designware_eth_enable(eth_mac_handle_t handle, int32_t control)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_mac_regs *mac_reg = mac_dev->priv->mac_regs_p;
+    eth_link_state_t link_state = mac_dev->phy_dev->link_state;
+
+    if (link_state == ETH_LINK_DOWN)
+        return -1;
+
+    switch (control) {
+    case CSI_ETH_MAC_CONTROL_TX:
+        mac_reg->conf |= CVI_TXENABLE;
+        break;
+    case CSI_ETH_MAC_CONTROL_RX:
+        mac_reg->conf |= CVI_RXENABLE;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+static int32_t designware_eth_disable(eth_mac_handle_t handle, int32_t arg)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_mac_regs *mac_reg = mac_dev->priv->mac_regs_p;
+
+    switch (arg) {
+    case CSI_ETH_MAC_CONTROL_TX:
+        mac_reg->conf &= ~CVI_TXENABLE;
+        break;
+    case CSI_ETH_MAC_CONTROL_RX:
+        mac_reg->conf &= ~CVI_RXENABLE;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+static int32_t designware_eth_start(eth_mac_handle_t handle)
+{
+    int32_t ret;
+
+    ret = designware_eth_init(handle);
+    if (ret)
+        return ret;
+
+    return 0;
+}
+
+void designware_eth_stop(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_mac_regs *mac_reg = mac_dev->priv->mac_regs_p;
+    struct dw_gmac_dma_regs *dma_reg = mac_dev->priv->dma_regs_p;
+
+    mac_reg->conf &= ~(CVI_RXENABLE | CVI_TXENABLE);
+    dma_reg->opmode &= ~(CVI_RXSTART | CVI_TXSTART);
+
+    //phy_shutdown(priv->phydev);
+}
+
+static int32_t designware_eth_send(eth_mac_handle_t handle, const uint8_t *frame, uint32_t length)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    struct dw_gmac_dma_regs *dma_reg = mac_dev->priv->dma_regs_p;
+    uint32_t desc_num = priv->tx_currdescnum;
+    struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num];
+    uint64_t desc_start = (uint64_t)desc_p;
+    uint64_t desc_end = desc_start + roundup(sizeof(*desc_p), DW_GMAC_DMA_ALIGN);
+    uint64_t data_start = desc_p->dmamac_addr;
+    uint64_t data_end = data_start + roundup(length, DW_GMAC_DMA_ALIGN);
+    uint32_t count = 0;
+
+    /*
+     * Strictly we only need to invalidate the "txrx_status" field
+     * for the following check, but on some platforms we cannot
+     * invalidate only 4 bytes, so we flush the entire descriptor,
+     * which is 16 bytes in total. This is safe because the
+     * individual descriptors in the array are each aligned to
+     * DW_GMAC_DMA_ALIGN and padded appropriately.
+     */
+
+    /* Check if the descriptor is owned by CPU */
+    while (1) {
+        rt_hw_cpu_dcache_invalidate(desc_start, desc_end - desc_start);
+        if (!(desc_p->txrx_status & CVI_DESC_TXSTS_OWNBYDMA)) {
+            break;
+        }
+        if (count > 1000) {
+            rt_kprintf("desc onwer is DMA\n");
+            return -1;
+        }
+        count ++;
+        rt_thread_mdelay(1);
+    }
+
+    memcpy((void *)data_start, frame, length);
+
+    /* Flush data to be sent */
+    rt_hw_cpu_dcache_clean(data_start, data_end - data_start);
+
+#if defined(CONFIG_DW_ALTDESCRIPTOR)
+    desc_p->txrx_status |= CVI_DESC_TXSTS_TXFIRST | CVI_DESC_TXSTS_TXLAST;
+    desc_p->dmamac_cntl &= ~CVI_DESC_TXCTRL_SIZE1MASK;
+    desc_p->dmamac_cntl |= (length << CVI_DESC_TXCTRL_SIZE1SHFT) &
+                   CVI_DESC_TXCTRL_SIZE1MASK;
+
+    desc_p->txrx_status &= ~(CVI_DESC_TXSTS_MSK);
+    desc_p->txrx_status |= CVI_DESC_TXSTS_OWNBYDMA;
+#else
+    desc_p->dmamac_cntl &= ~CVI_DESC_TXCTRL_SIZE1MASK;
+    desc_p->dmamac_cntl |= ((length << CVI_DESC_TXCTRL_SIZE1SHFT) &
+                   CVI_DESC_TXCTRL_SIZE1MASK) | CVI_DESC_TXCTRL_TXLAST |
+                   CVI_DESC_TXCTRL_TXFIRST;
+
+    desc_p->txrx_status = CVI_DESC_TXSTS_OWNBYDMA;
+#endif
+
+    /* Flush modified buffer descriptor */
+    rt_hw_cpu_dcache_clean(desc_start, desc_end - desc_start);
+
+    /* Test the wrap-around condition. */
+    if (++desc_num >= CVI_CONFIG_TX_DESCR_NUM)
+        desc_num = 0;
+
+    priv->tx_currdescnum = desc_num;
+
+    /* Start the transmission */
+    dma_reg->txpolldemand = CVI_POLL_DATA;
+
+    return 0;
+}
+
+static int32_t designware_eth_recv(eth_mac_handle_t handle, uint8_t **packetp)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    uint32_t status, desc_num = priv->rx_currdescnum;
+    struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
+    int32_t length = -1;
+    uint64_t desc_start = (uint64_t)desc_p;
+    uint64_t desc_end = desc_start +
+        roundup(sizeof(*desc_p), DW_GMAC_DMA_ALIGN);
+    uint64_t data_start = desc_p->dmamac_addr;
+    uint64_t data_end;
+
+    /* Invalidate entire buffer descriptor */
+    rt_hw_cpu_dcache_invalidate(desc_start, desc_end - desc_start);
+    status = desc_p->txrx_status;
+    /* Check  if the owner is the CPU */
+    if (!(status & CVI_DESC_RXSTS_OWNBYDMA)) {
+        length = (status & CVI_DESC_RXSTS_FRMLENMSK) >>
+             CVI_DESC_RXSTS_FRMLENSHFT;
+        /* Invalidate received data */
+        data_end = data_start + roundup(length, DW_GMAC_DMA_ALIGN);
+        rt_hw_cpu_dcache_invalidate(data_start, data_end - data_start);
+        *packetp = (uint8_t *)((uint64_t)desc_p->dmamac_addr);
+    }
+
+    return length;
+}
+
+static int32_t designware_free_pkt(eth_mac_handle_t handle)
+{
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    uint32_t desc_num = priv->rx_currdescnum;
+    struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
+    uint64_t desc_start = (uint64_t)desc_p;
+    uint64_t desc_end = desc_start +
+        roundup(sizeof(*desc_p), DW_GMAC_DMA_ALIGN);
+
+    /*
+     * Make the current descriptor valid again and go to
+     * the next one
+     */
+    desc_p->txrx_status |= CVI_DESC_RXSTS_OWNBYDMA;
+
+    /* Flush only status field - others weren't changed */
+    rt_hw_cpu_dcache_clean(desc_start, desc_end - desc_start);
+
+    /* Test the wrap-around condition. */
+    if (++desc_num >= CVI_CONFIG_RX_DESCR_NUM)
+        desc_num = 0;
+    priv->rx_currdescnum = desc_num;
+
+    return 0;
+}
+
+/**
+  \brief       Connect phy device to mac device.
+  \param[in]   handle_mac  mac handle
+  \param[in]   handle_phy  phy handle
+*/
+void dw_eth_mac_connect_phy(eth_mac_handle_t handle_mac, eth_phy_handle_t handle_phy)
+{
+    RT_ASSERT(handle_mac);
+    RT_ASSERT(handle_phy);
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle_mac;
+    eth_phy_dev_t *phy_dev = (eth_phy_dev_t *)handle_phy;
+
+    mac_dev->phy_dev = phy_dev;
+}
+
+/**
+  \brief       Read Ethernet PHY Register through Management Interface.
+  \param[in]   handle  ethernet handle
+  \param[in]   phy_addr  5-bit device address
+  \param[in]   reg_addr  5-bit register address
+  \param[out]  data      Pointer where the result is written to
+  \return      error code
+*/
+int32_t dw_eth_mac_phy_read(eth_mac_handle_t handle, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
+{
+    RT_ASSERT(handle);
+    RT_ASSERT(data);
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    struct dw_gmac_mac_regs *mac_reg = priv->mac_regs_p;
+    uint16_t miiaddr;
+    int32_t start;
+
+    miiaddr = ((phy_addr << CVI_MIIADDRSHIFT) & CVI_MII_ADDRMSK) |
+          ((reg_addr << CVI_MIIREGSHIFT) & CVI_MII_REGMSK);
+
+    mac_reg->miiaddr = (miiaddr | CVI_MII_CLKRANGE_150_250M | CVI_MII_BUSY);
+
+    start = rt_tick_get_millisecond();
+    while ((rt_tick_get_millisecond() - start) < CVI_CONFIG_MDIO_TIMEOUT) {
+        if (!(mac_reg->miiaddr & CVI_MII_BUSY)) {
+            *data = mac_reg->miidata;
+            return 0;
+        }
+        rt_hw_us_delay(10);
+    };
+
+    return -1;
+}
+
+/**
+  \brief       Write Ethernet PHY Register through Management Interface.
+  \param[in]   handle  ethernet handle
+  \param[in]   phy_addr  5-bit device address
+  \param[in]   reg_addr  5-bit register address
+  \param[in]   data      16-bit data to write
+  \return      error code
+*/
+int32_t dw_eth_mac_phy_write(eth_mac_handle_t handle, uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
+{
+    RT_ASSERT(handle);
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    struct dw_gmac_priv *priv = mac_dev->priv;
+    struct dw_gmac_mac_regs *mac_reg = priv->mac_regs_p;
+    uint16_t miiaddr;
+    int32_t start;
+
+    mac_reg->miidata = data;
+    miiaddr = ((phy_addr << CVI_MIIADDRSHIFT) & CVI_MII_ADDRMSK) |
+          ((reg_addr << CVI_MIIREGSHIFT) & CVI_MII_REGMSK) | CVI_MII_WRITE;
+
+    mac_reg->miiaddr = (miiaddr | CVI_MII_CLKRANGE_150_250M | CVI_MII_BUSY);
+
+    start = rt_tick_get_millisecond();
+    while ((rt_tick_get_millisecond() - start) < CVI_CONFIG_MDIO_TIMEOUT) {
+        if (!(mac_reg->miiaddr & CVI_MII_BUSY)) {
+            return 0;
+        }
+        rt_hw_us_delay(10);
+    };
+
+    return -1;
+}
+
+/**
+  \brief       Control Ethernet Interface.
+  \param[in]   handle  ethernet handle
+  \param[in]   control  Operation
+  \param[in]   arg      Argument of operation (optional)
+  \return      error code
+*/
+int32_t cvi_eth_mac_control(eth_mac_handle_t handle, uint32_t control, uint32_t arg)
+{
+    RT_ASSERT(handle);
+
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    int32_t ret = 0;
+
+    RT_ASSERT(mac_dev->phy_dev);
+
+    switch (control) {
+        case CSI_ETH_MAC_CONFIGURE:
+            if (arg) {
+                /* startup mac */
+                ret = designware_eth_start(handle);
+            } else {
+                /* stop mac */
+                designware_eth_stop(handle);
+            }
+            break;
+
+        case DRV_ETH_MAC_ADJUST_LINK:
+            ret = designware_adjust_link(handle);
+            break;
+
+        case CSI_ETH_MAC_CONTROL_TX:
+            if (arg) {
+                /* enable TX */
+                ret = designware_eth_enable(handle, CSI_ETH_MAC_CONTROL_TX);
+            } else {
+                /* disable TX */
+                ret = designware_eth_disable(handle, CSI_ETH_MAC_CONTROL_TX);
+            }
+            break;
+
+        case CSI_ETH_MAC_CONTROL_RX:
+            if (arg) {
+                /* enable RX */
+                ret = designware_eth_enable(handle, CSI_ETH_MAC_CONTROL_RX);
+            } else {
+                /* disable RX */
+                ret = designware_eth_disable(handle, CSI_ETH_MAC_CONTROL_RX);
+            }
+            break;
+
+        case DRV_ETH_MAC_CONTROL_IRQ:
+            if (arg) {
+                /* enable interrupt */
+            } else {
+                /* disable interrupt */
+            }
+            break;
+
+        default:
+            break;
+    };
+
+    return ret;
+}
+
+/**
+  \brief       Get Ethernet MAC Address.
+  \param[in]   handle  ethernet handle
+  \param[in]   mac  Pointer to address
+  \return      error code
+*/
+int32_t cvi_eth_mac_get_macaddr(eth_mac_handle_t handle, eth_mac_addr_t *mac)
+{
+    RT_ASSERT(handle);
+    RT_ASSERT(mac);
+
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+
+    designware_read_hwaddr(handle);
+
+    memcpy(mac->b, mac_dev->mac_addr, sizeof(mac_dev->mac_addr));
+
+    return 0;
+}
+
+/**
+  \brief       Set Ethernet MAC Address.
+  \param[in]   handle  ethernet handle
+  \param[in]   mac  Pointer to address
+  \return      error code
+*/
+int32_t cvi_eth_mac_set_macaddr(eth_mac_handle_t handle, const eth_mac_addr_t *mac)
+{
+    RT_ASSERT(handle);
+    RT_ASSERT(mac);
+
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+    memcpy(mac_dev->mac_addr, mac->b, sizeof(mac->b));
+
+    designware_write_hwaddr(handle);
+
+    return 0;
+}
+
+/**
+  \brief       Send Ethernet frame.
+  \param[in]   handle  ethernet handle
+  \param[in]   frame  Pointer to frame buffer with data to send
+  \param[in]   len    Frame buffer length in bytes
+  \param[in]   flags  Frame transmit flags (see CSI_ETH_MAC_TX_FRAME_...)
+  \return      error code
+*/
+int32_t cvi_eth_mac_send_frame(eth_mac_handle_t handle, const uint8_t *frame, uint32_t len, uint32_t flags)
+{
+    RT_ASSERT(handle);
+    RT_ASSERT(frame);
+
+    return designware_eth_send(handle, frame, len);
+}
+
+/**
+  \brief       Read data of received Ethernet frame.
+  \param[in]   handle  ethernet handle
+  \param[in]   frame  Pointer to frame buffer for data to read into
+  \param[in]   len    Frame buffer length in bytes
+  \return      number of data bytes read or execution status
+                 - value >= 0: number of data bytes read
+                 - value < 0: error occurred, value is execution status as defined with execution_status
+*/
+int32_t cvi_eth_mac_read_frame(eth_mac_handle_t handle, uint8_t *frame, uint32_t len)
+{
+    RT_ASSERT(handle);
+    RT_ASSERT(frame);
+
+    uint8_t *packet = NULL;
+    int32_t actual_length;
+
+    actual_length = designware_eth_recv(handle, &packet);
+
+    if (actual_length < 0) {
+        return -1;
+    }
+
+    /* process received packet */
+    actual_length = (actual_length > len) ? len : actual_length;
+
+    if (packet != NULL) {
+        memcpy(frame, packet, actual_length);
+    }
+
+    designware_free_pkt(handle);
+
+    return actual_length;
+}
+
+
+/**
+  \brief       This function is used to initialize Ethernet device and register an event callback.
+  \param[in]   idx device id
+  \param[in]   cb  callback to handle ethernet event
+  \return      return ethernet handle if success
+ */
+eth_mac_handle_t cvi_eth_mac_init(unsigned int base)
+{
+    gmac_dev_t *mac_dev = &gmac_instance[0];
+    struct dw_gmac_priv *priv, *priv_unalign;
+
+    mac_dev->base = (unsigned long)base;
+    // mac_dev->irq = (uint8_t)DW_MAC_IRQ;
+    // mac_dev->cb_event = cb_event;
+
+    priv = memalign(DW_GMAC_DMA_ALIGN, sizeof(struct dw_gmac_priv), (void **)&priv_unalign);
+    if (!priv)
+    {
+        rt_kprintf("malloc fail\n");
+        return NULL;
+    }
+
+    memset(priv_unalign, 0, sizeof(struct dw_gmac_priv) + DW_GMAC_DMA_ALIGN);
+
+    priv->mac_regs_p = (struct dw_gmac_mac_regs *)mac_dev->base;
+    priv->dma_regs_p = (struct dw_gmac_dma_regs *)(mac_dev->base + CVI_DW_DMA_BASE_OFFSET);
+
+    mac_dev->priv_unalign = priv_unalign;
+    mac_dev->priv = priv;
+
+    return (eth_mac_handle_t)mac_dev;
+}
+
+/**
+  \brief       This function is used to de-initialize Ethernet device.
+  \param[in]   handle  ethernet handle
+  \return      error code
+ */
+void de_eth_gmac_deinit(eth_mac_handle_t handle)
+{
+    RT_ASSERT(handle);
+    gmac_dev_t *mac_dev = (gmac_dev_t *)handle;
+
+    if (mac_dev->priv_unalign)
+    {
+        rt_free(mac_dev->priv_unalign);
+        mac_dev->priv_unalign = RT_NULL;
+    }
+}

+ 331 - 0
bsp/cvitek/drivers/libraries/eth/dw_eth_mac.h

@@ -0,0 +1,331 @@
+/*
+* Copyright (C) Cvitek Co., Ltd. 2019-2022. All rights reserved.
+*/
+
+#ifndef _DW_GMAC_182x_H_
+#define _DW_GMAC_182x_H_
+
+#include "cvi_eth_phy.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *eth_mac_handle_t;
+
+#define CSI_ETH_MAC_CONFIGURE           (0x01)      ///< Configure MAC; arg = configuration
+#define CSI_ETH_MAC_CONTROL_TX          (0x02)      ///< Transmitter; arg: 0=disabled (default), 1=enabled
+#define CSI_ETH_MAC_CONTROL_RX          (0x03)      ///< Receiver; arg: 0=disabled (default), 1=enabled
+#define CSI_ETH_MAC_FLUSH               (0x04)      ///< Flush buffer; arg = CSI_ETH_MAC_FLUSH_...
+#define CSI_ETH_MAC_SLEEP               (0x05)      ///< Sleep mode; arg: 1=enter and wait for Magic packet, 0=exit
+#define CSI_ETH_MAC_VLAN_FILTER         (0x06)      ///< VLAN Filter for received frames; arg15..0: VLAN Tag; arg16: optional CSI_ETH_MAC_VLAN_FILTER_ID_ONLY; 0=disabled (default)
+#define DRV_ETH_MAC_ADJUST_LINK         (0x07)      ///< Adjust MAC link state according to phy state; arg: phy handle
+#define DRV_ETH_MAC_CONTROL_IRQ         (0x08)      ///< Interrupt request; arg: 0=disable, 1=enable
+
+#define DW_GMAC_DMA_ALIGN 128
+
+#ifndef _DW_ETH_H
+#define _DW_ETH_H
+
+#define GMAC_NULL_PARAM_CHK(para)   CSI_PARAM_CHK(para, -1)
+#define GMAC_NULL_PARAM_CHK_NORETVAL(para)   CSI_PARAM_CHK_NORETVAL(para)
+
+#define CVI_CONFIG_SYS_HZ 1000
+#define CVI_CONFIG_TX_DESCR_NUM 16
+#define CVI_CONFIG_RX_DESCR_NUM 16
+#define CVI_CONFIG_ETH_BUFSIZE  2048
+#define CVI_TX_TOTAL_BUFSIZE    (CVI_CONFIG_ETH_BUFSIZE * CVI_CONFIG_TX_DESCR_NUM)
+#define CVI_RX_TOTAL_BUFSIZE    (CVI_CONFIG_ETH_BUFSIZE * CVI_CONFIG_RX_DESCR_NUM)
+
+#define CVI_CONFIG_MACRESET_TIMEOUT (3 * CVI_CONFIG_SYS_HZ)
+#define CVI_CONFIG_MDIO_TIMEOUT (3 * CVI_CONFIG_SYS_HZ)
+
+struct dw_gmac_mac_regs {
+    volatile uint32_t conf;     /* 0x00 */
+    volatile uint32_t framefilt;        /* 0x04 */
+    volatile uint32_t hashtablehigh;    /* 0x08 */
+    volatile uint32_t hashtablelow; /* 0x0c */
+    volatile uint32_t miiaddr;      /* 0x10 */
+    volatile uint32_t miidata;      /* 0x14 */
+    volatile uint32_t flowcontrol;  /* 0x18 */
+    volatile uint32_t vlantag;      /* 0x1c */
+    volatile uint32_t version;      /* 0x20 */
+    volatile uint32_t reserved_1[5];
+    volatile uint32_t intreg;       /* 0x38 */
+    volatile uint32_t intmask;      /* 0x3c */
+    volatile uint32_t macaddr0hi;       /* 0x40 */
+    volatile uint32_t macaddr0lo;       /* 0x44 */
+};
+
+/* MAC configuration register definitions */
+#define CVI_FRAMEBURSTENABLE    (1 << 21)
+#define CVI_MII_PORTSELECT      (1 << 15)
+#define CVI_FES_100         (1 << 14)
+#define CVI_DISABLERXOWN        (1 << 13)
+#define CVI_FULLDPLXMODE        (1 << 11)
+#define CVI_RXENABLE        (1 << 2)
+#define CVI_TXENABLE        (1 << 3)
+
+/* MII address register definitions */
+#define CVI_MII_BUSY        (1 << 0)
+#define CVI_MII_WRITE       (1 << 1)
+#define CVI_MII_CLKRANGE_60_100M    (0)
+#define CVI_MII_CLKRANGE_100_150M   (0x4)
+#define CVI_MII_CLKRANGE_20_35M (0x8)
+#define CVI_MII_CLKRANGE_35_60M (0xC)
+#define CVI_MII_CLKRANGE_150_250M   (0x10)
+#define CVI_MII_CLKRANGE_250_300M   (0x14)
+
+#define CVI_MIIADDRSHIFT        (11)
+#define CVI_MIIREGSHIFT     (6)
+#define CVI_MII_REGMSK      (0x1F << 6)
+#define CVI_MII_ADDRMSK     (0x1F << 11)
+
+typedef uint32_t  reg_type;
+struct dw_gmac_dma_regs {
+    volatile reg_type busmode;      /* 0x00 */
+    volatile reg_type txpolldemand; /* 0x04 */
+    volatile reg_type rxpolldemand; /* 0x08 */
+    volatile reg_type rxdesclistaddr;   /* 0x0c */
+    volatile reg_type txdesclistaddr;   /* 0x10 */
+    volatile reg_type status;       /* 0x14 */
+    volatile reg_type opmode;       /* 0x18 */
+    volatile reg_type intenable;        /* 0x1c */
+    volatile reg_type discardedcount;       /* 0x20 */
+    volatile reg_type wdtforri;     /* 0x24 */
+    //volatile reg_type reserved1[2];
+    volatile reg_type axibus;       /* 0x28 */
+    volatile reg_type reserved2[7];
+    volatile reg_type currhosttxdesc;   /* 0x48 */
+    volatile reg_type currhostrxdesc;   /* 0x4c */
+    volatile reg_type currhosttxbuffaddr;   /* 0x50 */
+    volatile reg_type currhostrxbuffaddr;   /* 0x54 */
+};
+
+/* Operation mode definitions */
+#define CVI_RXSTART         (1 << 1)
+#define CVI_TXSECONDFRAME       (1 << 2)
+#define CVI_TXSTART         (1 << 13)
+#define CVI_FLUSHTXFIFO     (1 << 20)
+#define CVI_STOREFORWARD        (1 << 21)
+#define CVI_DW_DMA_BASE_OFFSET  (0x1000)
+
+/* Default DMA Burst length */
+#ifndef CONFIG_DW_GMAC_DEFAULT_DMA_PBL
+#define CONFIG_DW_GMAC_DEFAULT_DMA_PBL 8
+#endif
+/* Status definitions */
+#define CVI_DMA_STATUS_ERI  0x00004000  /* Early Receive Interrupt */
+#define CVI_DMA_STATUS_RI   0x00000040  /* Receive Interrupt */
+#define CVI_DMA_STATUS_TI   0x00000001  /* Transmit Interrupt */
+
+/* Bus mode register definitions */
+
+#define CVI_DMAMAC_SRST     (1 << 0)
+#define CVI_RXHIGHPRIO      (1 << 1)
+#define CVI_FIXEDBURST      (1 << 16)
+#define CVI_PRIORXTX_11     (0 << 14)
+#define CVI_PRIORXTX_21     (1 << 14)
+#define CVI_PRIORXTX_31     (2 << 14)
+#define CVI_PRIORXTX_41     (3 << 14)
+#define CVI_DMA_PBL         (CONFIG_DW_GMAC_DEFAULT_DMA_PBL<<8)
+
+/* Poll demand definitions */
+#define CVI_POLL_DATA       (0xFFFFFFFF)
+
+
+/* Descriptior related definitions */
+#define CVI_MAC_MAX_FRAME_SZ    (1600)
+
+struct dmamacdescr {
+    unsigned int txrx_status;
+    unsigned int dmamac_cntl;
+    unsigned int dmamac_addr;
+    unsigned int dmamac_next;
+} __attribute__((aligned(DW_GMAC_DMA_ALIGN)));
+
+/*
+ * txrx_status definitions
+ */
+
+/* tx status bits definitions */
+#if defined(CONFIG_DW_ALTDESCRIPTOR)
+
+#define CVI_DESC_TXSTS_OWNBYDMA     (1 << 31)
+#define CVI_DESC_TXSTS_TXINT        (1 << 30)
+#define CVI_DESC_TXSTS_TXLAST       (1 << 29)
+#define CVI_DESC_TXSTS_TXFIRST      (1 << 28)
+#define CVI_DESC_TXSTS_TXCRCDIS     (1 << 27)
+
+#define CVI_DESC_TXSTS_TXPADDIS     (1 << 26)
+#define CVI_DESC_TXSTS_TXCHECKINSCTRL   (3 << 22)
+#define CVI_DESC_TXSTS_TXRINGEND        (1 << 21)
+#define CVI_DESC_TXSTS_TXCHAIN      (1 << 20)
+#define CVI_DESC_TXSTS_MSK          (0x1FFFF << 0)
+
+#else
+
+#define CVI_DESC_TXSTS_OWNBYDMA     (1 << 31)
+#define CVI_DESC_TXSTS_MSK          (0x1FFFF << 0)
+
+#endif
+
+/* rx status bits definitions */
+#define CVI_DESC_RXSTS_OWNBYDMA     (1 << 31)
+#define CVI_DESC_RXSTS_DAFILTERFAIL     (1 << 30)
+#define CVI_DESC_RXSTS_FRMLENMSK        (0x3FFF << 16)
+#define CVI_DESC_RXSTS_FRMLENSHFT       (16)
+
+#define CVI_DESC_RXSTS_ERROR        (1 << 15)
+#define CVI_DESC_RXSTS_RXTRUNCATED      (1 << 14)
+#define CVI_DESC_RXSTS_SAFILTERFAIL     (1 << 13)
+#define CVI_DESC_RXSTS_RXIPC_GIANTFRAME (1 << 12)
+#define CVI_DESC_RXSTS_RXDAMAGED        (1 << 11)
+#define CVI_DESC_RXSTS_RXVLANTAG        (1 << 10)
+#define CVI_DESC_RXSTS_RXFIRST      (1 << 9)
+#define CVI_DESC_RXSTS_RXLAST       (1 << 8)
+#define CVI_DESC_RXSTS_RXIPC_GIANT      (1 << 7)
+#define CVI_DESC_RXSTS_RXCOLLISION      (1 << 6)
+#define CVI_DESC_RXSTS_RXFRAMEETHER     (1 << 5)
+#define CVI_DESC_RXSTS_RXWATCHDOG       (1 << 4)
+#define CVI_DESC_RXSTS_RXMIIERROR       (1 << 3)
+#define CVI_DESC_RXSTS_RXDRIBBLING      (1 << 2)
+#define CVI_DESC_RXSTS_RXCRC        (1 << 1)
+
+/*
+ * dmamac_cntl definitions
+ */
+
+/* tx control bits definitions */
+#if defined(CONFIG_DW_ALTDESCRIPTOR)
+
+#define CVI_DESC_TXCTRL_SIZE1MASK       (0x1FFF << 0)
+#define CVI_DESC_TXCTRL_SIZE1SHFT       (0)
+#define CVI_DESC_TXCTRL_SIZE2MASK       (0x1FFF << 16)
+#define CVI_DESC_TXCTRL_SIZE2SHFT       (16)
+
+#else
+
+#define CVI_DESC_TXCTRL_TXINT       (1 << 31)
+#define CVI_DESC_TXCTRL_TXLAST      (1 << 30)
+#define CVI_DESC_TXCTRL_TXFIRST     (1 << 29)
+#define CVI_DESC_TXCTRL_TXCHECKINSCTRL  (3 << 27)
+#define CVI_DESC_TXCTRL_TXCRCDIS        (1 << 26)
+#define CVI_DESC_TXCTRL_TXRINGEND       (1 << 25)
+#define CVI_DESC_TXCTRL_TXCHAIN     (1 << 24)
+
+#define CVI_DESC_TXCTRL_SIZE1MASK       (0x7FF << 0)
+#define CVI_DESC_TXCTRL_SIZE1SHFT       (0)
+#define CVI_DESC_TXCTRL_SIZE2MASK       (0x7FF << 11)
+#define CVI_DESC_TXCTRL_SIZE2SHFT       (11)
+
+#endif
+
+/* rx control bits definitions */
+#if defined(CONFIG_DW_ALTDESCRIPTOR)
+
+#define CVI_DESC_RXCTRL_RXINTDIS        (1 << 31)
+#define CVI_DESC_RXCTRL_RXRINGEND       (1 << 15)
+#define CVI_DESC_RXCTRL_RXCHAIN     (1 << 14)
+
+#define CVI_DESC_RXCTRL_SIZE1MASK       (0x1FFF << 0)
+#define CVI_DESC_RXCTRL_SIZE1SHFT       (0)
+#define CVI_DESC_RXCTRL_SIZE2MASK       (0x1FFF << 16)
+#define CVI_DESC_RXCTRL_SIZE2SHFT       (16)
+
+#else
+
+#define CVI_DESC_RXCTRL_RXINTDIS        (1 << 31)
+#define CVI_DESC_RXCTRL_RXRINGEND       (1 << 25)
+#define CVI_DESC_RXCTRL_RXCHAIN     (1 << 24)
+
+#define CVI_DESC_RXCTRL_SIZE1MASK       (0x7FF << 0)
+#define CVI_DESC_RXCTRL_SIZE1SHFT       (0)
+#define CVI_DESC_RXCTRL_SIZE2MASK       (0x7FF << 11)
+#define CVI_DESC_RXCTRL_SIZE2SHFT       (11)
+
+#endif
+
+struct dw_gmac_priv {
+    struct dmamacdescr tx_mac_descrtable[CVI_CONFIG_TX_DESCR_NUM] __aligned(DW_GMAC_DMA_ALIGN);
+    struct dmamacdescr rx_mac_descrtable[CVI_CONFIG_RX_DESCR_NUM] __aligned(DW_GMAC_DMA_ALIGN);
+    char txbuffs[CVI_TX_TOTAL_BUFSIZE] __aligned(DW_GMAC_DMA_ALIGN);
+    char rxbuffs[CVI_RX_TOTAL_BUFSIZE] __aligned(DW_GMAC_DMA_ALIGN);
+
+    uint32_t interface;
+    uint32_t max_speed;
+    uint32_t tx_currdescnum;
+    uint32_t rx_currdescnum;
+
+    struct dw_gmac_mac_regs *mac_regs_p;
+    struct dw_gmac_dma_regs *dma_regs_p;
+
+    //struct gpio_desc reset_gpio;
+};
+
+#ifdef CONFIG_DM_ETH
+int designware_eth_ofdata_to_platdata(struct udevice *dev);
+int designware_eth_probe(struct udevice *dev);
+extern const struct eth_ops designware_eth_ops;
+
+struct dw_eth_pdata {
+    struct eth_pdata eth_pdata;
+    u32 reset_delays[3];
+};
+
+int designware_eth_init(struct dw_eth_dev *priv, u8 *enetaddr);
+int designware_eth_enable(struct dw_eth_dev *priv);
+int designware_eth_send(struct udevice *dev, void *packet, int length);
+int designware_eth_recv(struct udevice *dev, int flags, uchar **packetp);
+int designware_eth_free_pkt(struct udevice *dev, uchar *packet,
+                   int length);
+void designware_eth_stop(struct udevice *dev);
+int designware_eth_write_hwaddr(struct udevice *dev);
+#endif
+
+#endif
+
+typedef struct {
+    // csi_dev_t            dev;
+    eth_phy_dev_t       *phy_dev;
+    unsigned long       base;
+    uint8_t             irq;
+    // eth_event_cb_t      cb_event;
+    uint8_t             mac_addr[6];
+    struct dw_gmac_priv *priv_unalign;
+    struct dw_gmac_priv *priv;
+} gmac_dev_t;
+
+/**
+\brief Ethernet MAC Address
+*/
+typedef struct eth_mac_addr {
+  uint8_t b[6];                         ///< MAC Address (6 bytes), MSB first
+} eth_mac_addr_t;
+
+static inline void *memalign(uint32_t align, uint32_t size, void **mem_unalign)
+{
+    void *mem;
+    uint32_t offset;
+
+    *mem_unalign = (void *)rt_malloc(size + align);
+
+    if (!*mem_unalign) {
+        return NULL;
+    }
+
+    offset = *(uint32_t *)mem_unalign % align;
+
+    if (offset == 0) {
+        mem = (struct eqos_priv *)*mem_unalign;
+    } else {
+        mem = (struct eqos_priv *)(*mem_unalign + (align - offset));
+    }
+    return mem;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DW_GMAC_182x_H_ */

+ 383 - 0
bsp/cvitek/drivers/libraries/eth/eth_phy_cvitek.c

@@ -0,0 +1,383 @@
+/*
+* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
+*/
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
+
+// #include <mmio.h>
+
+#include "cvi_eth_phy.h"
+#include "mii.h"
+
+
+
+// #define CVI_ETH_PHY_LOOPBACK
+#define LOOPBACK_XMII2MAC       0x8000
+#define LOOPBACK_PCS2MAC        0x2000
+#define LOOPBACK_PMA2MAC        0x1000
+#define LOOPBACK_RMII2PHY       0x0080
+
+#define EPHY_EFUSE_VALID_BIT_BASE 0x03050120
+#define EPHY_EFUSE_TXECHORC_FLAG 0x00000100 // bit 8
+#define EPHY_EFUSE_TXITUNE_FLAG 0x00000200 // bit 9
+#define EPHY_EFUSE_TXRXTERM_FLAG 0x00000800 // bit 11
+
+static inline bool phy_if_mode_is_rgmii(phy_if_mode_t interface)
+{
+    return interface >= PHY_IF_MODE_RGMII && interface <= PHY_IF_MODE_RGMII_TXID;
+}
+
+#if defined(CVI_ETH_PHY_LOOPBACK)
+static int cv181x_set_phy_loopback(eth_phy_handle_t handle, phy_loopback_mode_t mode)
+{
+    return 0;
+}
+#endif
+
+/**
+  \brief       Configure the cv181x before make it start up.
+  \param[in]   handle  phy handle
+  \return      error code
+*/
+/* CVITEK cv181x */
+int32_t cv181x_config(eth_phy_handle_t handle)
+{
+    assert(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+    uint32_t val = 0;
+
+    // eth_phy_reset(dev);
+
+    // set rg_ephy_apb_rw_sel 0x0804@[0]=1/APB by using APB interface
+    mmio_write_32(0x03009804, 0x0001);
+
+    // Release 0x0800[0]=0/shutdown
+    // mmio_write_32(0x03009800, 0x0900);
+
+    // Release 0x0800[2]=1/dig_rst_n, Let mii_reg can be accessabile
+    // mmio_write_32(0x03009800, 0x0904);
+
+    //mdelay(10);
+
+    // ANA INIT (PD/EN), switch to MII-page5
+    mmio_write_32(0x0300907c, 0x0500);
+    // Release ANA_PD p5.0x10@[13:8] = 6'b001100
+    mmio_write_32(0x03009040, 0x0c00);
+    // Release ANA_EN p5.0x10@[7:0] = 8'b01111110
+    mmio_write_32(0x03009040, 0x0c7e);
+
+    // Wait PLL_Lock, Lock_Status p5.0x12@[15] = 1
+    //mdelay(1);
+
+    // Release 0x0800[1] = 1/ana_rst_n
+    mmio_write_32(0x03009800, 0x0906);
+
+    // ANA INIT
+    // @Switch to MII-page5
+    mmio_write_32(0x0300907c, 0x0500);
+
+// Efuse register
+    // Set Double Bias Current
+    //Set rg_eth_txitune1  0x03009064 [15:8]
+    //Set rg_eth_txitune0  0x03009064 [7:0]
+    if ((mmio_read_32(EPHY_EFUSE_VALID_BIT_BASE) & EPHY_EFUSE_TXITUNE_FLAG) ==
+        EPHY_EFUSE_TXITUNE_FLAG) {
+        val = ((mmio_read_32(0x03051024) >> 24) & 0xFF) |
+                (((mmio_read_32(0x03051024) >> 16) & 0xFF) << 8);
+        mmio_clrsetbits_32(0x03009064, 0xFFFF, val);
+    } else
+        mmio_write_32(0x03009064, 0x5a5a);
+
+    // Set Echo_I
+    // Set rg_eth_txechoiadj 0x03009054  [15:8]
+    if ((mmio_read_32(EPHY_EFUSE_VALID_BIT_BASE) & EPHY_EFUSE_TXECHORC_FLAG) ==
+        EPHY_EFUSE_TXECHORC_FLAG) {
+        mmio_clrsetbits_32(0x03009054, 0xFF00, ((mmio_read_32(0x03051024) >> 8) & 0xFF) << 8);
+    } else
+        mmio_write_32(0x03009054, 0x0000);
+
+    //Set TX_Rterm & Echo_RC_Delay
+    // Set rg_eth_txrterm_p1  0x03009058 [11:8]
+    // Set rg_eth_txrterm     0x03009058  [7:4]
+    // Set rg_eth_txechorcadj 0x03009058  [3:0]
+    if ((mmio_read_32(EPHY_EFUSE_VALID_BIT_BASE) & EPHY_EFUSE_TXRXTERM_FLAG) ==
+        EPHY_EFUSE_TXRXTERM_FLAG) {
+        val = (((mmio_read_32(0x03051020) >> 28) & 0xF) << 4) |
+                (((mmio_read_32(0x03051020) >> 24) & 0xF) << 8);
+        mmio_clrsetbits_32(0x03009058, 0xFF0, val);
+    } else
+        mmio_write_32(0x03009058, 0x0bb0);
+
+// ETH_100BaseT
+    // Set Rise update
+    mmio_write_32(0x0300905c, 0x0c10);
+
+    // Set Falling phase
+    mmio_write_32(0x03009068, 0x0003);
+
+    // Set Double TX Bias Current
+    mmio_write_32(0x03009054, 0x0000);
+
+    // Switch to MII-page16
+    mmio_write_32(0x0300907c, 0x1000);
+
+    // Set MLT3 Positive phase code, Set MLT3 +0
+    mmio_write_32(0x03009068, 0x1000);
+    mmio_write_32(0x0300906c, 0x3020);
+    mmio_write_32(0x03009070, 0x5040);
+    mmio_write_32(0x03009074, 0x7060);
+
+    // Set MLT3 +I
+    mmio_write_32(0x03009058, 0x1708);
+    mmio_write_32(0x0300905c, 0x3827);
+    mmio_write_32(0x03009060, 0x5748);
+    mmio_write_32(0x03009064, 0x7867);
+
+    // Switch to MII-page17
+    mmio_write_32(0x0300907c, 0x1100);
+
+    // Set MLT3 Negative phase code, Set MLT3 -0
+    mmio_write_32(0x03009040, 0x9080);
+    mmio_write_32(0x03009044, 0xb0a0);
+    mmio_write_32(0x03009048, 0xd0c0);
+    mmio_write_32(0x0300904c, 0xf0e0);
+
+    // Set MLT3 -I
+    mmio_write_32(0x03009050, 0x9788);
+    mmio_write_32(0x03009054, 0xb8a7);
+    mmio_write_32(0x03009058, 0xd7c8);
+    mmio_write_32(0x0300905c, 0xf8e7);
+
+    // @Switch to MII-page5
+    mmio_write_32(0x0300907c, 0x0500);
+
+    // En TX_Rterm
+    mmio_write_32(0x03009040, (0x0001 | mmio_read_32(0x03009040)));
+
+//  Link Pulse
+    // Switch to MII-page10
+    mmio_write_32(0x0300907c, 0x0a00);
+
+    // Set Link Pulse
+    mmio_write_32(0x03009040, 0x2000);
+    mmio_write_32(0x03009044, 0x3832);
+    mmio_write_32(0x03009048, 0x3132);
+    mmio_write_32(0x0300904c, 0x2d2f);
+    mmio_write_32(0x03009050, 0x2c2d);
+    mmio_write_32(0x03009054, 0x1b2b);
+    mmio_write_32(0x03009058, 0x94a0);
+    mmio_write_32(0x0300905c, 0x8990);
+    mmio_write_32(0x03009060, 0x8788);
+    mmio_write_32(0x03009064, 0x8485);
+    mmio_write_32(0x03009068, 0x8283);
+    mmio_write_32(0x0300906c, 0x8182);
+    mmio_write_32(0x03009070, 0x0081);
+
+// TP_IDLE
+    // Switch to MII-page11
+    mmio_write_32(0x0300907c, 0x0b00);
+
+// Set TP_IDLE
+    mmio_write_32(0x03009040, 0x5252);
+    mmio_write_32(0x03009044, 0x5252);
+    mmio_write_32(0x03009048, 0x4B52);
+    mmio_write_32(0x0300904c, 0x3D47);
+    mmio_write_32(0x03009050, 0xAA99);
+    mmio_write_32(0x03009054, 0x989E);
+    mmio_write_32(0x03009058, 0x9395);
+    mmio_write_32(0x0300905C, 0x9091);
+    mmio_write_32(0x03009060, 0x8E8F);
+    mmio_write_32(0x03009064, 0x8D8E);
+    mmio_write_32(0x03009068, 0x8C8C);
+    mmio_write_32(0x0300906C, 0x8B8B);
+    mmio_write_32(0x03009070, 0x008A);
+
+// ETH 10BaseT Data
+    // Switch to MII-page13
+    mmio_write_32(0x0300907c, 0x0d00);
+
+    mmio_write_32(0x03009040, 0x1E0A);
+    mmio_write_32(0x03009044, 0x3862);
+    mmio_write_32(0x03009048, 0x1E62);
+    mmio_write_32(0x0300904c, 0x2A08);
+    mmio_write_32(0x03009050, 0x244C);
+    mmio_write_32(0x03009054, 0x1A44);
+    mmio_write_32(0x03009058, 0x061C);
+
+    // Switch to MII-page14
+    mmio_write_32(0x0300907c, 0x0e00);
+
+    mmio_write_32(0x03009040, 0x2D30);
+    mmio_write_32(0x03009044, 0x3470);
+    mmio_write_32(0x03009048, 0x0648);
+    mmio_write_32(0x0300904c, 0x261C);
+    mmio_write_32(0x03009050, 0x3160);
+    mmio_write_32(0x03009054, 0x2D5E);
+
+    // Switch to MII-page15
+    mmio_write_32(0x0300907c, 0x0f00);
+
+    mmio_write_32(0x03009040, 0x2922);
+    mmio_write_32(0x03009044, 0x366E);
+    mmio_write_32(0x03009048, 0x0752);
+    mmio_write_32(0x0300904c, 0x2556);
+    mmio_write_32(0x03009050, 0x2348);
+    mmio_write_32(0x03009054, 0x0C30);
+
+    // Switch to MII-page16
+    mmio_write_32(0x0300907c, 0x1000);
+
+    mmio_write_32(0x03009040, 0x1E08);
+    mmio_write_32(0x03009044, 0x3868);
+    mmio_write_32(0x03009048, 0x1462);
+    mmio_write_32(0x0300904c, 0x1A0E);
+    mmio_write_32(0x03009050, 0x305E);
+    mmio_write_32(0x03009054, 0x2F62);
+
+// LED PAD MUX
+    mmio_write_32(0x030010e0, 0x05);
+    mmio_write_32(0x030010e4, 0x05);
+    //(SD1_CLK selphy)
+    mmio_write_32(0x050270b0, 0x11111111);
+    //(SD1_CMD selphy)
+    mmio_write_32(0x050270b4, 0x11111111);
+
+// LED
+    // Switch to MII-page1
+    mmio_write_32(0x0300907c, 0x0100);
+
+    // select LED_LNK/SPD/DPX out to LED_PAD
+    mmio_write_32(0x03009068, (mmio_read_32(0x03009068) & ~0x0f00));
+
+    // @Switch to MII-page0
+    mmio_write_32(0x0300907c, 0x0000);
+
+    // PHY_ID
+    mmio_write_32(0x03009008, 0x0043);
+    mmio_write_32(0x0300900c, 0x5649);
+
+    // Switch to MII-page19
+    mmio_write_32(0x0300907c, 0x1300);
+    mmio_write_32(0x03009058, 0x0012);
+    // set agc max/min swing
+    mmio_write_32(0x0300905C, 0x6848);
+
+    // Switch to MII-page18
+    mmio_write_32(0x0300907c, 0x1200);
+    // p18.0x12, lpf
+    mmio_write_32(0x03009048, 0x0808);
+    mmio_write_32(0x0300904C, 0x0808);
+// hpf
+//sean
+    mmio_write_32(0x03009050, 0x32f8);
+    mmio_write_32(0x03009054, 0xf8dc);
+
+    // Switch to MII-page0
+    mmio_write_32(0x0300907c, 0x0000);
+    // EPHY start auto-neg procedure
+    mmio_write_32(0x03009800, 0x090e);
+
+    // switch to MDIO control by ETH_MAC
+    mmio_write_32(0x03009804, 0x0000);
+
+    genphy_config(dev);
+
+#if defined(CVI_ETH_PHY_LOOPBACK)
+    cv181x_set_phy_loopback(handle, LOOPBACK_PCS2MAC);
+#endif
+
+    return 0;
+}
+
+/**
+  \brief       Parse 88E1xxx's speed and duplex from status register.
+  \param[in]   dev  phy device pointer
+  \return      error code
+*/
+static int32_t cv181x_parse_status(eth_phy_dev_t *dev)
+{
+    assert(dev);
+    assert(dev->priv);
+    eth_phy_priv_t *priv = dev->priv;
+    uint8_t phy_addr = dev->phy_addr;
+    uint16_t mii_reg;
+    int32_t ret;
+
+    ret = eth_phy_read(priv, phy_addr, CVI_MII_BMSR, &mii_reg);
+
+    if (ret != 0) {
+        return ret;
+    }
+
+    if (mii_reg & (CVI_BMSR_100FULL | CVI_BMSR_100HALF))
+        priv->link_info.speed = CSI_ETH_SPEED_100M;
+    else
+        priv->link_info.speed = CSI_ETH_SPEED_10M;
+
+    if (mii_reg & (CVI_BMSR_10FULL | CVI_BMSR_100FULL))
+        priv->link_info.duplex = CSI_ETH_DUPLEX_FULL;
+    else
+        priv->link_info.duplex = CSI_ETH_DUPLEX_HALF;
+
+    return 0;
+}
+
+/**
+  \brief       Start up the 88E1111.
+  \param[in]   handle  phy handle
+  \return      error code
+*/
+int32_t cv181x_start(eth_phy_handle_t handle)
+{
+    assert(handle);
+
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+    int32_t ret;
+
+    /* Read the Status (2x to make sure link is right) */
+    ret = genphy_update_link(dev);
+
+    if (ret) {
+        return ret;
+    }
+
+    return cv181x_parse_status(dev);
+}
+
+/**
+  \brief       Halt the cv181x.
+  \param[in]   handle  phy handle
+  \return      error code
+*/
+int32_t cv181x_stop(eth_phy_handle_t handle)
+{
+    return 0;
+}
+
+/**
+  \brief       Update the cv181x's link state.
+  \param[in]   handle  phy handle
+  \return      error code
+*/
+int32_t cv181x_update_link(eth_phy_handle_t handle)
+{
+    assert(handle);
+    eth_phy_dev_t *dev = (eth_phy_dev_t *)handle;
+    return cv181x_parse_status(dev);;
+}
+
+
+/* Support for cv181x PHYs */
+eth_phy_dev_t cv181x_device = {
+    .name = "CVITEK,CV181X",
+    .phy_id = 0x00435649,
+    .mask = 0xffffffff,
+    .features = CVI_PHY_BASIC_FEATURES,
+    .config = &cv181x_config,
+    .start = &cv181x_start,
+    .stop = &cv181x_stop,
+    //.loopback = &cv181x_loopback,
+    //.update_link = &cv181x_update_link,
+};

+ 188 - 0
bsp/cvitek/drivers/libraries/eth/mii.h

@@ -0,0 +1,188 @@
+/*
+* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
+*/
+
+#ifndef __MII_H__
+#define __MII_H__
+
+/* Basic mode control register. */
+#define CVI_BMCR_RESV           (0x003f)
+#define CVI_BMCR_SPEED1000      (0x0040)
+#define CVI_BMCR_CTST           (0x0080)
+#define CVI_BMCR_FULLDPLX       (0x0100)
+#define CVI_BMCR_ANRESTART      (0x0200)
+#define CVI_BMCR_ISOLATE        (0x0400)
+#define CVI_BMCR_PDOWN          (0x0800)
+#define CVI_BMCR_ANENABLE       (0x1000)
+#define CVI_BMCR_SPEED100       (0x2000)
+#define CVI_BMCR_LOOPBACK       (0x4000)
+#define CVI_BMCR_RESET          (0x8000)
+
+/* Basic mode status register. */
+#define CVI_BMSR_ERCAP          (0x0001)
+#define CVI_BMSR_JCD            (0x0002)
+#define CVI_BMSR_LSTATUS        (0x0004)
+#define CVI_BMSR_ANEGCAPABLE    (0x0008)
+#define CVI_BMSR_RFAULT         (0x0010)
+#define CVI_BMSR_ANEGCOMPLETE   (0x0020)
+#define CVI_BMSR_RESV           (0x00c0)
+#define CVI_BMSR_ESTATEN        (0x0100)
+#define CVI_BMSR_100HALF2       (0x0200)
+#define CVI_BMSR_100FULL2       (0x0400)
+#define CVI_BMSR_10HALF         (0x0800)
+#define CVI_BMSR_10FULL         (0x1000)
+#define CVI_BMSR_100HALF        (0x2000)
+#define CVI_BMSR_100FULL        (0x4000)
+#define CVI_BMSR_100BASE4       (0x8000)
+
+/* Advertisement control register. */
+#define CVI_ADVERTISE_CSMA            (0x0001)
+#define CVI_ADVERTISE_SLCT            (0x001f)
+#define CVI_ADVERTISE_10HALF          (0x0020)
+#define CVI_ADVERTISE_1000XFULL       (0x0020)
+#define CVI_ADVERTISE_10FULL          (0x0040)
+#define CVI_ADVERTISE_1000XHALF       (0x0040)
+#define CVI_ADVERTISE_100HALF         (0x0080)
+#define CVI_ADVERTISE_1000XPAUSE      (0x0080)
+#define CVI_ADVERTISE_100FULL         (0x0100)
+#define CVI_ADVERTISE_1000XPSE_ASYM   (0x0100)
+#define CVI_ADVERTISE_100BASE4        (0x0200)
+#define CVI_ADVERTISE_PAUSE_CAP       (0x0400)
+#define CVI_ADVERTISE_PAUSE_ASYM      (0x0800)
+#define CVI_ADVERTISE_RESV            (0x1000)
+#define CVI_ADVERTISE_RFAULT          (0x2000)
+#define CVI_ADVERTISE_LPACK           (0x4000)
+#define CVI_ADVERTISE_NPAGE           (0x8000)
+
+/* Generic MII registers. */
+
+#define CVI_MII_BMCR            (0x00)
+#define CVI_MII_BMSR            (0x01)
+#define CVI_MII_PHYSID1         (0x02)
+#define CVI_MII_PHYSID2         (0x03)
+#define CVI_MII_ADVERTISE       (0x04)
+#define CVI_MII_LPA             (0x05)
+#define CVI_MII_EXPANSION       (0x06)
+#define CVI_MII_CTRL1000        (0x09)
+#define CVI_MII_STAT1000        (0x0a)
+#define CVI_MII_ESTATUS         (0x0f)
+#define CVI_MII_DCOUNTER        (0x12)
+#define CVI_MII_FCSCOUNTER      (0x13)
+#define CVI_MII_NWAYTEST        (0x14)
+#define CVI_MII_RERRCOUNTER     (0x15)
+#define CVI_MII_SREVISION       (0x16)
+#define CVI_MII_RESV1           (0x17)
+#define CVI_MII_LBRERROR        (0x18)
+#define CVI_MII_PHYADDR         (0x19)
+#define CVI_MII_RESV2           (0x1a)
+#define CVI_MII_TPISTATUS       (0x1b)
+#define CVI_MII_NCONFIG         (0x1c)
+
+#define CVI_ADVERTISE_FULL (CVI_ADVERTISE_100FULL | CVI_ADVERTISE_10FULL | \
+            CVI_ADVERTISE_CSMA)
+#define CVI_ADVERTISE_ALL (CVI_ADVERTISE_10HALF | CVI_ADVERTISE_10FULL | \
+               CVI_ADVERTISE_100HALF | CVI_ADVERTISE_100FULL)
+
+/* Expansion register for auto-negotiation. */
+#define CVI_EXPANSION_NWAY            (0x0001)
+#define CVI_EXPANSION_LCWP            (0x0002)
+#define CVI_EXPANSION_ENABLENPAGE     (0x0004)
+#define CVI_EXPANSION_NPCAPABLE       (0x0008)
+#define CVI_EXPANSION_MFAULTS         (0x0010)
+#define CVI_ESTATUS_1000_THALF        (0x1000)
+#define CVI_ESTATUS_1000_TFULL        (0x2000)
+#define CVI_ESTATUS_1000_XHALF        (0x4000)
+#define CVI_ESTATUS_1000_XFULL        (0x8000)
+#define CVI_EXPANSION_RESV            (0xffe0)
+
+/* Link partner ability register. */
+#define CVI_LPA_SLCT                 (0x001f)
+#define CVI_LPA_10HALF               (0x0020)
+#define CVI_LPA_1000XFULL            (0x0020)
+#define CVI_LPA_10FULL               (0x0040)
+#define CVI_LPA_1000XHALF            (0x0040)
+#define CVI_LPA_100HALF              (0x0080)
+#define CVI_LPA_1000XPAUSE           (0x0080)
+#define CVI_LPA_100FULL              (0x0100)
+#define CVI_LPA_1000XPAUSE_ASYM      (0x0100)
+#define CVI_LPA_100BASE4             (0x0200)
+#define CVI_LPA_PAUSE_CAP            (0x0400)
+#define CVI_LPA_PAUSE_ASYM           (0x0800)
+#define CVI_LPA_RESV                 (0x1000)
+#define CVI_LPA_RFAULT               (0x2000)
+#define CVI_LPA_LPACK                (0x4000)
+#define CVI_LPA_NPAGE                (0x8000)
+
+#define CVI_LPA_DUPLEX      (CVI_LPA_10FULL | CVI_LPA_100FULL)
+#define CVI_LPA_100         (CVI_LPA_100FULL | CVI_LPA_100HALF | CVI_LPA_100BASE4)
+/* N-way test register. */
+#define CVI_NWAYTEST_RESV1      (0x00ff)
+#define CVI_NWAYTEST_LOOPBACK   (0x0100)
+#define CVI_NWAYTEST_RESV2      (0xfe00)
+
+/* 1000BASE-T Control register */
+#define CVI_ADVERTISE_1000FULL  0x0200
+#define CVI_ADVERTISE_1000HALF  0x0100
+
+/* 1000BASE-T Status register */
+#define CVI_LPA_1000LOCALRXOK   0x2000
+#define CVI_LPA_1000REMRXOK     0x1000
+#define CVI_LPA_1000FULL        0x0800
+#define CVI_LPA_1000HALF        0x0400
+
+/* Flow control flags */
+#define CVI_FLOW_CTRL_TX        0x01
+#define CVI_FLOW_CTRL_RX        0x02
+
+/**
+ * mii_nway_result
+ * @negotiated: value of MII ANAR and'd with ANLPAR
+ *
+ * Given a set of MII abilities, check each bit and returns the
+ * currently supported media, in the priority order defined by
+ * IEEE 802.3u.  We use LPA_xxx constants but note this is not the
+ * value of LPA solely, as described above.
+ *
+ * The one exception to IEEE 802.3u is that 100baseT4 is placed
+ * between 100T-full and 100T-half.  If your phy does not support
+ * 100T4 this is fine. If your phy places 100T4 elsewhere in the
+ * priority order, you will need to roll your own function.
+ */
+static inline unsigned int mii_nway_result (unsigned int negotiated)
+{
+    unsigned int ret;
+
+    if (negotiated & CVI_LPA_100FULL)
+        ret = CVI_LPA_100FULL;
+    else if (negotiated & CVI_LPA_100BASE4)
+        ret = CVI_LPA_100BASE4;
+    else if (negotiated & CVI_LPA_100HALF)
+        ret = CVI_LPA_100HALF;
+    else if (negotiated & CVI_LPA_10FULL)
+        ret = CVI_LPA_10FULL;
+    else
+        ret = CVI_LPA_10HALF;
+
+    return ret;
+}
+
+/**
+ * mii_duplex
+ * @duplex_lock: Non-zero if duplex is locked at full
+ * @negotiated: value of MII ANAR and'd with ANLPAR
+ *
+ * A small helper function for a common case.  Returns one
+ * if the media is operating or locked at full duplex, and
+ * returns zero otherwise.
+ */
+static inline unsigned int mii_duplex (unsigned int duplex_lock,
+                       unsigned int negotiated)
+{
+    if (duplex_lock)
+        return 1;
+    if (mii_nway_result(negotiated) & CVI_LPA_DUPLEX)
+        return 1;
+    return 0;
+}
+
+#endif /* __LINUX_MII_H__ */