Files
base/drivers/source/eth/sdrv_eth.c
2025-11-07 09:57:14 +08:00

763 lines
24 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* dwc_eth.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description: dw eth mac drv
*
* Revision History:
* -----------------
*/
#include <stdlib.h>
#include <string.h>
#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(&eth_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 <20><><EFBFBD><EFBFBD><EFBFBD>жϱ<D0B6><CFB1><EFBFBD>
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 <20><><EFBFBD><EFBFBD><EFBFBD>жϱ<D0B6><CFB1><EFBFBD>
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;
}