diff --git a/bsp/cvitek/README.md b/bsp/cvitek/README.md index afc690ed12..db1c27ae17 100755 --- a/bsp/cvitek/README.md +++ b/bsp/cvitek/README.md @@ -64,6 +64,7 @@ $ scons | timer | 支持 | | | wdt | 支持 | | | sdio | 支持 | | +| eth | 支持 | | ## 支持开发板 - milk-v duo: [https://milkv.io/duo](https://milkv.io/duo) diff --git a/bsp/cvitek/cv18xx_risc-v/.config b/bsp/cvitek/cv18xx_risc-v/.config index 9789b09682..15f3340217 100644 --- a/bsp/cvitek/cv18xx_risc-v/.config +++ b/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 diff --git a/bsp/cvitek/cv18xx_risc-v/board/Kconfig b/bsp/cvitek/cv18xx_risc-v/board/Kconfig index f1068c055a..1e759e33b2 100755 --- a/bsp/cvitek/cv18xx_risc-v/board/Kconfig +++ b/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 diff --git a/bsp/cvitek/cv18xx_risc-v/rtconfig.h b/bsp/cvitek/cv18xx_risc-v/rtconfig.h index 78dbc7934d..5d861738e0 100755 --- a/bsp/cvitek/cv18xx_risc-v/rtconfig.h +++ b/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 */ diff --git a/bsp/cvitek/drivers/SConscript b/bsp/cvitek/drivers/SConscript index 8b9fcc3ff2..e5257202a2 100755 --- a/bsp/cvitek/drivers/SConscript +++ b/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'): diff --git a/bsp/cvitek/drivers/drv_eth.c b/bsp/cvitek/drivers/drv_eth.c new file mode 100644 index 0000000000..b4e03fa4c8 --- /dev/null +++ b/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 +#include +#include + +#include "board.h" + +#define DBG_TAG "drv.eth" +#define DBG_LEVEL DBG_INFO +#include + +#include +#include + +#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); diff --git a/bsp/cvitek/drivers/drv_eth.h b/bsp/cvitek/drivers/drv_eth.h new file mode 100644 index 0000000000..c390ae441c --- /dev/null +++ b/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__ */ diff --git a/bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.c b/bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.c new file mode 100644 index 0000000000..38cc8de343 --- /dev/null +++ b/bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.c @@ -0,0 +1,606 @@ +/* +* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved. +*/ +#include + +#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; +} diff --git a/bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.h b/bsp/cvitek/drivers/libraries/eth/cvi_eth_phy.h new file mode 100644 index 0000000000..e5abd0da62 --- /dev/null +++ b/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 +#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_ */ diff --git a/bsp/cvitek/drivers/libraries/eth/dw_eth_mac.c b/bsp/cvitek/drivers/libraries/eth/dw_eth_mac.c new file mode 100644 index 0000000000..20599c95bb --- /dev/null +++ b/bsp/cvitek/drivers/libraries/eth/dw_eth_mac.c @@ -0,0 +1,717 @@ +/* +* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved. +*/ +#include + +#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; + } +} diff --git a/bsp/cvitek/drivers/libraries/eth/dw_eth_mac.h b/bsp/cvitek/drivers/libraries/eth/dw_eth_mac.h new file mode 100644 index 0000000000..208e3e7c37 --- /dev/null +++ b/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_ */ diff --git a/bsp/cvitek/drivers/libraries/eth/eth_phy_cvitek.c b/bsp/cvitek/drivers/libraries/eth/eth_phy_cvitek.c new file mode 100644 index 0000000000..989cd4b6a6 --- /dev/null +++ b/bsp/cvitek/drivers/libraries/eth/eth_phy_cvitek.c @@ -0,0 +1,383 @@ +/* +* Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved. +*/ +#include +#include +#include +#include + +// #include + +#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, +}; diff --git a/bsp/cvitek/drivers/libraries/eth/mii.h b/bsp/cvitek/drivers/libraries/eth/mii.h new file mode 100644 index 0000000000..d75ba1260f --- /dev/null +++ b/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__ */