diff --git a/components/drivers/ethernet/Kconfig b/components/drivers/ethernet/Kconfig new file mode 100644 index 00000000000..1f0f880a1d0 --- /dev/null +++ b/components/drivers/ethernet/Kconfig @@ -0,0 +1,13 @@ +menuconfig RT_USING_ETHERNET + bool "Using Ethernet device drivers" + depends on RT_USING_DM + depends on RT_USING_SAL + depends on RT_USING_NETDEV + depends on RT_USING_LWIP + 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/SConscript b/components/drivers/ethernet/SConscript new file mode 100644 index 00000000000..f292627b5f2 --- /dev/null +++ b/components/drivers/ethernet/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_ETHERNET']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['ethernet_dm.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + group = group + SConscript(os.path.join(d, 'SConscript')) + +Return('group') diff --git a/components/drivers/ethernet/cadence/Kconfig b/components/drivers/ethernet/cadence/Kconfig new file mode 100644 index 00000000000..c553b848de5 --- /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 00000000000..7050636833a --- /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 00000000000..fcac081dd23 --- /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 + +#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 *)ð->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, ð->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(ð->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(ð->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(ð->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(ð->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(ð->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", ®)) + { + 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(ð->parent, RT_TRUE); +} + +static void macb_phy_link_down(struct macb_eth *eth) +{ + if (eth->phy) + { + eth->phy->link = 0; + } + + eth_device_linkchange(ð->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(ð->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(ð->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(ð->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(ð->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(ð->mdio_lock, "macb_mdio", RT_IPC_FLAG_PRIO); + rt_sem_init(ð->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, + ð->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(ð->tx_sem); + rt_mutex_detach(ð->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(ð->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(ð->parent, rt_dm_dev_get_name(ð->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(ð->phy_retry_work, macb_phy_retry_work, eth); + rt_work_submit(ð->phy_retry_work, + rt_tick_from_millisecond(MACB_PHY_RETRY_INTERVAL_MS)); + eth_device_linkchange(ð->parent, RT_FALSE); + } + else if (eth->phy && eth->phy->link) + { + eth_device_linkchange(ð->parent, RT_TRUE); + } + else + { + eth_device_linkchange(ð->parent, RT_FALSE); + } +#else + if (macb_start(eth) == RT_EOK && eth->phy && eth->phy->link) + { + eth_device_linkchange(ð->parent, RT_TRUE); + } + else + { + eth_device_linkchange(ð->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(ð->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(ð->parent, RT_FALSE); + eth_device_deinit(ð->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(ð->tx_sem); + rt_mutex_detach(ð->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(ð->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 00000000000..46013d0e42d --- /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 +#include +#include + +#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 00000000000..2c61be85324 --- /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 + +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(ð->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 00000000000..f8080c33655 --- /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(ð->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(ð->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(ð->mdio_lock); + return -RT_EIO; + } + + rt_mutex_release(ð->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(ð->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(ð->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 00000000000..74f0df35d36 --- /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 + +#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 = ð->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 = ð->ptp_parent; + + if (!ptp->ops) + { + return; + } + + rt_ptp_clock_unregister(ptp); + ptp->ops = RT_NULL; +} diff --git a/components/drivers/ethernet/ethernet_dm.c b/components/drivers/ethernet/ethernet_dm.c new file mode 100644 index 00000000000..77560d71416 --- /dev/null +++ b/components/drivers/ethernet/ethernet_dm.c @@ -0,0 +1,47 @@ +/* + * 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 + +#include "ethernet_dm.h" + +static rt_uint32_t _qrand32(rt_uint32_t seed) +{ + rt_uint32_t ret; + static rt_uint32_t next = 1; + + next = next * 1103515245 + 12345; + + ret = (rt_uint32_t)(next / 65536) % 32768; + + if (seed) + { + ret *= seed; + } + + return ret; +} + +void ethernet_random_addr(struct eth_device *eth, rt_uint8_t *addr) +{ + rt_tick_t tick = rt_tick_get(); + rt_uint32_t rand = (_qrand32(tick) << 16) | _qrand32(tick); + const char *name = rt_dm_dev_get_name(ð->parent); + + addr[5] = ((rt_uint8_t *)&rand)[3]; + addr[4] = ((rt_uint8_t *)&rand)[2]; + addr[3] = name[0] & 0xf; + addr[2] = name[1] & 0xf; + addr[1] = ((rt_uint8_t *)&rand)[1]; + addr[0] = ((rt_uint8_t *)&rand)[0]; + + addr[0] &= 0xfe; /* Clear multicast bit */ + addr[0] |= 0x02; /* Set local assignment bit (IEEE802) */ +} diff --git a/components/drivers/ethernet/ethernet_dm.h b/components/drivers/ethernet/ethernet_dm.h new file mode 100644 index 00000000000..a846b065d63 --- /dev/null +++ b/components/drivers/ethernet/ethernet_dm.h @@ -0,0 +1,20 @@ +/* + * 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 __ETHERNET_DM_H__ +#define __ETHERNET_DM_H__ + +#include + +#include + +void ethernet_random_addr(struct eth_device *eth, rt_uint8_t *addr); + +#endif /* __ETHERNET_DM_H__ */ diff --git a/components/drivers/ethernet/synopsys/Kconfig b/components/drivers/ethernet/synopsys/Kconfig new file mode 100644 index 00000000000..649cdf4bbdd --- /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 00000000000..af7d646daf1 --- /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 00000000000..d2123c42832 --- /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(ð->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 00000000000..d2845b15c82 --- /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 00000000000..68a98e691fd --- /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 + +#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", ð->axi.wr_osr_lmt)) + { + eth->axi.wr_osr_lmt = 4; + } + + if (rt_ofw_prop_read_u32(np, "snps,rd_osr_lmt", ð->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", ð->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", ð->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 *)ð->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 *)ð->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 *)ð->dma_txpbl); + rt_ofw_prop_read_u32(np, "snps,rxpbl", (rt_uint32_t *)ð->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, ð->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", ®)) + { + 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 = ð->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(ð->parent, RT_TRUE); + return; + } + + eth_device_linkchange(ð->parent, RT_FALSE); + rt_work_submit(ð->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 = ð->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 = ð->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(ð->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(ð->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(ð->parent, rt_dm_dev_get_name(ð->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(ð->phy_retry_work, dwmac_phy_retry_work, eth); + eth->phy_work_inited = RT_TRUE; + eth_device_linkchange(ð->parent, RT_FALSE); + rt_work_submit(ð->phy_retry_work, + rt_tick_from_millisecond(DWMAC_PHY_RETRY_INTERVAL_MS)); + } + else if (eth->phy && eth->phy->link) + { + eth_device_linkchange(ð->parent, RT_TRUE); + } + else + { + eth_device_linkchange(ð->parent, RT_FALSE); + } +#else + if (dwmac_start(eth) == RT_EOK && eth->phy && eth->phy->link) + { + eth_device_linkchange(ð->parent, RT_TRUE); + } + else + { + eth_device_linkchange(ð->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(ð->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(ð->parent, RT_FALSE); + eth_device_deinit(ð->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 00000000000..b6e73f351e1 --- /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 +#include +#include + +#include +#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 00000000000..45b62efb7d6 --- /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 + +#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 = ð->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 = ð->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 00000000000..517a6c4622c --- /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__ */ diff --git a/components/drivers/phy/Kconfig b/components/drivers/phy/Kconfig index cf0560aed58..805ab602d12 100644 --- a/components/drivers/phy/Kconfig +++ b/components/drivers/phy/Kconfig @@ -8,5 +8,8 @@ menuconfig RT_USING_PHY_V2 default n if RT_USING_DM && RT_USING_PHY_V2 + config RT_PHY_V2_REALTEK + bool "Realtek" + osource "$(SOC_DM_PHY_DIR)/Kconfig" endif diff --git a/components/drivers/phy/SConscript b/components/drivers/phy/SConscript index ba227c72d46..2bf716b8a7e 100644 --- a/components/drivers/phy/SConscript +++ b/components/drivers/phy/SConscript @@ -1,17 +1,23 @@ from building import * -cwd = GetCurrentDir() +group = [] + +if not GetDepend(['RT_USING_PHY']) and not GetDepend(['RT_USING_PHY_V2']): + Return('group') + +cwd = GetCurrentDir() CPPPATH = [cwd, cwd + '/../include'] -src = Glob('*.c') -if GetDepend('RT_USING_OFW') == False: - SrcRemove(src, ['ofw.c']) -if GetDepend('RT_USING_PHY_V2') == False: - SrcRemove(src, ['general.c','mdio.c','ofw.c']) +src = ['phy.c'] + +if GetDepend(['RT_USING_PHY_V2']): + src += ['general.c', 'mdio.c'] + + if GetDepend(['RT_USING_OFW']): + src += ['ofw.c'] -if GetDepend('RT_USING_PHY_V2') == False: - if GetDepend('RT_USING_PHY') == False: - SrcRemove(src, ['phy.c']) + if GetDepend(['RT_PHY_V2_REALTEK']): + src += ['phy-realtek.c'] group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) diff --git a/components/drivers/phy/general.c b/components/drivers/phy/general.c index a75d76468a1..23aca4abe66 100644 --- a/components/drivers/phy/general.c +++ b/components/drivers/phy/general.c @@ -120,6 +120,7 @@ int rt_genphy_config_aneg(struct rt_phy_device *phydev) int result; int err; int ctl = RT_BMCR_ANRESTART; + if (phydev->autoneg != AUTONEG_ENABLE) { phydev->pause = 0; @@ -161,44 +162,41 @@ int rt_genphy_config_aneg(struct rt_phy_device *phydev) int rt_genphy_update_link(struct rt_phy_device *phydev) { - unsigned int mii_reg; - - mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + int bmcr, mii_reg; - if (phydev->link && mii_reg & RT_BMSR_LSTATUS) - return 0; + bmcr = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR); + if (bmcr < 0) + return bmcr; - if ((phydev->autoneg == AUTONEG_ENABLE) && - !(mii_reg & RT_BMSR_ANEGCOMPLETE)) - { - int i = 0; - LOG_I("Waiting for PHY auto negotiation to complete"); - while (!(mii_reg & RT_BMSR_ANEGCOMPLETE)) + if (bmcr & RT_BMCR_ANRESTART) + { + mii_reg = 0; + } + else + { + /* + * BMSR link status is latched low. Follow Linux genphy_update_link() + * and read it twice when the link was previously down. + */ + if (!phydev->link) { - - if (i > (RT_PHY_ANEG_TIMEOUT)) - { - LOG_E(" TIMEOUT!"); - phydev->link = 0; - return -ETIMEDOUT; - } - mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); - - rt_thread_delay(100); - i += 100; + if (mii_reg < 0) + return mii_reg; + if (mii_reg & RT_BMSR_LSTATUS) + goto _done; } - LOG_D(" Done"); - phydev->link = 1; - } else { - mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); - if (mii_reg & RT_BMSR_LSTATUS) - phydev->link = 1; - else - phydev->link = 0; + mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + if (mii_reg < 0) + return mii_reg; } +_done: + phydev->link = (mii_reg & RT_BMSR_LSTATUS) ? 1 : 0; + if (phydev->autoneg == AUTONEG_ENABLE && !(mii_reg & RT_BMSR_ANEGCOMPLETE)) + phydev->link = 0; + return 0; } @@ -343,8 +341,10 @@ int rt_genphy_startup(struct rt_phy_device *phydev) int ret; ret = rt_genphy_update_link(phydev); - if (ret) + if (ret < 0) return ret; - return rt_genphy_parse_link(phydev); + ret = rt_genphy_parse_link(phydev); + + return ret; } diff --git a/components/drivers/phy/mdio.c b/components/drivers/phy/mdio.c index 7f9be35478d..0c2706c16da 100644 --- a/components/drivers/phy/mdio.c +++ b/components/drivers/phy/mdio.c @@ -35,7 +35,8 @@ struct mii_bus *rt_mdio_get_bus_by_name(const char *busname) struct mii_bus *rt_mdio_alloc(void) { struct mii_bus *mii; - mii = rt_malloc(sizeof(struct mii_bus)); + + mii = rt_calloc(1, sizeof(*mii)); if (!mii) return RT_NULL; diff --git a/components/drivers/phy/ofw.c b/components/drivers/phy/ofw.c index 933255e8222..0eaaf9d7276 100644 --- a/components/drivers/phy/ofw.c +++ b/components/drivers/phy/ofw.c @@ -13,6 +13,7 @@ #define DBG_TAG "rtdm.phy" #define DBG_LVL DBG_LOG #include +#include "mdio.h" #include "ofw.h" static const char* const rt_phy_modes[] = @@ -108,22 +109,45 @@ rt_err_t rt_ofw_get_mac_addr(struct rt_ofw_node *np, rt_uint8_t *addr) rt_err_t rt_ofw_get_phyid(struct rt_ofw_node *np,rt_uint32_t *id) { + struct rt_ofw_prop *prop; const char *phy_id; unsigned int upper, lower; - int ret; - ret = rt_ofw_prop_read_string(np,"compatible",&phy_id); - if (ret) - return ret; + rt_ofw_foreach_prop_string(np, "compatible", prop, phy_id) + { + if (rt_sscanf(phy_id, "ethernet-phy-id%4x.%4x", &upper, &lower) == 2) + { + *id = ((upper & 0xffff) << 16) | (lower & 0xffff); + return RT_EOK; + } + } - ret = rt_sscanf(phy_id,"ethernet-phy-id%4x.%4x",&upper, &lower); - if(ret != 2) - return -RT_ERROR; + return -RT_ERROR; +} - *id = ((upper & 0xffff) << 16) | (lower & 0xffff); - return RT_EOK; +static int ofw_read_phy_id(struct mii_bus *bus, int addr, rt_uint32_t *id) +{ + int reg; + + reg = bus->read(bus, addr, RT_MDIO_DEVAD_NONE, RT_MII_PHYSID1); + if (reg < 0) + { + return -RT_EIO; + } + + *id = (reg & 0xffff) << 16; + reg = bus->read(bus, addr, RT_MDIO_DEVAD_NONE, RT_MII_PHYSID2); + if (reg < 0) + { + return -RT_EIO; + } + + *id |= (reg & 0xffff); + + return RT_EOK; } + struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus,struct rt_ofw_node *np,int phyaddr) { struct rt_phy_device *dev = RT_NULL; @@ -138,19 +162,57 @@ struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus,struct rt_ofw_node * return RT_NULL; } - ret = rt_ofw_get_phyid(np, &id); + ret = rt_ofw_get_phyid(phy_node, &id); if (ret) { - LOG_D("Failed to read eth PHY id, err: %d\n", ret); - return RT_NULL; + rt_uint32_t reg = 0; + int addr; + + if (rt_ofw_prop_read_u32(phy_node, "reg", ®)) + { + rt_ofw_node_put(phy_node); + return RT_NULL; + } + + addr = (phyaddr >= 0) ? phyaddr : (int)reg; + id = 0xffffffff; + + if (!ofw_read_phy_id(bus, addr, &id) && + (id & 0x1fffffff) != 0x1fffffff) + { + LOG_D("PHY id from MDIO: 0x%08x @ addr %d", id, addr); + } + else + { + id = 0xffffffff; + LOG_D("MDIO PHY id read failed @ addr %d, use generic PHY", addr); + } + + dev = rt_phy_device_create(bus, addr, id, RT_FALSE); + if (dev) + { + dev->node = phy_node; + } + else + { + rt_ofw_node_put(phy_node); + } + + return dev; } - LOG_D("Found a PHY id: 0x%x\n", id); + LOG_D("Found a PHY id: 0x%x", id); dev = rt_phy_device_create(bus, phyaddr, id, RT_FALSE); - if(dev) + if (dev) + { dev->node = phy_node; + } + else + { + rt_ofw_node_put(phy_node); + } return dev; } diff --git a/components/drivers/phy/phy-realtek.c b/components/drivers/phy/phy-realtek.c new file mode 100644 index 00000000000..01d081215fe --- /dev/null +++ b/components/drivers/phy/phy-realtek.c @@ -0,0 +1,274 @@ +/* + * 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 +#include + +#define DBG_TAG "rtdm.phy.rtl" +#define DBG_LVL DBG_INFO +#include + +#define RTL8211F_PHY_ID 0x001cc916 +#define RTL8211FVD_PHY_ID 0x001cc878 +#define RTL8211F_PHY_ID_MASK 0x001fffff + +#define RTL8211F_PAGE_SELECT 0x1f + +#define RTL8211F_PHYCR_PAGE 0x0a43 +#define RTL8211F_PHYCR1 0x18 +#define RTL8211F_PHYCR2 0x19 + +#define RTL8211F_RGMII_PAGE 0x0d08 +#define RTL8211F_TXCR 0x11 +#define RTL8211F_RXCR 0x15 +#define RTL8211F_TX_DELAY RT_BIT(8) +#define RTL8211F_RX_DELAY RT_BIT(3) + +#define RTL8211F_ALDPS_PLL_OFF RT_BIT(1) +#define RTL8211F_ALDPS_ENABLE RT_BIT(2) +#define RTL8211F_ALDPS_XTAL_OFF RT_BIT(12) +#define RTL8211F_CLKOUT_EN RT_BIT(0) + +struct rtl821x_priv +{ + rt_uint16_t phycr1; + rt_uint16_t phycr2; + rt_bool_t has_phycr2; +}; + +static int rtl8211f_read_page(struct rt_phy_device *phydev) +{ + return rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RTL8211F_PAGE_SELECT); +} + +static int rtl8211f_write_page(struct rt_phy_device *phydev, int page) +{ + return rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RTL8211F_PAGE_SELECT, page); +} + +static int rtl8211f_read_paged(struct rt_phy_device *phydev, int page, int reg) +{ + int oldpage, val; + + oldpage = rtl8211f_read_page(phydev); + if (oldpage < 0) + return oldpage; + + val = rtl8211f_write_page(phydev, page); + if (val < 0) + return val; + + val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, reg); + rtl8211f_write_page(phydev, oldpage); + + return val; +} + +static int rtl8211f_modify_paged(struct rt_phy_device *phydev, int page, + int reg, rt_uint16_t mask, rt_uint16_t set) +{ + int oldpage, val, ret; + + oldpage = rtl8211f_read_page(phydev); + if (oldpage < 0) + return oldpage; + + ret = rtl8211f_write_page(phydev, page); + if (ret < 0) + return ret; + + val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, reg); + if (val < 0) + { + rtl8211f_write_page(phydev, oldpage); + return val; + } + + val &= ~mask; + val |= set & mask; + + ret = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, reg, val); + rtl8211f_write_page(phydev, oldpage); + + return ret; +} + +static int rtl8211f_config_delay(struct rt_phy_device *phydev) +{ + rt_uint16_t tx_delay = 0; + rt_uint16_t rx_delay = 0; + int ret; + + switch (phydev->interface) + { + case RT_PHY_INTERFACE_MODE_RGMII: + break; + case RT_PHY_INTERFACE_MODE_RGMII_RXID: + rx_delay = RTL8211F_RX_DELAY; + break; + case RT_PHY_INTERFACE_MODE_RGMII_TXID: + tx_delay = RTL8211F_TX_DELAY; + break; + case RT_PHY_INTERFACE_MODE_RGMII_ID: + tx_delay = RTL8211F_TX_DELAY; + rx_delay = RTL8211F_RX_DELAY; + break; + default: + return 0; + } + + ret = rtl8211f_modify_paged(phydev, RTL8211F_RGMII_PAGE, RTL8211F_TXCR, + RTL8211F_TX_DELAY, tx_delay); + if (ret < 0) + return ret; + + return rtl8211f_modify_paged(phydev, RTL8211F_RGMII_PAGE, RTL8211F_RXCR, + RTL8211F_RX_DELAY, rx_delay); +} + +static rt_bool_t rtl8211f_ofw_bool(struct rt_phy_device *phydev, + const char *propname) +{ +#ifdef RT_USING_OFW + if (phydev->node) + return rt_ofw_prop_read_bool(phydev->node, propname); +#else + RT_UNUSED(phydev); + RT_UNUSED(propname); +#endif + + return RT_FALSE; +} + +static int rtl8211f_probe(struct rt_phy_device *phydev) +{ + struct rtl821x_priv *priv; + int val; + + priv = rt_calloc(1, sizeof(*priv)); + if (!priv) + return -RT_ENOMEM; + + val = rtl8211f_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1); + if (val < 0) + { + rt_free(priv); + return val; + } + + priv->phycr1 = val & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | + RTL8211F_ALDPS_XTAL_OFF); + if (rtl8211f_ofw_bool(phydev, "realtek,aldps-enable")) + { + priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | + RTL8211F_ALDPS_XTAL_OFF; + } + + priv->has_phycr2 = phydev->phy_id != RTL8211FVD_PHY_ID; + if (priv->has_phycr2) + { + val = rtl8211f_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2); + if (val < 0) + { + rt_free(priv); + return val; + } + + priv->phycr2 = val & RTL8211F_CLKOUT_EN; + if (rtl8211f_ofw_bool(phydev, "realtek,clkout-disable")) + priv->phycr2 &= ~RTL8211F_CLKOUT_EN; + } + + phydev->priv = priv; + + return 0; +} + +static int rtl8211f_config_init(struct rt_phy_device *phydev) +{ + struct rtl821x_priv *priv = phydev->priv; + int ret; + + if (!priv) + return -RT_EINVAL; + + ret = rtl8211f_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, + RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | + RTL8211F_ALDPS_XTAL_OFF, priv->phycr1); + if (ret < 0) + return ret; + + ret = rtl8211f_config_delay(phydev); + if (ret < 0) + return ret; + + if (priv->has_phycr2) + { + ret = rtl8211f_modify_paged(phydev, RTL8211F_PHYCR_PAGE, + RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, priv->phycr2); + if (ret < 0) + return ret; + + return rt_phy_reset(phydev); + } + + return 0; +} + +static int rtl8211f_config(struct rt_phy_device *phydev) +{ + int ret; + + ret = rtl8211f_config_init(phydev); + if (ret < 0) + return ret; + + return rt_genphy_config(phydev); +} + +static int rtl8211f_startup(struct rt_phy_device *phydev) +{ + int ret; + + ret = rt_genphy_startup(phydev); + if (ret < 0 || !phydev->link) + { + if (ret < 0) + return ret; + + return -ETIMEDOUT; + } + + return 0; +} + +static int rtl8211f_shutdown(struct rt_phy_device *phydev) +{ + if (phydev->priv) + { + rt_free(phydev->priv); + phydev->priv = RT_NULL; + } + + return 0; +} + +static struct rt_phy_driver rtl8211f_driver = +{ + .uid = RTL8211F_PHY_ID, + .mask = RTL8211F_PHY_ID_MASK, + .name = "RTL8211F", + .features = RT_PHY_GBIT_FEATURES, + .probe = rtl8211f_probe, + .config = rtl8211f_config, + .startup = rtl8211f_startup, + .shutdown = rtl8211f_shutdown, +}; +RT_PHY_DRIVER_REGISTER(rtl8211f_driver); diff --git a/components/drivers/phy/phy.c b/components/drivers/phy/phy.c index 47c35c21030..232f09a8b13 100644 --- a/components/drivers/phy/phy.c +++ b/components/drivers/phy/phy.c @@ -379,6 +379,7 @@ static struct rt_phy_device *create_phy_by_mask(struct mii_bus *bus, unsigned in return rt_phy_device_create(bus, addr, id, is_c45); } + phy_mask &= ~(1U << addr); } return RT_NULL; } @@ -512,7 +513,11 @@ rt_err_t rt_phy_device_register(struct rt_phy_device *pdev) return err; } if(!pdev->drv) + { pdev->drv = &genphy; + pdev->advertising = pdev->drv->features; + pdev->supported = pdev->drv->features; + } return RT_EOK; }