[dm][ethernet] add MACB and DWMAC ethernet support

They support PTP, too.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI 2026-07-22 22:09:03 +08:00 committed by Rbb666
parent 84da9b9393
commit c3e1dd7946
16 changed files with 5383 additions and 0 deletions

View File

@ -7,5 +7,7 @@ menuconfig RT_USING_ETHERNET
default n
if RT_USING_ETHERNET
rsource "cadence/Kconfig"
rsource "synopsys/Kconfig"
osource "$(SOC_DM_ETHERNET_DIR)/Kconfig"
endif

View File

@ -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

View File

@ -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')

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,356 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-25 GuEe-GUI first version
*/
#ifndef __MACB_H__
#define __MACB_H__
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include "../ethernet_dm.h"
/* Queue 0 (legacy offsets) */
#define GEM_NCR 0x0000
#define GEM_NCFGR 0x0004
#define GEM_NSR 0x0008
#define GEM_USRIO 0x000c
#define GEM_DMACFG 0x0010
#define GEM_TSR 0x0014
#define GEM_TSR_TGO RT_BIT(3)
#define GEM_TSR_COMP RT_BIT(5)
#define GEM_RSR 0x0020
#define GEM_ISR 0x0024
#define GEM_IER 0x0028
#define GEM_IDR 0x002c
#define GEM_IMR 0x0030
#define GEM_ISR0 0x0400 /* GEM queue 0 ISR */
#define GEM_IER0 0x0600 /* GEM queue 0 IER */
#define GEM_IDR0 0x0620 /* GEM queue 0 IDR */
#define GEM_IMR0 0x0640 /* GEM queue 0 IMR */
#define GEM_MAN 0x0034 /* PHY maintenance (was wrongly 0x340) */
/*
* GEM queue 0 uses legacy MACB ring/IRQ registers; GEM_*Q1+ live at 0x440/0x480.
* Linux: queue0 -> MACB_TBQP/RBQP/ISR/IER; queue1+ -> GEM_TBQP(n)/GEM_RBQP(n).
*/
#define GEM_RBQP 0x0018
#define GEM_TBQP 0x001c
#define GEM_RBQP1 0x0480
#define GEM_TBQP1 0x0440
#define MACB_MAX_QUEUES 8
#define GEM_QUEUE_TBQP(q) (0x0440 + ((q) << 2))
#define GEM_QUEUE_RBQP(q) (0x0480 + ((q) << 2))
#define GEM_JML 0x0048
#define GEM_PBUFRXCUT 0x0044
#define GEM_INTMOD 0x005c
#define GEM_INTMOD_TX_MOD_SHIFT 16
#define GEM_INTMOD_RX_MOD_SHIFT 0
#define GEM_SA1B 0x0088
#define GEM_SA1T 0x008c
#define GEM_DCFG1 0x0280
#define GEM_DCFG1_IRQCOR_SHIFT 23
#define GEM_DCFG5 0x0290
#define GEM_DCFG6 0x0294
#define GEM_DCFG6_DAW64_SHIFT 23
#define GEM_RBQPH 0x04d4
#define GEM_TBQPH 0x04c8
#define GEM_AMP 0x0054
#define GEM_AMP_AR2R_MAX_PIPE_SHIFT 0
#define GEM_AMP_AW2W_MAX_PIPE_SHIFT 8
#define GEM_AMP_AW2B_FILL_SHIFT 16
#define GEM_USRIO_RGMII RT_BIT(0)
/* TSU / 1588 */
#define GEM_TISUBN 0x01bc
#define GEM_TSH 0x01c0
#define GEM_TSL 0x01d0
#define GEM_TN 0x01d4
#define GEM_TA 0x01d8
#define GEM_TI 0x01dc
/* NCR */
#define GEM_NCR_LLB RT_BIT(1)
#define GEM_NCR_RE RT_BIT(2)
#define GEM_NCR_TE RT_BIT(3)
#define GEM_NCR_MPE RT_BIT(4)
#define GEM_NCR_CLRSTAT RT_BIT(5)
#define GEM_NCR_TSTART RT_BIT(9)
/*
* NCFGR: GEM uses same SPD/FD as MACB (bit0/1); gigabit is GEM_GBE @ bit10,
* not bit31.
*/
#define GEM_NCFGR_SPD RT_BIT(0) /* 100 Mbps */
#define GEM_NCFGR_FD RT_BIT(1) /* Full duplex */
#define GEM_NCFGR_GBE RT_BIT(10) /* 1000 Mbps (GEM) */
#define GEM_NCFGR_BIG RT_BIT(8) /* Receive 1536-byte frames */
#define GEM_NCFGR_DRFCS RT_BIT(17) /* Discard RX FCS */
/* DBW in NCFGR bits [22:21] */
#define GEM_NCFGR_DBW_SHIFT 21
/* MDC clock divider in NCFGR bits [20:18] */
#define GEM_NCFGR_CLK_SHIFT 18
/* NSR */
#define GEM_NSR_IDLE RT_BIT(2)
/* RSR */
#define GEM_RSR_BNA RT_BIT(0)
#define GEM_RSR_REC RT_BIT(1)
#define GEM_RSR_OVR RT_BIT(2)
/* ISR / IER / IDR / IMR: queue 0 @ 0x24..0x30; extra GEM queues @ 0x400/0x600 */
#define GEM_INT_MFD RT_BIT(0)
#define GEM_INT_HRESP RT_BIT(11)
#define GEM_INT_RCOMP RT_BIT(1)
#define GEM_INT_RXUBR RT_BIT(2)
#define GEM_INT_TXUBR RT_BIT(3)
#define GEM_INT_TUND RT_BIT(4)
#define GEM_INT_RLE RT_BIT(5)
#define GEM_INT_TXERR RT_BIT(6)
#define GEM_INT_TCOMP RT_BIT(7)
#define GEM_INT_LINK RT_BIT(9)
#define GEM_INT_ROVR RT_BIT(10)
#define GEM_INT_RX_BITS (GEM_INT_RCOMP | GEM_INT_RXUBR | GEM_INT_ROVR)
#define GEM_INT_TX_BITS (GEM_INT_TCOMP | GEM_INT_TUND | GEM_INT_RLE | GEM_INT_TXERR)
/* MAN (MDIO clause 22) */
#define GEM_MAN_SOF 1
#define GEM_MAN_WRITE 1
#define GEM_MAN_READ 2
#define GEM_MAN_CODE 2
#define GEM_MAN_DATA_OFF 0
#define GEM_MAN_DATA_SZ 16
#define GEM_MAN_CODE_OFF 16
#define GEM_MAN_CODE_SZ 2
#define GEM_MAN_REGA_OFF 18
#define GEM_MAN_REGA_SZ 5
#define GEM_MAN_PHYA_OFF 23
#define GEM_MAN_PHYA_SZ 5
#define GEM_MAN_RW_OFF 28
#define GEM_MAN_RW_SZ 2
#define GEM_MAN_SOF_OFF 30
#define GEM_MAN_SOF_SZ 2
#define GEM_MAN_BF(name, val) (((val) & ((1 << GEM_MAN_##name##_SZ) - 1)) << GEM_MAN_##name##_OFF)
/*
* RX descriptor word @addr: OWNERSHIP uses bit0 (RX_USED); WRAP is bit1.
* Word @ctrl: frame length in bits [11:0], SOF/EOF for single-buffer frames.
*/
#define GEM_RX_USED RT_BIT(0) /* In addr */
#define GEM_RX_WRAP RT_BIT(1) /* In addr */
#define GEM_RX_LEN_MASK 0x0fff /* FRMLEN in ctrl */
#define GEM_RX_SOF RT_BIT(14)
#define GEM_RX_EOF RT_BIT(15)
/* TX descriptor @ctrl (GEM length bits [13:0]) */
#define GEM_TX_LAST RT_BIT(15)
#define GEM_TX_NOCRC RT_BIT(16)
#define GEM_TX_WRAP RT_BIT(30)
#define GEM_TX_USED RT_BIT(31)
#define GEM_TX_LEN_MASK 0x3fff
/* DMACFG */
#define GEM_DMACFG_FBLDO_SHIFT 0
#define GEM_DMACFG_ENDIA_DESC_SHIFT 6
#define GEM_DMACFG_ENDIA_PKT_SHIFT 7
#define GEM_DMACFG_RXBMS_SHIFT 8
#define GEM_DMACFG_TXPBMS_SHIFT 10
#define GEM_DMACFG_RXBS_SHIFT 16
#define GEM_DMACFG_DDRP_SHIFT 24
#define GEM_DMACFG_RXEXT_SHIFT 28
#define GEM_DMACFG_TXEXT_SHIFT 29
#define GEM_DMACFG_ADDR64_SHIFT 30
#define GEM_DMACFG_RXBS_MASK 0xff
#define GEM_CLK_DIV8 0
#define GEM_CLK_DIV16 1
#define GEM_CLK_DIV32 2
#define GEM_CLK_DIV48 3
#define GEM_CLK_DIV64 4
#define GEM_CLK_DIV96 5
#define GEM_CLK_DIV128 6
#define GEM_CLK_DIV224 7
/* DCFG5: TSU present */
#define GEM_DCFG5_TSU_SHIFT 8
#define GEM_DBW32 0
#define GEM_DBW64 1
#define GEM_DBW128 2
#define MACB_CAP_USRIO_HAS_CLKEN RT_BIT(1)
#define MACB_CAP_USRIO_DEFAULT_MII_GMII RT_BIT(2)
#define MACB_CAP_NO_GIGABIT_HALF RT_BIT(3)
#define MACB_CAP_USRIO_DISABLED RT_BIT(4)
#define MACB_CAP_JUMBO RT_BIT(5)
#define MACB_CAP_GEM_HAS_PTP RT_BIT(6)
#define MACB_CAP_BD_RD_PREFETCH RT_BIT(7)
#define MACB_CAP_NEEDS_RSTONUBR RT_BIT(8)
#define MACB_CAP_MIIONRGMII RT_BIT(9)
#define MACB_CAP_NEED_TSUCLK RT_BIT(10)
#define MACB_CAP_QUEUE_DISABLE RT_BIT(11)
#define MACB_CAP_QBV RT_BIT(12)
#define MACB_CAP_CLK_HW_CHG RT_BIT(15)
#define MACB_CAP_MACB_IS_EMAC RT_BIT(16)
#define MACB_CAP_GIGABIT_MODE_AVAILABLE RT_BIT(18)
#define MACB_CAP_SG_DISABLED RT_BIT(19)
#define MACB_CAP_NO_LSO RT_BIT(24)
struct macb_eth;
struct macb_config
{
rt_uint32_t caps;
rt_uint8_t dma_burst_length;
rt_uint16_t jumbo_max_len;
rt_uint16_t max_tx_len;
};
struct macb_dma_desc
{
rt_uint32_t addr;
rt_uint32_t ctrl;
};
struct macb_dma_desc_64
{
rt_uint32_t addrh;
rt_uint32_t resvd;
};
#define MACB_RX_RING_SIZE 32
#define MACB_TX_RING_SIZE 16
#define MACB_RX_BUFFER_SIZE 1536
/* RXBS encoding: buffer_bytes / 64 */
#define MACB_RX_BUFFER_SIZE_DIV64 (MACB_RX_BUFFER_SIZE / 64)
struct macb_eth
{
struct eth_device parent;
#ifdef RT_ETHERNET_CADENCE_PTP
struct rt_ptp_clock ptp_parent;
#endif
struct rt_device *dev;
struct rt_phy_device *phy;
struct mii_bus *mii;
int irq;
void *regs;
struct rt_clk *pclk;
struct rt_clk *hclk;
struct rt_clk *tsu_clk;
struct rt_clk *tx_clk;
rt_uint8_t mac[6];
rt_bool_t is_gem;
rt_bool_t dma_64bit;
rt_bool_t native_io;
rt_bool_t isr_clear_on_write;
rt_uint32_t ncr_shadow;
rt_uint32_t ncfgr_shadow;
const struct macb_config *config;
rt_uint16_t max_tx_len;
int phy_interface;
rt_ssize_t phy_reset_pin;
rt_uint8_t phy_reset_active;
rt_uint16_t phy_reset_ms;
rt_uint8_t aw2w_max_pipe;
rt_uint8_t ar2r_max_pipe;
rt_bool_t use_aw2b_fill;
struct macb_dma_desc *rx_ring;
struct macb_dma_desc *tx_ring;
rt_uint8_t *rx_buffer;
rt_uint8_t *tx_buffer;
rt_size_t dma_blob_size;
rt_ubase_t dma_blob_handle;
rt_ubase_t rx_ring_dma;
rt_ubase_t tx_ring_dma;
rt_ubase_t rx_buffer_dma;
rt_ubase_t tx_buffer_dma;
struct macb_dma_desc *dummy_desc;
rt_ubase_t dummy_desc_dma;
rt_size_t dummy_desc_size;
rt_uint32_t rx_tail;
rt_uint32_t tx_head;
rt_uint32_t tx_tail;
rt_bool_t net_registered;
rt_bool_t irq_installed;
rt_bool_t hw_ready;
rt_bool_t mac_started;
rt_bool_t phy_configured;
#ifdef RT_USING_SYSTEM_WORKQUEUE
struct rt_work phy_retry_work;
int phy_retry_count;
#endif
struct rt_mutex mdio_lock;
struct rt_semaphore tx_sem;
};
#define raw_to_macb_eth(raw) \
rt_container_of(rt_container_of(raw, struct eth_device, parent), struct macb_eth, parent)
#define raw_to_macb_ptp(raw) \
rt_container_of(raw, struct macb_eth, ptp_parent)
rt_inline rt_uint32_t macb_readl(struct macb_eth *eth, rt_uint32_t off)
{
if (eth->native_io)
{
return *(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off);
}
return HWREG32(eth->regs + off);
}
rt_inline void macb_writel(struct macb_eth *eth, rt_uint32_t off, rt_uint32_t val)
{
if (eth->native_io)
{
*(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off) = val;
}
else
{
HWREG32(eth->regs + off) = val;
}
}
rt_err_t macb_eth_hw_init(struct macb_eth *eth);
void macb_eth_hw_stop(struct macb_eth *eth);
rt_err_t macb_eth_common_probe(struct macb_eth *eth);
rt_err_t macb_eth_common_remove(struct macb_eth *eth);
#ifdef RT_ETHERNET_CADENCE_PTP
rt_err_t macb_ptp_register(struct macb_eth *eth);
void macb_ptp_unregister(struct macb_eth *eth);
#endif
#endif /* __MACB_H__ */

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-25 GuEe-GUI first version
*/
#include "macb.h"
#define DBG_TAG "eth.macb.pci"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_ubase_t gem_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
{
(void)cell;
(void)parent_rate;
return 50000000;
}
static struct rt_clk_ops gem_clk_ops =
{
.recalc_rate = gem_clk_recalc_rate,
};
static struct rt_clk_cell *gem_cells[] =
{
&(struct rt_clk_cell)
{
.cell.name = "macb_pci_pclk",
.cell.ops = &gem_clk_ops,
},
&(struct rt_clk_cell)
{
.cell.name = "macb_pci_hclk",
.cell.ops = &gem_clk_ops,
},
};
static rt_err_t macb_pci_clk_init(void)
{
rt_err_t err;
static struct rt_clk_node *clk_np = RT_NULL;
if (clk_np)
{
return RT_EOK;
}
if (!(clk_np = rt_calloc(1, sizeof(*clk_np))))
{
return -RT_ENOMEM;
}
clk_np->cells = gem_cells;
clk_np->cells_nr = RT_ARRAY_SIZE(gem_cells);
if ((err = rt_clk_register(clk_np)))
{
rt_free(clk_np);
}
return err;
}
static rt_err_t macb_pci_probe(struct rt_pci_device *pdev)
{
rt_err_t err;
struct macb_eth *eth;
if ((err = macb_pci_clk_init()))
{
return err;
}
if (!(eth = rt_calloc(1, sizeof(*eth))))
{
return -RT_ENOMEM;
}
rt_pci_set_master(pdev);
eth->dev = &pdev->parent;
eth->regs = rt_pci_iomap(pdev, 0);
if (!eth->regs)
{
err = -RT_EIO;
goto _free_eth;
}
eth->irq = pdev->irq;
rt_pci_irq_unmask(pdev);
eth->pclk = rt_clk_get_by_name(eth->dev, "macb_pci_pclk");
if (rt_is_err_or_null(eth->pclk))
{
err = eth->pclk ? rt_ptr_err(eth->pclk) : -RT_EIO;
goto _unmap;
}
eth->hclk = rt_clk_get_by_name(eth->dev, "macb_pci_hclk");
if (rt_is_err_or_null(eth->hclk))
{
err = eth->hclk ? rt_ptr_err(eth->hclk) : -RT_EIO;
goto _put_pclk;
}
ethernet_random_addr(&eth->parent, eth->mac);
if ((err = macb_eth_hw_init(eth)))
{
goto _put_clks;
}
pdev->parent.user_data = eth;
if ((err = macb_eth_common_probe(eth)))
{
macb_eth_common_remove(eth);
pdev->parent.user_data = RT_NULL;
goto _put_clks;
}
return RT_EOK;
_put_clks:
rt_clk_put(eth->hclk);
_put_pclk:
rt_clk_put(eth->pclk);
_unmap:
rt_iounmap(eth->regs);
rt_pci_irq_mask(pdev);
_free_eth:
rt_free(eth);
return err;
}
static rt_err_t macb_pci_remove(struct rt_pci_device *pdev)
{
struct macb_eth *eth = pdev->parent.user_data;
macb_eth_common_remove(eth);
/* INTx is shared, don't mask all */
rt_hw_interrupt_umask(pdev->irq);
rt_pci_irq_mask(pdev);
rt_pci_clear_master(pdev);
rt_iounmap(eth->regs);
rt_clk_put(eth->hclk);
rt_clk_put(eth->pclk);
rt_free(eth);
return RT_EOK;
}
static const struct rt_pci_device_id macb_pci_ids[] =
{
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_CDNS, 0xe007), },
{ /* sentinel */ }
};
static struct rt_pci_driver macb_pci_driver =
{
.name = "macb-pci",
.ids = macb_pci_ids,
.probe = macb_pci_probe,
.remove = macb_pci_remove,
};
RT_PCI_DRIVER_EXPORT(macb_pci_driver);

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-25 GuEe-GUI first version
*/
#define GEM_MDIO_IDLE_RETRIES 100000
#define GEM_MDIO_POLL_US 10
#define GEM_MDIO_FIXED_TURNAROUND_US 100
static rt_bool_t gem_mdio_wait_idle(struct macb_eth *eth, int retries)
{
while (retries-- > 0)
{
rt_uint32_t nsr = macb_readl(eth, GEM_NSR);
/*
* GEM queue-0 may not expose a readable NSR @ 0x8 (reads 0xffffffff).
* Linux/U-Boot treat the idle bit as set; use a fixed turnaround.
*/
if (nsr == 0xffffffff)
{
rt_hw_us_delay(GEM_MDIO_FIXED_TURNAROUND_US);
return RT_TRUE;
}
if (nsr & GEM_NSR_IDLE)
{
return RT_TRUE;
}
rt_hw_us_delay(GEM_MDIO_POLL_US);
}
return RT_FALSE;
}
static rt_err_t gem_mdio_frame(struct macb_eth *eth, rt_uint32_t frame)
{
if (!gem_mdio_wait_idle(eth, GEM_MDIO_IDLE_RETRIES))
{
return -RT_ETIMEOUT;
}
macb_writel(eth, GEM_MAN, frame);
if (!eth->native_io)
{
macb_mmio_posted_barrier(eth);
}
if (!gem_mdio_wait_idle(eth, GEM_MDIO_IDLE_RETRIES))
{
if (!eth->native_io)
{
rt_hw_us_delay(GEM_MDIO_FIXED_TURNAROUND_US);
return RT_EOK;
}
return -RT_ETIMEOUT;
}
return RT_EOK;
}
static rt_bool_t gem_mdio_data_invalid(struct macb_eth *eth, rt_uint32_t data)
{
return data == 0xffff && !eth->native_io;
}
static rt_uint32_t gem_ncfgr_mdc_div(rt_ubase_t pclk_hz)
{
if (pclk_hz <= 20000000)
{
return GEM_CLK_DIV8 << GEM_NCFGR_CLK_SHIFT;
}
else if (pclk_hz <= 40000000)
{
return GEM_CLK_DIV16 << GEM_NCFGR_CLK_SHIFT;
}
else if (pclk_hz <= 80000000)
{
return GEM_CLK_DIV32 << GEM_NCFGR_CLK_SHIFT;
}
else if (pclk_hz <= 120000000)
{
return GEM_CLK_DIV48 << GEM_NCFGR_CLK_SHIFT;
}
else if (pclk_hz <= 160000000)
{
return GEM_CLK_DIV64 << GEM_NCFGR_CLK_SHIFT;
}
else if (pclk_hz <= 240000000)
{
return GEM_CLK_DIV96 << GEM_NCFGR_CLK_SHIFT;
}
else if (pclk_hz <= 320000000)
{
return GEM_CLK_DIV128 << GEM_NCFGR_CLK_SHIFT;
}
return GEM_CLK_DIV224 << GEM_NCFGR_CLK_SHIFT;
}
static rt_uint32_t macb_gem_dbw(struct macb_eth *eth)
{
rt_uint32_t dcfg1, dbw;
dcfg1 = macb_readl(eth, GEM_DCFG1);
dbw = (dcfg1 >> 25) & 0x7U;
switch (dbw)
{
case 4:
return GEM_DBW128 << GEM_NCFGR_DBW_SHIFT;
case 2:
return GEM_DBW64 << GEM_NCFGR_DBW_SHIFT;
default:
return GEM_DBW32 << GEM_NCFGR_DBW_SHIFT;
}
}
static int gem_mdio_read(struct mii_bus *bus, int addr, int devad, int reg)
{
rt_err_t err;
rt_uint32_t data, frame;
struct macb_eth *eth = bus->priv;
rt_mutex_take(&eth->mdio_lock, RT_WAITING_FOREVER);
frame = GEM_MAN_BF(SOF, GEM_MAN_SOF) | GEM_MAN_BF(RW, GEM_MAN_READ) |
GEM_MAN_BF(PHYA, addr) | GEM_MAN_BF(REGA, reg) |
GEM_MAN_BF(CODE, GEM_MAN_CODE);
err = gem_mdio_frame(eth, frame);
if (err)
{
LOG_W("MDIO read timeout addr=%d reg=%d nsr=0x%x ncr=0x%x",
addr, reg, macb_readl(eth, GEM_NSR), macb_readl(eth, GEM_NCR));
rt_mutex_release(&eth->mdio_lock);
return -RT_EIO;
}
data = macb_readl(eth, GEM_MAN) & 0xffff;
if (gem_mdio_data_invalid(eth, data))
{
LOG_W("MDIO read invalid data addr=%d reg=%d man=0x%x",
addr, reg, macb_readl(eth, GEM_MAN));
rt_mutex_release(&eth->mdio_lock);
return -RT_EIO;
}
rt_mutex_release(&eth->mdio_lock);
return (int)data;
}
static int gem_mdio_write(struct mii_bus *bus, int addr, int devad, int reg, rt_uint16_t val)
{
rt_err_t err;
rt_uint32_t frame;
struct macb_eth *eth = bus->priv;
rt_mutex_take(&eth->mdio_lock, RT_WAITING_FOREVER);
frame = GEM_MAN_BF(SOF, GEM_MAN_SOF) | GEM_MAN_BF(RW, GEM_MAN_WRITE) |
GEM_MAN_BF(PHYA, addr) | GEM_MAN_BF(REGA, reg) |
GEM_MAN_BF(CODE, GEM_MAN_CODE) | GEM_MAN_BF(DATA, val);
err = gem_mdio_frame(eth, frame);
rt_mutex_release(&eth->mdio_lock);
return err ? -RT_EIO : 0;
}
static rt_err_t macb_adjust_link(struct macb_eth *eth)
{
rt_uint32_t ncfgr;
struct rt_phy_device *phy = eth->phy;
if (!phy || !phy->link)
{
return -RT_ERROR;
}
/* Some platforms return unreadable NCFGR; keep a software shadow. */
ncfgr = eth->ncfgr_shadow;
ncfgr &= ~(GEM_NCFGR_SPD | GEM_NCFGR_FD | GEM_NCFGR_GBE |
(3u << GEM_NCFGR_DBW_SHIFT));
ncfgr |= macb_gem_dbw(eth);
if (phy->duplex == DUPLEX_FULL)
{
ncfgr |= GEM_NCFGR_FD;
}
if (phy->speed == SPEED_1000)
{
ncfgr |= GEM_NCFGR_GBE;
}
else if (phy->speed == SPEED_100)
{
ncfgr |= GEM_NCFGR_SPD;
}
eth->ncfgr_shadow = ncfgr;
macb_writel(eth, GEM_NCFGR, ncfgr);
if (!eth->native_io)
{
macb_mmio_posted_barrier(eth);
}
macb_set_tx_clk(eth, phy->speed);
if (eth->mac_started)
{
macb_gem_write_traffic_ier(eth);
}
return RT_EOK;
}
static int macb_mdio_bus_reset(struct mii_bus *bus)
{
struct macb_eth *eth = bus->priv;
macb_phy_reset(eth);
return 0;
}
static rt_err_t macb_mii_register(struct macb_eth *eth)
{
rt_err_t err;
eth->mii = rt_mdio_alloc();
if (!eth->mii)
{
return -RT_ENOMEM;
}
/* Enable MDIO management port (Linux macb_mii_init) */
eth->ncr_shadow |= GEM_NCR_MPE;
if (!eth->native_io)
{
macb_ncr_posted_write(eth, eth->ncr_shadow);
}
else
{
macb_writel(eth, GEM_NCR, eth->ncr_shadow);
}
eth->mii->priv = eth;
eth->mii->read = gem_mdio_read;
eth->mii->write = gem_mdio_write;
eth->mii->reset = macb_mdio_bus_reset;
rt_snprintf(eth->mii->name, RT_NAME_MAX, "mii%p", eth);
if ((err = rt_mdio_register(eth->mii)))
{
rt_free(eth->mii);
eth->mii = RT_NULL;
return err;
}
if (eth->mii->reset)
{
eth->mii->reset(eth->mii);
}
return RT_EOK;
}
static void macb_mii_unregister(struct macb_eth *eth)
{
if (eth->mii)
{
rt_mdio_unregister(eth->mii);
rt_free(eth->mii);
eth->mii = RT_NULL;
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-25 GuEe-GUI first version
*/
#include "macb.h"
#define DBG_TAG "eth.macb.ptp"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define NSEC_PER_SEC 1000000000L
#define GEM_PTP_MAX_FREQ 500000000
#define TSU_NSEC_MAX_VAL ((1 << 30) - 1)
rt_inline rt_uint32_t gem_readl(struct macb_eth *eth, rt_uint32_t off)
{
return *(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off);
}
rt_inline void gem_writel(struct macb_eth *eth, rt_uint32_t off, rt_uint32_t val)
{
*(volatile rt_uint32_t *)((rt_uint8_t *)eth->regs + off) = val;
}
static rt_bool_t gem_tsu_present(struct macb_eth *eth)
{
rt_uint32_t dcfg5 = gem_readl(eth, GEM_DCFG5);
return (dcfg5 & (1u << GEM_DCFG5_TSU_SHIFT)) ? RT_TRUE : RT_FALSE;
}
static rt_err_t macb_ptp_adjfreq(struct rt_ptp_clock *ptp, rt_base_t freq)
{
rt_int64_t adj;
rt_uint32_t inc;
struct macb_eth *eth = raw_to_macb_ptp(ptp);
/* Base increment in GEM_TI (NSINCR field) */
inc = gem_readl(eth, GEM_TI) & 0xff;
if (inc == 0)
{
inc = 1;
}
adj = (rt_int64_t)inc * (rt_int64_t)freq / 1000000000LL;
if (adj > 0x7fffffff || adj < -0x80000000LL)
{
return -RT_EINVAL;
}
gem_writel(eth, GEM_TI, (inc + (rt_uint32_t)adj) & 0xff);
return RT_EOK;
}
static rt_err_t macb_ptp_adjtime(struct rt_ptp_clock *ptp, rt_int64_t delta)
{
rt_uint32_t adj;
struct macb_eth *eth = raw_to_macb_ptp(ptp);
if (delta < 0)
{
delta = -delta;
adj = RT_BIT(31) | (rt_uint32_t)(delta > TSU_NSEC_MAX_VAL ? TSU_NSEC_MAX_VAL : delta);
}
else
{
adj = (rt_uint32_t)(delta > TSU_NSEC_MAX_VAL ? TSU_NSEC_MAX_VAL : delta);
}
gem_writel(eth, GEM_TA, adj);
return RT_EOK;
}
static rt_err_t macb_ptp_gettime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts)
{
rt_uint32_t tsh, tsl, tn;
struct macb_eth *eth = raw_to_macb_ptp(ptp);
tn = gem_readl(eth, GEM_TN);
tsl = gem_readl(eth, GEM_TSL);
tsh = gem_readl(eth, GEM_TSH) & 0xffffU;
ts->sec = ((rt_int64_t)tsh << 32) | tsl;
ts->nsec = (rt_int32_t)(tn & 0x3fffffffU);
return RT_EOK;
}
static rt_err_t macb_ptp_settime(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts)
{
struct macb_eth *eth = raw_to_macb_ptp(ptp);
if (!ts || ts->nsec < 0 || ts->nsec >= NSEC_PER_SEC)
{
return -RT_EINVAL;
}
/* TN clear, TSH, TSL, then TN */
gem_writel(eth, GEM_TN, 0);
gem_writel(eth, GEM_TSH, (rt_uint32_t)((rt_uint64_t)ts->sec >> 32) & 0xffffU);
gem_writel(eth, GEM_TSL, (rt_uint32_t)ts->sec);
gem_writel(eth, GEM_TN, (rt_uint32_t)ts->nsec);
return RT_EOK;
}
static rt_err_t macb_ptp_enable(struct rt_ptp_clock *ptp, struct rt_ptp_clock_request *req, rt_bool_t on)
{
return RT_EOK;
}
static const struct rt_ptp_ops macb_gem_ptp_ops =
{
.adjfreq = macb_ptp_adjfreq,
.adjtime = macb_ptp_adjtime,
.gettime = macb_ptp_gettime,
.settime = macb_ptp_settime,
.enable = macb_ptp_enable,
};
rt_err_t macb_ptp_register(struct macb_eth *eth)
{
rt_err_t err;
struct rt_ptp_clock *ptp = &eth->ptp_parent;
if (!gem_tsu_present(eth))
{
return -RT_ENOSYS;
}
ptp->ops = &macb_gem_ptp_ops;
ptp->max_freq = GEM_PTP_MAX_FREQ;
if ((err = rt_ptp_clock_register(ptp)))
{
ptp->ops = RT_NULL;
return err;
}
return RT_EOK;
}
void macb_ptp_unregister(struct macb_eth *eth)
{
struct rt_ptp_clock *ptp = &eth->ptp_parent;
if (!ptp->ops)
{
return;
}
rt_ptp_clock_unregister(ptp);
ptp->ops = RT_NULL;
}

View File

@ -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

View File

@ -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')

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-26 GuEe-GUI first version
*/
#include "dwmac-platform.h"
rt_err_t dwmac_platform_register(struct rt_platform_device *pdev,
const struct dwmac_config *config)
{
rt_err_t err;
struct rt_device *dev = &pdev->parent;
struct dwmac_eth *eth = rt_calloc(1, sizeof(*eth));
if (!eth)
{
return -RT_ENOMEM;
}
eth->dev = dev;
eth->config = config;
eth->irq = rt_dm_dev_get_irq(dev, 0);
eth->mac_base = rt_dm_dev_iomap(dev, 0);
if (!eth->mac_base)
{
err = -RT_EIO;
goto _fail;
}
#ifdef RT_USING_OFW
if (rt_ofw_get_mac_addr(dev->ofw_node, eth->mac))
#endif
{
ethernet_random_addr(&eth->parent, eth->mac);
}
dev->user_data = eth;
if ((err = dwmac_probe(eth)))
{
goto _fail_unmap;
}
return RT_EOK;
_fail_unmap:
dev->user_data = RT_NULL;
rt_iounmap(eth->mac_base);
_fail:
rt_free(eth);
return err;
}
rt_err_t dwmac_platform_remove(struct rt_platform_device *pdev)
{
struct dwmac_eth *eth = pdev->parent.user_data;
if (!eth)
{
return RT_EOK;
}
dwmac_remove(eth);
rt_iounmap(eth->mac_base);
rt_free(eth);
pdev->parent.user_data = RT_NULL;
return RT_EOK;
}

View File

@ -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__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,303 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-26 GuEe-GUI first version
*/
#ifndef __DWMAC_H__
#define __DWMAC_H__
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/misc.h>
#include "../ethernet_dm.h"
/* GMAC4 MAC registers (offset from mac_base) */
#define GMAC_CONFIG 0x0000
#define GMAC_PACKET_FILTER 0x0008
#define GMAC_MDIO_ADDR 0x0200
#define GMAC_MDIO_DATA 0x0204
#define GMAC_ADDR_HIGH(reg) (0x0300 + (reg) * 8)
#define GMAC_ADDR_LOW(reg) (0x0304 + (reg) * 8)
#define GMAC_RXQ_CTRL0 0x00a0
#define GMAC_RXQ_CTRL1 0x00a4
#define GMAC_CONFIG_RE RT_BIT(0)
#define GMAC_CONFIG_TE RT_BIT(1)
#define GMAC_CONFIG_DM RT_BIT(13)
#define GMAC_CONFIG_FES RT_BIT(14)
#define GMAC_CONFIG_PS RT_BIT(15)
#define GMAC_CONFIG_IPC RT_BIT(27)
#define GMAC_PF_PM RT_BIT(4)
#define GMAC_RX_DCB_QUEUE_ENABLE(q) RT_BIT(((q) * 2) + 1)
#define GMAC_RXQCTRL_MCBCQEN RT_BIT(20)
#define GMAC_RXQCTRL_MCBCQ_MASK RT_GENMASK(18, 16)
#define GMAC_RXQCTRL_MCBCQ_SHIFT 16
/* DMA top-level (offset from mac_base) */
#define DMA_BUS_MODE 0x1000
#define DMA_SYS_BUS_MODE 0x1004
#define DMA_BUS_MODE_SFT_RESET RT_BIT(0)
#define DMA_SYS_BUS_FB RT_BIT(0)
#define DMA_SYS_BUS_MB RT_BIT(14)
#define DMA_SYS_BUS_AAL RT_BIT(12)
#define DMA_AXI_BLEN16 RT_BIT(3)
#define DMA_AXI_BLEN8 RT_BIT(2)
#define DMA_AXI_BLEN4 RT_BIT(1)
#define DMA_AXI_BLEN32 RT_BIT(4)
#define DMA_AXI_BLEN64 RT_BIT(5)
#define DMA_AXI_BLEN128 RT_BIT(6)
#define DMA_AXI_BLEN256 RT_BIT(7)
#define DMA_AXI_WR_OSR_LMT_SHIFT 24
#define DMA_AXI_RD_OSR_LMT_SHIFT 16
#define DMA_CHAN_BASE 0x1100
#define DMA_CHAN_OFFSET 0x80
#define DMA_CHAN_CONTROL(c) (DMA_CHAN_BASE + (c) * DMA_CHAN_OFFSET)
#define DMA_CHAN_TX_CONTROL(c) (DMA_CHAN_CONTROL(c) + 0x04)
#define DMA_CHAN_RX_CONTROL(c) (DMA_CHAN_CONTROL(c) + 0x08)
#define DMA_CHAN_TX_BASE_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x14)
#define DMA_CHAN_RX_BASE_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x1c)
#define DMA_CHAN_TX_END_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x20)
#define DMA_CHAN_RX_END_ADDR(c) (DMA_CHAN_CONTROL(c) + 0x28)
#define DMA_CHAN_TX_RING_LEN(c) (DMA_CHAN_CONTROL(c) + 0x2c)
#define DMA_CHAN_RX_RING_LEN(c) (DMA_CHAN_CONTROL(c) + 0x30)
#define DMA_CHAN_INTR_ENA(c) (DMA_CHAN_CONTROL(c) + 0x34)
#define DMA_CHAN_STATUS(c) (DMA_CHAN_CONTROL(c) + 0x60)
#define DMA_CONTROL_ST RT_BIT(0)
#define DMA_CONTROL_SR RT_BIT(0)
#define DMA_CONTROL_OSP RT_BIT(4)
#define DMA_CONTROL_TSE RT_BIT(12)
#define DMA_BUS_MODE_PBL_SHIFT 16
#define DMA_BUS_MODE_RPBL_SHIFT 16
#define DMA_RBSZ_SHIFT 1
#define DMA_RBSZ_MASK RT_GENMASK(14, 1)
#define DMA_CHAN_STATUS_TI RT_BIT(0)
#define DMA_CHAN_STATUS_RI RT_BIT(6)
#define DMA_CHAN_STATUS_NIS RT_BIT(15)
#define DMA_CHAN_INTR_ENA_RIE RT_BIT(6)
#define DMA_CHAN_INTR_ENA_TIE RT_BIT(0)
#define DMA_CHAN_INTR_ENA_NIE RT_BIT(16)
#define DMA_CHAN_INTR_ENA_AIE RT_BIT(15)
#define DMA_CHAN_INTR_ENA_NIE_4_10 RT_BIT(15)
#define DMA_CHAN_INTR_ENA_AIE_4_10 RT_BIT(14)
#define DMA_CHAN_INTR_ENA_FBE RT_BIT(12)
#define DMA_CHAN_INTR_DEFAULT_MASK (DMA_CHAN_INTR_ENA_NIE | \
DMA_CHAN_INTR_ENA_AIE | \
DMA_CHAN_INTR_ENA_FBE | \
DMA_CHAN_INTR_ENA_RIE | \
DMA_CHAN_INTR_ENA_TIE)
#define DMA_CHAN_INTR_DEFAULT_MASK_4_10 (DMA_CHAN_INTR_ENA_NIE_4_10 | \
DMA_CHAN_INTR_ENA_AIE_4_10 | \
DMA_CHAN_INTR_ENA_FBE | \
DMA_CHAN_INTR_ENA_RIE | \
DMA_CHAN_INTR_ENA_TIE)
#define MTL_OPERATION_MODE 0x0c00
#define MTL_RXQ_DMA_MAP0 0x0c30
#define MTL_RXQ_DMA_QXMDMACH_MASK(q) (0xf << (8 * (q)))
#define MTL_RXQ_DMA_QXMDMACH(chan, q) ((chan) << (8 * (q)))
#define MTL_CHAN_BASE 0x0d00
#define MTL_CHAN_OFFSET 0x40
#define MTL_CHAN_TX_OP_MODE(c) (MTL_CHAN_BASE + (c) * MTL_CHAN_OFFSET)
#define MTL_CHAN_RX_OP_MODE(c) (MTL_CHAN_BASE + (c) * MTL_CHAN_OFFSET + 0x30)
#define MTL_OP_MODE_RSF RT_BIT(5)
#define MTL_OP_MODE_TSF RT_BIT(1)
#define MTL_OP_MODE_TXQEN RT_BIT(3)
/* Enhanced descriptors (DWMAC4) */
#define TDES2_BUFFER1_SIZE_MASK RT_GENMASK(13, 0)
#define TDES3_PACKET_SIZE_MASK RT_GENMASK(14, 0)
#define TDES3_CHECKSUM_INSERTION_SHIFT 16
#define TDES3_CHECKSUM_INSERTION_FULL (3 << TDES3_CHECKSUM_INSERTION_SHIFT)
#define TDES3_FIRST_DESCRIPTOR RT_BIT(29)
#define TDES3_LAST_DESCRIPTOR RT_BIT(28)
#define TDES3_OWN RT_BIT(31)
#define RDES3_OWN RT_BIT(31)
#define RDES3_INT_ON_COMPLETION_EN RT_BIT(30)
#define RDES3_BUFFER1_VALID_ADDR RT_BIT(24)
#define RDES3_PACKET_SIZE_MASK RT_GENMASK(14, 0)
#define MII_ADDR_GBUSY RT_BIT(0)
#define MII_DATA_GD_MASK RT_GENMASK(15, 0)
#define MII_GMAC4_GOC_SHIFT 2
#define MII_GMAC4_WRITE (1 << MII_GMAC4_GOC_SHIFT)
#define MII_GMAC4_READ (3 << MII_GMAC4_GOC_SHIFT)
#define MII_GMAC4_ADDR_SHIFT 21
#define MII_GMAC4_ADDR_MASK RT_GENMASK(25, 21)
#define MII_GMAC4_REG_SHIFT 16
#define MII_GMAC4_REG_MASK RT_GENMASK(20, 16)
#define MII_GMAC4_CSR_SHIFT 8
#define MII_GMAC4_CSR_100_150M (0x4 << MII_GMAC4_CSR_SHIFT)
#define DWMAC_RX_RING_SIZE 32
#define DWMAC_TX_RING_SIZE 16
#define DWMAC_RX_BUFFER_SIZE 1536
#define DWMAC_CHAN 0
#define DWMAC_DMA_PBL 8
#define DWMAC_MDIO_TIMEOUT_US 10000
enum dwmac_core_type
{
DWMAC_CORE_GMAC4,
};
struct dwmac_desc
{
rt_uint32_t des0;
rt_uint32_t des1;
rt_uint32_t des2;
rt_uint32_t des3;
};
#define DWMAC_CAP_TSO RT_BIT(0)
#define DWMAC_CAP_PTP RT_BIT(1)
struct dwmac_axi_cfg
{
rt_uint32_t wr_osr_lmt;
rt_uint32_t rd_osr_lmt;
rt_bool_t fixed_burst;
rt_bool_t mixed_burst;
rt_bool_t aal;
rt_uint32_t blen_mask;
};
struct dwmac_mtl_cfg
{
rt_uint32_t rx_queues;
rt_uint32_t tx_queues;
};
struct dwmac_eth;
struct dwmac_plat_ops
{
rt_uint32_t flags;
rt_err_t (*parse_ofw)(struct dwmac_eth *eth);
rt_err_t (*init)(struct dwmac_eth *eth);
void (*exit)(struct dwmac_eth *eth);
rt_err_t (*fix_link_speed)(struct dwmac_eth *eth, int speed);
};
struct dwmac_config
{
enum dwmac_core_type core_type;
rt_uint32_t caps;
rt_uint8_t dma_pbl;
const struct dwmac_plat_ops *plat;
};
struct dwmac_eth
{
struct eth_device parent;
struct rt_device *dev;
void *mac_base;
int irq;
struct rt_phy_device *phy;
struct mii_bus *mii;
rt_uint8_t mac[6];
int phy_interface;
struct rt_clk *stmmac_clk;
struct rt_clk *mac_clk_speed;
struct rt_clk *mac_clk_rx;
struct rt_clk *mac_clk_tx;
struct rt_clk *clk_mac_refout;
struct rt_clk *ptp_clk;
struct rt_clk *aclk;
struct rt_clk *pclk;
struct rt_reset_control *rst;
#ifdef RT_USING_REGULATOR
struct rt_regulator *phy_supply;
#endif
rt_ssize_t phy_reset_pin;
rt_uint8_t phy_reset_active;
rt_uint32_t phy_reset_delays[3];
struct dwmac_desc *rx_ring;
struct dwmac_desc *tx_ring;
rt_ubase_t rx_ring_dma;
rt_ubase_t tx_ring_dma;
rt_uint8_t *rx_buffers;
rt_ubase_t rx_buffers_dma;
rt_uint8_t *tx_buffers;
rt_ubase_t tx_buffers_dma;
rt_size_t dma_blob_size;
rt_ubase_t dma_blob_handle;
rt_uint32_t tx_idx;
rt_uint32_t rx_idx;
rt_uint8_t dma_pbl;
rt_uint8_t dma_txpbl;
rt_uint8_t dma_rxpbl;
rt_bool_t tso_en;
struct dwmac_axi_cfg axi;
struct dwmac_mtl_cfg mtl;
rt_bool_t hw_ready;
rt_bool_t mac_started;
rt_bool_t net_registered;
rt_bool_t irq_installed;
rt_bool_t phy_configured;
#ifdef RT_USING_SYSTEM_WORKQUEUE
struct rt_work phy_retry_work;
rt_bool_t phy_work_inited;
#endif
const struct dwmac_config *config;
void *plat_priv;
#ifdef RT_ETHERNET_DWMAC_PTP
struct rt_ptp_clock ptp_parent;
rt_uint32_t ptp_clk_rate;
rt_uint32_t default_addend;
rt_uint32_t sub_second_inc;
rt_uint32_t systime_flags;
rt_uint8_t pps_out_num;
rt_uint8_t aux_snapshot_num;
#endif /* RT_ETHERNET_DWMAC_PTP */
};
#define dwmac_readl(eth, reg) HWREG32((rt_uint8_t *)(eth)->mac_base + (reg))
#define dwmac_writel(eth, reg, val) HWREG32((rt_uint8_t *)(eth)->mac_base + (reg)) = (val)
rt_inline const struct dwmac_plat_ops *dwmac_plat(const struct dwmac_eth *eth)
{
if (eth && eth->config && eth->config->plat)
{
return eth->config->plat;
}
return RT_NULL;
}
rt_err_t dwmac_probe(struct dwmac_eth *eth);
rt_err_t dwmac_remove(struct dwmac_eth *eth);
rt_err_t dwmac_eth_common_probe(struct dwmac_eth *eth);
rt_err_t dwmac_eth_common_remove(struct dwmac_eth *eth);
#ifdef RT_ETHERNET_DWMAC_PTP
rt_err_t dwmac_ptp_register(struct dwmac_eth *eth);
void dwmac_ptp_unregister(struct dwmac_eth *eth);
#endif /* RT_ETHERNET_DWMAC_PTP */
#endif /* __DWMAC_H__ */

