[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:
parent
84da9b9393
commit
c3e1dd7946
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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__ */
|
||||
|
|
@ -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(ð->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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = ð->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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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')
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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__ */
|
||||
|
|
@ -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 = ð->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;
|
||||
}
|
||||
|
|
@ -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__ */
|
||||
Loading…
Reference in New Issue