/** * dwc_eth.c * * Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * * Description: dw eth mac drv * * Revision History: * ----------------- */ #include #include #include "debug.h" #include "param.h" #include "armv7-r/barriers.h" #include "armv7-r/cache.h" #include "irq_num.h" #include "irq.h" #include "sdrv_mac_lld.h" #include "sdrv_eth.h" #include "phy/phy.h" #include "app/app_frm_signal.h" /* Eth frame header and crc length. */ #define ETH_FRAME_HEADER_LEN (6 + 6 + 2) #define ETH_FRAME_OVERHEAD_LEN (ETH_FRAME_HEADER_LEN + 4) #if !CONFIG_ETH_USE_HEAP #define MAX_ETH_CNT 2 #ifndef CONFIG_ARCH_CACHE_LINE #define CONFIG_ARCH_CACHE_LINE 32 #endif typedef struct { struct eqos_desc desc; } __ALIGNED(EQOS_DESCRIPTOR_ALIGN) eqos_desc_t; struct eqos_data { bool used; dwc_eth_dev_t dev; struct eqos_priv eqos; eqos_desc_t descs[EQOS_DESCRIPTORS_NUM]; uint8_t rx_dma_buf[EQOS_RX_BUFFER_SIZE] __ALIGNED(CONFIG_ARCH_CACHE_LINE); uint8_t tx_dma_buf[EQOS_TX_BUFFER_SIZE] __ALIGNED(CONFIG_ARCH_CACHE_LINE); }; static struct eqos_data g_eqos_data[MAX_ETH_CNT]; #endif static void *eqos_alloc_descs(struct eqos_priv *eqos, unsigned int num) { /* Align dma descriptors base address & size to cache line to avoid * descriptors corrupted by cacahe line invalidation needed by other * address, if descriptors has not been wrote to ram. */ eqos->desc_size = ALIGN(sizeof(struct eqos_desc), (unsigned int)EQOS_DESCRIPTOR_ALIGN); #if !CONFIG_ETH_USE_HEAP return containerof(eqos, struct eqos_data, eqos)->descs; #else #if __GNUC__ return (void *)memalign(eqos->desc_size, num * eqos->desc_size); #else return aligned_alloc(eqos->desc_size, num * eqos->desc_size); #endif #endif } #if CONFIG_ETH_USE_HEAP static void eqos_free_descs(void *descs) { free(descs); } #endif static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos, unsigned int num, bool rx) { void *desc = (uint8_t *)eqos->descs + ((rx ? EQOS_DESCRIPTORS_TX : 0) + num) * eqos->desc_size; return desc; } /** * @brief Init the Controller DMA Block */ static inline void mac_init_dma(uint32_t regbase, struct eqos_priv *eqos) { uint8_t ch_cnt = 0U; dwmac_dma_bus_init(regbase, DMA_AXI_MAX_OSR_LIMIT | DMA_SYS_BUS_AAL | DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | DMA_AXI_BLEN4); for (ch_cnt = 0; ch_cnt < ETH_MAX_DMA_CHANNEL; ch_cnt++) { // DMA Control dwmac_dma_init_channel(regbase, 0, ch_cnt, eqos->dsl); // rx ring length dwmac_set_rx_ring_len(regbase, EQOS_DESCRIPTORS_RX - 1, ch_cnt); // init_rx_chan dwmac_dma_init_rx_chan(regbase, (paddr_t)eqos_get_desc(eqos, 0, true), EQOS_MAX_PACKET_SIZE, ch_cnt); // rx bufsize dwmac_set_bfsize(regbase, 2048, ch_cnt); // tx ring length dwmac_set_tx_ring_len(regbase, EQOS_DESCRIPTORS_TX - 1, ch_cnt); // init_tx_chan dwmac_dma_init_tx_chan(regbase, (paddr_t)eqos_get_desc(eqos, 0, false), ch_cnt); // set tx tail ptr to next address after the last tx descriptor, so that // dma will never suspend. dwmac_set_tx_tail_ptr(regbase, (uint32_t)eqos_get_desc(eqos, EQOS_DESCRIPTORS_TX, false), ch_cnt); // set rx tail ptr to next address after the last tx descriptor, so that // dma will never suspend. dwmac_set_rx_tail_ptr(regbase, (paddr_t)eqos_get_desc(eqos, EQOS_DESCRIPTORS_RX, true), ch_cnt); dwmac_enable_dma_irq(regbase, ch_cnt, false, false); } } /** * @brief Init the Controller MTL Block */ static inline void mac_init_mtl(uint32_t regbase) { uint8_t ch_cnt = 0U; dwmac_prog_mtl_rx_algorithms(regbase, MTL_RX_ALGORITHM_SP); dwmac_prog_mtl_tx_algorithms(regbase, MTL_OPERATION_SCHALG_WRR); for (ch_cnt = 0; ch_cnt < ETH_MAX_DMA_CHANNEL; ch_cnt++) { // rx mtl map with dma dwmac_map_mtl_dma(regbase, ch_cnt, ch_cnt); dwmac_rx_queue_enable(regbase, MTL_QUEUE_MODE_DCB, ch_cnt); dwmac_dma_tx_chan_op_mode(regbase, ch_cnt, SF_MODE, 10240, MTL_QUEUE_MODE_DCB); dwmac_dma_rx_chan_op_mode(regbase, ch_cnt, SF_MODE, 10240, MTL_QUEUE_MODE_DCB); } } static inline void mac_start(uint32_t regbase, bool start) { uint8_t ch_cnt = 0U; for (ch_cnt = 0; ch_cnt < ETH_MAX_DMA_CHANNEL; ch_cnt++) { if (start) { // start rx dwmac_dma_start_rx(regbase, ch_cnt); // start tx dwmac_dma_start_tx(regbase, ch_cnt); } else { // stop rx dwmac_dma_stop_rx(regbase, ch_cnt); // stop tx dwmac_dma_stop_tx(regbase, ch_cnt); } } } static void eth_mac_init(dwc_eth_config_t *cfg, struct eqos_priv *eqos) { //DMA core sw reset dwmac_dma_reset(cfg->base); uint32_t skip_len = eqos->desc_size - sizeof(struct eqos_desc); uint8_t bus_width = cfg->dma_bus_width ? cfg->dma_bus_width : 64; eqos->dsl = skip_len * 8 / bus_width; //DMA init mac_init_dma(cfg->base, eqos); //MTL init mac_init_mtl(cfg->base); //core init phy_speed_t speed = (cfg->phy[0].phy_addr == ~0) ? PHY_SPEED_1000 : cfg->phy[0].speed; dwmac_core_init(cfg->base, cfg->mtu, speed, cfg->mac_addr); dwmac_mac_rx_queue_mcbc_routing(cfg->base, 0); } static inline void set_phyif_mode(dwc_eth_config_t *cfg) { if (cfg->set_phy_intf) cfg->set_phy_intf(cfg->base, cfg->phy_intf_mode); } static inline void config_eth_clk(dwc_eth_config_t *cfg) { if (cfg->timer_sec_clk) sdrv_ckgen_set_rate(cfg->timer_sec_clk, 125000000); if (cfg->phy_ref_clk) sdrv_ckgen_set_rate(cfg->phy_ref_clk, 125000000); if (cfg->rmii_clk) sdrv_ckgen_set_rate(cfg->rmii_clk, 50000000); if (cfg->tx_clk) sdrv_ckgen_set_rate(cfg->tx_clk, 250000000); } static inline void reset_eth(dwc_eth_config_t *cfg) { if (cfg->rst) sdrv_rstgen_reset(cfg->rst); } static err_t dwc_eth_init(struct net_driver_s *dev) { int ret = 0; int i; dwc_eth_dev_t *eth_dev = (dwc_eth_dev_t *)dev->d_private; dwc_eth_config_t *cfg = eth_dev->config; struct eqos_priv *eqos; if (eth_dev->init) return 0; else eth_dev->init = true; //set SCR ethernet operating mode, e.g. RGMII, SGMII, etc. set_phyif_mode(cfg); //set ethernet Clk config_eth_clk(cfg); //ethernet reset reset_eth(cfg); #if !CONFIG_ETH_USE_HEAP struct eqos_data *eqosd = containerof(eth_dev, struct eqos_data, dev); eqos = &eqosd->eqos; #else eqos = (struct eqos_priv *)malloc(sizeof(struct eqos_priv)); ASSERT(eqos); memset(eqos, 0, sizeof(struct eqos_priv)); #endif eqos->descs = eqos_alloc_descs(eqos, EQOS_DESCRIPTORS_NUM); if (!eqos->descs) { ssdk_printf(SSDK_INFO, "%s: eqos_alloc_descs() failed\n", __func__); ret = -1; } eqos->tx_desc_idx = 0; eqos->rx_desc_idx = 0; #if !CONFIG_ETH_USE_HEAP eqos->tx_dma_buf = eqosd->tx_dma_buf; eqos->rx_dma_buf = eqosd->rx_dma_buf; #else /* Align tx dma buf base address & size to cache line to avoid tx * dma buf corrupted by cacahe line invalidation needed by other * address, if tx dma buf data has not been wrote to ram. */ #if __GNUC__ eqos->tx_dma_buf = (void *)memalign(CONFIG_ARCH_CACHE_LINE, EQOS_TX_BUFFER_SIZE); #else eqos->tx_dma_buf = (void *)aligned_alloc(CONFIG_ARCH_CACHE_LINE, EQOS_TX_BUFFER_SIZE); #endif ASSERT(eqos->tx_dma_buf); memset(eqos->tx_dma_buf, 0, EQOS_TX_BUFFER_SIZE); /* rx dma buf base address & size should be cache line aligned. * Otherwise data before or after rx dma buf may locate in same * cache line with rx dma buf, when invalidate rx dma buf cache, * heap data before or after rx dma buf may be corrupted. */ #if __GNUC__ eqos->rx_dma_buf = (void *)memalign(CONFIG_ARCH_CACHE_LINE, EQOS_RX_BUFFER_SIZE); #else eqos->rx_dma_buf = (void *)aligned_alloc(CONFIG_ARCH_CACHE_LINE, EQOS_RX_BUFFER_SIZE); #endif ASSERT(eqos->rx_dma_buf); memset(eqos->rx_dma_buf, 0, EQOS_RX_BUFFER_SIZE); #endif //init phy phy_init(ð_dev->phy_bus); //init mac eth_mac_init(cfg, eqos); // set up dma tx/rx descriptors memset(eqos->descs, 0, eqos->desc_size * EQOS_DESCRIPTORS_NUM); for (i = 0; i < EQOS_DESCRIPTORS_TX; i++) { struct eqos_desc *tx_desc = eqos_get_desc(eqos, i, false); arch_clean_cache_range((addr_t)tx_desc, sizeof(struct eqos_desc)); } for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) { struct eqos_desc *rx_desc = eqos_get_desc(eqos, i, true); rx_desc->des0 = (paddr_t)eqos->rx_dma_buf + (i * EQOS_MAX_PACKET_SIZE); DMB; rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_IOC | EQOS_DESC3_BUF1V; arch_clean_cache_range((addr_t)rx_desc, sizeof(struct eqos_desc)); arch_invalidate_cache_range((addr_t)((char *)eqos->rx_dma_buf + (i * EQOS_MAX_PACKET_SIZE)), EQOS_MAX_PACKET_SIZE); } //enable rx/tx mac_start(cfg->base, true); eqos->started = true; eth_dev->eqos = eqos; return ret; } static inline void eqos_rx_lock(struct eqos_priv *eqos) { EQOS_RX_LOCK(eqos); } static inline void eqos_rx_unlock(struct eqos_priv *eqos) { EQOS_RX_UNLOCK(eqos); } static inline void eqos_tx_lock(struct eqos_priv *eqos) { EQOS_TX_LOCK(eqos); } static inline void eqos_tx_unlock(struct eqos_priv *eqos) { EQOS_TX_UNLOCK(eqos); } static err_t dwc_eth_txavail(struct net_driver_s *dev, struct pbuf *p) { int i, ret = 0; struct eqos_desc *tx_desc; uint8_t *tx_buf; uint32_t offset = 0; struct pbuf *q = NULL; dwc_eth_dev_t *eth_dev = (dwc_eth_dev_t *)dev->d_private; dwc_eth_config_t *cfg = eth_dev->config; struct eqos_priv *eqos = eth_dev->eqos; if (p->tot_len > (cfg->mtu + ETH_FRAME_HEADER_LEN)) { ssdk_printf(SSDK_INFO, "eth tx len > MTU, %d\n", p->tot_len); return -1; } eqos_tx_lock(eqos); tx_desc = eqos_get_desc(eqos, eqos->tx_desc_idx, false); for (i = 0; i < 100000; i++) { arch_invalidate_cache_range((addr_t)tx_desc, sizeof(struct eqos_desc)); if ((tx_desc->des3 & EQOS_DESC3_OWN) == 0) { goto start_tx; } } ssdk_printf(SSDK_INFO, "%s: Previous tX on desc %d timeout\n", __func__, eqos->tx_desc_idx); ret = -1; goto tx_out; start_tx: tx_buf = (uint8_t *)eqos->tx_dma_buf + eqos->tx_desc_idx * EQOS_MAX_PACKET_SIZE; //copy data to pbuf for (q = p; q != NULL; q = q->next) { memcpy((uint8_t *)(tx_buf + offset), (uint8_t *)((uint8_t *)q->payload), q->len); offset += q->len; } arch_clean_cache_range((addr_t)tx_buf, p->tot_len); eqos->tx_desc_idx ++; eqos->tx_desc_idx %= EQOS_DESCRIPTORS_TX; tx_desc->des0 = (paddr_t)tx_buf; tx_desc->des1 = 0; tx_desc->des2 = p->tot_len; /* * Make sure that if HW sees the _OWN write below, it will see all the * writes to the rest of the descriptor too. */ DMB; tx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | (p->tot_len); arch_clean_cache_range((addr_t)tx_desc, sizeof(struct eqos_desc)); dwmac_set_tx_tail_ptr(cfg->base, (uint32_t)eqos_get_desc(eqos, EQOS_DESCRIPTORS_TX, false), 0); tx_out: eqos_tx_unlock(eqos); return ret; } static uint32_t dwc_eth_getmac(struct net_driver_s *dev, void *buf, uint32_t max_len) { dwc_eth_dev_t *eth_dev = (dwc_eth_dev_t *)dev->d_private; uint8_t *mac_addr = eth_dev->config->mac_addr; if (mac_addr == NULL) { mac_addr = dwmac_get_default_mac_addr(); } memcpy(buf, (void *)mac_addr, max_len); return max_len; } static uint32_t dwc_eth_getmtu(struct net_driver_s *dev) { dwc_eth_dev_t *eth_dev = (dwc_eth_dev_t *)dev->d_private; return eth_dev->config->mtu; } static int dwc_eth_rx_poll(struct net_driver_s *dev) { struct eqos_desc *rx_desc; uint32_t length = 0; uint8_t *recbuf; struct pbuf *rx_pbuf = NULL; dwc_eth_dev_t *eth_dev = (dwc_eth_dev_t *)dev->d_private; dwc_eth_config_t *cfg = eth_dev->config; struct eqos_priv *eqos = eth_dev->eqos; eqos_rx_lock(eqos); rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true); arch_invalidate_cache_range((addr_t)rx_desc, sizeof(struct eqos_desc)); if (rx_desc->des3 & EQOS_DESC3_OWN) { eqos_rx_unlock(eqos); return -1; } /* * Make sure reading des3 before dma buffer. * Avoid speculative reading of buffer before des3 own bit cleared. */ DMB; recbuf = (uint8_t *)eqos->rx_dma_buf + (eqos->rx_desc_idx * EQOS_MAX_PACKET_SIZE); length = rx_desc->des3 & 0x7FFF; arch_invalidate_cache_range((addr_t)((char *)recbuf), length); if (length <= (cfg->mtu + ETH_FRAME_OVERHEAD_LEN)) { rx_pbuf = pbuf_alloc(PBUF_RAW, length, PBUF_POOL); if (rx_pbuf) { uint32_t offset = 0; struct pbuf *q = NULL; for (q = rx_pbuf; q != NULL; q = q->next) { memcpy( (uint8_t *)((uint8_t *)q->payload ), ((uint8_t *)recbuf + offset), q->len); offset += q->len; } } } else { ssdk_printf(SSDK_INFO, "len > 1500! desc3 = 0x%x\n", rx_desc->des3); } //free packet rx_desc->des0 = (paddr_t)recbuf; rx_desc->des1 = 0; rx_desc->des2 = 0; /* * Make sure DMA observes new des3 value after observing * new des0 & des1 & des2 value under any condition, especially * when des3 located in a different cache line and the cache line * is automatically evicted due to corresponding cache set full. */ DMB; rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_IOC | EQOS_DESC3_BUF1V; arch_clean_cache_range((addr_t)rx_desc, sizeof(struct eqos_desc)); dwmac_set_rx_tail_ptr(cfg->base, (uint32_t)eqos_get_desc(eqos, EQOS_DESCRIPTORS_RX, true), 0); eqos->rx_desc_idx++; eqos->rx_desc_idx %= EQOS_DESCRIPTORS_RX; eqos_rx_unlock(eqos); if (rx_pbuf) netdev_input(dev, rx_pbuf); return length; } static inline void __dwc_eth_enable_dma_rx_int(dwc_eth_dev_t *dwc_eth) { for (int chn = 0; chn < ETH_MAX_DMA_CHANNEL; chn++) { dwmac_enable_dma_rx_int(dwc_eth->config->base, chn, true); } } __UNUSED static inline void __dwc_eth_disable_dma_rx_int(dwc_eth_dev_t *dwc_eth) { for (int chn = 0; chn < ETH_MAX_DMA_CHANNEL; chn++) { dwmac_enable_dma_rx_int(dwc_eth->config->base, chn, false); } } void dwc_eth_enable_dma_rx_int(struct net_driver_s *dev, dma_rx_int_cb_t callback) { ASSERT(dev); dwc_eth_dev_t *dwc_eth = dev->d_private; dwc_eth->rx_cb = callback; __dwc_eth_enable_dma_rx_int(dwc_eth); } /** * @brief try to read rx packet and send the packet to lwip stack. * Should be called in thread context. * If enable DMA rx interrupt, i.e. poll is false, this function * should be called after DMA rx interrupt triggered. DMA rx * interrupt will be disabled in ISR, and re-enabled in this * function after reading out all rx packets, this is helpful to * avoid too much rx interrupt. * * @param [in] dev eth device * @param [in] poll true for polling read, false for interrupt read * @return int total length of rx packets or -1 if rx nothing */ int dwc_eth_rx(struct net_driver_s *dev, bool poll) { ASSERT(dev); int rx_pkt_len = 0; int len; do { len = dwc_eth_rx_poll(dev); if (len != -1) rx_pkt_len += len; } while (len != -1); if (!poll) { dwc_eth_dev_t *dwc_eth = dev->d_private; /* After reading all packets out, re-enable eth dma rx * interrupt which had been disabled in rx int service * routine. This strategy can avoid too much eth interrupt. */ __dwc_eth_enable_dma_rx_int(dwc_eth); } return rx_pkt_len ? rx_pkt_len : -1; } static int dwc_eth_irq_handler(uint32_t irq, void *arg) { struct net_driver_s *dev = arg; dwc_eth_dev_t *dwc_eth = dev->d_private; uint32_t reg_base = dwc_eth->config->base; uint32_t status = dwmac_get_dma_int_status(reg_base); uint32_t sub_sts, detail; uint8_t chn = (irq == ENET1_SBD_INTR_NUM) ? 1 : 2; ssdk_printf(SSDK_DEBUG, "%s int status 0x%x\n", __func__, status); if (!status) { return -1; } irq_state_t state = enter_critical_section();//20250426 增加中断保护 if (status & (1 << 17)) { /* MAC interrupt */ sub_sts = dwmac_get_mac_int_status(reg_base); ssdk_printf(SSDK_EMERG, "%s %d mac int status 0x%x\n", __func__, irq, sub_sts); if (sub_sts & (1 << 0)) { /* RGMII/SMII PHY link status changed. Read of MAC_PHYIF_Control_Status * register will clear the interrupt status. */ detail = dwmac_get_mac_phyif_control_status(reg_base); if (detail & (1 << 19)) ssdk_printf(SSDK_EMERG, "ETH%d phy link up\n", chn); else ssdk_printf(SSDK_EMERG, "ETH%d phy link down\n", chn); } if (sub_sts & (3 << 1)) { /* TBI/RTBI/SGMII PHY link status changed, or TBI/RTBI/SGMII PHY * auto-negotiation is completed. Read of MAC_AN_Status register * will clear the interrupt status. */ detail = dwmac_get_mac_an_status(reg_base); ssdk_printf(SSDK_EMERG, "ETH%d mac an status 0x%x\n", chn, detail); } if (sub_sts & (1 << 4)) { /* A magic packet or Wake-on-LAN packet is received. * Read of MAC_PMT_Control_Status register will clear * the interrupt status. */ detail = dwmac_get_mac_pmt_control_status(reg_base); ssdk_printf(SSDK_EMERG, "ETH%d mac pmt ctrl status 0x%x\n", chn, detail); } if (sub_sts & (1 << 5)) { /* LPI state entry or exit in the MAC Transmitter or Receiver. * Read of MAC_LPI_Control_Status register will clear the * interrupt status. */ detail = dwmac_get_mac_lpi_control_status(reg_base); ssdk_printf(SSDK_EMERG, "ETH%d mac lpi ctrl status 0x%x\n", chn, detail); } } if (status & (1 << 16)) { /* MTL interrupt */ sub_sts = dwmac_get_mtl_int_status(reg_base); if (sub_sts & 0xFFFFFF00) { ssdk_printf(SSDK_EMERG, "ETH%d mtl int status 0x%x\n", chn, sub_sts); ASSERT(0); } for (int i = 0; i < ETH_MAX_DMA_CHANNEL; i++) { if (sub_sts & (1 << i)) { detail = dwmac_get_mtl_q_int_status(reg_base, i); dwmac_clr_mtl_q_int_status(reg_base, i, detail); if (detail & (1 << 16)) ssdk_printf(SSDK_WARNING, "ETH%d mtl queue %d rx overflow\n", chn, i); if (detail & (1 << 1)) ssdk_printf(SSDK_WARNING, "ETH%d mtl queue %d ABS updated\n", chn, i); if (detail & (1 << 1)) ssdk_printf(SSDK_WARNING, "ETH%d mtl queue %d tx underflow\n", chn, i); } } } if (status & 0x7F) { /* DMA interrupt */ for (int chn = 0; chn < ETH_MAX_DMA_CHANNEL; chn++) { sub_sts = dwmac_get_dma_chn_status(reg_base, chn); dwmac_clear_dma_chn_int_status(reg_base, chn); if (sub_sts & BIT(6)) { /* DMA rx interrupt */ ssdk_printf(SSDK_DEBUG, "%s: chn %d rx int\n", __func__, chn); dwmac_enable_dma_rx_int(reg_base, chn, false); if (dwc_eth->rx_cb) dwc_eth->rx_cb(dev); } if (sub_sts & BIT(0)) { /* DMA tx interrupt */ ssdk_printf(SSDK_DEBUG, "%s: chn %d tx int\n", __func__, chn); /* Nothing to do for now. */ } } } exit_critical_section(state);//20250426 增加中断保护 return 0; } static int dwc_eqos_mdio_read(phy_bus_t *bus, uint32_t phyaddr, uint32_t devaddr, uint32_t reg) { dwc_eth_dev_t *dwc_eth_dev = containerof(bus, dwc_eth_dev_t, phy_bus); uint32_t reg_base = dwc_eth_dev->config->base; return mac_mdio_read(reg_base, phyaddr, devaddr, reg); } static int dwc_eqos_mdio_write(phy_bus_t *bus, uint32_t phyaddr, uint32_t devaddr, uint32_t reg, uint32_t data) { dwc_eth_dev_t *dwc_eth_dev = containerof(bus, dwc_eth_dev_t, phy_bus); uint32_t reg_base = dwc_eth_dev->config->base; return mac_mdio_write(reg_base, phyaddr, devaddr, reg, data); } static phy_bus_ops_t g_dwc_eqos_mdio_ops = { .mdio_read = dwc_eqos_mdio_read, .mdio_write = dwc_eqos_mdio_write }; static void dwc_eth_phy_bus_init(phy_bus_t *bus) { list_initialize(&bus->phy_dev_list); bus->ref_cnt = 0; bus->ops = &g_dwc_eqos_mdio_ops; } static dwc_eth_dev_t *alloc_dwc_eth_device(void) { #if !CONFIG_ETH_USE_HEAP for (int i = 0; i < MAX_ETH_CNT; i++) { if (!g_eqos_data[i].used) { g_eqos_data[i].used = true; return &g_eqos_data[i].dev; } } return NULL; #else return (dwc_eth_dev_t *)malloc(sizeof(dwc_eth_dev_t)); #endif } int dwc_eth_probe(struct net_driver_s *dev, dwc_eth_config_t *cfg) { ASSERT(dev && cfg); dev->d_init = dwc_eth_init; dev->d_txavail = dwc_eth_txavail; dev->d_getmac = dwc_eth_getmac; dev->d_getmtu = dwc_eth_getmtu; dwc_eth_dev_t *dw_eth_dev = alloc_dwc_eth_device(); ASSERT(dw_eth_dev); dw_eth_dev->init = false; dw_eth_dev->config = cfg; dev->d_private = dw_eth_dev; dev->d_ipaddr.addr = (cfg->ip[3] << 24) | (cfg->ip[2] << 16) | (cfg->ip[1] << 8) | cfg->ip[0]; dev->d_netmask.addr = (cfg->mask[3] << 24) | (cfg->mask[2] << 16) | (cfg->mask[1] << 8) | cfg->mask[0]; dev->d_gateway.addr = (1 << 24) | (cfg->ip[2] << 16) | (cfg->ip[1] << 8) | cfg->ip[0]; dwc_eth_phy_bus_init(&dw_eth_dev->phy_bus); for (int i = 0; cfg->phy[i].phy_addr != ~0; i++) { phy_dev_register(&dw_eth_dev->phy_bus, &cfg->phy[i]); } int ret = netdev_register(dev, NET_LL_ETHERNET); irq_attach(cfg->irq_num, dwc_eth_irq_handler, dev); irq_enable(cfg->irq_num); return ret; }