View File

@ -0,0 +1,480 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-26 GuEe-GUI first version
*/
#include "dwmac_ptp.h"
#define DBG_TAG "eth.dwmac.ptp"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define NSEC_PER_SEC 1000000000LL
static rt_err_t dwmac_ptp_poll_tcr(struct dwmac_eth *eth, rt_uint32_t mask,
rt_bool_t wait_clear)
{
rt_uint32_t val;
rt_tick_t start = rt_tick_get();
rt_tick_t timeout = rt_tick_from_millisecond(100);
while (rt_tick_get() - start < timeout)
{
val = dwmac_ptp_readl(eth, PTP_TCR);
if (wait_clear ? !(val & mask) : (val & mask))
{
return RT_EOK;
}
rt_hw_us_delay(10);
}
return -RT_ETIMEOUT;
}
static void dwmac_ptp_config_hw_tstamping(struct dwmac_eth *eth, rt_uint32_t flags)
{
rt_uint32_t val = dwmac_ptp_readl(eth, PTP_TCR);
val &= ~(PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR);
val |= flags;
dwmac_ptp_writel(eth, PTP_TCR, val);
}
static rt_err_t dwmac_ptp_config_sub_second_inc(struct dwmac_eth *eth,
rt_uint32_t ptp_rate, rt_uint32_t *out_inc)
{
rt_uint64_t inc;
rt_uint32_t reg;
inc = (2000000000ULL / ptp_rate);
if (inc > PTP_SSIR_SSINC_MAX)
{
inc = PTP_SSIR_SSINC_MAX;
}
reg = (rt_uint32_t)inc << GMAC4_PTP_SSIR_SSINC_SHIFT;
dwmac_ptp_writel(eth, PTP_SSIR, reg);
if (out_inc)
{
*out_inc = (rt_uint32_t)inc;
}
return RT_EOK;
}
static rt_err_t dwmac_ptp_config_addend(struct dwmac_eth *eth, rt_uint32_t addend)
{
rt_uint32_t val;
dwmac_ptp_writel(eth, PTP_TAR, addend);
val = dwmac_ptp_readl(eth, PTP_TCR);
val |= PTP_TCR_TSADDREG;
dwmac_ptp_writel(eth, PTP_TCR, val);
return dwmac_ptp_poll_tcr(eth, PTP_TCR_TSADDREG, RT_TRUE);
}
static rt_err_t dwmac_ptp_init_systime(struct dwmac_eth *eth,
rt_uint32_t sec, rt_uint32_t nsec)
{
rt_uint32_t val;
dwmac_ptp_writel(eth, PTP_STSUR, sec);
dwmac_ptp_writel(eth, PTP_STNSUR, nsec);
val = dwmac_ptp_readl(eth, PTP_TCR);
val |= PTP_TCR_TSINIT;
dwmac_ptp_writel(eth, PTP_TCR, val);
return dwmac_ptp_poll_tcr(eth, PTP_TCR_TSINIT, RT_TRUE);
}
static rt_err_t dwmac_ptp_adjust_systime(struct dwmac_eth *eth,
rt_uint32_t sec, rt_uint32_t nsec, rt_bool_t sub)
{
rt_uint32_t val;
if (sub)
{
sec = (rt_uint32_t)(-(rt_int32_t)sec);
if (dwmac_ptp_readl(eth, PTP_TCR) & PTP_TCR_TSCTRLSSR)
{
nsec = PTP_DIGITAL_ROLLOVER_MODE - nsec;
}
}
dwmac_ptp_writel(eth, PTP_STSUR, sec);
val = (sub ? RT_BIT(PTP_STNSUR_ADDSUB_SHIFT) : 0) | nsec;
dwmac_ptp_writel(eth, PTP_STNSUR, val);
val = dwmac_ptp_readl(eth, PTP_TCR);
val |= PTP_TCR_TSUPDT;
dwmac_ptp_writel(eth, PTP_TCR, val);
return dwmac_ptp_poll_tcr(eth, PTP_TCR_TSUPDT, RT_TRUE);
}
static void dwmac_ptp_get_systime(struct dwmac_eth *eth, rt_uint64_t *systime)
{
rt_uint64_t ns, sec0, sec1;
sec1 = dwmac_ptp_readl(eth, PTP_STSR);
do
{
sec0 = sec1;
ns = dwmac_ptp_readl(eth, PTP_STNSR);
sec1 = dwmac_ptp_readl(eth, PTP_STSR);
} while (sec0 != sec1);
if (systime)
{
*systime = ns + sec1 * NSEC_PER_SEC;
}
}
static rt_uint32_t dwmac_ptp_get_clk_rate(struct dwmac_eth *eth)
{
rt_uint32_t rate = 0;
if (!rt_is_err_or_null(eth->ptp_clk))
{
rate = (rt_uint32_t)rt_clk_get_rate(eth->ptp_clk);
}
if (!rate && !rt_is_err_or_null(eth->stmmac_clk))
{
rate = (rt_uint32_t)rt_clk_get_rate(eth->stmmac_clk);
}
return rate;
}
static rt_err_t dwmac_ptp_poll_acr(struct dwmac_eth *eth, rt_uint32_t mask)
{
rt_uint32_t val;
rt_tick_t start = rt_tick_get();
rt_tick_t timeout = rt_tick_from_millisecond(10);
while (rt_tick_get() - start < timeout)
{
val = dwmac_ptp_readl(eth, PTP_ACR);
if (!(val & mask))
{
return RT_EOK;
}
rt_hw_us_delay(10);
}
return -RT_ETIMEOUT;
}
static void dwmac_ptp_discover_caps(struct dwmac_eth *eth)
{
rt_uint32_t feat1, feat2;
eth->pps_out_num = 1;
eth->aux_snapshot_num = 1;
feat1 = dwmac_reg_readl(eth, GMAC_HW_FEATURE1);
feat2 = dwmac_reg_readl(eth, GMAC_HW_FEATURE2);
if (feat1)
{
eth->pps_out_num = (feat1 & GMAC_HW_FEAT_PPSOUTNUM_MASK) >> 24;
if (!eth->pps_out_num)
{
eth->pps_out_num = 1;
}
}
if (feat2)
{
eth->aux_snapshot_num = (feat2 & GMAC_HW_FEAT_AUXSNAPNUM_MASK) >> 28;
if (!eth->aux_snapshot_num)
{
eth->aux_snapshot_num = 1;
}
}
}
static rt_err_t dwmac_ptp_fixed_pps(struct dwmac_eth *eth, rt_bool_t on)
{
rt_uint32_t val = dwmac_reg_readl(eth, MAC_PPS_CONTROL);
if (on)
{
val |= PPSEN0;
}
else
{
val &= ~PPSEN0;
val = (val & ~PPSx_MASK(0)) | PPSCMDx(0, 0x5);
}
dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
return RT_EOK;
}
static rt_err_t dwmac_ptp_flex_pps(struct dwmac_eth *eth, int index,
const struct rt_ptp_request_perout *perout, rt_bool_t on)
{
rt_uint32_t tnsec, val;
rt_uint64_t period;
if (index >= eth->pps_out_num)
{
return -RT_EINVAL;
}
tnsec = dwmac_reg_readl(eth, MAC_PPSx_TARGET_TIME_NSEC(index));
if (tnsec & TRGTBUSY0)
{
return -RT_EBUSY;
}
val = dwmac_reg_readl(eth, MAC_PPS_CONTROL);
val &= ~PPSx_MASK(index);
if (!on)
{
val |= PPSCMDx(index, 0x5);
val |= PPSEN0;
dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
return RT_EOK;
}
val |= TRGTMODSELx(index, 0x2);
val |= PPSEN0;
dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
dwmac_reg_writel(eth, MAC_PPSx_TARGET_TIME_SEC(index),
(rt_uint32_t)perout->start_phase.sec);
dwmac_reg_writel(eth, MAC_PPSx_TARGET_TIME_NSEC(index),
(rt_uint32_t)perout->start_phase.nsec);
period = (rt_uint64_t)perout->period.sec * NSEC_PER_SEC + perout->period.nsec;
period /= eth->sub_second_inc;
if (period <= 1)
{
return -RT_EINVAL;
}
dwmac_reg_writel(eth, MAC_PPSx_INTERVAL(index), (rt_uint32_t)(period - 1));
period >>= 1;
if (period <= 1)
{
return -RT_EINVAL;
}
dwmac_reg_writel(eth, MAC_PPSx_WIDTH(index), (rt_uint32_t)(period - 1));
val = dwmac_reg_readl(eth, MAC_PPS_CONTROL);
val |= PPSCMDx(index, 0x2);
dwmac_reg_writel(eth, MAC_PPS_CONTROL, val);
return RT_EOK;
}
static rt_err_t dwmac_ptp_extts(struct dwmac_eth *eth,
const struct rt_ptp_request_extts *extts, rt_bool_t on)
{
rt_uint32_t acr;
if (extts->chan >= eth->aux_snapshot_num)
{
return -RT_EINVAL;
}
acr = dwmac_ptp_readl(eth, PTP_ACR);
if (on)
{
acr |= PTP_ACR_ATSEN(extts->chan);
acr |= PTP_ACR_ATSFC;
}
else
{
acr &= ~PTP_ACR_ATSEN(extts->chan);
}
dwmac_ptp_writel(eth, PTP_ACR, acr);
if (on)
{
return dwmac_ptp_poll_acr(eth, PTP_ACR_ATSFC);
}
return RT_EOK;
}
rt_err_t dwmac_ptp_hw_init(struct dwmac_eth *eth)
{
rt_uint64_t temp;
rt_uint32_t sec_inc = 0;
eth->ptp_clk_rate = dwmac_ptp_get_clk_rate(eth);
if (!eth->ptp_clk_rate)
{
LOG_W("PTP clock rate unknown");
return -RT_EINVAL;
}
dwmac_ptp_discover_caps(eth);
eth->systime_flags = DWMAC_PTP_HWTS_ACTIVE;
dwmac_ptp_config_hw_tstamping(eth, eth->systime_flags);
dwmac_ptp_config_sub_second_inc(eth, eth->ptp_clk_rate, &sec_inc);
eth->sub_second_inc = sec_inc;
temp = NSEC_PER_SEC / sec_inc;
temp <<= 32;
eth->default_addend = (rt_uint32_t)(temp / eth->ptp_clk_rate);
if (dwmac_ptp_config_addend(eth, eth->default_addend))
{
return -RT_ETIMEOUT;
}
return dwmac_ptp_init_systime(eth, 0, 0);
}
static rt_err_t dwmac_ptp_adjfreq(struct rt_ptp_clock *ptp, rt_base_t freq)
{
rt_int64_t adj;
rt_uint32_t addend;
struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
adj = (rt_int64_t)eth->default_addend * (rt_int64_t)freq / NSEC_PER_SEC;
addend = eth->default_addend + (rt_uint32_t)adj;
return dwmac_ptp_config_addend(eth, addend);
}
static rt_err_t dwmac_ptp_adjtime(struct rt_ptp_clock *ptp, rt_int64_t delta)
{
rt_uint32_t sec, nsec;
rt_bool_t sub = RT_FALSE;
struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
if (delta < 0)
{
sub = RT_TRUE;
delta = -delta;
}
sec = (rt_uint32_t)(delta / NSEC_PER_SEC);
nsec = (rt_uint32_t)(delta % NSEC_PER_SEC);
return dwmac_ptp_adjust_systime(eth, sec, nsec, sub);
}
static rt_err_t dwmac_ptp_gettime(struct rt_ptp_clock *ptp, struct rt_ptp_clock_time *ts)
{
rt_uint64_t ns = 0;
struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
dwmac_ptp_get_systime(eth, &ns);
ts->sec = (rt_int64_t)(ns / NSEC_PER_SEC);
ts->nsec = (rt_int32_t)(ns % NSEC_PER_SEC);
return RT_EOK;
}
static rt_err_t dwmac_ptp_settime(struct rt_ptp_clock *ptp, const struct rt_ptp_clock_time *ts)
{
struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
if (!ts || ts->nsec < 0 || ts->nsec >= NSEC_PER_SEC)
{
return -RT_EINVAL;
}
return dwmac_ptp_init_systime(eth, (rt_uint32_t)ts->sec, (rt_uint32_t)ts->nsec);
}
static rt_err_t dwmac_ptp_enable(struct rt_ptp_clock *ptp,
struct rt_ptp_clock_request *req, rt_bool_t on)
{
struct dwmac_eth *eth = raw_to_dwmac_ptp(ptp);
if (!req)
{
return -RT_EINVAL;
}
switch (req->type)
{
case PTP_CLK_REQ_PPS:
return dwmac_ptp_fixed_pps(eth, on);
case PTP_CLK_REQ_EXTTS:
return dwmac_ptp_extts(eth, &req->extts, on);
case PTP_CLK_REQ_PEROUT:
return dwmac_ptp_flex_pps(eth, (int)req->perout.chan, &req->perout, on);
default:
return -RT_ENOSYS;
}
}
static const struct rt_ptp_ops dwmac_ptp_ops =
{
.adjfreq = dwmac_ptp_adjfreq,
.adjtime = dwmac_ptp_adjtime,
.gettime = dwmac_ptp_gettime,
.settime = dwmac_ptp_settime,
.enable = dwmac_ptp_enable,
};
rt_err_t dwmac_ptp_register(struct dwmac_eth *eth)
{
rt_err_t err;
struct rt_ptp_clock *ptp = &eth->ptp_parent;
if ((err = dwmac_ptp_hw_init(eth)))
{
return err;
}
ptp->ops = &dwmac_ptp_ops;
ptp->max_freq = (rt_int32_t)eth->ptp_clk_rate;
ptp->pps = 1;
ptp->extts_nr = eth->aux_snapshot_num;
ptp->perout_nr = eth->pps_out_num;
if ((err = rt_ptp_clock_register(ptp)))
{
ptp->ops = RT_NULL;
return err;
}
LOG_D("PTP clock registered, rate=%u Hz addend=0x%08x ssinc=%u pps=%u extts=%u",
eth->ptp_clk_rate, eth->default_addend, eth->sub_second_inc,
eth->pps_out_num, eth->aux_snapshot_num);
return RT_EOK;
}
void dwmac_ptp_unregister(struct dwmac_eth *eth)
{
struct rt_ptp_clock *ptp = &eth->ptp_parent;
if (!ptp->ops)
{
return;
}
rt_ptp_clock_unregister(ptp);
ptp->ops = RT_NULL;
}

View File

@ -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__ */