summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/drivers/ethernet/Kconfig2
-rw-r--r--components/drivers/ethernet/cadence/Kconfig15
-rw-r--r--components/drivers/ethernet/cadence/SConscript21
-rw-r--r--components/drivers/ethernet/cadence/macb.c2146
-rw-r--r--components/drivers/ethernet/cadence/macb.h356
-rw-r--r--components/drivers/ethernet/cadence/macb_pci.c179
-rw-r--r--components/drivers/ethernet/cadence/macb_phy.c282
-rw-r--r--components/drivers/ethernet/cadence/macb_ptp.c162
-rw-r--r--components/drivers/ethernet/synopsys/Kconfig11
-rw-r--r--components/drivers/ethernet/synopsys/SConscript18
-rw-r--r--components/drivers/ethernet/synopsys/dwmac-platform.c77
-rw-r--r--components/drivers/ethernet/synopsys/dwmac-platform.h20
-rw-r--r--components/drivers/ethernet/synopsys/dwmac.c1230
-rw-r--r--components/drivers/ethernet/synopsys/dwmac.h303
-rw-r--r--components/drivers/ethernet/synopsys/dwmac_ptp.c480
-rw-r--r--components/drivers/ethernet/synopsys/dwmac_ptp.h81
16 files changed, 5383 insertions, 0 deletions
diff --git a/components/drivers/ethernet/Kconfig b/components/drivers/ethernet/Kconfig
index deb89f4e5d..1f0f880a1d 100644
--- a/components/drivers/ethernet/Kconfig
+++ b/components/drivers/ethernet/Kconfig
@@ -7,5 +7,7 @@ menuconfig RT_USING_ETHERNET
default n
if RT_USING_ETHERNET
+ rsource "cadence/Kconfig"
+ rsource "synopsys/Kconfig"
osource "$(SOC_DM_ETHERNET_DIR)/Kconfig"
endif
diff --git a/components/drivers/ethernet/cadence/Kconfig b/components/drivers/ethernet/cadence/Kconfig
new file mode 100644
index 0000000000..c553b848de
--- /dev/null
+++ b/components/drivers/ethernet/cadence/Kconfig
@@ -0,0 +1,15 @@
+config RT_ETHERNET_CADENCE
+ bool "Cadence GEM/MACB Ethernet"
+ depends on RT_USING_PHY_V2
+ depends on RT_USING_DMA
+ depends on RT_USING_PIN
+
+config RT_ETHERNET_CADENCE_PCI
+ bool "PCI supported"
+ depends on RT_ETHERNET_CADENCE
+ depends on RT_USING_PCI
+
+config RT_ETHERNET_CADENCE_PTP
+ bool "PTP supported"
+ depends on RT_ETHERNET_CADENCE
+ depends on RT_USING_PTP
diff --git a/components/drivers/ethernet/cadence/SConscript b/components/drivers/ethernet/cadence/SConscript
new file mode 100644
index 0000000000..7050636833
--- /dev/null
+++ b/components/drivers/ethernet/cadence/SConscript
@@ -0,0 +1,21 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_ETHERNET_CADENCE']):
+ Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../../include', cwd + '/../../phy']
+
+src = ['macb.c']
+
+if GetDepend(['RT_ETHERNET_CADENCE_PCI']):
+ src += ['macb_pci.c']
+
+if GetDepend(['RT_ETHERNET_CADENCE_PTP']):
+ src += ['macb_ptp.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/drivers/ethernet/cadence/macb.c b/components/drivers/ethernet/cadence/macb.c
new file mode 100644
index 0000000000..fcac081dd2
--- /dev/null
+++ b/components/drivers/ethernet/cadence/macb.c
@@ -0,0 +1,2146 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-25 GuEe-GUI first version
+ */
+
+#include "macb.h"
+
+#define DBG_TAG "eth.macb"
+#define DBG_LVL DBG_WARNING
+#include <rtdbg.h>
+
+#define NEXT_TX(i) (((i) + 1) & (MACB_TX_RING_SIZE - 1))
+#define NEXT_RX(i) (((i) + 1) & (MACB_RX_RING_SIZE - 1))
+
+#define MACB_PHY_RETRY_INTERVAL_MS 2000
+#define MACB_PHY_RETRY_MAX 15
+
+static const struct macb_config macb_cfg_emac, macb_cfg_default_gem;
+
+static rt_size_t macb_desc_stride(struct macb_eth *eth);
+static void macb_dma_flush(struct macb_eth *eth, void *ptr, rt_size_t size);
+static void macb_dma_inval(struct macb_eth *eth, void *ptr, rt_size_t size);
+static rt_err_t macb_mac_enable(struct macb_eth *eth);
+
+rt_weak void macb_plat_mac_enable_prepare(void)
+{
+}
+
+static void macb_mmio_posted_barrier(struct macb_eth *eth)
+{
+ (void)macb_readl(eth, GEM_NCR);
+ if (!eth->native_io)
+ {
+ rt_hw_dmb();
+ }
+}
+
+static void macb_dma_read_barrier(struct macb_eth *eth)
+{
+ if (!eth->native_io)
+ {
+ rt_hw_dmb();
+ (void)macb_readl(eth, GEM_NCR);
+ }
+}
+
+#ifdef RT_USING_OFW
+static struct rt_clk *macb_find_provider_clk(struct rt_ofw_node *dev_np,
+ const char *output_name)
+{
+ struct rt_ofw_node *bus, *child;
+
+ if (!dev_np || !output_name)
+ {
+ return RT_NULL;
+ }
+
+ for (bus = dev_np->parent; bus; bus = bus->parent)
+ {
+ rt_ofw_foreach_child_node(bus, child)
+ {
+ rt_ssize_t n, i;
+ const char *oname;
+
+ if (!rt_ofw_get_prop(child, "#clock-cells", RT_NULL))
+ {
+ continue;
+ }
+
+ n = rt_ofw_count_of_clk(child);
+ for (i = 0; i < n; ++i)
+ {
+ if (rt_ofw_prop_read_string_index(child, "clock-output-names",
+ i, &oname))
+ {
+ continue;
+ }
+
+ if (rt_strcmp(oname, output_name))
+ {
+ continue;
+ }
+
+ return rt_ofw_get_clk(child, (int)i);
+ }
+ }
+ }
+
+ return RT_NULL;
+}
+#endif
+
+static void macb_set_tx_clk(struct macb_eth *eth, int speed)
+{
+ rt_base_t rate, rounded;
+ rt_err_t err;
+
+ if (!eth->tx_clk)
+ {
+ return;
+ }
+
+ if (eth->config && (eth->config->caps & MACB_CAP_CLK_HW_CHG))
+ {
+ return;
+ }
+
+#ifdef RT_USING_PHY_V2
+ if (eth->phy_interface == RT_PHY_INTERFACE_MODE_MII)
+ {
+ return;
+ }
+#endif
+
+ switch (speed)
+ {
+ case SPEED_10:
+ rate = 2500000;
+ break;
+ case SPEED_100:
+ rate = 25000000;
+ break;
+ case SPEED_1000:
+ rate = 125000000;
+ break;
+ default:
+ return;
+ }
+
+ err = rt_clk_prepare_enable(eth->tx_clk);
+ if (err)
+ {
+ LOG_W("tx_clk enable failed: %s", rt_strerror(err));
+ return;
+ }
+
+ rounded = rt_clk_round_rate(eth->tx_clk, rate);
+ if (rounded > 0)
+ {
+ rate = rounded;
+ }
+
+ err = rt_clk_set_rate(eth->tx_clk, rate);
+ if (err)
+ {
+ LOG_W("tx_clk set_rate %ld Hz failed: %s", (long)rate, rt_strerror(err));
+ }
+}
+
+static rt_size_t macb_desc_sync_size(struct macb_eth *eth)
+{
+ return eth->dma_64bit ?
+ macb_desc_stride(eth) : sizeof(struct macb_dma_desc);
+}
+
+static void macb_desc_ring_sync(struct macb_eth *eth, rt_bool_t rx, rt_bool_t flush)
+{
+ rt_size_t bytes;
+ void *ptr;
+
+ if (rx)
+ {
+ bytes = macb_desc_stride(eth) * MACB_RX_RING_SIZE;
+ ptr = eth->rx_ring;
+ }
+ else
+ {
+ bytes = macb_desc_stride(eth) * MACB_TX_RING_SIZE;
+ ptr = eth->tx_ring;
+ }
+
+ if (flush)
+ {
+ macb_dma_flush(eth, ptr, bytes);
+ }
+ else
+ {
+ macb_dma_inval(eth, ptr, bytes);
+ }
+}
+
+static rt_ubase_t macb_dma_flags(struct macb_eth *eth)
+{
+ /*
+ * Bus DMA above 4G (dma-ranges): use uncached CPU mappings so
+ * descriptor/buffer writes reach the GEM engine without flush alone.
+ */
+ if (eth->dma_64bit)
+ {
+ return RT_DMA_F_LINEAR | RT_DMA_F_NOCACHE;
+ }
+
+ return RT_DMA_F_LINEAR;
+}
+
+static rt_bool_t macb_hw_supports_dma64(struct macb_eth *eth)
+{
+ rt_uint32_t dcfg6 = macb_readl(eth, GEM_DCFG6);
+
+ if (dcfg6 == 0xffffffff)
+ {
+ return RT_FALSE;
+ }
+
+ return !!((dcfg6 >> GEM_DCFG6_DAW64_SHIFT) & 1u);
+}
+
+static void macb_update_dma64(struct macb_eth *eth, rt_bool_t wide_bus_addr)
+{
+ if (wide_bus_addr || macb_hw_supports_dma64(eth))
+ {
+ eth->dma_64bit = RT_TRUE;
+ }
+}
+
+static void macb_gem_program_dmacfg(struct macb_eth *eth);
+static void macb_gem_init_axi(struct macb_eth *eth);
+static void macb_gem_init_intmod(struct macb_eth *eth);
+static void macb_gem_init_usrio(struct macb_eth *eth);
+
+static void macb_gem_setup_dma_hw(struct macb_eth *eth)
+{
+ macb_gem_program_dmacfg(eth);
+ macb_gem_init_axi(eth);
+ macb_gem_init_intmod(eth);
+ macb_gem_init_usrio(eth);
+}
+
+static void macb_clear_gem_irq(struct macb_eth *eth, rt_uint32_t status);
+static void macb_gem_commit_irq_enable(struct macb_eth *eth);
+static void macb_gem_write_traffic_ier(struct macb_eth *eth);
+static void macb_service_traffic(struct macb_eth *eth);
+static rt_bool_t macb_gem_irq_use_q0_bank(struct macb_eth *eth);
+static void macb_clocks_get(struct macb_eth *eth, struct rt_device *dev)
+{
+ eth->pclk = rt_clk_get_by_name(dev, "pclk");
+ eth->hclk = rt_clk_get_by_name(dev, "hclk");
+ eth->tsu_clk = rt_clk_get_by_name(dev, "tsu_clk");
+ eth->tx_clk = rt_clk_get_by_name(dev, "tx_clk");
+
+ if (!eth->tx_clk)
+ {
+ eth->tx_clk = rt_clk_get_by_index(dev, 3);
+ }
+
+ if (!eth->tx_clk)
+ {
+ eth->tx_clk = rt_clk_get_by_name(dev, "clk_eth");
+ }
+
+#ifdef RT_USING_OFW
+ if (!eth->tx_clk && dev->ofw_node)
+ {
+ eth->tx_clk = macb_find_provider_clk(dev->ofw_node, "clk_eth");
+ }
+#endif
+
+ if (!eth->tx_clk)
+ {
+ LOG_W("tx_clk missing (RGMII TX clock required for transmit)");
+ }
+}
+
+static void macb_dma_flush(struct macb_eth *eth, void *ptr, rt_size_t size)
+{
+ if (ptr && size)
+ {
+ rt_dma_sync_out_data(eth->dev, ptr, size, RT_NULL, macb_dma_flags(eth));
+ }
+}
+
+static void macb_dma_inval(struct macb_eth *eth, void *ptr, rt_size_t size)
+{
+ if (ptr && size)
+ {
+ rt_dma_sync_in_data(eth->dev, ptr, size, 0, macb_dma_flags(eth));
+ }
+}
+
+static rt_size_t macb_desc_stride(struct macb_eth *eth)
+{
+ return eth->dma_64bit ?
+ sizeof(struct macb_dma_desc) + sizeof(struct macb_dma_desc_64) :
+ sizeof(struct macb_dma_desc);
+}
+
+static struct macb_dma_desc *macb_ring_desc(struct macb_dma_desc *ring,
+ struct macb_eth *eth, rt_uint32_t idx)
+{
+ return (struct macb_dma_desc *)((rt_uint8_t *)ring + idx * macb_desc_stride(eth));
+}
+
+static struct macb_dma_desc *macb_tx_desc_at(struct macb_eth *eth, rt_uint32_t idx)
+{
+ return macb_ring_desc(eth->tx_ring, eth, idx);
+}
+
+static struct macb_dma_desc *macb_rx_desc_at(struct macb_eth *eth, rt_uint32_t idx)
+{
+ return macb_ring_desc(eth->rx_ring, eth, idx);
+}
+
+static void macb_set_desc_addr(struct macb_eth *eth, struct macb_dma_desc *desc,
+ rt_uint64_t addr)
+{
+ if (eth->dma_64bit)
+ {
+ struct macb_dma_desc_64 *desc64 = (struct macb_dma_desc_64 *)
+ ((rt_uint8_t *)desc + sizeof(*desc));
+
+ desc64->addrh = (rt_uint32_t)(addr >> 32);
+ rt_hw_wmb();
+ }
+
+ desc->addr = (rt_uint32_t)addr;
+}
+
+static void macb_ncr_write(struct macb_eth *eth, rt_uint32_t val)
+{
+ eth->ncr_shadow = val;
+ macb_writel(eth, GEM_NCR, val);
+}
+
+static void macb_ncr_posted_write(struct macb_eth *eth, rt_uint32_t val)
+{
+ macb_ncr_write(eth, val);
+ macb_mmio_posted_barrier(eth);
+}
+
+static rt_bool_t macb_hw_is_native_io(struct macb_eth *eth)
+{
+ rt_uint32_t val = GEM_NCR_LLB;
+
+ macb_ncr_write(eth, val);
+ val = macb_readl(eth, GEM_NCR);
+ macb_ncr_write(eth, 0);
+
+ return val == GEM_NCR_LLB;
+}
+
+static void macb_gem_kick_tx(struct macb_eth *eth)
+{
+ rt_uint32_t ncr;
+
+ /*
+ * Linux macb_tx_restart(): merge TSTART with live NCR, then read
+ * back to flush posted MMIO writes so the kick reaches GEM.
+ */
+ macb_mmio_posted_barrier(eth);
+ ncr = macb_readl(eth, GEM_NCR) | GEM_NCR_TSTART;
+ macb_ncr_write(eth, ncr);
+ macb_mmio_posted_barrier(eth);
+ eth->ncr_shadow = macb_readl(eth, GEM_NCR);
+ eth->ncr_shadow &= ~GEM_NCR_TSTART;
+}
+
+static void macb_gem_tx_restart(struct macb_eth *eth)
+{
+ rt_uint64_t tbqp;
+ rt_uint32_t head_idx;
+ rt_size_t stride = macb_desc_stride(eth);
+
+ if (eth->tx_head == eth->tx_tail || !stride)
+ {
+ return;
+ }
+
+ tbqp = macb_readl(eth, GEM_TBQP);
+ if (eth->dma_64bit)
+ {
+ tbqp |= (rt_uint64_t)macb_readl(eth, GEM_TBQPH) << 32;
+ }
+
+ head_idx = eth->tx_head;
+ tbqp = (tbqp - eth->tx_ring_dma) / stride;
+ tbqp &= MACB_TX_RING_SIZE - 1;
+ head_idx &= MACB_TX_RING_SIZE - 1;
+
+ if (tbqp == head_idx)
+ {
+ return;
+ }
+
+ macb_gem_kick_tx(eth);
+}
+
+static rt_bool_t macb_gem_irq_use_q0_bank(struct macb_eth *eth)
+{
+ rt_uint32_t dcfg1 = macb_readl(eth, GEM_DCFG1);
+ if (dcfg1 == 0xffffffff)
+ {
+ return RT_FALSE;
+ }
+
+ /* Linux: GEM DCFG1.IRQCOR selects queue-0 bank @ 0x400/0x600. */
+ return !!((dcfg1 >> GEM_DCFG1_IRQCOR_SHIFT) & 1u);
+}
+
+static rt_uint32_t macb_gem_isr(struct macb_eth *eth)
+{
+ rt_uint32_t isr, isr0 = 0;
+ rt_bool_t use_q0 = macb_gem_irq_use_q0_bank(eth);
+
+ if (use_q0)
+ {
+ isr0 = macb_readl(eth, GEM_ISR0);
+ if (isr0 && isr0 != 0xffffffff)
+ {
+ return isr0;
+ }
+ }
+
+ isr = macb_readl(eth, GEM_ISR);
+ if (isr && isr != 0xffffffff)
+ {
+ return isr;
+ }
+
+ /*
+ * Legacy ISR @ 0x24 may read 0 when pending; try queue-0 bank
+ * @ 0x400 when DCFG1.IRQCOR is clear or ISR0 was not tried yet.
+ */
+ if (!use_q0 && (eth->native_io || !isr0))
+ {
+ isr0 = macb_readl(eth, GEM_ISR0);
+ if (isr0 && isr0 != 0xffffffff)
+ {
+ return isr0;
+ }
+ }
+
+ return 0;
+}
+
+static void macb_init_ring_bases(struct macb_eth *eth)
+{
+ if (eth->dma_64bit)
+ {
+ macb_writel(eth, GEM_RBQPH, (rt_uint32_t)(eth->rx_ring_dma >> 32));
+ macb_writel(eth, GEM_TBQPH, (rt_uint32_t)(eth->tx_ring_dma >> 32));
+ macb_writel(eth, GEM_RBQP, (rt_uint32_t)eth->rx_ring_dma);
+ macb_writel(eth, GEM_TBQP, (rt_uint32_t)eth->tx_ring_dma);
+ }
+ else
+ {
+ macb_writel(eth, GEM_RBQP, (rt_uint32_t)eth->rx_ring_dma);
+ macb_writel(eth, GEM_TBQP, (rt_uint32_t)eth->tx_ring_dma);
+ }
+
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+ else if (!eth->mac_started)
+ {
+ (void)macb_readl(eth, GEM_TBQP);
+ if (eth->dma_64bit)
+ {
+ (void)macb_readl(eth, GEM_TBQPH);
+ }
+ }
+ else
+ {
+ rt_hw_dsb();
+ }
+}
+
+static rt_uint32_t macb_gem_build_dmacfg(struct macb_eth *eth)
+{
+ rt_uint32_t dmacfg = 0;
+
+ dmacfg |= (MACB_RX_BUFFER_SIZE_DIV64 & GEM_DMACFG_RXBS_MASK) << GEM_DMACFG_RXBS_SHIFT;
+ dmacfg |= RT_BIT(GEM_DMACFG_TXPBMS_SHIFT);
+ dmacfg |= (3u << GEM_DMACFG_RXBMS_SHIFT);
+ if (!eth->native_io)
+ {
+ dmacfg |= RT_BIT(GEM_DMACFG_ENDIA_DESC_SHIFT);
+ }
+ if (eth->dma_64bit)
+ {
+ dmacfg |= RT_BIT(GEM_DMACFG_ADDR64_SHIFT);
+ }
+ if (eth->config && eth->config->dma_burst_length)
+ {
+ dmacfg |= (eth->config->dma_burst_length & 0x1f) << GEM_DMACFG_FBLDO_SHIFT;
+ }
+
+ return dmacfg;
+}
+
+static void macb_detect_caps(struct macb_eth *eth)
+{
+ rt_uint32_t dcfg1 = macb_readl(eth, GEM_DCFG1);
+
+ eth->isr_clear_on_write = RT_FALSE;
+
+ if (dcfg1 != 0xffffffff &&
+ !((dcfg1 >> GEM_DCFG1_IRQCOR_SHIFT) & 1u))
+ {
+ eth->isr_clear_on_write = RT_TRUE;
+ }
+}
+
+static void macb_gem_program_dmacfg(struct macb_eth *eth)
+{
+ rt_uint32_t dmacfg = macb_gem_build_dmacfg(eth);
+
+ macb_writel(eth, GEM_DMACFG, dmacfg);
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+ else
+ {
+ (void)macb_readl(eth, GEM_DMACFG);
+ }
+}
+
+/*
+ * U-Boot gmac_init_multi_queues(): point unused queue 1+ rings at a
+ * dummy TX_USED descriptor so the GEM DMA engine stays idle on them.
+ */
+static rt_err_t macb_gem_init_multi_queues(struct macb_eth *eth)
+{
+ rt_uint32_t queue_mask;
+ int i, num_queues = 1;
+
+ if (!eth->is_gem || !eth->dummy_desc)
+ {
+ return RT_EOK;
+ }
+
+ eth->dummy_desc->ctrl = GEM_TX_USED;
+ eth->dummy_desc->addr = 0;
+ if (eth->dma_64bit)
+ {
+ struct macb_dma_desc_64 *d64 = (struct macb_dma_desc_64 *)
+ ((rt_uint8_t *)eth->dummy_desc + sizeof(*eth->dummy_desc));
+
+ d64->addrh = 0;
+ }
+ macb_dma_flush(eth, eth->dummy_desc, eth->dummy_desc_size);
+
+ queue_mask = macb_readl(eth, GEM_DCFG6) & 0xff;
+ if (queue_mask == 0xffffffff)
+ {
+ queue_mask = 0x1;
+ }
+ queue_mask |= 0x1;
+
+ for (i = 1; i < MACB_MAX_QUEUES; ++i)
+ {
+ if (queue_mask & RT_BIT(i))
+ {
+ ++num_queues;
+ }
+ }
+
+ for (i = 1; i < num_queues; ++i)
+ {
+ /*
+ * Queue 1+ low pointers only; TBQPH/RBQPH are shared for queue 0
+ * (see Linux macb_init_buffers()).
+ */
+ macb_writel(eth, GEM_QUEUE_TBQP(i - 1), (rt_uint32_t)eth->dummy_desc_dma);
+ macb_writel(eth, GEM_QUEUE_RBQP(i - 1), (rt_uint32_t)eth->dummy_desc_dma);
+ }
+
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+ else
+ {
+ (void)macb_readl(eth, GEM_TBQP);
+ }
+ return RT_EOK;
+}
+
+static rt_bool_t macb_phy_interface_is_rgmii(int mode)
+{
+#ifdef RT_USING_PHY_V2
+ return mode == RT_PHY_INTERFACE_MODE_RGMII ||
+ mode == RT_PHY_INTERFACE_MODE_RGMII_ID ||
+ mode == RT_PHY_INTERFACE_MODE_RGMII_RXID ||
+ mode == RT_PHY_INTERFACE_MODE_RGMII_TXID;
+#else
+ RT_UNUSED(mode);
+ return RT_FALSE;
+#endif
+}
+
+static void macb_parse_dt_props(struct macb_eth *eth)
+{
+ struct rt_device *dev = eth->dev;
+ rt_uint32_t val;
+
+#ifdef RT_USING_OFW
+ if (!dev || !dev->ofw_node)
+ {
+ return;
+ }
+
+#ifdef RT_USING_PHY_V2
+ if (rt_ofw_get_interface(dev->ofw_node, (rt_phy_interface *)&eth->phy_interface))
+ {
+ eth->phy_interface = RT_PHY_INTERFACE_MODE_NA;
+ }
+#endif
+
+ eth->phy_reset_pin = rt_pin_get_named_pin(dev, "phy-reset", 0,
+ RT_NULL, &eth->phy_reset_active);
+ if (eth->phy_reset_pin < 0)
+ {
+ eth->phy_reset_pin = -1;
+ }
+
+ eth->phy_reset_ms = 10;
+ rt_dm_dev_prop_read_u32(dev, "phy-reset-duration", &val);
+ if (val > 0 && val <= 1000)
+ {
+ eth->phy_reset_ms = val;
+ }
+
+ if (!rt_dm_dev_prop_read_u32(dev, "cdns,aw2w-max-pipe", &val))
+ {
+ eth->aw2w_max_pipe = val;
+ }
+ if (!rt_dm_dev_prop_read_u32(dev, "cdns,ar2r-max-pipe", &val))
+ {
+ eth->ar2r_max_pipe = val;
+ }
+ eth->use_aw2b_fill = rt_dm_dev_prop_read_bool(dev, "cdns,use-aw2b-fill");
+#endif
+}
+
+static void macb_phy_reset(struct macb_eth *eth)
+{
+ rt_uint32_t ms;
+
+ if (eth->phy_reset_pin < 0)
+ {
+ return;
+ }
+
+ ms = eth->phy_reset_ms ? eth->phy_reset_ms : 10;
+
+ rt_pin_mode(eth->phy_reset_pin, PIN_MODE_OUTPUT);
+ rt_pin_write(eth->phy_reset_pin, eth->phy_reset_active);
+ rt_hw_us_delay(ms * 1000);
+ rt_pin_write(eth->phy_reset_pin, !eth->phy_reset_active);
+ rt_hw_us_delay(100000);
+}
+
+static void macb_gem_init_usrio(struct macb_eth *eth)
+{
+ rt_uint32_t usrio = 0;
+ rt_uint32_t ncr_save = eth->ncr_shadow;
+ int mode = eth->phy_interface;
+
+ if (eth->config && (eth->config->caps & MACB_CAP_USRIO_DISABLED))
+ {
+ return;
+ }
+
+#ifdef RT_USING_PHY_V2
+ if (mode == RT_PHY_INTERFACE_MODE_NA && eth->phy)
+ {
+ mode = eth->phy->interface;
+ }
+#endif
+
+ if (macb_phy_interface_is_rgmii(mode))
+ {
+ usrio = GEM_USRIO_RGMII;
+ }
+
+ /* Program USRIO with MAC TX/RX off so the mode latch takes effect. */
+ macb_ncr_write(eth, ncr_save & ~(GEM_NCR_TE | GEM_NCR_RE));
+ macb_writel(eth, GEM_USRIO, usrio);
+ rt_hw_dsb();
+ macb_ncr_write(eth, ncr_save);
+}
+
+static void macb_gem_init_intmod(struct macb_eth *eth)
+{
+ rt_uint32_t throttle = (1000 * 50) / 800;
+ rt_uint32_t intmod = 0;
+
+ intmod |= (throttle & 0xffu) << GEM_INTMOD_TX_MOD_SHIFT;
+ intmod |= (throttle & 0xffu) << GEM_INTMOD_RX_MOD_SHIFT;
+ macb_writel(eth, GEM_INTMOD, intmod);
+}
+
+static void macb_gem_init_axi(struct macb_eth *eth)
+{
+ rt_uint32_t amp;
+ rt_uint8_t aw2w = eth->aw2w_max_pipe;
+ rt_uint8_t ar2r = eth->ar2r_max_pipe;
+
+ if (!aw2w && !ar2r && !eth->use_aw2b_fill)
+ {
+ return;
+ }
+
+ /* Preserve reset defaults; zeroing AMP can break remote GEM DMA. */
+ amp = macb_readl(eth, GEM_AMP);
+ if (eth->use_aw2b_fill)
+ {
+ amp |= RT_BIT(GEM_AMP_AW2B_FILL_SHIFT);
+ }
+ if (aw2w)
+ {
+ amp &= ~((rt_uint32_t)0xffu << GEM_AMP_AW2W_MAX_PIPE_SHIFT);
+ amp |= (aw2w & 0xffu) << GEM_AMP_AW2W_MAX_PIPE_SHIFT;
+ }
+ if (ar2r)
+ {
+ amp &= ~((rt_uint32_t)0xffu << GEM_AMP_AR2R_MAX_PIPE_SHIFT);
+ amp |= (ar2r & 0xffu) << GEM_AMP_AR2R_MAX_PIPE_SHIFT;
+ }
+
+ macb_writel(eth, GEM_AMP, amp);
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+}
+
+static void macb_rx_refill(struct macb_eth *eth, rt_uint32_t idx)
+{
+ struct macb_dma_desc *d = macb_rx_desc_at(eth, idx);
+ rt_uint64_t p = eth->rx_buffer_dma + idx * MACB_RX_BUFFER_SIZE;
+ rt_size_t dsz = macb_desc_sync_size(eth);
+
+ d->ctrl = 0;
+ if (eth->dma_64bit)
+ {
+ macb_set_desc_addr(eth, d, p);
+ }
+ else
+ {
+ d->addr = (rt_uint32_t)p;
+ }
+ d->addr &= ~GEM_RX_USED;
+ if (idx == MACB_RX_RING_SIZE - 1)
+ {
+ d->addr |= GEM_RX_WRAP;
+ }
+
+ macb_dma_flush(eth, d, dsz);
+}
+
+static void macb_tx_cleanup(struct macb_eth *eth)
+{
+ rt_uint32_t tail = eth->tx_tail;
+
+ while (tail != eth->tx_head)
+ {
+ struct macb_dma_desc *d = macb_tx_desc_at(eth, tail);
+ rt_size_t dsz = macb_desc_sync_size(eth);
+
+ macb_dma_inval(eth, d, dsz);
+ if (!eth->native_io)
+ {
+ macb_dma_read_barrier(eth);
+ }
+ else
+ {
+ rt_hw_dmb();
+ }
+ if (!(d->ctrl & GEM_TX_USED))
+ {
+ break;
+ }
+
+ d->ctrl = GEM_TX_USED;
+ if (tail == MACB_TX_RING_SIZE - 1)
+ {
+ d->ctrl |= GEM_TX_WRAP;
+ }
+ rt_hw_dmb();
+ macb_dma_flush(eth, d, dsz);
+
+ rt_sem_release(&eth->tx_sem);
+ tail = NEXT_TX(tail);
+ }
+
+ eth->tx_tail = tail;
+}
+
+/*
+ * Poll TX/RX completion without clearing GEM ISR/TSR/IDR. Used when a
+ * hardware IRQ is installed (level-triggered MSI/MSIX must stay asserted).
+ */
+static void macb_poll_completions(struct macb_eth *eth)
+{
+ struct macb_dma_desc *d;
+
+ macb_tx_cleanup(eth);
+
+ d = macb_rx_desc_at(eth, eth->rx_tail);
+ macb_dma_inval(eth, d, macb_desc_sync_size(eth));
+ rt_hw_dmb();
+ if (d->addr & GEM_RX_USED)
+ {
+ eth_device_ready(&eth->parent);
+ }
+}
+
+static rt_err_t macb_eth_tx(rt_device_t dev, struct pbuf *p)
+{
+ struct macb_eth *eth = raw_to_macb_eth(dev);
+ struct macb_dma_desc *d;
+ rt_uint32_t ctrl;
+ rt_uint8_t *buf;
+
+ if (!eth->hw_ready || !eth->mac_started)
+ {
+ return -RT_EBUSY;
+ }
+
+ if (!eth->phy || !eth->phy->link)
+ {
+ return -RT_EBUSY;
+ }
+
+ if (p->tot_len > eth->max_tx_len || p->tot_len > MACB_RX_BUFFER_SIZE)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (rt_sem_take(&eth->tx_sem, RT_WAITING_FOREVER) != RT_EOK)
+ {
+ return -RT_EBUSY;
+ }
+
+ {
+ rt_uint32_t slot = eth->tx_head;
+ rt_uint64_t buf_dma = eth->tx_buffer_dma + slot * MACB_RX_BUFFER_SIZE;
+
+ d = macb_tx_desc_at(eth, slot);
+ buf = eth->tx_buffer + slot * MACB_RX_BUFFER_SIZE;
+
+ pbuf_copy_partial(p, buf, p->tot_len, 0);
+ rt_hw_dmb();
+ macb_dma_flush(eth, buf, p->tot_len);
+
+ ctrl = (p->tot_len & GEM_TX_LEN_MASK) | GEM_TX_LAST;
+ if (slot == MACB_TX_RING_SIZE - 1)
+ {
+ ctrl |= GEM_TX_WRAP;
+ }
+
+ macb_set_desc_addr(eth, d, buf_dma);
+ rt_hw_wmb();
+ d->ctrl = ctrl;
+ rt_hw_wmb();
+ macb_desc_ring_sync(eth, RT_FALSE, RT_TRUE);
+
+ eth->tx_head = NEXT_TX(eth->tx_head);
+ macb_gem_kick_tx(eth);
+ macb_gem_tx_restart(eth);
+ /*
+ * With irq_installed: poll completions only. service_traffic()
+ * clears ISR and can drop level-triggered MSI/MSIX lines.
+ */
+ if (eth->irq_installed)
+ {
+ macb_poll_completions(eth);
+ }
+ else
+ {
+ macb_service_traffic(eth);
+ }
+ }
+
+ eth_device_ready(&eth->parent);
+
+ return RT_EOK;
+}
+
+static struct pbuf *macb_eth_rx(rt_device_t dev)
+{
+ struct macb_eth *eth = raw_to_macb_eth(dev);
+ struct macb_dma_desc *d;
+ rt_uint32_t ctrl;
+ rt_uint32_t addr;
+ rt_uint16_t len;
+ struct pbuf *p = RT_NULL;
+
+ if (eth->irq_installed)
+ {
+ macb_poll_completions(eth);
+ }
+ else
+ {
+ macb_service_traffic(eth);
+ }
+
+ d = macb_rx_desc_at(eth, eth->rx_tail);
+ macb_dma_inval(eth, d, macb_desc_sync_size(eth));
+ rt_hw_dmb();
+ addr = d->addr;
+
+ /* Ownership: HW sets RX_USED in addr when a frame is in the buffer */
+ if (!(addr & GEM_RX_USED))
+ {
+ return RT_NULL;
+ }
+
+ ctrl = d->ctrl;
+
+ if (!((ctrl & GEM_RX_SOF) && (ctrl & GEM_RX_EOF)))
+ {
+ macb_rx_refill(eth, eth->rx_tail);
+ eth->rx_tail = NEXT_RX(eth->rx_tail);
+ return RT_NULL;
+ }
+
+ len = (rt_uint16_t)(ctrl & GEM_RX_LEN_MASK);
+ if (len == 0 || len > MACB_RX_BUFFER_SIZE)
+ {
+ macb_rx_refill(eth, eth->rx_tail);
+ eth->rx_tail = NEXT_RX(eth->rx_tail);
+ return RT_NULL;
+ }
+
+ {
+ rt_uint8_t *buf = eth->rx_buffer + eth->rx_tail * MACB_RX_BUFFER_SIZE;
+
+ macb_dma_inval(eth, buf, len);
+ rt_hw_dmb();
+
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
+ if (p)
+ {
+ rt_memcpy(p->payload, buf, len);
+ }
+ }
+
+ macb_rx_refill(eth, eth->rx_tail);
+ eth->rx_tail = NEXT_RX(eth->rx_tail);
+
+ return p;
+}
+
+static rt_err_t macb_eth_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t err = RT_EOK;
+ struct macb_eth *eth = raw_to_macb_eth(dev);
+
+ switch (cmd)
+ {
+ case NIOCTL_GADDR:
+ if (args)
+ {
+ rt_memcpy(args, eth->mac, sizeof(eth->mac));
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ default:
+ err = -RT_EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops macb_eth_ops =
+{
+ .control = macb_eth_control,
+};
+#endif
+
+static void macb_clear_gem_irq(struct macb_eth *eth, rt_uint32_t status)
+{
+ if (!status || status == 0xffffffff)
+ {
+ return;
+ }
+
+ if (status & GEM_INT_RX_BITS)
+ {
+ macb_writel(eth, GEM_RSR, macb_readl(eth, GEM_RSR));
+ }
+
+ if (status & GEM_INT_TX_BITS)
+ {
+ macb_writel(eth, GEM_TSR, macb_readl(eth, GEM_TSR));
+ }
+
+ if (macb_gem_irq_use_q0_bank(eth))
+ {
+ macb_writel(eth, GEM_ISR0, status);
+ }
+ macb_writel(eth, GEM_ISR, status);
+ macb_mmio_posted_barrier(eth);
+}
+
+static void macb_gem_commit_irq_enable(struct macb_eth *eth)
+{
+ rt_uint32_t mask = GEM_INT_RX_BITS | GEM_INT_TX_BITS | GEM_INT_HRESP;
+ rt_uint32_t imr, pending;
+ rt_bool_t use_q0 = macb_gem_irq_use_q0_bank(eth);
+
+ if (use_q0)
+ {
+ pending = macb_readl(eth, GEM_ISR0);
+ if (pending == 0xffffffff)
+ {
+ pending = 0;
+ }
+ }
+ else
+ {
+ pending = macb_readl(eth, GEM_ISR);
+ }
+ if (pending != 0xffffffff && pending)
+ {
+ macb_clear_gem_irq(eth, pending);
+ }
+
+ if (use_q0)
+ {
+ macb_writel(eth, GEM_IDR0, 0xffffffff);
+ macb_writel(eth, GEM_ISR0, 0xffffffff);
+ }
+ macb_writel(eth, GEM_IDR, 0xffffffff);
+ if (eth->isr_clear_on_write)
+ {
+ macb_writel(eth, GEM_ISR, 0xffffffff);
+ }
+
+ macb_writel(eth, GEM_IER, mask);
+ if (use_q0)
+ {
+ macb_writel(eth, GEM_IER0, mask);
+ }
+ macb_mmio_posted_barrier(eth);
+
+ imr = use_q0 ? macb_readl(eth, GEM_IMR0) : macb_readl(eth, GEM_IMR);
+ if (imr == 0xffffffff)
+ {
+ imr = macb_readl(eth, GEM_IMR);
+ }
+ if (!(imr & GEM_INT_RCOMP))
+ {
+ if (use_q0)
+ {
+ macb_writel(eth, GEM_IER0, GEM_INT_RCOMP);
+ }
+ else
+ {
+ macb_writel(eth, GEM_IER, GEM_INT_RCOMP);
+ }
+ macb_mmio_posted_barrier(eth);
+ if (use_q0)
+ {
+ macb_writel(eth, GEM_IER0, mask);
+ }
+ else
+ {
+ macb_writel(eth, GEM_IER, mask);
+ }
+ macb_mmio_posted_barrier(eth);
+ imr = use_q0 ? macb_readl(eth, GEM_IMR0) : macb_readl(eth, GEM_IMR);
+ }
+}
+
+static void macb_gem_write_traffic_ier(struct macb_eth *eth)
+{
+ rt_uint32_t mask = GEM_INT_RX_BITS | GEM_INT_TX_BITS | GEM_INT_HRESP;
+
+ macb_writel(eth, GEM_IER, mask);
+ if (macb_gem_irq_use_q0_bank(eth))
+ {
+ macb_writel(eth, GEM_IER0, mask);
+ }
+ macb_mmio_posted_barrier(eth);
+}
+
+static void macb_service_traffic(struct macb_eth *eth)
+{
+ rt_uint32_t status = macb_gem_isr(eth);
+
+ if (!status)
+ {
+ rt_uint32_t rsr = macb_readl(eth, GEM_RSR);
+ rt_uint32_t tsr = macb_readl(eth, GEM_TSR);
+
+ if (rsr & (GEM_RSR_REC | GEM_RSR_BNA | GEM_RSR_OVR))
+ {
+ status |= GEM_INT_RCOMP;
+ }
+ if (tsr & (GEM_TSR_TGO | GEM_TSR_COMP))
+ {
+ status |= GEM_INT_TCOMP;
+ }
+ }
+
+ while (status)
+ {
+ if (status & GEM_INT_RX_BITS)
+ {
+ rt_uint32_t rsr = macb_readl(eth, GEM_RSR);
+
+ macb_writel(eth, GEM_IDR, GEM_INT_RX_BITS);
+ macb_writel(eth, GEM_RSR, rsr);
+ eth_device_ready(&eth->parent);
+ macb_gem_write_traffic_ier(eth);
+ }
+
+ if (status & GEM_INT_TX_BITS)
+ {
+ rt_uint32_t tsr = macb_readl(eth, GEM_TSR);
+
+ macb_writel(eth, GEM_IDR, GEM_INT_TCOMP);
+ macb_writel(eth, GEM_TSR, tsr);
+ macb_tx_cleanup(eth);
+ macb_writel(eth, GEM_IER, GEM_INT_TCOMP);
+ macb_mmio_posted_barrier(eth);
+ }
+
+ if (status & GEM_INT_HRESP)
+ {
+ LOG_E("GEM HRESP bus error");
+ }
+
+ macb_clear_gem_irq(eth, status);
+ status = macb_gem_isr(eth);
+ if (!eth->native_io && status == 0xffffffff)
+ {
+ break;
+ }
+ }
+
+ macb_tx_cleanup(eth);
+}
+
+static void macb_eth_isr(int irq, void *param)
+{
+ struct macb_eth *eth = param;
+
+ RT_UNUSED(irq);
+
+ macb_service_traffic(eth);
+}
+
+#include "macb_phy.c"
+
+static int macb_find_phy_addr(struct macb_eth *eth)
+{
+ int addr;
+
+#ifdef RT_USING_OFW
+ struct rt_ofw_node *phy_np;
+
+ if (eth->dev && eth->dev->ofw_node &&
+ (phy_np = rt_ofw_parse_phandle(eth->dev->ofw_node, "phy-handle", 0)))
+ {
+ rt_uint32_t reg;
+
+ if (!rt_ofw_prop_read_u32(phy_np, "reg", &reg))
+ {
+ rt_ofw_node_put(phy_np);
+ return (int)reg;
+ }
+
+ rt_ofw_node_put(phy_np);
+ }
+#endif
+
+ for (addr = 0; addr < 32; ++addr)
+ {
+ int id = gem_mdio_read(eth->mii, addr, 0, RT_MII_PHYSID1);
+
+ if (id > 0 && id != 0xffff)
+ {
+ return addr;
+ }
+ }
+
+ return -1;
+}
+
+static void macb_set_mac_addr(struct macb_eth *eth)
+{
+ rt_uint32_t lo;
+ rt_uint16_t hi;
+
+ lo = eth->mac[0] | (eth->mac[1] << 8) | (eth->mac[2] << 16) | (eth->mac[3] << 24);
+ hi = eth->mac[4] | (eth->mac[5] << 8);
+ macb_writel(eth, GEM_SA1B, lo);
+ macb_writel(eth, GEM_SA1T, hi);
+}
+
+static rt_bool_t macb_phy_poll_link(struct macb_eth *eth)
+{
+ rt_uint32_t bmsr;
+
+ if (!eth->phy)
+ {
+ return RT_FALSE;
+ }
+
+ bmsr = rt_phy_read(eth->phy, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
+ if (bmsr < 0 || bmsr == 0xffff)
+ {
+ eth->phy->link = 0;
+ return RT_FALSE;
+ }
+
+ if (!(bmsr & RT_BMSR_LSTATUS))
+ {
+ bmsr = rt_phy_read(eth->phy, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
+ }
+ if (bmsr < 0 || bmsr == 0xffff || !(bmsr & RT_BMSR_LSTATUS))
+ {
+ eth->phy->link = 0;
+ return RT_FALSE;
+ }
+
+ eth->phy->link = 1;
+ if (rt_genphy_parse_link(eth->phy))
+ {
+ eth->phy->link = 0;
+ return RT_FALSE;
+ }
+
+ return RT_TRUE;
+}
+
+static rt_err_t macb_start(struct macb_eth *eth)
+{
+ rt_err_t err;
+
+ if (eth->mac_started)
+ {
+ return RT_EOK;
+ }
+
+ if (!eth->phy)
+ {
+ return -RT_ERROR;
+ }
+
+ if (!eth->phy_configured)
+ {
+ err = rt_phy_config(eth->phy);
+ if (err < 0)
+ {
+ LOG_W("PHY config failed: %s", rt_strerror(err));
+ return err;
+ }
+
+ eth->phy_configured = RT_TRUE;
+ }
+
+ err = rt_phy_startup(eth->phy);
+ if (err)
+ {
+ LOG_W("PHY startup failed: %s", rt_strerror(err));
+ return err;
+ }
+
+ if (!eth->phy->link)
+ {
+ return RT_EOK;
+ }
+
+ return macb_mac_enable(eth);
+}
+
+static void macb_phy_link_up(struct macb_eth *eth)
+{
+ if (!eth->mac_started)
+ {
+ if (macb_mac_enable(eth) != RT_EOK)
+ {
+ LOG_W("MAC enable on link up failed");
+ return;
+ }
+ }
+ else if (macb_adjust_link(eth))
+ {
+ LOG_W("Adjust PHY link failed");
+ return;
+ }
+ eth_device_linkchange(&eth->parent, RT_TRUE);
+}
+
+static void macb_phy_link_down(struct macb_eth *eth)
+{
+ if (eth->phy)
+ {
+ eth->phy->link = 0;
+ }
+
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+}
+
+static void macb_prepare_rings(struct macb_eth *eth)
+{
+ for (int i = 0; i < MACB_RX_RING_SIZE; ++i)
+ {
+ macb_rx_refill(eth, i);
+ }
+
+ eth->rx_tail = 0;
+ eth->tx_head = 0;
+ eth->tx_tail = 0;
+ rt_hw_dmb();
+ macb_init_ring_bases(eth);
+ macb_gem_init_multi_queues(eth);
+ /* U-Boot gmac_init_multi_queues() touches TBQPH/RBQPH — restore queue 0. */
+ macb_init_ring_bases(eth);
+}
+
+static rt_err_t macb_mac_enable(struct macb_eth *eth)
+{
+ rt_err_t err;
+
+ if (eth->mac_started)
+ {
+ return RT_EOK;
+ }
+
+ if (!eth->phy || !eth->phy->link)
+ {
+ return -RT_ERROR;
+ }
+
+ if ((err = macb_adjust_link(eth)))
+ {
+ LOG_W("Adjust link failed: %s", rt_strerror(err));
+ return err;
+ }
+
+ macb_plat_mac_enable_prepare();
+ macb_prepare_rings(eth);
+ macb_gem_program_dmacfg(eth);
+ macb_gem_init_usrio(eth);
+ if (eth->dma_64bit)
+ {
+ macb_gem_init_axi(eth);
+ macb_gem_init_intmod(eth);
+ }
+ (void)macb_gem_isr(eth);
+ if (macb_gem_irq_use_q0_bank(eth))
+ {
+ macb_writel(eth, GEM_IDR0, 0xffffffff);
+ }
+ macb_writel(eth, GEM_IDR, 0xffffffff);
+ if (eth->isr_clear_on_write)
+ {
+ if (macb_gem_irq_use_q0_bank(eth))
+ {
+ macb_writel(eth, GEM_ISR0, 0xffffffff);
+ }
+ macb_writel(eth, GEM_ISR, 0xffffffff);
+ }
+ macb_mmio_posted_barrier(eth);
+
+ /* Linux mac_link_up(): enable IER before asserting TE|RE. */
+ macb_gem_commit_irq_enable(eth);
+
+ macb_ncr_posted_write(eth, GEM_NCR_TE | GEM_NCR_RE | GEM_NCR_MPE);
+ eth->ncr_shadow = GEM_NCR_TE | GEM_NCR_RE | GEM_NCR_MPE;
+ macb_init_ring_bases(eth);
+
+ if (eth->dma_64bit)
+ {
+ rt_uint32_t dmacfg = macb_readl(eth, GEM_DMACFG);
+
+ if (!(dmacfg & RT_BIT(GEM_DMACFG_ADDR64_SHIFT)))
+ {
+ LOG_E("DMACFG missing ADDR64 bit");
+ }
+ }
+
+ eth_device_ready(&eth->parent);
+
+ eth->mac_started = RT_TRUE;
+
+ return RT_EOK;
+}
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+static void macb_phy_retry_work(struct rt_work *work, void *work_data)
+{
+ struct macb_eth *eth = work_data;
+
+ RT_UNUSED(work);
+
+ if (!eth->mac_started)
+ {
+ if (macb_start(eth) == RT_EOK)
+ {
+ eth->phy_retry_count = 0;
+ if (eth->phy->link)
+ {
+ macb_phy_link_up(eth);
+ }
+ else
+ {
+ macb_phy_link_down(eth);
+ rt_work_submit(&eth->phy_retry_work,
+ rt_tick_from_millisecond(MACB_PHY_RETRY_INTERVAL_MS));
+ }
+ }
+ else
+ {
+ eth->phy_retry_count++;
+ if (eth->phy_retry_count < MACB_PHY_RETRY_MAX)
+ {
+ rt_work_submit(&eth->phy_retry_work,
+ rt_tick_from_millisecond(MACB_PHY_RETRY_INTERVAL_MS));
+ }
+ else
+ {
+ LOG_W("MAC start retry stopped");
+ eth->phy_retry_count = 0;
+ }
+ }
+ return;
+ }
+
+ if (eth->phy->link)
+ {
+ eth->phy_retry_count = 0;
+ if (!eth->mac_started)
+ {
+ macb_phy_link_up(eth);
+ }
+ return;
+ }
+
+ if (macb_phy_poll_link(eth))
+ {
+ eth->phy_retry_count = 0;
+ macb_phy_link_up(eth);
+ return;
+ }
+
+ eth->phy_retry_count++;
+ if (eth->phy_retry_count < MACB_PHY_RETRY_MAX)
+ {
+ rt_work_submit(&eth->phy_retry_work,
+ rt_tick_from_millisecond(MACB_PHY_RETRY_INTERVAL_MS));
+ }
+ else
+ {
+ LOG_W("PHY link retry stopped, wait for link IRQ");
+ eth->phy_retry_count = 0;
+ }
+}
+#endif
+
+rt_err_t macb_eth_hw_init(struct macb_eth *eth)
+{
+ rt_err_t err;
+ int phy_addr;
+ void *blob;
+ rt_size_t blob_sz, rx_ring_bytes, tx_ring_bytes;
+ rt_ubase_t dma_handle;
+ rt_ubase_t pclk_hz = 0;
+ rt_uint32_t ncfgr;
+
+ if (!eth->config)
+ {
+ eth->config = &macb_cfg_default_gem;
+ }
+
+ eth->max_tx_len = eth->config->max_tx_len ? eth->config->max_tx_len : 0x3fc0; /* GEM_MAX_TX_LEN */
+
+ if (eth->pclk)
+ {
+ rt_clk_prepare_enable(eth->pclk);
+ pclk_hz = rt_clk_get_rate(eth->pclk);
+ }
+ if (eth->hclk)
+ {
+ rt_clk_prepare_enable(eth->hclk);
+ }
+ if (eth->tsu_clk)
+ {
+ rt_clk_prepare_enable(eth->tsu_clk);
+ }
+ rt_mutex_init(&eth->mdio_lock, "macb_mdio", RT_IPC_FLAG_PRIO);
+ rt_sem_init(&eth->tx_sem, "macb_tx", MACB_TX_RING_SIZE, RT_IPC_FLAG_FIFO);
+
+ eth->is_gem = RT_TRUE;
+ eth->dma_64bit = RT_FALSE;
+ eth->native_io = RT_FALSE;
+ eth->mac_started = RT_FALSE;
+ eth->hw_ready = RT_FALSE;
+ eth->ncr_shadow = 0;
+ eth->ncfgr_shadow = 0;
+
+ macb_writel(eth, GEM_NCR, 0);
+ rt_hw_us_delay(20);
+ macb_writel(eth, GEM_IDR, 0xffffffff);
+ macb_writel(eth, GEM_ISR, 0xffffffff);
+
+ eth->native_io = macb_hw_is_native_io(eth);
+ macb_update_dma64(eth, RT_FALSE);
+ macb_detect_caps(eth);
+
+ macb_writel(eth, GEM_JML, eth->config->jumbo_max_len ? eth->config->jumbo_max_len : 0x2400);
+
+ /* NCFGR: MDC divider, DBW, standard GEM flags */
+ pclk_hz = pclk_hz ? : 150000000;
+
+ ncfgr = gem_ncfgr_mdc_div(pclk_hz);
+ ncfgr |= GEM_NCFGR_BIG | GEM_NCFGR_DRFCS;
+ ncfgr |= macb_gem_dbw(eth);
+ eth->ncfgr_shadow = ncfgr;
+ macb_writel(eth, GEM_NCFGR, ncfgr);
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+
+ rx_ring_bytes = macb_desc_stride(eth) * MACB_RX_RING_SIZE;
+ tx_ring_bytes = macb_desc_stride(eth) * MACB_TX_RING_SIZE;
+ blob_sz = rx_ring_bytes + tx_ring_bytes +
+ MACB_RX_RING_SIZE * MACB_RX_BUFFER_SIZE +
+ MACB_TX_RING_SIZE * MACB_RX_BUFFER_SIZE;
+
+ {
+ rt_ubase_t dma_flags = macb_dma_flags(eth);
+
+ blob = rt_dma_alloc(eth->dev, blob_sz, &dma_handle, dma_flags);
+ }
+
+ if (!blob)
+ {
+ err = -RT_ENOMEM;
+ goto _fail_sem;
+ }
+
+ eth->rx_ring = (struct macb_dma_desc *)blob;
+ eth->tx_ring = (struct macb_dma_desc *)((rt_uint8_t *)blob + rx_ring_bytes);
+ eth->rx_buffer = (rt_uint8_t *)((rt_uint8_t *)eth->tx_ring + tx_ring_bytes);
+ eth->tx_buffer = eth->rx_buffer + MACB_RX_RING_SIZE * MACB_RX_BUFFER_SIZE;
+ eth->dma_blob_size = blob_sz;
+ eth->dma_blob_handle = dma_handle;
+
+ eth->rx_ring_dma = dma_handle;
+ eth->tx_ring_dma = dma_handle + rx_ring_bytes;
+ eth->rx_buffer_dma = dma_handle + rx_ring_bytes + tx_ring_bytes;
+ eth->tx_buffer_dma = eth->rx_buffer_dma + MACB_RX_RING_SIZE * MACB_RX_BUFFER_SIZE;
+
+ if (eth->rx_ring_dma > 0xffffffffULL)
+ {
+ macb_update_dma64(eth, RT_TRUE);
+ }
+
+ if (eth->rx_ring_dma > 0xffffffffULL && !eth->dma_64bit)
+ {
+ LOG_E("DMA bus addr %#llx exceeds 32-bit; enable GEM ADDR64",
+ (unsigned long long)eth->rx_ring_dma);
+ err = -RT_EINVAL;
+ goto _fail_blob;
+ }
+
+ if (eth->dma_64bit)
+ {
+ macb_gem_setup_dma_hw(eth);
+ }
+ else
+ {
+ rt_uint32_t dmacfg;
+
+ dmacfg = macb_readl(eth, GEM_DMACFG);
+ dmacfg &= ~(GEM_DMACFG_RXBS_MASK << GEM_DMACFG_RXBS_SHIFT);
+ dmacfg |= (MACB_RX_BUFFER_SIZE_DIV64 & GEM_DMACFG_RXBS_MASK) << GEM_DMACFG_RXBS_SHIFT;
+ dmacfg |= RT_BIT(GEM_DMACFG_TXPBMS_SHIFT);
+ dmacfg |= (3u << GEM_DMACFG_RXBMS_SHIFT);
+ dmacfg &= ~RT_BIT(GEM_DMACFG_ENDIA_PKT_SHIFT);
+ dmacfg &= ~RT_BIT(GEM_DMACFG_ENDIA_DESC_SHIFT);
+ dmacfg &= ~RT_BIT(GEM_DMACFG_ADDR64_SHIFT);
+ if (eth->config->dma_burst_length)
+ {
+ dmacfg &= ~(0x1f << GEM_DMACFG_FBLDO_SHIFT);
+ dmacfg |= (eth->config->dma_burst_length & 0x1f) << GEM_DMACFG_FBLDO_SHIFT;
+ }
+ macb_writel(eth, GEM_DMACFG, dmacfg);
+ macb_writel(eth, GEM_USRIO, 0);
+ }
+
+ eth->dummy_desc_size = macb_desc_stride(eth);
+ if (eth->dma_64bit)
+ {
+ if (!(eth->dummy_desc = rt_dma_alloc(eth->dev, eth->dummy_desc_size,
+ &eth->dummy_desc_dma, macb_dma_flags(eth))))
+ {
+ err = -RT_ENOMEM;
+ goto _fail_blob;
+ }
+ }
+
+ rt_memset(eth->rx_ring, 0, rx_ring_bytes + tx_ring_bytes);
+
+ for (int i = 0; i < MACB_RX_RING_SIZE; ++i)
+ {
+ macb_rx_refill(eth, i);
+ }
+
+ for (int i = 0; i < MACB_TX_RING_SIZE; ++i)
+ {
+ struct macb_dma_desc *d = macb_tx_desc_at(eth, i);
+ rt_uint64_t p = eth->tx_buffer_dma + i * MACB_RX_BUFFER_SIZE;
+ rt_size_t dsz = macb_desc_sync_size(eth);
+
+ if (eth->dma_64bit)
+ {
+ macb_set_desc_addr(eth, d, p);
+ }
+ else
+ {
+ d->addr = (rt_uint32_t)p;
+ }
+ d->ctrl = GEM_TX_USED;
+ if (i == MACB_TX_RING_SIZE - 1)
+ {
+ d->ctrl |= GEM_TX_WRAP;
+ }
+ macb_dma_flush(eth, d, dsz);
+ }
+
+ eth->rx_tail = 0;
+ eth->tx_head = 0;
+ eth->tx_tail = 0;
+
+ rt_hw_dmb();
+
+ /* Ring bases are programmed in macb_mac_enable(), like Linux mac_link_up. */
+ macb_set_mac_addr(eth);
+
+ if ((err = macb_mii_register(eth)))
+ {
+ goto _fail_blob;
+ }
+
+ phy_addr = macb_find_phy_addr(eth);
+ if (phy_addr < 0)
+ {
+ LOG_E("No PHY found on MDIO");
+ err = -RT_EINVAL;
+ goto _fail_mii;
+ }
+
+#ifdef RT_USING_PHY_V2
+ eth->phy = rt_phy_get_device(eth->mii, eth->dev->ofw_node, phy_addr,
+ (rt_phy_interface)eth->phy_interface);
+#else
+ eth->phy = rt_phy_device_create(eth->mii, phy_addr, 0xffffffff, RT_FALSE);
+#endif
+ if (!eth->phy)
+ {
+ LOG_E("PHY not found on MDIO");
+ err = -RT_ERROR;
+ goto _fail_mii;
+ }
+
+ if ((err = rt_phy_config(eth->phy)) < 0)
+ {
+ LOG_E("PHY config failed: %s", rt_strerror(err));
+ goto _fail_phy;
+ }
+
+ eth->phy_configured = RT_TRUE;
+
+ if ((err = rt_phy_startup(eth->phy)))
+ {
+ LOG_E("PHY startup failed: %s", rt_strerror(err));
+ goto _fail_phy;
+ }
+
+ if (!eth->phy->link)
+ {
+ return RT_EOK;
+ }
+
+ if ((err = macb_mac_enable(eth)))
+ {
+ LOG_W("MAC enable failed: %s", rt_strerror(err));
+ }
+
+ return RT_EOK;
+
+_fail_phy:
+ if (eth->phy)
+ {
+ rt_phy_shutdown(eth->phy);
+ eth->phy = RT_NULL;
+ }
+
+_fail_mii:
+ macb_mii_unregister(eth);
+
+_fail_blob:
+ if (eth->dummy_desc)
+ {
+ rt_dma_free(eth->dev, eth->dummy_desc_size, eth->dummy_desc,
+ eth->dummy_desc_dma, RT_DMA_F_LINEAR);
+ eth->dummy_desc = RT_NULL;
+ eth->dummy_desc_size = 0;
+ }
+ if (eth->rx_ring)
+ {
+ rt_dma_free(eth->dev, eth->dma_blob_size, eth->rx_ring, eth->dma_blob_handle,
+ RT_DMA_F_LINEAR);
+ eth->rx_ring = RT_NULL;
+ eth->dma_blob_size = 0;
+ }
+
+_fail_sem:
+ rt_sem_detach(&eth->tx_sem);
+ rt_mutex_detach(&eth->mdio_lock);
+ return err;
+}
+
+void macb_eth_hw_stop(struct macb_eth *eth)
+{
+ if (!eth || !eth->regs)
+ {
+ return;
+ }
+
+ eth->mac_started = RT_FALSE;
+ eth->hw_ready = RT_FALSE;
+ macb_writel(eth, GEM_IDR, 0xffffffff);
+ macb_ncr_write(eth, 0);
+}
+
+rt_err_t macb_eth_common_probe(struct macb_eth *eth)
+{
+ rt_err_t err;
+
+#ifdef RT_USING_DEVICE_OPS
+ eth->parent.parent.ops = &macb_eth_ops;
+#else
+ eth->parent.parent.control = macb_eth_control;
+#endif
+ eth->parent.eth_tx = macb_eth_tx;
+ eth->parent.eth_rx = macb_eth_rx;
+
+ if ((err = rt_dm_dev_set_name_auto(&eth->parent.parent, "e")) < 0)
+ {
+ return err;
+ }
+
+ if (eth->irq >= 0)
+ {
+ rt_hw_interrupt_install(eth->irq, macb_eth_isr, eth, "macb");
+ rt_hw_interrupt_umask(eth->irq);
+ eth->irq_installed = RT_TRUE;
+ }
+
+ if ((err = eth_device_init(&eth->parent, rt_dm_dev_get_name(&eth->parent.parent))))
+ {
+ return err;
+ }
+
+ eth->net_registered = RT_TRUE;
+
+#ifdef RT_ETHERNET_CADENCE_PTP
+ if ((err = macb_ptp_register(eth)))
+ {
+ LOG_W("PTP register failed: %s", rt_strerror(err));
+ }
+#endif
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+ if (eth->phy && !eth->mac_started)
+ {
+ rt_work_init(&eth->phy_retry_work, macb_phy_retry_work, eth);
+ rt_work_submit(&eth->phy_retry_work,
+ rt_tick_from_millisecond(MACB_PHY_RETRY_INTERVAL_MS));
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ }
+ else if (eth->phy && eth->phy->link)
+ {
+ eth_device_linkchange(&eth->parent, RT_TRUE);
+ }
+ else
+ {
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ }
+#else
+ if (macb_start(eth) == RT_EOK && eth->phy && eth->phy->link)
+ {
+ eth_device_linkchange(&eth->parent, RT_TRUE);
+ }
+ else
+ {
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ }
+#endif
+
+ return RT_EOK;
+}
+
+rt_err_t macb_eth_common_remove(struct macb_eth *eth)
+{
+#ifdef RT_ETHERNET_CADENCE_PTP
+ macb_ptp_unregister(eth);
+#endif
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+ rt_work_cancel(&eth->phy_retry_work);
+#endif
+
+ if (eth->irq_installed)
+ {
+ rt_hw_interrupt_mask(eth->irq);
+ rt_pic_detach_irq(eth->irq, eth);
+ eth->irq_installed = RT_FALSE;
+ }
+
+ if (eth->net_registered)
+ {
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ eth_device_deinit(&eth->parent);
+ eth->net_registered = RT_FALSE;
+ }
+
+ macb_eth_hw_stop(eth);
+
+ if (eth->phy)
+ {
+ rt_phy_shutdown(eth->phy);
+ eth->phy = RT_NULL;
+ }
+
+ macb_mii_unregister(eth);
+
+ if (eth->dummy_desc)
+ {
+ rt_dma_free(eth->dev, eth->dummy_desc_size, eth->dummy_desc,
+ eth->dummy_desc_dma, RT_DMA_F_LINEAR);
+ eth->dummy_desc = RT_NULL;
+ eth->dummy_desc_size = 0;
+ }
+
+ if (eth->rx_ring)
+ {
+ rt_dma_free(eth->dev, eth->dma_blob_size, eth->rx_ring, eth->dma_blob_handle,
+ RT_DMA_F_LINEAR);
+ eth->rx_ring = RT_NULL;
+ eth->dma_blob_size = 0;
+ }
+
+ rt_sem_detach(&eth->tx_sem);
+ rt_mutex_detach(&eth->mdio_lock);
+
+ return RT_EOK;
+}
+
+static void macb_regs_iounmap(struct macb_eth *eth)
+{
+ if (eth->regs)
+ {
+ rt_iounmap(eth->regs);
+ }
+}
+
+static rt_err_t macb_platform_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t err;
+ struct rt_device *dev = &pdev->parent;
+ struct macb_eth *eth = rt_calloc(1, sizeof(*eth));
+
+ if (!eth)
+ {
+ return -RT_ENOMEM;
+ }
+
+ eth->dev = dev;
+ eth->config = &macb_cfg_default_gem;
+ if (pdev->id && pdev->id->data)
+ {
+ eth->config = (const struct macb_config *)pdev->id->data;
+ }
+ macb_parse_dt_props(eth);
+
+ if (eth->config == &macb_cfg_emac)
+ {
+ LOG_W("AT91 EMAC (cdns,emac / cdns,at91rm9200-emac): driver uses GEM path; verify on hardware");
+ }
+
+ eth->irq = rt_dm_dev_get_irq(dev, 0);
+ eth->regs = rt_dm_dev_iomap(dev, 0);
+
+ if (!eth->regs)
+ {
+ rt_free(eth);
+ return -RT_EIO;
+ }
+
+ macb_clocks_get(eth, dev);
+
+#ifdef RT_USING_OFW
+ if (rt_ofw_get_mac_addr(dev->ofw_node, eth->mac))
+#endif
+ {
+ ethernet_random_addr(&eth->parent, eth->mac);
+ }
+
+ dev->user_data = eth;
+
+ if ((err = macb_eth_hw_init(eth)))
+ {
+ LOG_E("hw init failed: %s", rt_strerror(err));
+ goto _unmap;
+ }
+
+ eth->hw_ready = RT_TRUE;
+
+ if ((err = macb_eth_common_probe(eth)))
+ {
+ macb_eth_common_remove(eth);
+ goto _unmap;
+ }
+
+ return RT_EOK;
+
+_unmap:
+ dev->user_data = RT_NULL;
+ macb_regs_iounmap(eth);
+ rt_free(eth);
+ return err;
+}
+
+static rt_err_t macb_platform_remove(struct rt_platform_device *pdev)
+{
+ struct macb_eth *eth = pdev->parent.user_data;
+
+ macb_eth_common_remove(eth);
+ macb_regs_iounmap(eth);
+ rt_free(eth);
+
+ return RT_EOK;
+}
+
+static const struct macb_config macb_cfg_default_gem =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_JUMBO | MACB_CAP_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_at91sam9260 =
+{
+ .caps = MACB_CAP_USRIO_HAS_CLKEN | MACB_CAP_USRIO_DEFAULT_MII_GMII,
+ .dma_burst_length = 0,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_pc302gem =
+{
+ .caps = MACB_CAP_SG_DISABLED | MACB_CAP_GIGABIT_MODE_AVAILABLE,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_sama5d2 =
+{
+ .caps = MACB_CAP_USRIO_DEFAULT_MII_GMII | MACB_CAP_JUMBO,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_sama5d29 =
+{
+ .caps = MACB_CAP_USRIO_DEFAULT_MII_GMII | MACB_CAP_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_sama5d3 =
+{
+ .caps = MACB_CAP_SG_DISABLED | MACB_CAP_GIGABIT_MODE_AVAILABLE |
+ MACB_CAP_USRIO_DEFAULT_MII_GMII | MACB_CAP_JUMBO,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_sama5d3_macb =
+{
+ .caps = MACB_CAP_SG_DISABLED | MACB_CAP_USRIO_HAS_CLKEN | MACB_CAP_USRIO_DEFAULT_MII_GMII,
+ .dma_burst_length = 0,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_sama5d4 =
+{
+ .caps = MACB_CAP_USRIO_DEFAULT_MII_GMII,
+ .dma_burst_length = 4,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_emac =
+{
+ .caps = MACB_CAP_NEEDS_RSTONUBR | MACB_CAP_MACB_IS_EMAC,
+ .dma_burst_length = 0,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_np4 =
+{
+ .caps = MACB_CAP_USRIO_DISABLED,
+ .dma_burst_length = 0,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_zynqmp =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_JUMBO | MACB_CAP_GEM_HAS_PTP |
+ MACB_CAP_BD_RD_PREFETCH,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_zynq =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_NO_GIGABIT_HALF | MACB_CAP_NEEDS_RSTONUBR,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_mpfs =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_JUMBO | MACB_CAP_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 4040,
+ .max_tx_len = 4040,
+};
+
+static const struct macb_config macb_cfg_sama7g5_gem =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_CLK_HW_CHG |
+ MACB_CAP_USRIO_DEFAULT_MII_GMII | MACB_CAP_MIIONRGMII | MACB_CAP_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_sama7g5_emac =
+{
+ .caps = MACB_CAP_USRIO_DEFAULT_MII_GMII | MACB_CAP_USRIO_HAS_CLKEN | MACB_CAP_MIIONRGMII |
+ MACB_CAP_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 0,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_versal =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_JUMBO | MACB_CAP_GEM_HAS_PTP |
+ MACB_CAP_BD_RD_PREFETCH | MACB_CAP_NEED_TSUCLK | MACB_CAP_QUEUE_DISABLE |
+ MACB_CAP_QBV,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_eyeq5 =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_JUMBO | MACB_CAP_GEM_HAS_PTP |
+ MACB_CAP_QUEUE_DISABLE | MACB_CAP_NO_LSO,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct macb_config macb_cfg_fu540 =
+{
+ .caps = MACB_CAP_GIGABIT_MODE_AVAILABLE | MACB_CAP_JUMBO | MACB_CAP_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .jumbo_max_len = 10240,
+ .max_tx_len = 0,
+};
+
+static const struct rt_ofw_node_id macb_eth_ofw_ids[] =
+{
+ { .compatible = "cdns,at91sam9260-macb", .data = &macb_cfg_at91sam9260 },
+ { .compatible = "cdns,macb", .data = &macb_cfg_default_gem },
+ { .compatible = "cdns,np4-macb", .data = &macb_cfg_np4 },
+ { .compatible = "cdns,pc302-gem", .data = &macb_cfg_pc302gem },
+ { .compatible = "cdns,gem", .data = &macb_cfg_pc302gem },
+ { .compatible = "cdns,sam9x60-macb", .data = &macb_cfg_at91sam9260 },
+ { .compatible = "atmel,sama5d2-gem", .data = &macb_cfg_sama5d2 },
+ { .compatible = "atmel,sama5d29-gem", .data = &macb_cfg_sama5d29 },
+ { .compatible = "atmel,sama5d3-gem", .data = &macb_cfg_sama5d3 },
+ { .compatible = "atmel,sama5d3-macb", .data = &macb_cfg_sama5d3_macb },
+ { .compatible = "atmel,sama5d4-gem", .data = &macb_cfg_sama5d4 },
+ { .compatible = "cdns,at91rm9200-emac", .data = &macb_cfg_emac },
+ { .compatible = "cdns,emac", .data = &macb_cfg_emac },
+ { .compatible = "cdns,zynqmp-gem", .data = &macb_cfg_zynqmp },
+ { .compatible = "cdns,zynq-gem", .data = &macb_cfg_zynq },
+ { .compatible = "sifive,fu540-c000-gem", .data = &macb_cfg_fu540 },
+ { .compatible = "microchip,mpfs-macb", .data = &macb_cfg_mpfs },
+ { .compatible = "microchip,sama7g5-gem", .data = &macb_cfg_sama7g5_gem },
+ { .compatible = "microchip,sama7g5-emac", .data = &macb_cfg_sama7g5_emac },
+ { .compatible = "mobileye,eyeq5-gem", .data = &macb_cfg_eyeq5 },
+ { .compatible = "xlnx,zynqmp-gem", .data = &macb_cfg_zynqmp },
+ { .compatible = "xlnx,zynq-gem", .data = &macb_cfg_zynq },
+ { .compatible = "xlnx,versal-gem", .data = &macb_cfg_versal },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver macb_eth_driver =
+{
+ .name = "eth-macb",
+ .ids = macb_eth_ofw_ids,
+
+ .probe = macb_platform_probe,
+ .remove = macb_platform_remove,
+};
+RT_PLATFORM_DRIVER_EXPORT(macb_eth_driver);
diff --git a/components/drivers/ethernet/cadence/macb.h b/components/drivers/ethernet/cadence/macb.h
new file mode 100644
index 0000000000..46013d0e42
--- /dev/null
+++ b/components/drivers/ethernet/cadence/macb.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-25 GuEe-GUI first version
+ */
+
+#ifndef __MACB_H__
+#define __MACB_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include "../ethernet_dm.h"
+
+/* Queue 0 (legacy offsets) */
+#define GEM_NCR 0x0000
+#define GEM_NCFGR 0x0004
+#define GEM_NSR 0x0008
+#define GEM_USRIO 0x000c
+#define GEM_DMACFG 0x0010
+#define GEM_TSR 0x0014
+#define GEM_TSR_TGO RT_BIT(3)
+#define GEM_TSR_COMP RT_BIT(5)
+#define GEM_RSR 0x0020
+#define GEM_ISR 0x0024
+#define GEM_IER 0x0028
+#define GEM_IDR 0x002c
+#define GEM_IMR 0x0030
+#define GEM_ISR0 0x0400 /* GEM queue 0 ISR */
+#define GEM_IER0 0x0600 /* GEM queue 0 IER */
+#define GEM_IDR0 0x0620 /* GEM queue 0 IDR */
+#define GEM_IMR0 0x0640 /* GEM queue 0 IMR */
+#define GEM_MAN 0x0034 /* PHY maintenance (was wrongly 0x340) */
+/*
+ * GEM queue 0 uses legacy MACB ring/IRQ registers; GEM_*Q1+ live at 0x440/0x480.
+ * Linux: queue0 -> MACB_TBQP/RBQP/ISR/IER; queue1+ -> GEM_TBQP(n)/GEM_RBQP(n).
+ */
+#define GEM_RBQP 0x0018
+#define GEM_TBQP 0x001c
+#define GEM_RBQP1 0x0480
+#define GEM_TBQP1 0x0440
+#define MACB_MAX_QUEUES 8
+#define GEM_QUEUE_TBQP(q) (0x0440 + ((q) << 2))
+#define GEM_QUEUE_RBQP(q) (0x0480 + ((q) << 2))
+
+#define GEM_JML 0x0048
+#define GEM_PBUFRXCUT 0x0044
+#define GEM_INTMOD 0x005c
+#define GEM_INTMOD_TX_MOD_SHIFT 16
+#define GEM_INTMOD_RX_MOD_SHIFT 0
+
+#define GEM_SA1B 0x0088
+#define GEM_SA1T 0x008c
+
+#define GEM_DCFG1 0x0280
+#define GEM_DCFG1_IRQCOR_SHIFT 23
+#define GEM_DCFG5 0x0290
+#define GEM_DCFG6 0x0294
+#define GEM_DCFG6_DAW64_SHIFT 23
+
+#define GEM_RBQPH 0x04d4
+#define GEM_TBQPH 0x04c8
+
+#define GEM_AMP 0x0054
+#define GEM_AMP_AR2R_MAX_PIPE_SHIFT 0
+#define GEM_AMP_AW2W_MAX_PIPE_SHIFT 8
+#define GEM_AMP_AW2B_FILL_SHIFT 16
+
+#define GEM_USRIO_RGMII RT_BIT(0)
+
+/* TSU / 1588 */
+#define GEM_TISUBN 0x01bc
+#define GEM_TSH 0x01c0
+#define GEM_TSL 0x01d0
+#define GEM_TN 0x01d4
+#define GEM_TA 0x01d8
+#define GEM_TI 0x01dc
+
+/* NCR */
+#define GEM_NCR_LLB RT_BIT(1)
+#define GEM_NCR_RE RT_BIT(2)
+#define GEM_NCR_TE RT_BIT(3)
+#define GEM_NCR_MPE RT_BIT(4)
+#define GEM_NCR_CLRSTAT RT_BIT(5)
+#define GEM_NCR_TSTART RT_BIT(9)
+
+/*
+ * NCFGR: GEM uses same SPD/FD as MACB (bit0/1); gigabit is GEM_GBE @ bit10,
+ * not bit31.
+ */
+#define GEM_NCFGR_SPD RT_BIT(0) /* 100 Mbps */
+#define GEM_NCFGR_FD RT_BIT(1) /* Full duplex */
+#define GEM_NCFGR_GBE RT_BIT(10) /* 1000 Mbps (GEM) */
+#define GEM_NCFGR_BIG RT_BIT(8) /* Receive 1536-byte frames */
+#define GEM_NCFGR_DRFCS RT_BIT(17) /* Discard RX FCS */
+
+/* DBW in NCFGR bits [22:21] */
+#define GEM_NCFGR_DBW_SHIFT 21
+
+/* MDC clock divider in NCFGR bits [20:18] */
+#define GEM_NCFGR_CLK_SHIFT 18
+
+/* NSR */
+#define GEM_NSR_IDLE RT_BIT(2)
+
+/* RSR */
+#define GEM_RSR_BNA RT_BIT(0)
+#define GEM_RSR_REC RT_BIT(1)
+#define GEM_RSR_OVR RT_BIT(2)
+
+/* ISR / IER / IDR / IMR: queue 0 @ 0x24..0x30; extra GEM queues @ 0x400/0x600 */
+#define GEM_INT_MFD RT_BIT(0)
+#define GEM_INT_HRESP RT_BIT(11)
+#define GEM_INT_RCOMP RT_BIT(1)
+#define GEM_INT_RXUBR RT_BIT(2)
+#define GEM_INT_TXUBR RT_BIT(3)
+#define GEM_INT_TUND RT_BIT(4)
+#define GEM_INT_RLE RT_BIT(5)
+#define GEM_INT_TXERR RT_BIT(6)
+#define GEM_INT_TCOMP RT_BIT(7)
+#define GEM_INT_LINK RT_BIT(9)
+#define GEM_INT_ROVR RT_BIT(10)
+
+#define GEM_INT_RX_BITS (GEM_INT_RCOMP | GEM_INT_RXUBR | GEM_INT_ROVR)
+#define GEM_INT_TX_BITS (GEM_INT_TCOMP | GEM_INT_TUND | GEM_INT_RLE | GEM_INT_TXERR)
+
+/* MAN (MDIO clause 22) */
+#define GEM_MAN_SOF 1
+#define GEM_MAN_WRITE 1
+#define GEM_MAN_READ 2
+#define GEM_MAN_CODE 2
+
+#define GEM_MAN_DATA_OFF 0
+#define GEM_MAN_DATA_SZ 16
+#define GEM_MAN_CODE_OFF 16
+#define GEM_MAN_CODE_SZ 2
+#define GEM_MAN_REGA_OFF 18
+#define GEM_MAN_REGA_SZ 5
+#define GEM_MAN_PHYA_OFF 23
+#define GEM_MAN_PHYA_SZ 5
+#define GEM_MAN_RW_OFF 28
+#define GEM_MAN_RW_SZ 2
+#define GEM_MAN_SOF_OFF 30
+#define GEM_MAN_SOF_SZ 2
+#define GEM_MAN_BF(name, val) (((val) & ((1 << GEM_MAN_##name##_SZ) - 1)) << GEM_MAN_##name##_OFF)
+
+/*
+ * RX descriptor word @addr: OWNERSHIP uses bit0 (RX_USED); WRAP is bit1.
+ * Word @ctrl: frame length in bits [11:0], SOF/EOF for single-buffer frames.
+ */
+#define GEM_RX_USED RT_BIT(0) /* In addr */
+#define GEM_RX_WRAP RT_BIT(1) /* In addr */
+#define GEM_RX_LEN_MASK 0x0fff /* FRMLEN in ctrl */
+#define GEM_RX_SOF RT_BIT(14)
+#define GEM_RX_EOF RT_BIT(15)
+
+/* TX descriptor @ctrl (GEM length bits [13:0]) */
+#define GEM_TX_LAST RT_BIT(15)
+#define GEM_TX_NOCRC RT_BIT(16)
+#define GEM_TX_WRAP RT_BIT(30)
+#define GEM_TX_USED RT_BIT(31)
+#define GEM_TX_LEN_MASK 0x3fff
+
+/* DMACFG */
+#define GEM_DMACFG_FBLDO_SHIFT 0
+#define GEM_DMACFG_ENDIA_DESC_SHIFT 6
+#define GEM_DMACFG_ENDIA_PKT_SHIFT 7
+#define GEM_DMACFG_RXBMS_SHIFT 8
+#define GEM_DMACFG_TXPBMS_SHIFT 10
+#define GEM_DMACFG_RXBS_SHIFT 16
+#define GEM_DMACFG_DDRP_SHIFT 24
+#define GEM_DMACFG_RXEXT_SHIFT 28
+#define GEM_DMACFG_TXEXT_SHIFT 29
+#define GEM_DMACFG_ADDR64_SHIFT 30
+
+#define GEM_DMACFG_RXBS_MASK 0xff
+
+#define GEM_CLK_DIV8 0
+#define GEM_CLK_DIV16 1
+#define GEM_CLK_DIV32 2
+#define GEM_CLK_DIV48 3
+#define GEM_CLK_DIV64 4
+#define GEM_CLK_DIV96 5
+#define GEM_CLK_DIV128 6
+#define GEM_CLK_DIV224 7
+
+/* DCFG5: TSU present */
+#define GEM_DCFG5_TSU_SHIFT 8
+
+#define GEM_DBW32 0
+#define GEM_DBW64 1
+#define GEM_DBW128 2
+
+#define MACB_CAP_USRIO_HAS_CLKEN RT_BIT(1)
+#define MACB_CAP_USRIO_DEFAULT_MII_GMII RT_BIT(2)
+#define MACB_CAP_NO_GIGABIT_HALF RT_BIT(3)
+#define MACB_CAP_USRIO_DISABLED RT_BIT(4)
+#define MACB_CAP_JUMBO RT_BIT(5)
+#define MACB_CAP_GEM_HAS_PTP RT_BIT(6)
+#define MACB_CAP_BD_RD_PREFETCH RT_BIT(7)
+#define MACB_CAP_NEEDS_RSTONUBR RT_BIT(8)
+#define MACB_CAP_MIIONRGMII RT_BIT(9)
+#define MACB_CAP_NEED_TSUCLK RT_BIT(10)
+#define MACB_CAP_QUEUE_DISABLE RT_BIT(11)
+#define MACB_CAP_QBV RT_BIT(12)
+#define MACB_CAP_CLK_HW_CHG RT_BIT(15)
+#define MACB_CAP_MACB_IS_EMAC RT_BIT(16)
+#define MACB_CAP_GIGABIT_MODE_AVAILABLE RT_BIT(18)
+#define MACB_CAP_SG_DISABLED RT_BIT(19)
+#define MACB_CAP_NO_LSO RT_BIT(24)
+
+struct macb_eth;
+
+struct macb_config
+{
+ rt_uint32_t caps;
+ rt_uint8_t dma_burst_length;
+ rt_uint16_t jumbo_max_len;
+ rt_uint16_t max_tx_len;
+};
+
+struct macb_dma_desc
+{
+ rt_uint32_t addr;
+ rt_uint32_t ctrl;
+};
+
+struct macb_dma_desc_64
+{
+ rt_uint32_t addrh;
+ rt_uint32_t resvd;
+};
+
+#define MACB_RX_RING_SIZE 32
+#define MACB_TX_RING_SIZE 16
+#define MACB_RX_BUFFER_SIZE 1536
+
+/* RXBS encoding: buffer_bytes / 64 */
+#define MACB_RX_BUFFER_SIZE_DIV64 (MACB_RX_BUFFER_SIZE / 64)
+
+struct macb_eth
+{
+ struct eth_device parent;
+#ifdef RT_ETHERNET_CADENCE_PTP
+ struct rt_ptp_clock ptp_parent;
+#endif
+ struct rt_device *dev;
+
+ struct rt_phy_device *phy;
+ struct mii_bus *mii;
+
+ int irq;
+ void *regs;
+
+ struct rt_clk *pclk;
+ struct rt_clk *hclk;
+ struct rt_clk *tsu_clk;
+ struct rt_clk *tx_clk;
+
+ rt_uint8_t mac[6];
+ rt_bool_t is_gem;
+ rt_bool_t dma_64bit;
+ rt_bool_t native_io;
+ rt_bool_t isr_clear_on_write;
+ rt_uint32_t ncr_shadow;
+ rt_uint32_t ncfgr_shadow;
+
+ const struct macb_config *config;
+ rt_uint16_t max_tx_len;
+
+ int phy_interface;
+ rt_ssize_t phy_reset_pin;
+ rt_uint8_t phy_reset_active;
+ rt_uint16_t phy_reset_ms;
+ rt_uint8_t aw2w_max_pipe;
+ rt_uint8_t ar2r_max_pipe;
+ rt_bool_t use_aw2b_fill;
+
+ struct macb_dma_desc *rx_ring;
+ struct macb_dma_desc *tx_ring;
+ rt_uint8_t *rx_buffer;
+ rt_uint8_t *tx_buffer;
+ rt_size_t dma_blob_size;
+ rt_ubase_t dma_blob_handle;
+ rt_ubase_t rx_ring_dma;
+ rt_ubase_t tx_ring_dma;
+ rt_ubase_t rx_buffer_dma;
+ rt_ubase_t tx_buffer_dma;
+
+ struct macb_dma_desc *dummy_desc;
+ rt_ubase_t dummy_desc_dma;
+ rt_size_t dummy_desc_size;
+
+ rt_uint32_t rx_tail;
+ rt_uint32_t tx_head;
+ rt_uint32_t tx_tail;
+
+ rt_bool_t net_registered;
+ rt_bool_t irq_installed;
+ rt_bool_t hw_ready;
+ rt_bool_t mac_started;
+ rt_bool_t phy_configured;
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+ struct rt_work phy_retry_work;
+ int phy_retry_count;
+#endif
+
+ struct rt_mutex mdio_lock;
+ struct rt_semaphore tx_sem;
+};
+
+#define raw_to_macb_eth(raw) \
+ rt_container_of(rt_container_of(raw, struct eth_device, parent), struct macb_eth, parent)
+#define raw_to_macb_ptp(raw) \
+ rt_container_of(raw, struct macb_eth, ptp_parent)
+
+rt_inline rt_uint32_t macb_readl(struct macb_eth *eth, rt_uint32_t off)
+{
+ if (eth->native_io)
+ {
+ return *(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off);
+ }
+
+ return HWREG32(eth->regs + off);
+}
+
+rt_inline void macb_writel(struct macb_eth *eth, rt_uint32_t off, rt_uint32_t val)
+{
+ if (eth->native_io)
+ {
+ *(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off) = val;
+ }
+ else
+ {
+ HWREG32(eth->regs + off) = val;
+ }
+}
+
+rt_err_t macb_eth_hw_init(struct macb_eth *eth);
+void macb_eth_hw_stop(struct macb_eth *eth);
+
+rt_err_t macb_eth_common_probe(struct macb_eth *eth);
+rt_err_t macb_eth_common_remove(struct macb_eth *eth);
+
+#ifdef RT_ETHERNET_CADENCE_PTP
+rt_err_t macb_ptp_register(struct macb_eth *eth);
+void macb_ptp_unregister(struct macb_eth *eth);
+#endif
+
+#endif /* __MACB_H__ */
diff --git a/components/drivers/ethernet/cadence/macb_pci.c b/components/drivers/ethernet/cadence/macb_pci.c
new file mode 100644
index 0000000000..2c61be8532
--- /dev/null
+++ b/components/drivers/ethernet/cadence/macb_pci.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-25 GuEe-GUI first version
+ */
+
+#include "macb.h"
+
+#define DBG_TAG "eth.macb.pci"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static rt_ubase_t gem_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
+{
+ (void)cell;
+ (void)parent_rate;
+ return 50000000;
+}
+
+static struct rt_clk_ops gem_clk_ops =
+{
+ .recalc_rate = gem_clk_recalc_rate,
+};
+
+static struct rt_clk_cell *gem_cells[] =
+{
+ &(struct rt_clk_cell)
+ {
+ .cell.name = "macb_pci_pclk",
+ .cell.ops = &gem_clk_ops,
+ },
+ &(struct rt_clk_cell)
+ {
+ .cell.name = "macb_pci_hclk",
+ .cell.ops = &gem_clk_ops,
+ },
+};
+
+static rt_err_t macb_pci_clk_init(void)
+{
+ rt_err_t err;
+ static struct rt_clk_node *clk_np = RT_NULL;
+
+ if (clk_np)
+ {
+ return RT_EOK;
+ }
+
+ if (!(clk_np = rt_calloc(1, sizeof(*clk_np))))
+ {
+ return -RT_ENOMEM;
+ }
+
+ clk_np->cells = gem_cells;
+ clk_np->cells_nr = RT_ARRAY_SIZE(gem_cells);
+
+ if ((err = rt_clk_register(clk_np)))
+ {
+ rt_free(clk_np);
+ }
+
+ return err;
+}
+
+static rt_err_t macb_pci_probe(struct rt_pci_device *pdev)
+{
+ rt_err_t err;
+ struct macb_eth *eth;
+
+ if ((err = macb_pci_clk_init()))
+ {
+ return err;
+ }
+
+ if (!(eth = rt_calloc(1, sizeof(*eth))))
+ {
+ return -RT_ENOMEM;
+ }
+
+ rt_pci_set_master(pdev);
+
+ eth->dev = &pdev->parent;
+
+ eth->regs = rt_pci_iomap(pdev, 0);
+ if (!eth->regs)
+ {
+ err = -RT_EIO;
+ goto _free_eth;
+ }
+
+ eth->irq = pdev->irq;
+ rt_pci_irq_unmask(pdev);
+
+ eth->pclk = rt_clk_get_by_name(eth->dev, "macb_pci_pclk");
+
+ if (rt_is_err_or_null(eth->pclk))
+ {
+ err = eth->pclk ? rt_ptr_err(eth->pclk) : -RT_EIO;
+ goto _unmap;
+ }
+
+ eth->hclk = rt_clk_get_by_name(eth->dev, "macb_pci_hclk");
+
+ if (rt_is_err_or_null(eth->hclk))
+ {
+ err = eth->hclk ? rt_ptr_err(eth->hclk) : -RT_EIO;
+ goto _put_pclk;
+ }
+
+ ethernet_random_addr(&eth->parent, eth->mac);
+
+ if ((err = macb_eth_hw_init(eth)))
+ {
+ goto _put_clks;
+ }
+
+ pdev->parent.user_data = eth;
+
+ if ((err = macb_eth_common_probe(eth)))
+ {
+ macb_eth_common_remove(eth);
+ pdev->parent.user_data = RT_NULL;
+ goto _put_clks;
+ }
+
+ return RT_EOK;
+
+_put_clks:
+ rt_clk_put(eth->hclk);
+_put_pclk:
+ rt_clk_put(eth->pclk);
+_unmap:
+ rt_iounmap(eth->regs);
+ rt_pci_irq_mask(pdev);
+_free_eth:
+ rt_free(eth);
+
+ return err;
+}
+
+static rt_err_t macb_pci_remove(struct rt_pci_device *pdev)
+{
+ struct macb_eth *eth = pdev->parent.user_data;
+
+ macb_eth_common_remove(eth);
+
+ /* INTx is shared, don't mask all */
+ rt_hw_interrupt_umask(pdev->irq);
+ rt_pci_irq_mask(pdev);
+ rt_pci_clear_master(pdev);
+
+ rt_iounmap(eth->regs);
+ rt_clk_put(eth->hclk);
+ rt_clk_put(eth->pclk);
+
+ rt_free(eth);
+
+ return RT_EOK;
+}
+
+static const struct rt_pci_device_id macb_pci_ids[] =
+{
+ { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_CDNS, 0xe007), },
+ { /* sentinel */ }
+};
+
+static struct rt_pci_driver macb_pci_driver =
+{
+ .name = "macb-pci",
+
+ .ids = macb_pci_ids,
+ .probe = macb_pci_probe,
+ .remove = macb_pci_remove,
+};
+RT_PCI_DRIVER_EXPORT(macb_pci_driver);
diff --git a/components/drivers/ethernet/cadence/macb_phy.c b/components/drivers/ethernet/cadence/macb_phy.c
new file mode 100644
index 0000000000..f8080c3365
--- /dev/null
+++ b/components/drivers/ethernet/cadence/macb_phy.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-25 GuEe-GUI first version
+ */
+
+#define GEM_MDIO_IDLE_RETRIES 100000
+#define GEM_MDIO_POLL_US 10
+#define GEM_MDIO_FIXED_TURNAROUND_US 100
+
+static rt_bool_t gem_mdio_wait_idle(struct macb_eth *eth, int retries)
+{
+ while (retries-- > 0)
+ {
+ rt_uint32_t nsr = macb_readl(eth, GEM_NSR);
+
+ /*
+ * GEM queue-0 may not expose a readable NSR @ 0x8 (reads 0xffffffff).
+ * Linux/U-Boot treat the idle bit as set; use a fixed turnaround.
+ */
+ if (nsr == 0xffffffff)
+ {
+ rt_hw_us_delay(GEM_MDIO_FIXED_TURNAROUND_US);
+ return RT_TRUE;
+ }
+
+ if (nsr & GEM_NSR_IDLE)
+ {
+ return RT_TRUE;
+ }
+
+ rt_hw_us_delay(GEM_MDIO_POLL_US);
+ }
+
+ return RT_FALSE;
+}
+
+static rt_err_t gem_mdio_frame(struct macb_eth *eth, rt_uint32_t frame)
+{
+ if (!gem_mdio_wait_idle(eth, GEM_MDIO_IDLE_RETRIES))
+ {
+ return -RT_ETIMEOUT;
+ }
+
+ macb_writel(eth, GEM_MAN, frame);
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+
+ if (!gem_mdio_wait_idle(eth, GEM_MDIO_IDLE_RETRIES))
+ {
+ if (!eth->native_io)
+ {
+ rt_hw_us_delay(GEM_MDIO_FIXED_TURNAROUND_US);
+ return RT_EOK;
+ }
+
+ return -RT_ETIMEOUT;
+ }
+
+ return RT_EOK;
+}
+
+static rt_bool_t gem_mdio_data_invalid(struct macb_eth *eth, rt_uint32_t data)
+{
+ return data == 0xffff && !eth->native_io;
+}
+
+static rt_uint32_t gem_ncfgr_mdc_div(rt_ubase_t pclk_hz)
+{
+ if (pclk_hz <= 20000000)
+ {
+ return GEM_CLK_DIV8 << GEM_NCFGR_CLK_SHIFT;
+ }
+ else if (pclk_hz <= 40000000)
+ {
+ return GEM_CLK_DIV16 << GEM_NCFGR_CLK_SHIFT;
+ }
+ else if (pclk_hz <= 80000000)
+ {
+ return GEM_CLK_DIV32 << GEM_NCFGR_CLK_SHIFT;
+ }
+ else if (pclk_hz <= 120000000)
+ {
+ return GEM_CLK_DIV48 << GEM_NCFGR_CLK_SHIFT;
+ }
+ else if (pclk_hz <= 160000000)
+ {
+ return GEM_CLK_DIV64 << GEM_NCFGR_CLK_SHIFT;
+ }
+ else if (pclk_hz <= 240000000)
+ {
+ return GEM_CLK_DIV96 << GEM_NCFGR_CLK_SHIFT;
+ }
+ else if (pclk_hz <= 320000000)
+ {
+ return GEM_CLK_DIV128 << GEM_NCFGR_CLK_SHIFT;
+ }
+
+ return GEM_CLK_DIV224 << GEM_NCFGR_CLK_SHIFT;
+}
+
+static rt_uint32_t macb_gem_dbw(struct macb_eth *eth)
+{
+ rt_uint32_t dcfg1, dbw;
+
+ dcfg1 = macb_readl(eth, GEM_DCFG1);
+ dbw = (dcfg1 >> 25) & 0x7U;
+
+ switch (dbw)
+ {
+ case 4:
+ return GEM_DBW128 << GEM_NCFGR_DBW_SHIFT;
+ case 2:
+ return GEM_DBW64 << GEM_NCFGR_DBW_SHIFT;
+ default:
+ return GEM_DBW32 << GEM_NCFGR_DBW_SHIFT;
+ }
+}
+
+static int gem_mdio_read(struct mii_bus *bus, int addr, int devad, int reg)
+{
+ rt_err_t err;
+ rt_uint32_t data, frame;
+ struct macb_eth *eth = bus->priv;
+
+ rt_mutex_take(&eth->mdio_lock, RT_WAITING_FOREVER);
+
+ frame = GEM_MAN_BF(SOF, GEM_MAN_SOF) | GEM_MAN_BF(RW, GEM_MAN_READ) |
+ GEM_MAN_BF(PHYA, addr) | GEM_MAN_BF(REGA, reg) |
+ GEM_MAN_BF(CODE, GEM_MAN_CODE);
+
+ err = gem_mdio_frame(eth, frame);
+ if (err)
+ {
+ LOG_W("MDIO read timeout addr=%d reg=%d nsr=0x%x ncr=0x%x",
+ addr, reg, macb_readl(eth, GEM_NSR), macb_readl(eth, GEM_NCR));
+ rt_mutex_release(&eth->mdio_lock);
+ return -RT_EIO;
+ }
+
+ data = macb_readl(eth, GEM_MAN) & 0xffff;
+ if (gem_mdio_data_invalid(eth, data))
+ {
+ LOG_W("MDIO read invalid data addr=%d reg=%d man=0x%x",
+ addr, reg, macb_readl(eth, GEM_MAN));
+ rt_mutex_release(&eth->mdio_lock);
+ return -RT_EIO;
+ }
+
+ rt_mutex_release(&eth->mdio_lock);
+ return (int)data;
+}
+
+static int gem_mdio_write(struct mii_bus *bus, int addr, int devad, int reg, rt_uint16_t val)
+{
+ rt_err_t err;
+ rt_uint32_t frame;
+ struct macb_eth *eth = bus->priv;
+
+ rt_mutex_take(&eth->mdio_lock, RT_WAITING_FOREVER);
+
+ frame = GEM_MAN_BF(SOF, GEM_MAN_SOF) | GEM_MAN_BF(RW, GEM_MAN_WRITE) |
+ GEM_MAN_BF(PHYA, addr) | GEM_MAN_BF(REGA, reg) |
+ GEM_MAN_BF(CODE, GEM_MAN_CODE) | GEM_MAN_BF(DATA, val);
+
+ err = gem_mdio_frame(eth, frame);
+
+ rt_mutex_release(&eth->mdio_lock);
+ return err ? -RT_EIO : 0;
+}
+
+static rt_err_t macb_adjust_link(struct macb_eth *eth)
+{
+ rt_uint32_t ncfgr;
+ struct rt_phy_device *phy = eth->phy;
+
+ if (!phy || !phy->link)
+ {
+ return -RT_ERROR;
+ }
+
+ /* Some platforms return unreadable NCFGR; keep a software shadow. */
+ ncfgr = eth->ncfgr_shadow;
+ ncfgr &= ~(GEM_NCFGR_SPD | GEM_NCFGR_FD | GEM_NCFGR_GBE |
+ (3u << GEM_NCFGR_DBW_SHIFT));
+ ncfgr |= macb_gem_dbw(eth);
+
+ if (phy->duplex == DUPLEX_FULL)
+ {
+ ncfgr |= GEM_NCFGR_FD;
+ }
+
+ if (phy->speed == SPEED_1000)
+ {
+ ncfgr |= GEM_NCFGR_GBE;
+ }
+ else if (phy->speed == SPEED_100)
+ {
+ ncfgr |= GEM_NCFGR_SPD;
+ }
+
+ eth->ncfgr_shadow = ncfgr;
+ macb_writel(eth, GEM_NCFGR, ncfgr);
+ if (!eth->native_io)
+ {
+ macb_mmio_posted_barrier(eth);
+ }
+ macb_set_tx_clk(eth, phy->speed);
+
+ if (eth->mac_started)
+ {
+ macb_gem_write_traffic_ier(eth);
+ }
+
+ return RT_EOK;
+}
+
+static int macb_mdio_bus_reset(struct mii_bus *bus)
+{
+ struct macb_eth *eth = bus->priv;
+
+ macb_phy_reset(eth);
+ return 0;
+}
+
+static rt_err_t macb_mii_register(struct macb_eth *eth)
+{
+ rt_err_t err;
+
+ eth->mii = rt_mdio_alloc();
+ if (!eth->mii)
+ {
+ return -RT_ENOMEM;
+ }
+
+ /* Enable MDIO management port (Linux macb_mii_init) */
+ eth->ncr_shadow |= GEM_NCR_MPE;
+ if (!eth->native_io)
+ {
+ macb_ncr_posted_write(eth, eth->ncr_shadow);
+ }
+ else
+ {
+ macb_writel(eth, GEM_NCR, eth->ncr_shadow);
+ }
+
+ eth->mii->priv = eth;
+ eth->mii->read = gem_mdio_read;
+ eth->mii->write = gem_mdio_write;
+ eth->mii->reset = macb_mdio_bus_reset;
+ rt_snprintf(eth->mii->name, RT_NAME_MAX, "mii%p", eth);
+
+ if ((err = rt_mdio_register(eth->mii)))
+ {
+ rt_free(eth->mii);
+ eth->mii = RT_NULL;
+ return err;
+ }
+
+ if (eth->mii->reset)
+ {
+ eth->mii->reset(eth->mii);
+ }
+
+ return RT_EOK;
+}
+
+static void macb_mii_unregister(struct macb_eth *eth)
+{
+ if (eth->mii)
+ {
+ rt_mdio_unregister(eth->mii);
+ rt_free(eth->mii);
+ eth->mii = RT_NULL;
+ }
+}
diff --git a/components/drivers/ethernet/cadence/macb_ptp.c b/components/drivers/ethernet/cadence/macb_ptp.c
new file mode 100644
index 0000000000..74f0df35d3
--- /dev/null
+++ b/components/drivers/ethernet/cadence/macb_ptp.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-25 GuEe-GUI first version
+ */
+
+#include "macb.h"
+
+#define DBG_TAG "eth.macb.ptp"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define NSEC_PER_SEC 1000000000L
+#define GEM_PTP_MAX_FREQ 500000000
+#define TSU_NSEC_MAX_VAL ((1 << 30) - 1)
+
+rt_inline rt_uint32_t gem_readl(struct macb_eth *eth, rt_uint32_t off)
+{
+ return *(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off);
+}
+
+rt_inline void gem_writel(struct macb_eth *eth, rt_uint32_t off, rt_uint32_t val)
+{
+ *(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off) = val;
+}
+
+static rt_bool_t gem_tsu_present(struct macb_eth *eth)
+{
+ rt_uint32_t dcfg5 = gem_readl(eth, GEM_DCFG5);
+
+ return (dcfg5 & (1u << GEM_DCFG5_TSU_SHIFT)) ? RT_TRUE : RT_FALSE;
+}
+
+static rt_err_t macb_ptp_adjfreq(struct rt_ptp_clock *ptp, rt_base_t freq)
+{
+ rt_int64_t adj;
+ rt_uint32_t inc;
+ struct macb_eth *eth = raw_to_macb_ptp(ptp);
+
+ /* Base increment in GEM_TI (NSINCR field) */
+ inc = gem_readl(eth, GEM_TI) & 0xff;
+ if (inc == 0)
+ {
+ inc = 1;
+ }
+
+ adj = (rt_int64_t)inc * (rt_int64_t)freq / 1000000000LL;
+ if (adj > 0x7fffffff || adj < -0x80000000LL)
+ {
+ return -RT_EINVAL;
+ }
+
+ gem_writel(eth, GEM_TI, (inc + (rt_uint32_t)adj) & 0xff);
+
+ return RT_EOK;
+}
+
+static rt_err_t macb_ptp_adjtime(struct rt_ptp_clock *ptp, rt_int64_t delta)
+{
+ rt_uint32_t adj;
+ struct macb_eth *eth = raw_to_macb_ptp(ptp);
+
+ if (delta < 0)
+ {
+ delta = -delta;
+ adj = RT_BIT(31) | (rt_uint32_t)(delta > TSU_NSEC_MAX_VAL ? TSU_NSEC_MAX_VAL : delta);
+ }
+ else
+ {
+ adj = (rt_uint32_t)(delta > TSU_NSEC_MAX_VAL ? TSU_NSEC_MAX_VAL : delta);
+ }
+
+ gem_writel(eth, GEM_TA, adj);
+
+ return RT_EOK;
+}
+
+static rt_err_t macb_ptp_gettime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts)
+{
+ rt_uint32_t tsh, tsl, tn;
+ struct macb_eth *eth = raw_to_macb_ptp(ptp);
+
+ tn = gem_readl(eth, GEM_TN);
+ tsl = gem_readl(eth, GEM_TSL);
+ tsh = gem_readl(eth, GEM_TSH) & 0xffffU;
+
+ ts->sec = ((rt_int64_t)tsh << 32) | tsl;
+ ts->nsec = (rt_int32_t)(tn & 0x3fffffffU);
+
+ return RT_EOK;
+}
+
+static rt_err_t macb_ptp_settime(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts)
+{
+ struct macb_eth *eth = raw_to_macb_ptp(ptp);
+
+ if (!ts || ts->nsec < 0 || ts->nsec >= NSEC_PER_SEC)
+ {
+ return -RT_EINVAL;
+ }
+
+ /* TN clear, TSH, TSL, then TN */
+ gem_writel(eth, GEM_TN, 0);
+ gem_writel(eth, GEM_TSH, (rt_uint32_t)((rt_uint64_t)ts->sec >> 32) & 0xffffU);
+ gem_writel(eth, GEM_TSL, (rt_uint32_t)ts->sec);
+ gem_writel(eth, GEM_TN, (rt_uint32_t)ts->nsec);
+
+ return RT_EOK;
+}
+
+static rt_err_t macb_ptp_enable(struct rt_ptp_clock *ptp, struct rt_ptp_clock_request *req, rt_bool_t on)
+{
+ return RT_EOK;
+}
+
+static const struct rt_ptp_ops macb_gem_ptp_ops =
+{
+ .adjfreq = macb_ptp_adjfreq,
+ .adjtime = macb_ptp_adjtime,
+ .gettime = macb_ptp_gettime,
+ .settime = macb_ptp_settime,
+ .enable = macb_ptp_enable,
+};
+
+rt_err_t macb_ptp_register(struct macb_eth *eth)
+{
+ rt_err_t err;
+ struct rt_ptp_clock *ptp = &eth->ptp_parent;
+
+ if (!gem_tsu_present(eth))
+ {
+ return -RT_ENOSYS;
+ }
+
+ ptp->ops = &macb_gem_ptp_ops;
+ ptp->max_freq = GEM_PTP_MAX_FREQ;
+
+ if ((err = rt_ptp_clock_register(ptp)))
+ {
+ ptp->ops = RT_NULL;
+ return err;
+ }
+
+ return RT_EOK;
+}
+
+void macb_ptp_unregister(struct macb_eth *eth)
+{
+ struct rt_ptp_clock *ptp = &eth->ptp_parent;
+
+ if (!ptp->ops)
+ {
+ return;
+ }
+
+ rt_ptp_clock_unregister(ptp);
+ ptp->ops = RT_NULL;
+}
diff --git a/components/drivers/ethernet/synopsys/Kconfig b/components/drivers/ethernet/synopsys/Kconfig
new file mode 100644
index 0000000000..649cdf4bbd
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/Kconfig
@@ -0,0 +1,11 @@
+config RT_ETHERNET_DWMAC
+ bool "Synopsys DesignWare Ethernet MAC (GMAC4)"
+ depends on RT_USING_PHY_V2
+ depends on RT_USING_DMA
+ depends on RT_USING_PIN
+ default n
+
+config RT_ETHERNET_DWMAC_PTP
+ bool "PTP supported"
+ depends on RT_ETHERNET_DWMAC
+ depends on RT_USING_PTP
diff --git a/components/drivers/ethernet/synopsys/SConscript b/components/drivers/ethernet/synopsys/SConscript
new file mode 100644
index 0000000000..af7d646daf
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/SConscript
@@ -0,0 +1,18 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_ETHERNET_DWMAC']):
+ Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../../include', cwd + '/../../phy']
+
+src = ['dwmac.c', 'dwmac-platform.c']
+
+if GetDepend(['RT_ETHERNET_DWMAC_PTP']):
+ src += ['dwmac_ptp.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/drivers/ethernet/synopsys/dwmac-platform.c b/components/drivers/ethernet/synopsys/dwmac-platform.c
new file mode 100644
index 0000000000..d2123c4283
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/dwmac-platform.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-11-26 GuEe-GUI first version
+ */
+
+#include "dwmac-platform.h"
+
+rt_err_t dwmac_platform_register(struct rt_platform_device *pdev,
+ const struct dwmac_config *config)
+{
+ rt_err_t err;
+ struct rt_device *dev = &pdev->parent;
+ struct dwmac_eth *eth = rt_calloc(1, sizeof(*eth));
+
+ if (!eth)
+ {
+ return -RT_ENOMEM;
+ }
+
+ eth->dev = dev;
+ eth->config = config;
+
+ eth->irq = rt_dm_dev_get_irq(dev, 0);
+ eth->mac_base = rt_dm_dev_iomap(dev, 0);
+ if (!eth->mac_base)
+ {
+ err = -RT_EIO;
+ goto _fail;
+ }
+
+#ifdef RT_USING_OFW
+ if (rt_ofw_get_mac_addr(dev->ofw_node, eth->mac))
+#endif
+ {
+ ethernet_random_addr(&eth->parent, eth->mac);
+ }
+
+ dev->user_data = eth;
+
+ if ((err = dwmac_probe(eth)))
+ {
+ goto _fail_unmap;
+ }
+
+ return RT_EOK;
+
+_fail_unmap:
+ dev->user_data = RT_NULL;
+ rt_iounmap(eth->mac_base);
+
+_fail:
+ rt_free(eth);
+
+ return err;
+}
+
+rt_err_t dwmac_platform_remove(struct rt_platform_device *pdev)
+{
+ struct dwmac_eth *eth = pdev->parent.user_data;
+
+ if (!eth)
+ {
+ return RT_EOK;
+ }
+
+ dwmac_remove(eth);
+ rt_iounmap(eth->mac_base);
+ rt_free(eth);
+ pdev->parent.user_data = RT_NULL;
+
+ return RT_EOK;
+}
diff --git a/components/drivers/ethernet/synopsys/dwmac-platform.h b/components/drivers/ethernet/synopsys/dwmac-platform.h
new file mode 100644
index 0000000000..d2845b15c8
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/dwmac-platform.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-11-26 GuEe-GUI first version
+ */
+
+#ifndef __DWMAC_PLATFORM_H__
+#define __DWMAC_PLATFORM_H__
+
+#include "dwmac.h"
+
+rt_err_t dwmac_platform_register(struct rt_platform_device *pdev,
+ const struct dwmac_config *config);
+rt_err_t dwmac_platform_remove(struct rt_platform_device *pdev);
+
+#endif /* __DWMAC_PLATFORM_H__ */
diff --git a/components/drivers/ethernet/synopsys/dwmac.c b/components/drivers/ethernet/synopsys/dwmac.c
new file mode 100644
index 0000000000..68a98e691f
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/dwmac.c
@@ -0,0 +1,1230 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-11-26 GuEe-GUI first version
+ */
+
+#include "dwmac.h"
+#ifdef RT_ETHERNET_DWMAC_PTP
+#include "dwmac_ptp.h"
+#endif
+
+#define DBG_TAG "eth.dwmac"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define NEXT_TX(i) (((i) + 1) & (DWMAC_TX_RING_SIZE - 1))
+#define NEXT_RX(i) (((i) + 1) & (DWMAC_RX_RING_SIZE - 1))
+#define ETH_ZLEN 60
+#define DWMAC_PHY_RETRY_INTERVAL_MS 2000
+#define DWMAC_DMA_ALLOC_FLAGS (RT_DMA_F_LINEAR | RT_DMA_F_32BITS)
+#define DWMAC_DMA_ADDR_MAX 0xffffffffULL
+
+static rt_err_t dwmac_poll_bit(struct dwmac_eth *eth, rt_uint32_t reg,
+ rt_uint32_t mask, rt_bool_t wait_clear)
+{
+ rt_uint32_t val;
+ rt_tick_t start = rt_tick_get();
+ rt_tick_t timeout = rt_tick_from_millisecond(100);
+
+ while (rt_tick_get() - start < timeout)
+ {
+ val = dwmac_readl(eth, reg);
+ if (wait_clear ? !(val & mask) : (val & mask))
+ {
+ return RT_EOK;
+ }
+ rt_hw_us_delay(10);
+ }
+
+ return -RT_ETIMEOUT;
+}
+
+static rt_err_t dwmac_dma_reset(struct dwmac_eth *eth)
+{
+ rt_uint32_t val = dwmac_readl(eth, DMA_BUS_MODE);
+
+ dwmac_writel(eth, DMA_BUS_MODE, val | DMA_BUS_MODE_SFT_RESET);
+
+ return dwmac_poll_bit(eth, DMA_BUS_MODE, DMA_BUS_MODE_SFT_RESET, RT_TRUE);
+}
+
+static rt_uint32_t dwmac_mdio_format_addr(int pa, int gr)
+{
+ return ((pa << MII_GMAC4_ADDR_SHIFT) & MII_GMAC4_ADDR_MASK) |
+ ((gr << MII_GMAC4_REG_SHIFT) & MII_GMAC4_REG_MASK) |
+ MII_GMAC4_CSR_100_150M |
+ MII_ADDR_GBUSY;
+}
+
+static rt_err_t dwmac_mdio_wait(struct dwmac_eth *eth)
+{
+ return dwmac_poll_bit(eth, GMAC_MDIO_ADDR, MII_ADDR_GBUSY, RT_TRUE);
+}
+
+static int dwmac_mdio_read(struct mii_bus *bus, int addr, int devad, int reg)
+{
+ struct dwmac_eth *eth = bus->priv;
+ rt_uint32_t maddr;
+ rt_err_t err;
+
+ RT_UNUSED(devad);
+
+ err = dwmac_mdio_wait(eth);
+ if (err)
+ {
+ return err;
+ }
+
+ maddr = dwmac_mdio_format_addr(addr, reg) | MII_GMAC4_READ;
+ dwmac_writel(eth, GMAC_MDIO_DATA, 0);
+ dwmac_writel(eth, GMAC_MDIO_ADDR, maddr);
+
+ err = dwmac_mdio_wait(eth);
+ if (err)
+ {
+ return err;
+ }
+
+ return dwmac_readl(eth, GMAC_MDIO_DATA) & MII_DATA_GD_MASK;
+}
+
+static int dwmac_mdio_write(struct mii_bus *bus, int addr, int devad, int reg, rt_uint16_t val)
+{
+ struct dwmac_eth *eth = bus->priv;
+ rt_uint32_t maddr;
+ rt_err_t err;
+
+ RT_UNUSED(devad);
+
+ err = dwmac_mdio_wait(eth);
+ if (err)
+ {
+ return err;
+ }
+
+ maddr = dwmac_mdio_format_addr(addr, reg) | MII_GMAC4_WRITE;
+ dwmac_writel(eth, GMAC_MDIO_DATA, val);
+ dwmac_writel(eth, GMAC_MDIO_ADDR, maddr);
+
+ return dwmac_mdio_wait(eth);
+}
+
+static void dwmac_phy_reset_gpio(struct dwmac_eth *eth)
+{
+ if (eth->phy_reset_pin < 0)
+ {
+ return;
+ }
+
+ rt_pin_mode(eth->phy_reset_pin, PIN_MODE_OUTPUT);
+ if (eth->phy_reset_delays[0])
+ {
+ rt_hw_us_delay(eth->phy_reset_delays[0]);
+ }
+ rt_pin_write(eth->phy_reset_pin, eth->phy_reset_active);
+ if (eth->phy_reset_delays[1])
+ {
+ rt_hw_us_delay(eth->phy_reset_delays[1]);
+ }
+ rt_pin_write(eth->phy_reset_pin, !eth->phy_reset_active);
+ if (eth->phy_reset_delays[2])
+ {
+ rt_hw_us_delay(eth->phy_reset_delays[2]);
+ }
+}
+
+#define DWMAC_AXI_BLEN_MAX 7
+
+static rt_uint32_t dwmac_axi_blen_to_mask(rt_uint32_t size)
+{
+ switch (size)
+ {
+ case 256:
+ return DMA_AXI_BLEN256;
+ case 128:
+ return DMA_AXI_BLEN128;
+ case 64:
+ return DMA_AXI_BLEN64;
+ case 32:
+ return DMA_AXI_BLEN32;
+ case 16:
+ return DMA_AXI_BLEN16;
+ case 8:
+ return DMA_AXI_BLEN8;
+ case 4:
+ return DMA_AXI_BLEN4;
+ default:
+ return 0;
+ }
+}
+
+#ifdef RT_USING_OFW
+static void dwmac_parse_axi_config(struct dwmac_eth *eth, struct rt_ofw_node *np)
+{
+ rt_uint32_t blen[DWMAC_AXI_BLEN_MAX];
+ int count, i;
+
+ if (rt_ofw_prop_read_u32(np, "snps,wr_osr_lmt", &eth->axi.wr_osr_lmt))
+ {
+ eth->axi.wr_osr_lmt = 4;
+ }
+
+ if (rt_ofw_prop_read_u32(np, "snps,rd_osr_lmt", &eth->axi.rd_osr_lmt))
+ {
+ eth->axi.rd_osr_lmt = 8;
+ }
+
+ eth->axi.fixed_burst = rt_ofw_prop_read_bool(np, "snps,fb");
+ eth->axi.mixed_burst = rt_ofw_prop_read_bool(np, "snps,mb");
+ eth->axi.aal = rt_ofw_prop_read_bool(np, "snps,aal");
+
+ count = rt_ofw_prop_read_u32_array_index(np, "snps,blen", 0,
+ DWMAC_AXI_BLEN_MAX, blen);
+ for (i = 0; i < count; ++i)
+ {
+ eth->axi.blen_mask |= dwmac_axi_blen_to_mask(blen[i]);
+ }
+}
+
+static void dwmac_parse_mtl_config(struct dwmac_eth *eth, struct rt_ofw_node *np)
+{
+ struct rt_ofw_node *rx_np;
+ struct rt_ofw_node *tx_np;
+
+ eth->mtl.rx_queues = 1;
+ eth->mtl.tx_queues = 1;
+
+ rx_np = rt_ofw_parse_phandle(np, "snps,mtl-rx-config", 0);
+ if (rx_np)
+ {
+ rt_ofw_prop_read_u32(rx_np, "snps,rx-queues-to-use", &eth->mtl.rx_queues);
+ rt_ofw_node_put(rx_np);
+ }
+
+ tx_np = rt_ofw_parse_phandle(np, "snps,mtl-tx-config", 0);
+ if (tx_np)
+ {
+ rt_ofw_prop_read_u32(tx_np, "snps,tx-queues-to-use", &eth->mtl.tx_queues);
+ rt_ofw_node_put(tx_np);
+ }
+}
+#endif /* RT_USING_OFW */
+
+static void dwmac_parse_dt_props(struct dwmac_eth *eth)
+{
+ const struct dwmac_config *cfg = eth->config;
+
+ eth->dma_pbl = cfg && cfg->dma_pbl ? cfg->dma_pbl : DWMAC_DMA_PBL;
+ eth->dma_txpbl = eth->dma_pbl;
+ eth->dma_rxpbl = eth->dma_pbl;
+ eth->axi.wr_osr_lmt = 4;
+ eth->axi.rd_osr_lmt = 8;
+ eth->axi.blen_mask = DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | DMA_AXI_BLEN4;
+ eth->mtl.rx_queues = 1;
+ eth->mtl.tx_queues = 1;
+
+#ifdef RT_USING_OFW
+ struct rt_device *dev = eth->dev;
+ struct rt_ofw_node *np = dev->ofw_node;
+ struct rt_ofw_node *axi_np;
+
+ if (rt_ofw_get_interface(dev->ofw_node, (rt_phy_interface *)&eth->phy_interface))
+ {
+ eth->phy_interface = RT_PHY_INTERFACE_MODE_RGMII;
+ }
+
+ eth->tso_en = rt_ofw_prop_read_bool(np, "snps,tso") ||
+ (cfg && (cfg->caps & DWMAC_CAP_TSO));
+ eth->axi.mixed_burst = rt_ofw_prop_read_bool(np, "snps,mixed-burst");
+
+ if (!rt_ofw_prop_read_u32(np, "snps,pbl", (rt_uint32_t *)&eth->dma_pbl))
+ {
+ eth->dma_txpbl = eth->dma_pbl;
+ eth->dma_rxpbl = eth->dma_pbl;
+ }
+
+ rt_ofw_prop_read_u32(np, "snps,txpbl", (rt_uint32_t *)&eth->dma_txpbl);
+ rt_ofw_prop_read_u32(np, "snps,rxpbl", (rt_uint32_t *)&eth->dma_rxpbl);
+ eth->axi.aal = rt_ofw_prop_read_bool(np, "snps,aal");
+ eth->axi.fixed_burst = rt_ofw_prop_read_bool(np, "snps,fixed-burst");
+
+ axi_np = rt_ofw_parse_phandle(np, "snps,axi-config", 0);
+ if (axi_np)
+ {
+ dwmac_parse_axi_config(eth, axi_np);
+ rt_ofw_node_put(axi_np);
+ }
+
+ dwmac_parse_mtl_config(eth, np);
+
+#ifdef RT_USING_PIN
+ eth->phy_reset_pin = rt_pin_get_named_pin(dev, "snps,reset", 0,
+ RT_NULL, &eth->phy_reset_active);
+ if (eth->phy_reset_pin < 0)
+ {
+ LOG_W("PHY reset gpio not found: %d", eth->phy_reset_pin);
+ eth->phy_reset_pin = -1;
+ }
+
+ rt_ofw_prop_read_u32_array_index(dev->ofw_node, "snps,reset-delays-us",
+ 0, 3, eth->phy_reset_delays);
+#endif /* RT_USING_PIN */
+#else
+ eth->phy_interface = RT_PHY_INTERFACE_MODE_RGMII;
+#endif /* RT_USING_OFW */
+}
+
+static void dwmac_clocks_get(struct dwmac_eth *eth, struct rt_device *dev)
+{
+ eth->stmmac_clk = rt_clk_get_by_name(dev, "stmmaceth");
+ eth->mac_clk_speed = rt_clk_get_by_name(dev, "clk_mac_speed");
+ eth->mac_clk_rx = rt_clk_get_by_name(dev, "mac_clk_rx");
+ eth->mac_clk_tx = rt_clk_get_by_name(dev, "mac_clk_tx");
+ eth->clk_mac_refout = rt_clk_get_by_name(dev, "clk_mac_refout");
+ eth->ptp_clk = rt_clk_get_by_name(dev, "ptp_ref");
+ eth->aclk = rt_clk_get_by_name(dev, "aclk_mac");
+ eth->pclk = rt_clk_get_by_name(dev, "pclk_mac");
+}
+
+static rt_err_t dwmac_clk_prepare_enable(struct rt_clk *clk)
+{
+ if (rt_is_err_or_null(clk))
+ {
+ return RT_EOK;
+ }
+
+ return rt_clk_prepare_enable(clk);
+}
+
+static void dwmac_clk_disable_unprepare(struct rt_clk *clk)
+{
+ if (!rt_is_err_or_null(clk))
+ {
+ rt_clk_disable_unprepare(clk);
+ }
+}
+
+static rt_err_t dwmac_clocks_enable(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+
+ if ((err = dwmac_clk_prepare_enable(eth->aclk)))
+ {
+ return err;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->pclk)))
+ {
+ goto _fail_aclk;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->stmmac_clk)))
+ {
+ goto _fail_pclk;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->mac_clk_speed)))
+ {
+ goto _fail_stmmac;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->mac_clk_rx)))
+ {
+ goto _fail_mac_speed;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->mac_clk_tx)))
+ {
+ goto _fail_mac_rx;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->clk_mac_refout)))
+ {
+ goto _fail_mac_tx;
+ }
+
+ if ((err = dwmac_clk_prepare_enable(eth->ptp_clk)))
+ {
+ goto _fail_refout;
+ }
+
+ return RT_EOK;
+
+_fail_refout:
+ dwmac_clk_disable_unprepare(eth->clk_mac_refout);
+_fail_mac_tx:
+ dwmac_clk_disable_unprepare(eth->mac_clk_tx);
+_fail_mac_rx:
+ dwmac_clk_disable_unprepare(eth->mac_clk_rx);
+_fail_mac_speed:
+ dwmac_clk_disable_unprepare(eth->mac_clk_speed);
+_fail_stmmac:
+ dwmac_clk_disable_unprepare(eth->stmmac_clk);
+_fail_pclk:
+ dwmac_clk_disable_unprepare(eth->pclk);
+_fail_aclk:
+ return err;
+}
+
+static void dwmac_clocks_disable(struct dwmac_eth *eth)
+{
+ dwmac_clk_disable_unprepare(eth->ptp_clk);
+ dwmac_clk_disable_unprepare(eth->clk_mac_refout);
+ dwmac_clk_disable_unprepare(eth->mac_clk_tx);
+ dwmac_clk_disable_unprepare(eth->mac_clk_rx);
+ dwmac_clk_disable_unprepare(eth->mac_clk_speed);
+ dwmac_clk_disable_unprepare(eth->stmmac_clk);
+ dwmac_clk_disable_unprepare(eth->pclk);
+ dwmac_clk_disable_unprepare(eth->aclk);
+}
+
+#ifdef RT_USING_REGULATOR
+static rt_err_t dwmac_phy_supply_enable(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+#ifdef RT_USING_OFW
+ rt_phandle supply_phandle = 0;
+ const char *supply_name = RT_NULL;
+#endif
+
+#ifdef RT_USING_OFW
+ if (eth->dev && eth->dev->ofw_node)
+ {
+ if (rt_ofw_prop_read_u32(eth->dev->ofw_node, "phy-supply",
+ &supply_phandle))
+ {
+ LOG_D("No phy-supply property");
+ return RT_EOK;
+ }
+
+ LOG_D("PHY supply phandle=0x%08x", supply_phandle);
+ }
+#endif /* RT_USING_OFW */
+
+ eth->phy_supply = rt_regulator_get(eth->dev, "phy");
+ if (rt_is_err(eth->phy_supply))
+ {
+ err = rt_ptr_err(eth->phy_supply);
+ LOG_W("PHY supply get failed: %s", rt_strerror(err));
+ eth->phy_supply = RT_NULL;
+ return err;
+ }
+ if (!eth->phy_supply)
+ {
+#ifdef RT_USING_OFW
+ if (supply_phandle)
+ {
+ struct rt_ofw_node *supply_np;
+
+ supply_np = rt_ofw_find_node_by_phandle(supply_phandle);
+ if (supply_np)
+ {
+ rt_ofw_prop_read_string(supply_np, "regulator-name",
+ &supply_name);
+ rt_ofw_node_put(supply_np);
+ }
+
+ if (supply_name)
+ {
+ eth->phy_supply = rt_regulator_get(RT_NULL, supply_name);
+ if (rt_is_err(eth->phy_supply))
+ {
+ err = rt_ptr_err(eth->phy_supply);
+ LOG_W("PHY supply %s get failed: %s", supply_name,
+ rt_strerror(err));
+ eth->phy_supply = RT_NULL;
+ return err;
+ }
+ }
+ }
+#endif /* RT_USING_OFW */
+
+ if (eth->phy_supply)
+ {
+ goto _enable;
+ }
+
+ LOG_W("PHY supply not available");
+ return RT_EOK;
+ }
+
+_enable:
+ err = rt_regulator_enable(eth->phy_supply);
+ if (err)
+ {
+ rt_regulator_put(eth->phy_supply);
+ eth->phy_supply = RT_NULL;
+ return err;
+ }
+
+ LOG_D("PHY supply enabled, voltage=%d uV",
+ rt_regulator_get_voltage(eth->phy_supply));
+
+ return RT_EOK;
+}
+
+static void dwmac_phy_supply_disable(struct dwmac_eth *eth)
+{
+ if (!eth->phy_supply)
+ {
+ return;
+ }
+
+ rt_regulator_disable(eth->phy_supply);
+ rt_regulator_put(eth->phy_supply);
+ eth->phy_supply = RT_NULL;
+}
+#else
+static rt_err_t dwmac_phy_supply_enable(struct dwmac_eth *eth)
+{
+ RT_UNUSED(eth);
+
+ return RT_EOK;
+}
+
+static void dwmac_phy_supply_disable(struct dwmac_eth *eth)
+{
+ RT_UNUSED(eth);
+}
+#endif /* RT_USING_REGULATOR */
+
+static void dwmac_set_mac_addr(struct dwmac_eth *eth)
+{
+ rt_uint32_t lo = eth->mac[0] | (eth->mac[1] << 8) |
+ (eth->mac[2] << 16) | (eth->mac[3] << 24);
+ rt_uint16_t hi = eth->mac[4] | (eth->mac[5] << 8);
+
+ dwmac_writel(eth, GMAC_ADDR_LOW(0), lo);
+ dwmac_writel(eth, GMAC_ADDR_HIGH(0), hi | RT_BIT(31));
+}
+
+static rt_err_t dwmac_mii_register(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+
+ eth->mii = rt_mdio_alloc();
+ if (!eth->mii)
+ {
+ return -RT_ENOMEM;
+ }
+
+ eth->mii->priv = eth;
+ eth->mii->read = dwmac_mdio_read;
+ eth->mii->write = dwmac_mdio_write;
+ rt_snprintf(eth->mii->name, RT_NAME_MAX, "mii%p", eth);
+
+ if ((err = rt_mdio_register(eth->mii)))
+ {
+ rt_free(eth->mii);
+ eth->mii = RT_NULL;
+ return err;
+ }
+
+ return RT_EOK;
+}
+
+static void dwmac_mii_unregister(struct dwmac_eth *eth)
+{
+ if (eth->mii)
+ {
+ rt_mdio_unregister(eth->mii);
+ rt_free(eth->mii);
+ eth->mii = RT_NULL;
+ }
+}
+
+static int dwmac_find_phy_addr(struct dwmac_eth *eth)
+{
+#ifdef RT_USING_OFW
+ struct rt_ofw_node *phy_np;
+
+ if (eth->dev && eth->dev->ofw_node &&
+ (phy_np = rt_ofw_parse_phandle(eth->dev->ofw_node, "phy-handle", 0)))
+ {
+ rt_uint32_t reg;
+
+ if (!rt_ofw_prop_read_u32(phy_np, "reg", &reg))
+ {
+ rt_ofw_node_put(phy_np);
+ return (int)reg;
+ }
+
+ rt_ofw_node_put(phy_np);
+ }
+#endif /* RT_USING_OFW */
+
+ for (int addr = 0; addr < 32; ++addr)
+ {
+ int id = dwmac_mdio_read(eth->mii, addr, 0, RT_MII_PHYSID1);
+
+ if (id > 0 && id != 0xffff)
+ {
+ return addr;
+ }
+ }
+
+ return -1;
+}
+
+static rt_err_t dwmac_dma_alloc_rings(struct dwmac_eth *eth)
+{
+ rt_size_t desc_bytes, rx_buf_bytes, tx_buf_bytes, total;
+ rt_ubase_t handle;
+ rt_uint64_t dma_end;
+ void *blob;
+
+ desc_bytes = sizeof(struct dwmac_desc) * (DWMAC_RX_RING_SIZE + DWMAC_TX_RING_SIZE);
+ rx_buf_bytes = DWMAC_RX_BUFFER_SIZE * DWMAC_RX_RING_SIZE;
+ tx_buf_bytes = DWMAC_RX_BUFFER_SIZE * DWMAC_TX_RING_SIZE;
+ total = desc_bytes + rx_buf_bytes + tx_buf_bytes;
+
+ blob = rt_dma_alloc(eth->dev, total, &handle, DWMAC_DMA_ALLOC_FLAGS);
+ if (!blob)
+ {
+ return -RT_ENOMEM;
+ }
+
+ dma_end = (rt_uint64_t)handle + total - 1;
+ if ((rt_uint64_t)handle > DWMAC_DMA_ADDR_MAX ||
+ dma_end > DWMAC_DMA_ADDR_MAX)
+ {
+ LOG_E("DMA address 0x%llx-0x%llx exceeds 32-bit range",
+ (unsigned long long)handle, (unsigned long long)dma_end);
+ rt_dma_free(eth->dev, total, blob, handle, DWMAC_DMA_ALLOC_FLAGS);
+ return -RT_EINVAL;
+ }
+
+ eth->rx_ring = blob;
+ eth->tx_ring = (struct dwmac_desc *)((rt_uint8_t *)blob +
+ sizeof(struct dwmac_desc) * DWMAC_RX_RING_SIZE);
+ eth->rx_buffers = (rt_uint8_t *)blob + desc_bytes;
+ eth->tx_buffers = eth->rx_buffers + rx_buf_bytes;
+
+ eth->rx_ring_dma = handle;
+ eth->tx_ring_dma = handle + sizeof(struct dwmac_desc) * DWMAC_RX_RING_SIZE;
+ eth->rx_buffers_dma = handle + desc_bytes;
+ eth->tx_buffers_dma = eth->rx_buffers_dma + rx_buf_bytes;
+
+ eth->dma_blob_size = total;
+ eth->dma_blob_handle = handle;
+
+ rt_memset(blob, 0, total);
+
+ return RT_EOK;
+}
+
+static void dwmac_init_rx_desc(struct dwmac_eth *eth, int idx)
+{
+ struct dwmac_desc *desc = &eth->rx_ring[idx];
+ rt_ubase_t buf_dma = eth->rx_buffers_dma + idx * DWMAC_RX_BUFFER_SIZE;
+
+ desc->des0 = (rt_uint32_t)buf_dma;
+ desc->des1 = 0;
+ desc->des2 = 0;
+ desc->des3 = RDES3_OWN | RDES3_BUFFER1_VALID_ADDR |
+ RDES3_INT_ON_COMPLETION_EN;
+}
+
+static void dwmac_dma_init(struct dwmac_eth *eth)
+{
+ rt_uint32_t val;
+ int c = DWMAC_CHAN;
+ rt_size_t ring_bytes = sizeof(struct dwmac_desc) *
+ (DWMAC_RX_RING_SIZE + DWMAC_TX_RING_SIZE);
+
+ for (int i = 0; i < DWMAC_RX_RING_SIZE; ++i)
+ {
+ dwmac_init_rx_desc(eth, i);
+ }
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, eth->rx_ring, ring_bytes);
+
+ val = 0;
+ if (eth->axi.fixed_burst)
+ {
+ val |= DMA_SYS_BUS_FB;
+ }
+ if (eth->axi.mixed_burst)
+ {
+ val |= DMA_SYS_BUS_MB;
+ }
+ if (eth->axi.aal)
+ {
+ val |= DMA_SYS_BUS_AAL;
+ }
+ val |= eth->axi.blen_mask;
+ val |= (eth->axi.wr_osr_lmt << DMA_AXI_WR_OSR_LMT_SHIFT);
+ val |= (eth->axi.rd_osr_lmt << DMA_AXI_RD_OSR_LMT_SHIFT);
+ dwmac_writel(eth, DMA_SYS_BUS_MODE, val);
+
+ /* MTL: store-and-forward, enable TX queue 0 */
+ val = dwmac_readl(eth, MTL_CHAN_TX_OP_MODE(c));
+ val |= MTL_OP_MODE_TSF | MTL_OP_MODE_TXQEN;
+ dwmac_writel(eth, MTL_CHAN_TX_OP_MODE(c), val);
+
+ val = dwmac_readl(eth, MTL_CHAN_RX_OP_MODE(c));
+ val |= MTL_OP_MODE_RSF;
+ dwmac_writel(eth, MTL_CHAN_RX_OP_MODE(c), val);
+
+ dwmac_writel(eth, GMAC_RXQ_CTRL0, GMAC_RX_DCB_QUEUE_ENABLE(0));
+
+ val = dwmac_readl(eth, GMAC_RXQ_CTRL1);
+ val &= ~GMAC_RXQCTRL_MCBCQ_MASK;
+ val |= (0 << GMAC_RXQCTRL_MCBCQ_SHIFT) & GMAC_RXQCTRL_MCBCQ_MASK;
+ val |= GMAC_RXQCTRL_MCBCQEN;
+ dwmac_writel(eth, GMAC_RXQ_CTRL1, val);
+
+ val = dwmac_readl(eth, MTL_RXQ_DMA_MAP0);
+ val &= ~MTL_RXQ_DMA_QXMDMACH_MASK(0);
+ val |= MTL_RXQ_DMA_QXMDMACH(DWMAC_CHAN, 0);
+ dwmac_writel(eth, MTL_RXQ_DMA_MAP0, val);
+
+ /* DMA channel */
+ val = eth->dma_txpbl << DMA_BUS_MODE_PBL_SHIFT;
+ if (eth->tso_en)
+ {
+ val |= DMA_CONTROL_TSE;
+ }
+ dwmac_writel(eth, DMA_CHAN_TX_CONTROL(c), val | DMA_CONTROL_OSP);
+
+ val = eth->dma_rxpbl << DMA_BUS_MODE_RPBL_SHIFT;
+ val |= ((DWMAC_RX_BUFFER_SIZE << DMA_RBSZ_SHIFT) & DMA_RBSZ_MASK);
+ dwmac_writel(eth, DMA_CHAN_RX_CONTROL(c), val);
+
+ dwmac_writel(eth, DMA_CHAN_TX_BASE_ADDR(c), (rt_uint32_t)eth->tx_ring_dma);
+ dwmac_writel(eth, DMA_CHAN_RX_BASE_ADDR(c), (rt_uint32_t)eth->rx_ring_dma);
+ dwmac_writel(eth, DMA_CHAN_TX_RING_LEN(c), DWMAC_TX_RING_SIZE - 1);
+ dwmac_writel(eth, DMA_CHAN_RX_RING_LEN(c), DWMAC_RX_RING_SIZE - 1);
+
+ dwmac_writel(eth, DMA_CHAN_RX_END_ADDR(c),
+ (rt_uint32_t)(eth->rx_ring_dma +
+ (DWMAC_RX_RING_SIZE - 1) * sizeof(struct dwmac_desc)));
+
+ dwmac_writel(eth, GMAC_PACKET_FILTER, GMAC_PF_PM);
+}
+
+static rt_err_t dwmac_adjust_link(struct dwmac_eth *eth)
+{
+ rt_uint32_t cfg;
+ const struct dwmac_plat_ops *plat = dwmac_plat(eth);
+
+ if (!eth->phy || !eth->phy->link)
+ {
+ return -RT_ERROR;
+ }
+
+ cfg = dwmac_readl(eth, GMAC_CONFIG);
+ cfg &= ~(GMAC_CONFIG_PS | GMAC_CONFIG_FES | GMAC_CONFIG_DM);
+
+ if (eth->phy->duplex == DUPLEX_FULL)
+ {
+ cfg |= GMAC_CONFIG_DM;
+ }
+
+ switch (eth->phy->speed)
+ {
+ case SPEED_1000:
+ break;
+ case SPEED_100:
+ cfg |= GMAC_CONFIG_PS | GMAC_CONFIG_FES;
+ break;
+ case SPEED_10:
+ cfg |= GMAC_CONFIG_PS;
+ break;
+ default:
+ return -RT_EINVAL;
+ }
+
+ if (plat && plat->fix_link_speed)
+ {
+ rt_err_t err = plat->fix_link_speed(eth, eth->phy->speed);
+
+ if (err)
+ {
+ return err;
+ }
+ }
+
+ cfg |= GMAC_CONFIG_TE | GMAC_CONFIG_RE | GMAC_CONFIG_IPC;
+ dwmac_writel(eth, GMAC_CONFIG, cfg);
+
+ /* Start DMA engines */
+ cfg = dwmac_readl(eth, DMA_CHAN_TX_CONTROL(DWMAC_CHAN));
+ dwmac_writel(eth, DMA_CHAN_TX_CONTROL(DWMAC_CHAN), cfg | DMA_CONTROL_ST);
+
+ cfg = dwmac_readl(eth, DMA_CHAN_RX_CONTROL(DWMAC_CHAN));
+ dwmac_writel(eth, DMA_CHAN_RX_CONTROL(DWMAC_CHAN), cfg | DMA_CONTROL_SR);
+
+ dwmac_writel(eth, DMA_CHAN_STATUS(DWMAC_CHAN), 0xffffffff);
+ dwmac_writel(eth, DMA_CHAN_INTR_ENA(DWMAC_CHAN),
+ DMA_CHAN_INTR_DEFAULT_MASK_4_10);
+
+ eth->mac_started = RT_TRUE;
+
+ return RT_EOK;
+}
+
+static rt_err_t dwmac_start(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+
+ if (!eth->phy)
+ {
+ return -RT_ERROR;
+ }
+
+ if (!eth->phy_configured)
+ {
+ if ((err = rt_phy_config(eth->phy)) < 0)
+ {
+ return err;
+ }
+ eth->phy_configured = RT_TRUE;
+ }
+
+ if ((err = rt_phy_startup(eth->phy)) < 0)
+ {
+ return err;
+ }
+
+ return dwmac_adjust_link(eth);
+}
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+static void dwmac_phy_retry_work(struct rt_work *work, void *work_data)
+{
+ struct dwmac_eth *eth = work_data;
+ rt_err_t err;
+
+ RT_UNUSED(work);
+
+ if (!eth || !eth->net_registered || !eth->phy)
+ {
+ return;
+ }
+
+ err = dwmac_start(eth);
+ if (err == RT_EOK && eth->phy->link)
+ {
+ eth_device_linkchange(&eth->parent, RT_TRUE);
+ return;
+ }
+
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ rt_work_submit(&eth->phy_retry_work,
+ rt_tick_from_millisecond(DWMAC_PHY_RETRY_INTERVAL_MS));
+}
+#endif /* RT_USING_SYSTEM_WORKQUEUE */
+
+static rt_err_t dwmac_eth_tx(rt_device_t dev, struct pbuf *p)
+{
+ struct dwmac_eth *eth = rt_container_of(dev, struct dwmac_eth, parent.parent);
+ struct dwmac_desc *desc;
+ rt_uint32_t idx = eth->tx_idx;
+ rt_uint8_t *payload;
+ rt_size_t len = p->tot_len;
+ rt_uint32_t tdes3;
+
+ if (!eth->mac_started || !eth->phy || !eth->phy->link)
+ {
+ return -RT_ERROR;
+ }
+
+ desc = &eth->tx_ring[idx];
+ if (desc->des3 & TDES3_OWN)
+ {
+ return -RT_EBUSY;
+ }
+
+ payload = eth->tx_buffers + idx * DWMAC_RX_BUFFER_SIZE;
+
+ pbuf_copy_partial(p, payload, len, 0);
+ if (len < ETH_ZLEN)
+ {
+ rt_memset(payload + len, 0, ETH_ZLEN - len);
+ len = ETH_ZLEN;
+ }
+
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, payload, len);
+
+ desc->des0 = (rt_uint32_t)(eth->tx_buffers_dma + idx * DWMAC_RX_BUFFER_SIZE);
+ desc->des1 = 0;
+ desc->des2 = len & TDES2_BUFFER1_SIZE_MASK;
+ tdes3 = (len & TDES3_PACKET_SIZE_MASK) |
+ TDES3_FIRST_DESCRIPTOR | TDES3_LAST_DESCRIPTOR | TDES3_OWN;
+#ifdef RT_LWIP_USING_HW_CHECKSUM
+ tdes3 |= TDES3_CHECKSUM_INSERTION_FULL;
+#endif
+ rt_hw_wmb();
+ desc->des3 = tdes3;
+
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, desc, sizeof(*desc));
+
+ eth->tx_idx = NEXT_TX(idx);
+ rt_hw_wmb();
+ dwmac_writel(eth, DMA_CHAN_TX_END_ADDR(DWMAC_CHAN),
+ (rt_uint32_t)(eth->tx_ring_dma +
+ eth->tx_idx * sizeof(struct dwmac_desc)));
+ rt_hw_dsb();
+
+ return RT_EOK;
+}
+
+static struct pbuf *dwmac_eth_rx(rt_device_t dev)
+{
+ struct dwmac_eth *eth = rt_container_of(dev, struct dwmac_eth, parent.parent);
+ struct dwmac_desc *desc;
+ struct pbuf *p = RT_NULL;
+ rt_uint32_t idx = eth->rx_idx;
+ rt_uint32_t len;
+ rt_uint8_t *payload;
+
+ desc = &eth->rx_ring[idx];
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, desc, sizeof(*desc));
+
+ if (desc->des3 & RDES3_OWN)
+ {
+ return RT_NULL;
+ }
+
+ len = desc->des3 & RDES3_PACKET_SIZE_MASK;
+ if (len == 0 || len > DWMAC_RX_BUFFER_SIZE)
+ {
+ goto refill;
+ }
+
+ payload = eth->rx_buffers + idx * DWMAC_RX_BUFFER_SIZE;
+
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, payload, len);
+
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
+ if (p)
+ {
+ pbuf_take(p, payload, len);
+ }
+
+refill:
+ dwmac_init_rx_desc(eth, idx);
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, desc, sizeof(*desc));
+ dwmac_writel(eth, DMA_CHAN_RX_END_ADDR(DWMAC_CHAN),
+ (rt_uint32_t)(eth->rx_ring_dma + idx * sizeof(struct dwmac_desc)));
+ eth->rx_idx = NEXT_RX(idx);
+
+ return p;
+}
+
+static rt_err_t dwmac_eth_control(rt_device_t dev, int cmd, void *args)
+{
+ struct dwmac_eth *eth = rt_container_of(dev, struct dwmac_eth, parent.parent);
+
+ switch (cmd)
+ {
+ case NIOCTL_GADDR:
+ if (!args)
+ {
+ return -RT_EINVAL;
+ }
+ rt_memcpy(args, eth->mac, sizeof(eth->mac));
+ return RT_EOK;
+
+ default:
+ return -RT_EINVAL;
+ }
+}
+
+static void dwmac_eth_isr(int irq, void *param)
+{
+ rt_uint32_t status;
+ struct dwmac_eth *eth = param;
+
+ RT_UNUSED(irq);
+
+ status = dwmac_readl(eth, DMA_CHAN_STATUS(DWMAC_CHAN));
+ if (!status)
+ {
+ return;
+ }
+
+ dwmac_writel(eth, DMA_CHAN_STATUS(DWMAC_CHAN), status);
+
+ if (status & (DMA_CHAN_STATUS_RI | DMA_CHAN_STATUS_TI | DMA_CHAN_STATUS_NIS))
+ {
+ eth_device_ready(&eth->parent);
+ }
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops dwmac_eth_ops =
+{
+ .control = dwmac_eth_control,
+};
+#endif
+
+rt_err_t dwmac_eth_common_probe(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+
+#ifdef RT_USING_DEVICE_OPS
+ eth->parent.parent.ops = &dwmac_eth_ops;
+#else
+ eth->parent.parent.control = dwmac_eth_control;
+#endif
+ eth->parent.eth_tx = dwmac_eth_tx;
+ eth->parent.eth_rx = dwmac_eth_rx;
+
+ if ((err = rt_dm_dev_set_name_auto(&eth->parent.parent, "e")) < 0)
+ {
+ return err;
+ }
+
+ if (eth->irq >= 0)
+ {
+ rt_hw_interrupt_install(eth->irq, dwmac_eth_isr, eth, "dwmac");
+ rt_hw_interrupt_umask(eth->irq);
+ eth->irq_installed = RT_TRUE;
+ }
+
+ if ((err = eth_device_init(&eth->parent, rt_dm_dev_get_name(&eth->parent.parent))))
+ {
+ return err;
+ }
+
+ eth->net_registered = RT_TRUE;
+
+#ifdef RT_ETHERNET_DWMAC_PTP
+ if (eth->config && (eth->config->caps & DWMAC_CAP_PTP))
+ {
+ if ((err = dwmac_ptp_register(eth)))
+ {
+ LOG_W("PTP register failed: %s", rt_strerror(err));
+ }
+ }
+#endif /* RT_ETHERNET_DWMAC_PTP */
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+ if (eth->phy && !eth->phy->link)
+ {
+ rt_work_init(&eth->phy_retry_work, dwmac_phy_retry_work, eth);
+ eth->phy_work_inited = RT_TRUE;
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ rt_work_submit(&eth->phy_retry_work,
+ rt_tick_from_millisecond(DWMAC_PHY_RETRY_INTERVAL_MS));
+ }
+ else if (eth->phy && eth->phy->link)
+ {
+ eth_device_linkchange(&eth->parent, RT_TRUE);
+ }
+ else
+ {
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ }
+#else
+ if (dwmac_start(eth) == RT_EOK && eth->phy && eth->phy->link)
+ {
+ eth_device_linkchange(&eth->parent, RT_TRUE);
+ }
+ else
+ {
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ }
+#endif
+
+ return RT_EOK;
+}
+
+rt_err_t dwmac_eth_common_remove(struct dwmac_eth *eth)
+{
+#ifdef RT_ETHERNET_DWMAC_PTP
+ dwmac_ptp_unregister(eth);
+#endif
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+ if (eth->phy_work_inited)
+ {
+ rt_work_cancel(&eth->phy_retry_work);
+ eth->phy_work_inited = RT_FALSE;
+ }
+#endif
+
+ if (eth->irq_installed)
+ {
+ rt_hw_interrupt_mask(eth->irq);
+ rt_pic_detach_irq(eth->irq, eth);
+ eth->irq_installed = RT_FALSE;
+ }
+
+ if (eth->net_registered)
+ {
+ eth_device_linkchange(&eth->parent, RT_FALSE);
+ eth_device_deinit(&eth->parent);
+ eth->net_registered = RT_FALSE;
+ }
+
+ if (eth->phy)
+ {
+ rt_phy_shutdown(eth->phy);
+ eth->phy = RT_NULL;
+ }
+
+ dwmac_mii_unregister(eth);
+
+ return RT_EOK;
+}
+
+rt_err_t dwmac_probe(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+ const struct dwmac_plat_ops *plat = dwmac_plat(eth);
+ int phy_addr;
+
+ dwmac_parse_dt_props(eth);
+ dwmac_clocks_get(eth, eth->dev);
+
+ if (plat && plat->parse_ofw)
+ {
+ err = plat->parse_ofw(eth);
+ if (err)
+ {
+ return err;
+ }
+ }
+
+ eth->rst = rt_reset_control_get_by_name(eth->dev, "stmmaceth");
+ if (!rt_is_err_or_null(eth->rst))
+ {
+ rt_reset_control_assert(eth->rst);
+ rt_hw_us_delay(20);
+ rt_reset_control_deassert(eth->rst);
+ }
+
+ if ((err = dwmac_clocks_enable(eth)))
+ {
+ goto _fail_clk;
+ }
+
+ if (plat && plat->init)
+ {
+ err = plat->init(eth);
+ if (err)
+ {
+ goto _fail_plat;
+ }
+ }
+
+ if ((err = dwmac_phy_supply_enable(eth)))
+ {
+ LOG_E("PHY supply enable failed: %s", rt_strerror(err));
+ goto _fail_plat;
+ }
+
+ dwmac_phy_reset_gpio(eth);
+
+ if ((err = dwmac_dma_reset(eth)))
+ {
+ LOG_E("DMA reset failed");
+ goto _fail_phy_supply;
+ }
+
+ if ((err = dwmac_dma_alloc_rings(eth)))
+ {
+ goto _fail_phy_supply;
+ }
+
+ dwmac_dma_init(eth);
+ dwmac_set_mac_addr(eth);
+
+ if ((err = dwmac_mii_register(eth)))
+ {
+ goto _fail_blob;
+ }
+
+ phy_addr = dwmac_find_phy_addr(eth);
+ if (phy_addr < 0)
+ {
+ LOG_E("No PHY on MDIO");
+ err = -RT_EINVAL;
+ goto _fail_mii;
+ }
+
+ eth->phy = rt_phy_get_device(eth->mii, eth->dev->ofw_node, phy_addr,
+ (rt_phy_interface)eth->phy_interface);
+ if (!eth->phy)
+ {
+ LOG_E("PHY attach failed");
+ err = -RT_ERROR;
+ goto _fail_mii;
+ }
+
+ eth->hw_ready = RT_TRUE;
+
+ if ((err = dwmac_eth_common_probe(eth)))
+ {
+ dwmac_eth_common_remove(eth);
+ goto _fail_mii;
+ }
+
+ return RT_EOK;
+
+_fail_mii:
+ dwmac_mii_unregister(eth);
+
+_fail_blob:
+ if (eth->rx_ring)
+ {
+ rt_dma_free(eth->dev, eth->dma_blob_size, eth->rx_ring,
+ eth->dma_blob_handle, DWMAC_DMA_ALLOC_FLAGS);
+ eth->rx_ring = RT_NULL;
+ }
+
+_fail_phy_supply:
+ dwmac_phy_supply_disable(eth);
+
+_fail_plat:
+ if (plat && plat->exit)
+ {
+ plat->exit(eth);
+ }
+ dwmac_clocks_disable(eth);
+
+_fail_clk:
+ if (!rt_is_err_or_null(eth->rst))
+ {
+ rt_reset_control_put(eth->rst);
+ }
+
+ return err;
+}
+
+rt_err_t dwmac_remove(struct dwmac_eth *eth)
+{
+ const struct dwmac_plat_ops *plat = dwmac_plat(eth);
+
+ dwmac_eth_common_remove(eth);
+
+ if (eth->rx_ring)
+ {
+ rt_dma_free(eth->dev, eth->dma_blob_size, eth->rx_ring,
+ eth->dma_blob_handle, DWMAC_DMA_ALLOC_FLAGS);
+ eth->rx_ring = RT_NULL;
+ }
+
+ dwmac_phy_supply_disable(eth);
+
+ if (plat && plat->exit)
+ {
+ plat->exit(eth);
+ }
+
+ dwmac_clocks_disable(eth);
+
+ if (!rt_is_err_or_null(eth->rst))
+ {
+ rt_reset_control_put(eth->rst);
+ }
+
+ return RT_EOK;
+}
diff --git a/components/drivers/ethernet/synopsys/dwmac.h b/components/drivers/ethernet/synopsys/dwmac.h
new file mode 100644
index 0000000000..b6e73f351e
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/dwmac.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-11-26 GuEe-GUI first version
+ */
+
+#ifndef __DWMAC_H__
+#define __DWMAC_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <drivers/misc.h>
+#include "../ethernet_dm.h"
+
+/* GMAC4 MAC registers (offset from mac_base) */
+#define GMAC_CONFIG 0x0000
+#define GMAC_PACKET_FILTER 0x0008
+#define GMAC_MDIO_ADDR 0x0200
+#define GMAC_MDIO_DATA 0x0204
+#define GMAC_ADDR_HIGH(reg) (0x0300 + (reg) * 8)
+#define GMAC_ADDR_LOW(reg) (0x0304 + (reg) * 8)
+#define GMAC_RXQ_CTRL0 0x00a0
+#define GMAC_RXQ_CTRL1 0x00a4
+
+#define GMAC_CONFIG_RE RT_BIT(0)
+#define GMAC_CONFIG_TE RT_BIT(1)
+#define GMAC_CONFIG_DM RT_BIT(13)
+#define GMAC_CONFIG_FES RT_BIT(14)
+#define GMAC_CONFIG_PS RT_BIT(15)
+#define GMAC_CONFIG_IPC RT_BIT(27)
+
+#define GMAC_PF_PM RT_BIT(4)
+
+#define GMAC_RX_DCB_QUEUE_ENABLE(q) RT_BIT(((q) * 2) + 1)
+#define GMAC_RXQCTRL_MCBCQEN RT_BIT(20)
+#define GMAC_RXQCTRL_MCBCQ_MASK RT_GENMASK(18, 16)
+#define GMAC_RXQCTRL_MCBCQ_SHIFT 16
+
+/* DMA top-level (offset from mac_base) */
+#define DMA_BUS_MODE 0x1000
+#define DMA_SYS_BUS_MODE 0x1004
+#define DMA_BUS_MODE_SFT_RESET RT_BIT(0)
+#define DMA_SYS_BUS_FB RT_BIT(0)
+#define DMA_SYS_BUS_MB RT_BIT(14)
+#define DMA_SYS_BUS_AAL RT_BIT(12)
+#define DMA_AXI_BLEN16 RT_BIT(3)
+#define DMA_AXI_BLEN8 RT_BIT(2)
+#define DMA_AXI_BLEN4 RT_BIT(1)
+#define DMA_AXI_BLEN32 RT_BIT(4)
+#define DMA_AXI_BLEN64 RT_BIT(5)
+#define DMA_AXI_BLEN128 RT_BIT(6)
+#define DMA_AXI_BLEN256 RT_BIT(7)
+#define DMA_AXI_WR_OSR_LMT_SHIFT 24
+#define DMA_AXI_RD_OSR_LMT_SHIFT 16
+
+#define DMA_CHAN_BASE 0x1100
+#define DMA_CHAN_OFFSET 0x80
+#define DMA_CHAN_CONTROL(c) (DMA_CHAN_BASE + (c) * DMA_CHAN_OFFSET)
+#define DMA_CHAN_TX_CONTROL(c) (DMA_CHAN_CONTROL(c) + 0x04)
+#define DMA_CHAN_RX_CONTROL(c) (DMA_CHAN_CONTROL(c) + 0x08)
+#define DMA_CHAN_TX_BASE_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x14)
+#define DMA_CHAN_RX_BASE_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x1c)
+#define DMA_CHAN_TX_END_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x20)
+#define DMA_CHAN_RX_END_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x28)
+#define DMA_CHAN_TX_RING_LEN(c) (DMA_CHAN_CONTROL(c) + 0x2c)
+#define DMA_CHAN_RX_RING_LEN(c) (DMA_CHAN_CONTROL(c) + 0x30)
+#define DMA_CHAN_INTR_ENA(c) (DMA_CHAN_CONTROL(c) + 0x34)
+#define DMA_CHAN_STATUS(c) (DMA_CHAN_CONTROL(c) + 0x60)
+
+#define DMA_CONTROL_ST RT_BIT(0)
+#define DMA_CONTROL_SR RT_BIT(0)
+#define DMA_CONTROL_OSP RT_BIT(4)
+#define DMA_CONTROL_TSE RT_BIT(12)
+#define DMA_BUS_MODE_PBL_SHIFT 16
+#define DMA_BUS_MODE_RPBL_SHIFT 16
+#define DMA_RBSZ_SHIFT 1
+#define DMA_RBSZ_MASK RT_GENMASK(14, 1)
+
+#define DMA_CHAN_STATUS_TI RT_BIT(0)
+#define DMA_CHAN_STATUS_RI RT_BIT(6)
+#define DMA_CHAN_STATUS_NIS RT_BIT(15)
+
+#define DMA_CHAN_INTR_ENA_RIE RT_BIT(6)
+#define DMA_CHAN_INTR_ENA_TIE RT_BIT(0)
+#define DMA_CHAN_INTR_ENA_NIE RT_BIT(16)
+#define DMA_CHAN_INTR_ENA_AIE RT_BIT(15)
+#define DMA_CHAN_INTR_ENA_NIE_4_10 RT_BIT(15)
+#define DMA_CHAN_INTR_ENA_AIE_4_10 RT_BIT(14)
+#define DMA_CHAN_INTR_ENA_FBE RT_BIT(12)
+#define DMA_CHAN_INTR_DEFAULT_MASK (DMA_CHAN_INTR_ENA_NIE | \
+ DMA_CHAN_INTR_ENA_AIE | \
+ DMA_CHAN_INTR_ENA_FBE | \
+ DMA_CHAN_INTR_ENA_RIE | \
+ DMA_CHAN_INTR_ENA_TIE)
+#define DMA_CHAN_INTR_DEFAULT_MASK_4_10 (DMA_CHAN_INTR_ENA_NIE_4_10 | \
+ DMA_CHAN_INTR_ENA_AIE_4_10 | \
+ DMA_CHAN_INTR_ENA_FBE | \
+ DMA_CHAN_INTR_ENA_RIE | \
+ DMA_CHAN_INTR_ENA_TIE)
+
+#define MTL_OPERATION_MODE 0x0c00
+#define MTL_RXQ_DMA_MAP0 0x0c30
+#define MTL_RXQ_DMA_QXMDMACH_MASK(q) (0xf << (8 * (q)))
+#define MTL_RXQ_DMA_QXMDMACH(chan, q) ((chan) << (8 * (q)))
+#define MTL_CHAN_BASE 0x0d00
+#define MTL_CHAN_OFFSET 0x40
+#define MTL_CHAN_TX_OP_MODE(c) (MTL_CHAN_BASE + (c) * MTL_CHAN_OFFSET)
+#define MTL_CHAN_RX_OP_MODE(c) (MTL_CHAN_BASE + (c) * MTL_CHAN_OFFSET + 0x30)
+#define MTL_OP_MODE_RSF RT_BIT(5)
+#define MTL_OP_MODE_TSF RT_BIT(1)
+#define MTL_OP_MODE_TXQEN RT_BIT(3)
+
+/* Enhanced descriptors (DWMAC4) */
+#define TDES2_BUFFER1_SIZE_MASK RT_GENMASK(13, 0)
+#define TDES3_PACKET_SIZE_MASK RT_GENMASK(14, 0)
+#define TDES3_CHECKSUM_INSERTION_SHIFT 16
+#define TDES3_CHECKSUM_INSERTION_FULL (3 << TDES3_CHECKSUM_INSERTION_SHIFT)
+#define TDES3_FIRST_DESCRIPTOR RT_BIT(29)
+#define TDES3_LAST_DESCRIPTOR RT_BIT(28)
+#define TDES3_OWN RT_BIT(31)
+#define RDES3_OWN RT_BIT(31)
+#define RDES3_INT_ON_COMPLETION_EN RT_BIT(30)
+#define RDES3_BUFFER1_VALID_ADDR RT_BIT(24)
+#define RDES3_PACKET_SIZE_MASK RT_GENMASK(14, 0)
+
+#define MII_ADDR_GBUSY RT_BIT(0)
+#define MII_DATA_GD_MASK RT_GENMASK(15, 0)
+#define MII_GMAC4_GOC_SHIFT 2
+#define MII_GMAC4_WRITE (1 << MII_GMAC4_GOC_SHIFT)
+#define MII_GMAC4_READ (3 << MII_GMAC4_GOC_SHIFT)
+#define MII_GMAC4_ADDR_SHIFT 21
+#define MII_GMAC4_ADDR_MASK RT_GENMASK(25, 21)
+#define MII_GMAC4_REG_SHIFT 16
+#define MII_GMAC4_REG_MASK RT_GENMASK(20, 16)
+#define MII_GMAC4_CSR_SHIFT 8
+#define MII_GMAC4_CSR_100_150M (0x4 << MII_GMAC4_CSR_SHIFT)
+
+#define DWMAC_RX_RING_SIZE 32
+#define DWMAC_TX_RING_SIZE 16
+#define DWMAC_RX_BUFFER_SIZE 1536
+#define DWMAC_CHAN 0
+#define DWMAC_DMA_PBL 8
+#define DWMAC_MDIO_TIMEOUT_US 10000
+
+enum dwmac_core_type
+{
+ DWMAC_CORE_GMAC4,
+};
+
+struct dwmac_desc
+{
+ rt_uint32_t des0;
+ rt_uint32_t des1;
+ rt_uint32_t des2;
+ rt_uint32_t des3;
+};
+
+#define DWMAC_CAP_TSO RT_BIT(0)
+#define DWMAC_CAP_PTP RT_BIT(1)
+
+struct dwmac_axi_cfg
+{
+ rt_uint32_t wr_osr_lmt;
+ rt_uint32_t rd_osr_lmt;
+ rt_bool_t fixed_burst;
+ rt_bool_t mixed_burst;
+ rt_bool_t aal;
+ rt_uint32_t blen_mask;
+};
+
+struct dwmac_mtl_cfg
+{
+ rt_uint32_t rx_queues;
+ rt_uint32_t tx_queues;
+};
+
+struct dwmac_eth;
+
+struct dwmac_plat_ops
+{
+ rt_uint32_t flags;
+
+ rt_err_t (*parse_ofw)(struct dwmac_eth *eth);
+ rt_err_t (*init)(struct dwmac_eth *eth);
+ void (*exit)(struct dwmac_eth *eth);
+ rt_err_t (*fix_link_speed)(struct dwmac_eth *eth, int speed);
+};
+
+struct dwmac_config
+{
+ enum dwmac_core_type core_type;
+ rt_uint32_t caps;
+ rt_uint8_t dma_pbl;
+ const struct dwmac_plat_ops *plat;
+};
+
+struct dwmac_eth
+{
+ struct eth_device parent;
+ struct rt_device *dev;
+
+ void *mac_base;
+ int irq;
+
+ struct rt_phy_device *phy;
+ struct mii_bus *mii;
+
+ rt_uint8_t mac[6];
+ int phy_interface;
+
+ struct rt_clk *stmmac_clk;
+ struct rt_clk *mac_clk_speed;
+ struct rt_clk *mac_clk_rx;
+ struct rt_clk *mac_clk_tx;
+ struct rt_clk *clk_mac_refout;
+ struct rt_clk *ptp_clk;
+ struct rt_clk *aclk;
+ struct rt_clk *pclk;
+ struct rt_reset_control *rst;
+#ifdef RT_USING_REGULATOR
+ struct rt_regulator *phy_supply;
+#endif
+
+ rt_ssize_t phy_reset_pin;
+ rt_uint8_t phy_reset_active;
+ rt_uint32_t phy_reset_delays[3];
+
+ struct dwmac_desc *rx_ring;
+ struct dwmac_desc *tx_ring;
+ rt_ubase_t rx_ring_dma;
+ rt_ubase_t tx_ring_dma;
+ rt_uint8_t *rx_buffers;
+ rt_ubase_t rx_buffers_dma;
+ rt_uint8_t *tx_buffers;
+ rt_ubase_t tx_buffers_dma;
+ rt_size_t dma_blob_size;
+ rt_ubase_t dma_blob_handle;
+
+ rt_uint32_t tx_idx;
+ rt_uint32_t rx_idx;
+
+ rt_uint8_t dma_pbl;
+ rt_uint8_t dma_txpbl;
+ rt_uint8_t dma_rxpbl;
+ rt_bool_t tso_en;
+ struct dwmac_axi_cfg axi;
+ struct dwmac_mtl_cfg mtl;
+
+ rt_bool_t hw_ready;
+ rt_bool_t mac_started;
+ rt_bool_t net_registered;
+ rt_bool_t irq_installed;
+ rt_bool_t phy_configured;
+
+#ifdef RT_USING_SYSTEM_WORKQUEUE
+ struct rt_work phy_retry_work;
+ rt_bool_t phy_work_inited;
+#endif
+
+ const struct dwmac_config *config;
+ void *plat_priv;
+
+#ifdef RT_ETHERNET_DWMAC_PTP
+ struct rt_ptp_clock ptp_parent;
+ rt_uint32_t ptp_clk_rate;
+ rt_uint32_t default_addend;
+ rt_uint32_t sub_second_inc;
+ rt_uint32_t systime_flags;
+ rt_uint8_t pps_out_num;
+ rt_uint8_t aux_snapshot_num;
+#endif /* RT_ETHERNET_DWMAC_PTP */
+};
+
+#define dwmac_readl(eth, reg) HWREG32((rt_uint8_t *)(eth)->mac_base + (reg))
+#define dwmac_writel(eth, reg, val) HWREG32((rt_uint8_t *)(eth)->mac_base + (reg)) = (val)
+
+rt_inline const struct dwmac_plat_ops *dwmac_plat(const struct dwmac_eth *eth)
+{
+ if (eth && eth->config && eth->config->plat)
+ {
+ return eth->config->plat;
+ }
+
+ return RT_NULL;
+}
+
+rt_err_t dwmac_probe(struct dwmac_eth *eth);
+rt_err_t dwmac_remove(struct dwmac_eth *eth);
+rt_err_t dwmac_eth_common_probe(struct dwmac_eth *eth);
+rt_err_t dwmac_eth_common_remove(struct dwmac_eth *eth);
+
+#ifdef RT_ETHERNET_DWMAC_PTP
+rt_err_t dwmac_ptp_register(struct dwmac_eth *eth);
+void dwmac_ptp_unregister(struct dwmac_eth *eth);
+#endif /* RT_ETHERNET_DWMAC_PTP */
+
+#endif /* __DWMAC_H__ */
diff --git a/components/drivers/ethernet/synopsys/dwmac_ptp.c b/components/drivers/ethernet/synopsys/dwmac_ptp.c
new file mode 100644
index 0000000000..45b62efb7d
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/dwmac_ptp.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-11-26 GuEe-GUI first version
+ */
+
+#include "dwmac_ptp.h"
+
+#define DBG_TAG "eth.dwmac.ptp"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define NSEC_PER_SEC 1000000000LL
+
+static rt_err_t dwmac_ptp_poll_tcr(struct dwmac_eth *eth, rt_uint32_t mask,
+ rt_bool_t wait_clear)
+{
+ rt_uint32_t val;
+ rt_tick_t start = rt_tick_get();
+ rt_tick_t timeout = rt_tick_from_millisecond(100);
+
+ while (rt_tick_get() - start < timeout)
+ {
+ val = dwmac_ptp_readl(eth, PTP_TCR);
+ if (wait_clear ? !(val & mask) : (val & mask))
+ {
+ return RT_EOK;
+ }
+ rt_hw_us_delay(10);
+ }
+
+ return -RT_ETIMEOUT;
+}
+
+static void dwmac_ptp_config_hw_tstamping(struct dwmac_eth *eth, rt_uint32_t flags)
+{
+ rt_uint32_t val = dwmac_ptp_readl(eth, PTP_TCR);
+
+ val &= ~(PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR);
+ val |= flags;
+ dwmac_ptp_writel(eth, PTP_TCR, val);
+}
+
+static rt_err_t dwmac_ptp_config_sub_second_inc(struct dwmac_eth *eth,
+ rt_uint32_t ptp_rate, rt_uint32_t *out_inc)
+{
+ rt_uint64_t inc;
+ rt_uint32_t reg;
+
+ inc = (2000000000ULL / ptp_rate);
+ if (inc > PTP_SSIR_SSINC_MAX)
+ {
+ inc = PTP_SSIR_SSINC_MAX;
+ }
+
+ reg = (rt_uint32_t)inc << GMAC4_PTP_SSIR_SSINC_SHIFT;
+ dwmac_ptp_writel(eth, PTP_SSIR, reg);
+
+ if (out_inc)
+ {
+ *out_inc = (rt_uint32_t)inc;
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t dwmac_ptp_config_addend(struct dwmac_eth *eth, rt_uint32_t addend)
+{
+ rt_uint32_t val;
+
+ dwmac_ptp_writel(eth, PTP_TAR, addend);
+
+ val = dwmac_ptp_readl(eth, PTP_TCR);
+ val |= PTP_TCR_TSADDREG;
+ dwmac_ptp_writel(eth, PTP_TCR, val);
+
+ return dwmac_ptp_poll_tcr(eth, PTP_TCR_TSADDREG, RT_TRUE);
+}
+
+static rt_err_t dwmac_ptp_init_systime(struct dwmac_eth *eth,
+ rt_uint32_t sec, rt_uint32_t nsec)
+{
+ rt_uint32_t val;
+
+ dwmac_ptp_writel(eth, PTP_STSUR, sec);
+ dwmac_ptp_writel(eth, PTP_STNSUR, nsec);
+
+ val = dwmac_ptp_readl(eth, PTP_TCR);
+ val |= PTP_TCR_TSINIT;
+ dwmac_ptp_writel(eth, PTP_TCR, val);
+
+ return dwmac_ptp_poll_tcr(eth, PTP_TCR_TSINIT, RT_TRUE);
+}
+
+static rt_err_t dwmac_ptp_adjust_systime(struct dwmac_eth *eth,
+ rt_uint32_t sec, rt_uint32_t nsec, rt_bool_t sub)
+{
+ rt_uint32_t val;
+
+ if (sub)
+ {
+ sec = (rt_uint32_t)(-(rt_int32_t)sec);
+ if (dwmac_ptp_readl(eth, PTP_TCR) & PTP_TCR_TSCTRLSSR)
+ {
+ nsec = PTP_DIGITAL_ROLLOVER_MODE - nsec;
+ }
+ }
+
+ dwmac_ptp_writel(eth, PTP_STSUR, sec);
+ val = (sub ? RT_BIT(PTP_STNSUR_ADDSUB_SHIFT) : 0) | nsec;
+ dwmac_ptp_writel(eth, PTP_STNSUR, val);
+
+ val = dwmac_ptp_readl(eth, PTP_TCR);
+ val |= PTP_TCR_TSUPDT;
+ dwmac_ptp_writel(eth, PTP_TCR, val);
+
+ return dwmac_ptp_poll_tcr(eth, PTP_TCR_TSUPDT, RT_TRUE);
+}
+
+static void dwmac_ptp_get_systime(struct dwmac_eth *eth, rt_uint64_t *systime)
+{
+ rt_uint64_t ns, sec0, sec1;
+
+ sec1 = dwmac_ptp_readl(eth, PTP_STSR);
+ do
+ {
+ sec0 = sec1;
+ ns = dwmac_ptp_readl(eth, PTP_STNSR);
+ sec1 = dwmac_ptp_readl(eth, PTP_STSR);
+ } while (sec0 != sec1);
+
+ if (systime)
+ {
+ *systime = ns + sec1 * NSEC_PER_SEC;
+ }
+}
+
+static rt_uint32_t dwmac_ptp_get_clk_rate(struct dwmac_eth *eth)
+{
+ rt_uint32_t rate = 0;
+
+ if (!rt_is_err_or_null(eth->ptp_clk))
+ {
+ rate = (rt_uint32_t)rt_clk_get_rate(eth->ptp_clk);
+ }
+
+ if (!rate && !rt_is_err_or_null(eth->stmmac_clk))
+ {
+ rate = (rt_uint32_t)rt_clk_get_rate(eth->stmmac_clk);
+ }
+
+ return rate;
+}
+
+static rt_err_t dwmac_ptp_poll_acr(struct dwmac_eth *eth, rt_uint32_t mask)
+{
+ rt_uint32_t val;
+ rt_tick_t start = rt_tick_get();
+ rt_tick_t timeout = rt_tick_from_millisecond(10);
+
+ while (rt_tick_get() - start < timeout)
+ {
+ val = dwmac_ptp_readl(eth, PTP_ACR);
+ if (!(val & mask))
+ {
+ return RT_EOK;
+ }
+ rt_hw_us_delay(10);
+ }
+
+ return -RT_ETIMEOUT;
+}
+
+static void dwmac_ptp_discover_caps(struct dwmac_eth *eth)
+{
+ rt_uint32_t feat1, feat2;
+
+ eth->pps_out_num = 1;
+ eth->aux_snapshot_num = 1;
+
+ feat1 = dwmac_reg_readl(eth, GMAC_HW_FEATURE1);
+ feat2 = dwmac_reg_readl(eth, GMAC_HW_FEATURE2);
+
+ if (feat1)
+ {
+ eth->pps_out_num = (feat1 & GMAC_HW_FEAT_PPSOUTNUM_MASK) >> 24;
+ if (!eth->pps_out_num)
+ {
+ eth->pps_out_num = 1;
+ }
+ }
+
+ if (feat2)
+ {
+ eth->aux_snapshot_num = (feat2 & GMAC_HW_FEAT_AUXSNAPNUM_MASK) >> 28;
+ if (!eth->aux_snapshot_num)
+ {
+ eth->aux_snapshot_num = 1;
+ }
+ }
+}
+
+static rt_err_t dwmac_ptp_fixed_pps(struct dwmac_eth *eth, rt_bool_t on)
+{
+ rt_uint32_t val = dwmac_reg_readl(eth, MAC_PPS_CONTROL);
+
+ if (on)
+ {
+ val |= PPSEN0;
+ }
+ else
+ {
+ val &= ~PPSEN0;
+ val = (val & ~PPSx_MASK(0)) | PPSCMDx(0, 0x5);
+ }
+
+ dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
+
+ return RT_EOK;
+}
+
+static rt_err_t dwmac_ptp_flex_pps(struct dwmac_eth *eth, int index,
+ const struct rt_ptp_request_perout *perout, rt_bool_t on)
+{
+ rt_uint32_t tnsec, val;
+ rt_uint64_t period;
+
+ if (index >= eth->pps_out_num)
+ {
+ return -RT_EINVAL;
+ }
+
+ tnsec = dwmac_reg_readl(eth, MAC_PPSx_TARGET_TIME_NSEC(index));
+ if (tnsec & TRGTBUSY0)
+ {
+ return -RT_EBUSY;
+ }
+
+ val = dwmac_reg_readl(eth, MAC_PPS_CONTROL);
+ val &= ~PPSx_MASK(index);
+
+ if (!on)
+ {
+ val |= PPSCMDx(index, 0x5);
+ val |= PPSEN0;
+ dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
+ return RT_EOK;
+ }
+
+ val |= TRGTMODSELx(index, 0x2);
+ val |= PPSEN0;
+ dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
+
+ dwmac_reg_writel(eth, MAC_PPSx_TARGET_TIME_SEC(index),
+ (rt_uint32_t)perout->start_phase.sec);
+ dwmac_reg_writel(eth, MAC_PPSx_TARGET_TIME_NSEC(index),
+ (rt_uint32_t)perout->start_phase.nsec);
+
+ period = (rt_uint64_t)perout->period.sec * NSEC_PER_SEC + perout->period.nsec;
+ period /= eth->sub_second_inc;
+ if (period <= 1)
+ {
+ return -RT_EINVAL;
+ }
+
+ dwmac_reg_writel(eth, MAC_PPSx_INTERVAL(index), (rt_uint32_t)(period - 1));
+
+ period >>= 1;
+ if (period <= 1)
+ {
+ return -RT_EINVAL;
+ }
+
+ dwmac_reg_writel(eth, MAC_PPSx_WIDTH(index), (rt_uint32_t)(period - 1));
+
+ val = dwmac_reg_readl(eth, MAC_PPS_CONTROL);
+ val |= PPSCMDx(index, 0x2);
+ dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
+
+ return RT_EOK;
+}
+
+static rt_err_t dwmac_ptp_extts(struct dwmac_eth *eth,
+ const struct rt_ptp_request_extts *extts, rt_bool_t on)
+{
+ rt_uint32_t acr;
+
+ if (extts->chan >= eth->aux_snapshot_num)
+ {
+ return -RT_EINVAL;
+ }
+
+ acr = dwmac_ptp_readl(eth, PTP_ACR);
+
+ if (on)
+ {
+ acr |= PTP_ACR_ATSEN(extts->chan);
+ acr |= PTP_ACR_ATSFC;
+ }
+ else
+ {
+ acr &= ~PTP_ACR_ATSEN(extts->chan);
+ }
+
+ dwmac_ptp_writel(eth, PTP_ACR, acr);
+
+ if (on)
+ {
+ return dwmac_ptp_poll_acr(eth, PTP_ACR_ATSFC);
+ }
+
+ return RT_EOK;
+}
+
+rt_err_t dwmac_ptp_hw_init(struct dwmac_eth *eth)
+{
+ rt_uint64_t temp;
+ rt_uint32_t sec_inc = 0;
+
+ eth->ptp_clk_rate = dwmac_ptp_get_clk_rate(eth);
+ if (!eth->ptp_clk_rate)
+ {
+ LOG_W("PTP clock rate unknown");
+ return -RT_EINVAL;
+ }
+
+ dwmac_ptp_discover_caps(eth);
+
+ eth->systime_flags = DWMAC_PTP_HWTS_ACTIVE;
+ dwmac_ptp_config_hw_tstamping(eth, eth->systime_flags);
+
+ dwmac_ptp_config_sub_second_inc(eth, eth->ptp_clk_rate, &sec_inc);
+ eth->sub_second_inc = sec_inc;
+
+ temp = NSEC_PER_SEC / sec_inc;
+ temp <<= 32;
+ eth->default_addend = (rt_uint32_t)(temp / eth->ptp_clk_rate);
+
+ if (dwmac_ptp_config_addend(eth, eth->default_addend))
+ {
+ return -RT_ETIMEOUT;
+ }
+
+ return dwmac_ptp_init_systime(eth, 0, 0);
+}
+
+static rt_err_t dwmac_ptp_adjfreq(struct rt_ptp_clock *ptp, rt_base_t freq)
+{
+ rt_int64_t adj;
+ rt_uint32_t addend;
+ struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
+
+ adj = (rt_int64_t)eth->default_addend * (rt_int64_t)freq / NSEC_PER_SEC;
+ addend = eth->default_addend + (rt_uint32_t)adj;
+
+ return dwmac_ptp_config_addend(eth, addend);
+}
+
+static rt_err_t dwmac_ptp_adjtime(struct rt_ptp_clock *ptp, rt_int64_t delta)
+{
+ rt_uint32_t sec, nsec;
+ rt_bool_t sub = RT_FALSE;
+ struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
+
+ if (delta < 0)
+ {
+ sub = RT_TRUE;
+ delta = -delta;
+ }
+
+ sec = (rt_uint32_t)(delta / NSEC_PER_SEC);
+ nsec = (rt_uint32_t)(delta % NSEC_PER_SEC);
+
+ return dwmac_ptp_adjust_systime(eth, sec, nsec, sub);
+}
+
+static rt_err_t dwmac_ptp_gettime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts)
+{
+ rt_uint64_t ns = 0;
+ struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
+
+ dwmac_ptp_get_systime(eth, &ns);
+ ts->sec = (rt_int64_t)(ns / NSEC_PER_SEC);
+ ts->nsec = (rt_int32_t)(ns % NSEC_PER_SEC);
+
+ return RT_EOK;
+}
+
+static rt_err_t dwmac_ptp_settime(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts)
+{
+ struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
+
+ if (!ts || ts->nsec < 0 || ts->nsec >= NSEC_PER_SEC)
+ {
+ return -RT_EINVAL;
+ }
+
+ return dwmac_ptp_init_systime(eth, (rt_uint32_t)ts->sec, (rt_uint32_t)ts->nsec);
+}
+
+static rt_err_t dwmac_ptp_enable(struct rt_ptp_clock *ptp,
+ struct rt_ptp_clock_request *req, rt_bool_t on)
+{
+ struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
+
+ if (!req)
+ {
+ return -RT_EINVAL;
+ }
+
+ switch (req->type)
+ {
+ case PTP_CLK_REQ_PPS:
+ return dwmac_ptp_fixed_pps(eth, on);
+
+ case PTP_CLK_REQ_EXTTS:
+ return dwmac_ptp_extts(eth, &req->extts, on);
+
+ case PTP_CLK_REQ_PEROUT:
+ return dwmac_ptp_flex_pps(eth, (int)req->perout.chan, &req->perout, on);
+
+ default:
+ return -RT_ENOSYS;
+ }
+}
+
+static const struct rt_ptp_ops dwmac_ptp_ops =
+{
+ .adjfreq = dwmac_ptp_adjfreq,
+ .adjtime = dwmac_ptp_adjtime,
+ .gettime = dwmac_ptp_gettime,
+ .settime = dwmac_ptp_settime,
+ .enable = dwmac_ptp_enable,
+};
+
+rt_err_t dwmac_ptp_register(struct dwmac_eth *eth)
+{
+ rt_err_t err;
+ struct rt_ptp_clock *ptp = &eth->ptp_parent;
+
+ if ((err = dwmac_ptp_hw_init(eth)))
+ {
+ return err;
+ }
+
+ ptp->ops = &dwmac_ptp_ops;
+ ptp->max_freq = (rt_int32_t)eth->ptp_clk_rate;
+ ptp->pps = 1;
+ ptp->extts_nr = eth->aux_snapshot_num;
+ ptp->perout_nr = eth->pps_out_num;
+
+ if ((err = rt_ptp_clock_register(ptp)))
+ {
+ ptp->ops = RT_NULL;
+ return err;
+ }
+
+ LOG_D("PTP clock registered, rate=%u Hz addend=0x%08x ssinc=%u pps=%u extts=%u",
+ eth->ptp_clk_rate, eth->default_addend, eth->sub_second_inc,
+ eth->pps_out_num, eth->aux_snapshot_num);
+
+ return RT_EOK;
+}
+
+void dwmac_ptp_unregister(struct dwmac_eth *eth)
+{
+ struct rt_ptp_clock *ptp = &eth->ptp_parent;
+
+ if (!ptp->ops)
+ {
+ return;
+ }
+
+ rt_ptp_clock_unregister(ptp);
+ ptp->ops = RT_NULL;
+}
diff --git a/components/drivers/ethernet/synopsys/dwmac_ptp.h b/components/drivers/ethernet/synopsys/dwmac_ptp.h
new file mode 100644
index 0000000000..517a6c4622
--- /dev/null
+++ b/components/drivers/ethernet/synopsys/dwmac_ptp.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-11-26 GuEe-GUI first version
+ */
+
+#ifndef __DWMAC_PTP_H__
+#define __DWMAC_PTP_H__
+
+#include "dwmac.h"
+
+#define DWMAC_PTP_OFFSET 0xb00
+
+#define PTP_TCR 0x00
+#define PTP_SSIR 0x04
+#define PTP_STSR 0x08
+#define PTP_STNSR 0x0c
+#define PTP_STSUR 0x10
+#define PTP_STNSUR 0x14
+#define PTP_TAR 0x18
+#define PTP_ACR 0x40
+
+#define PTP_ACR_ATSFC RT_BIT(0)
+#define PTP_ACR_ATSEN0 RT_BIT(4)
+#define PTP_ACR_ATSEN(i) RT_BIT(4 + (i))
+
+#define MAC_PPS_CONTROL 0xb70
+#define MAC_PPSx_TARGET_TIME_SEC(x) (0xb80 + ((x) * 0x10))
+#define MAC_PPSx_TARGET_TIME_NSEC(x) (0xb84 + ((x) * 0x10))
+#define MAC_PPSx_INTERVAL(x) (0xb88 + ((x) * 0x10))
+#define MAC_PPSx_WIDTH(x) (0xb8c + ((x) * 0x10))
+
+#define PPSEN0 RT_BIT(4)
+#define TRGTBUSY0 RT_BIT(31)
+
+#define PPS_MAXIDX(x) ((((x) + 1) * 8) - 1)
+#define PPS_MINIDX(x) ((x) * 8)
+#define PPSx_MASK(x) RT_GENMASK(PPS_MAXIDX(x), PPS_MINIDX(x))
+#define PPSCMDx(x, val) (RT_GENMASK(PPS_MINIDX(x) + 3, PPS_MINIDX(x)) & \
+ ((val) << PPS_MINIDX(x)))
+#define TRGTMODSELx(x, val) (RT_GENMASK(PPS_MAXIDX(x) - 1, PPS_MAXIDX(x) - 2) & \
+ ((val) << (PPS_MAXIDX(x) - 2)))
+
+#define dwmac_reg_readl(eth, reg) HWREG32((rt_uint8_t *)(eth)->mac_base + (reg))
+#define dwmac_reg_writel(eth, reg, val) HWREG32((rt_uint8_t *)(eth)->mac_base + (reg)) = (val)
+
+#define GMAC_HW_FEATURE1 0x120
+#define GMAC_HW_FEATURE2 0x124
+#define GMAC_HW_FEAT_PPSOUTNUM_MASK RT_GENMASK(26, 24)
+#define GMAC_HW_FEAT_AUXSNAPNUM_MASK RT_GENMASK(30, 28)
+
+#define PTP_TCR_TSENA RT_BIT(0)
+#define PTP_TCR_TSCFUPDT RT_BIT(1)
+#define PTP_TCR_TSINIT RT_BIT(2)
+#define PTP_TCR_TSUPDT RT_BIT(3)
+#define PTP_TCR_TSADDREG RT_BIT(5)
+#define PTP_TCR_TSCTRLSSR RT_BIT(9)
+
+#define PTP_STNSUR_ADDSUB_SHIFT 31
+#define PTP_DIGITAL_ROLLOVER_MODE 0x3b9aca00
+#define PTP_SSIR_SSINC_MAX 0xff
+#define GMAC4_PTP_SSIR_SSINC_SHIFT 16
+
+#define DWMAC_PTP_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR)
+
+#define dwmac_ptp_readl(eth, reg) HWREG32((rt_uint8_t *)(eth)->mac_base + DWMAC_PTP_OFFSET + (reg))
+#define dwmac_ptp_writel(eth, reg, val) HWREG32((rt_uint8_t *)(eth)->mac_base + DWMAC_PTP_OFFSET + (reg)) = (val)
+
+#ifdef RT_ETHERNET_DWMAC_PTP
+#define raw_to_dwmac_ptp(raw) rt_container_of(raw, struct dwmac_eth, ptp_parent)
+
+rt_err_t dwmac_ptp_hw_init(struct dwmac_eth *eth);
+rt_err_t dwmac_ptp_register(struct dwmac_eth *eth);
+void dwmac_ptp_unregister(struct dwmac_eth *eth);
+#endif /* RT_ETHERNET_DWMAC_PTP */
+
+#endif /* __DWMAC_PTP_H__ */