* [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors @ 2017-04-13 13:33 Rafal Ozieblo 2017-04-13 13:38 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo ` (3 more replies) 0 siblings, 4 replies; 14+ messages in thread From: Rafal Ozieblo @ 2017-04-13 13:33 UTC (permalink / raw) To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica Cc: Rafal Ozieblo This patch adds support for PTP timestamps in DMA buffer descriptors. It checks capability at runtime and uses appropriate buffer descriptor. Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> --- drivers/net/ethernet/cadence/Kconfig | 10 ++- drivers/net/ethernet/cadence/macb.c | 133 +++++++++++++++++++++++++++-------- drivers/net/ethernet/cadence/macb.h | 36 ++++++++-- 3 files changed, 141 insertions(+), 38 deletions(-) diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index 608bea1..427d65a 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -29,7 +29,15 @@ config MACB support for the MACB/GEM chip. To compile this driver as a module, choose M here: the module - will be called macb. + will be macb. + +config MACB_USE_HWSTAMP + bool "Use IEEE 1588 hwstamp" + depends on MACB + default y + imply PTP_1588_CLOCK + ---help--- + Enable IEEE 1588 Precision Time Protocol (PTP) support for MACB. config MACB_PCI tristate "Cadence PCI MACB/GEM support" diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 5cbd1e7..59d459b 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -79,33 +79,84 @@ #define MACB_HALT_TIMEOUT 1230 /* DMA buffer descriptor might be different size - * depends on hardware configuration. + * depends on hardware configuration: + * + * 1. dma address width 32 bits: + * word 1: 32 bit address of Data Buffer + * word 2: control + * + * 2. dma address width 64 bits: + * word 1: 32 bit address of Data Buffer + * word 2: control + * word 3: upper 32 bit address of Data Buffer + * word 4: unused + * + * 3. dma address width 32 bits with hardware timestamping: + * word 1: 32 bit address of Data Buffer + * word 2: control + * word 3: timestamp word 1 + * word 4: timestamp word 2 + * + * 4. dma address width 64 bits with hardware timestamping: + * word 1: 32 bit address of Data Buffer + * word 2: control + * word 3: upper 32 bit address of Data Buffer + * word 4: unused + * word 5: timestamp word 1 + * word 6: timestamp word 2 */ static unsigned int macb_dma_desc_get_size(struct macb *bp) { -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) - return sizeof(struct macb_dma_desc) + sizeof(struct macb_dma_desc_64); +#ifdef MACB_EXT_DESC + unsigned int desc_size; + + switch (bp->hw_dma_cap) { + case HW_DMA_CAP_64B: + desc_size = sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_64); + break; + case HW_DMA_CAP_PTP: + desc_size = sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_ptp); + break; + case HW_DMA_CAP_64B_PTP: + desc_size = sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_64) + + sizeof(struct macb_dma_desc_ptp); + break; + default: + desc_size = sizeof(struct macb_dma_desc); + } + return desc_size; #endif return sizeof(struct macb_dma_desc); } -static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int idx) +static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int desc_idx) { -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - /* Dma buffer descriptor is 4 words length (instead of 2 words) - * for 64b GEM. - */ - if (bp->hw_dma_cap == HW_DMA_CAP_64B) - idx <<= 1; +#ifdef MACB_EXT_DESC + switch (bp->hw_dma_cap) { + case HW_DMA_CAP_64B: + case HW_DMA_CAP_PTP: + desc_idx <<= 1; + break; + case HW_DMA_CAP_64B_PTP: + desc_idx *= 3; + break; + default: + break; + } + return desc_idx; #endif - return idx; + return desc_idx; } #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT static struct macb_dma_desc_64 *macb_64b_desc(struct macb *bp, struct macb_dma_desc *desc) { - return (struct macb_dma_desc_64 *)((void *)desc + sizeof(struct macb_dma_desc)); + if (bp->hw_dma_cap & HW_DMA_CAP_64B) + return (struct macb_dma_desc_64 *)((void *)desc + sizeof(struct macb_dma_desc)); + return NULL; } #endif @@ -600,7 +651,7 @@ static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, dma_addr_ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT struct macb_dma_desc_64 *desc_64; - if (bp->hw_dma_cap == HW_DMA_CAP_64B) { + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { desc_64 = macb_64b_desc(bp, desc); desc_64->addrh = upper_32_bits(addr); } @@ -614,7 +665,7 @@ static dma_addr_t macb_get_addr(struct macb *bp, struct macb_dma_desc *desc) #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT struct macb_dma_desc_64 *desc_64; - if (bp->hw_dma_cap == HW_DMA_CAP_64B) { + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { desc_64 = macb_64b_desc(bp, desc); addr = ((u64)(desc_64->addrh) << 32); } @@ -713,7 +764,7 @@ static void macb_tx_error_task(struct work_struct *work) /* Reinitialize the TX desc queue */ queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma)); #endif /* Make TX ring reflect state of hardware */ @@ -1921,9 +1972,13 @@ static void macb_configure_dma(struct macb *bp) dmacfg &= ~GEM_BIT(TXCOEN); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) dmacfg |= GEM_BIT(ADDR64); #endif +#ifdef CONFIG_MACB_USE_HWSTAMP + if (bp->hw_dma_cap & HW_DMA_CAP_PTP) + dmacfg |= GEM_BIT(RXEXT) | GEM_BIT(TXEXT); +#endif netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n", dmacfg); gem_writel(bp, DMACFG, dmacfg); @@ -1971,14 +2026,15 @@ static void macb_init_hw(struct macb *bp) /* Initialize TX and RX buffers */ macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) macb_writel(bp, RBQPH, upper_32_bits(bp->rx_ring_dma)); #endif for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) - queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma)); + if (bp->hw_dma_cap & HW_DMA_CAP_64B) + queue_writel(queue, TBQPH, + upper_32_bits(queue->tx_ring_dma)); #endif /* Enable interrupts */ @@ -2579,6 +2635,18 @@ static void macb_configure_caps(struct macb *bp, dcfg = gem_readl(bp, DCFG2); if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0) bp->caps |= MACB_CAPS_FIFO_MODE; + /* if HWSTAMP is configure and gem has the capability */ +#ifdef CONFIG_MACB_USE_HWSTAMP + bp->ptp_hw_support = false; + if (gem_has_ptp(bp)) { + if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) + pr_err("GEM doesn't support hardware ptp.\n"); + else { + pr_emerg("rozieblo: ptp_hw_support = true"); + bp->ptp_hw_support = true; + } + } +#endif } dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); @@ -2716,7 +2784,7 @@ static int macb_init(struct platform_device *pdev) queue->IMR = GEM_IMR(hw_q - 1); queue->TBQP = GEM_TBQP(hw_q - 1); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue->TBQPH = GEM_TBQPH(hw_q - 1); #endif } else { @@ -2727,7 +2795,7 @@ static int macb_init(struct platform_device *pdev) queue->IMR = MACB_IMR; queue->TBQP = MACB_TBQP; #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue->TBQPH = MACB_TBQPH; #endif } @@ -3307,19 +3375,24 @@ static int macb_probe(struct platform_device *pdev) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) { - dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); - bp->hw_dma_cap = HW_DMA_CAP_64B; - } else - bp->hw_dma_cap = HW_DMA_CAP_32B; -#endif - spin_lock_init(&bp->lock); /* setup capabilities */ macb_configure_caps(bp, macb_config); +#ifdef MACB_EXT_DESC + bp->hw_dma_cap = HW_DMA_CAP_32B; +#endif +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) { + dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); + bp->hw_dma_cap |= HW_DMA_CAP_64B; + } +#endif +#ifdef CONFIG_MACB_USE_HWSTAMP + if (bp->ptp_hw_support) + bp->hw_dma_cap |= HW_DMA_CAP_PTP; +#endif platform_set_drvdata(pdev, dev); dev->irq = platform_get_irq(pdev, 0); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index ec037b0..2606970 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -12,6 +12,10 @@ #include <linux/phy.h> +#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) +#define MACB_EXT_DESC +#endif + #define MACB_GREGS_NBR 16 #define MACB_GREGS_VERSION 2 #define MACB_MAX_QUEUES 8 @@ -269,6 +273,10 @@ #define GEM_RXBS_SIZE 8 #define GEM_DDRP_OFFSET 24 /* disc_when_no_ahb */ #define GEM_DDRP_SIZE 1 +#define GEM_RXEXT_OFFSET 28 /* RX extended Buffer Descriptor mode */ +#define GEM_RXEXT_SIZE 1 +#define GEM_TXEXT_OFFSET 29 /* TX extended Buffer Descriptor mode */ +#define GEM_TXEXT_SIZE 1 #define GEM_ADDR64_OFFSET 30 /* Address bus width - 64b or 32b */ #define GEM_ADDR64_SIZE 1 @@ -425,6 +433,11 @@ #define GEM_TX_PKT_BUFF_OFFSET 21 #define GEM_TX_PKT_BUFF_SIZE 1 + +/* Bitfields in DCFG5. */ +#define GEM_TSU_OFFSET 8 +#define GEM_TSU_SIZE 1 + /* Bitfields in DCFG6. */ #define GEM_PBUF_LSO_OFFSET 27 #define GEM_PBUF_LSO_SIZE 1 @@ -546,16 +559,21 @@ struct macb_dma_desc { u32 ctrl; }; -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -enum macb_hw_dma_cap { - HW_DMA_CAP_32B, - HW_DMA_CAP_64B, -}; +#ifdef MACB_EXT_DESC +#define HW_DMA_CAP_32B 0 +#define HW_DMA_CAP_64B (1 << 0) +#define HW_DMA_CAP_PTP (1 << 1) +#define HW_DMA_CAP_64B_PTP (HW_DMA_CAP_64B | HW_DMA_CAP_PTP) struct macb_dma_desc_64 { u32 addrh; u32 resvd; }; + +struct macb_dma_desc_ptp { + u32 ts_1; + u32 ts_2; +}; #endif /* DMA descriptor bitfields */ @@ -954,8 +972,12 @@ struct macb { u32 wol; struct macb_ptp_info *ptp_info; /* macb-ptp interface */ -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - enum macb_hw_dma_cap hw_dma_cap; +#ifdef MACB_EXT_DESC + uint8_t hw_dma_cap; +#endif + +#ifdef CONFIG_MACB_USE_HWSTAMP + bool ptp_hw_support; #endif }; -- 2.4.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/4] net: macb: Add tsu_clk to device tree 2017-04-13 13:33 [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Rafal Ozieblo @ 2017-04-13 13:38 ` Rafal Ozieblo 2017-04-19 21:57 ` Rob Herring 2017-04-13 13:39 ` [PATCH 3/4] net: macb: Add hardware PTP support Rafal Ozieblo ` (2 subsequent siblings) 3 siblings, 1 reply; 14+ messages in thread From: Rafal Ozieblo @ 2017-04-13 13:38 UTC (permalink / raw) To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica Cc: Rafal Ozieblo Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> --- Documentation/devicetree/bindings/net/macb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index 1506e94..27966ae 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -22,6 +22,7 @@ Required properties: Required elements: 'pclk', 'hclk' Optional elements: 'tx_clk' Optional elements: 'rx_clk' applies to cdns,zynqmp-gem + Optional elements: 'tsu_clk' - clocks: Phandles to input clocks. Optional properties for PHY child node: -- 2.4.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/4] net: macb: Add tsu_clk to device tree 2017-04-13 13:38 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo @ 2017-04-19 21:57 ` Rob Herring 0 siblings, 0 replies; 14+ messages in thread From: Rob Herring @ 2017-04-19 21:57 UTC (permalink / raw) To: Rafal Ozieblo Cc: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica On Thu, Apr 13, 2017 at 02:38:06PM +0100, Rafal Ozieblo wrote: > Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> > --- > Documentation/devicetree/bindings/net/macb.txt | 1 + > 1 file changed, 1 insertion(+) Acked-by: Rob Herring <robh@kernel.org> ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 3/4] net: macb: Add hardware PTP support 2017-04-13 13:33 [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Rafal Ozieblo 2017-04-13 13:38 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo @ 2017-04-13 13:39 ` Rafal Ozieblo 2017-04-14 6:03 ` kbuild test robot ` (2 more replies) 2017-04-13 13:39 ` [PATCH 4/4] net: macb: Add macb_ptp to compilation chain Rafal Ozieblo 2017-04-14 7:43 ` [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Richard Cochran 3 siblings, 3 replies; 14+ messages in thread From: Rafal Ozieblo @ 2017-04-13 13:39 UTC (permalink / raw) To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica Cc: Rafal Ozieblo This patch is based on original Harini's patch and Andrei's patch, implemented in a separate file to ease the review/maintanance and integration with other platforms. This driver does support GEM-GXL: - Register ptp clock framework - Initialize PTP related registers - HW time stamp on the PTP Ethernet packets are received using the SO_TIMESTAMPING API. Where timers are obtained from the dma buffer descriptors Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> --- drivers/net/ethernet/cadence/macb.c | 99 ++++- drivers/net/ethernet/cadence/macb.h | 140 ++++++ drivers/net/ethernet/cadence/macb_ptp.c | 724 ++++++++++++++++++++++++++++++++ 3 files changed, 958 insertions(+), 5 deletions(-) create mode 100755 drivers/net/ethernet/cadence/macb_ptp.c diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 59d459b..603bac1 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -826,6 +826,15 @@ static void macb_tx_interrupt(struct macb_queue *queue) /* First, update TX stats if needed */ if (skb) { +#ifdef CONFIG_MACB_USE_HWSTAMP + if (gem_ptp_do_txstamp(queue, skb, desc) == 0) { + /* skb now belongs to timestamp buffer + * and will be removed later + */ + tx_skb->skb = NULL; + schedule_work(&queue->tx_ts_task); + } +#endif netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n", macb_tx_ring_wrap(bp, tail), skb->data); @@ -992,6 +1001,10 @@ static int gem_rx(struct macb *bp, int budget) bp->dev->stats.rx_packets++; bp->dev->stats.rx_bytes += skb->len; +#ifdef CONFIG_MACB_USE_HWSTAMP + gem_ptp_do_rxstamp(bp, skb, desc); +#endif + #if defined(DEBUG) && defined(VERBOSE_DEBUG) netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", skb->len, skb->csum); @@ -1314,6 +1327,11 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) queue_writel(queue, ISR, MACB_BIT(HRESP)); } +#ifdef CONFIG_MACB_USE_HWSTAMP + if (status & MACB_PTP_INT_MASK) + macb_ptp_int(queue, status); +#endif + status = queue_readl(queue, ISR); } @@ -1643,8 +1661,10 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Make newly initialized descriptor visible to hardware */ wmb(); - - skb_tx_timestamp(skb); +#ifdef CONFIG_MACB_USE_HWSTAMP + if (!bp->ptp_hw_support) +#endif + skb_tx_timestamp(skb); macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); @@ -2502,6 +2522,71 @@ static int macb_set_ringparam(struct net_device *netdev, return 0; } +#ifdef CONFIG_MACB_USE_HWSTAMP +static unsigned int gem_get_tsu_rate(struct macb *bp) +{ + struct clk *tsu_clk; + unsigned int tsu_rate; + + tsu_clk = devm_clk_get(&bp->pdev->dev, "tsu_clk"); + if (!IS_ERR(tsu_clk)) + tsu_rate = clk_get_rate(tsu_clk); + /* try pclk instead */ + else if (!IS_ERR(bp->pclk)) { + tsu_clk = bp->pclk; + tsu_rate = clk_get_rate(tsu_clk); + } else + return -ENOTSUPP; + return tsu_rate; +} + +static s32 gem_get_ptp_max_adj(void) +{ + return 64E6; +} + +static int gem_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct macb *bp = netdev_priv(dev); + + ethtool_op_get_ts_info(dev, info); + if (!bp->ptp_hw_support) + return 0; + + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = + (1 << HWTSTAMP_TX_ONESTEP_SYNC) | + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); + info->phc_index = -1; + + if (bp->ptp_clock) + info->phc_index = ptp_clock_index(bp->ptp_clock); + + return 0; +} + +static struct macb_ptp_info gem_ptp_info = { + .ptp_init = gem_ptp_init, + .ptp_remove = gem_ptp_remove, + .get_ptp_max_adj = gem_get_ptp_max_adj, + .get_tsu_rate = gem_get_tsu_rate, + .get_ts_info = gem_get_ts_info, + .get_hwtst = gem_get_hwtst, + .set_hwtst = gem_set_hwtst, +}; +#endif + static int macb_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) { @@ -2642,8 +2727,8 @@ static void macb_configure_caps(struct macb *bp, if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) pr_err("GEM doesn't support hardware ptp.\n"); else { - pr_emerg("rozieblo: ptp_hw_support = true"); bp->ptp_hw_support = true; + bp->ptp_info = &gem_ptp_info; } } #endif @@ -3252,7 +3337,9 @@ static const struct macb_config np4_config = { }; static const struct macb_config zynqmp_config = { - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO, + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, @@ -3286,7 +3373,9 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids); #endif /* CONFIG_OF */ static const struct macb_config default_gem_config = { - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO, + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 2606970..5295045 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -11,6 +11,9 @@ #define _MACB_H #include <linux/phy.h> +#include <linux/ptp_clock.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/net_tstamp.h> #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) #define MACB_EXT_DESC @@ -90,6 +93,10 @@ #define GEM_SA3T 0x009C /* Specific3 Top */ #define GEM_SA4B 0x00A0 /* Specific4 Bottom */ #define GEM_SA4T 0x00A4 /* Specific4 Top */ +#define GEM_EFTSH 0x00e8 /* PTP Event Frame Transmitted Seconds Register 47:32 */ +#define GEM_EFRSH 0x00ec /* PTP Event Frame Received Seconds Register 47:32 */ +#define GEM_PEFTSH 0x00f0 /* PTP Peer Event Frame Transmitted Seconds Register 47:32 */ +#define GEM_PEFRSH 0x00f4 /* PTP Peer Event Frame Received Seconds Register 47:32 */ #define GEM_OTX 0x0100 /* Octets transmitted */ #define GEM_OCTTXL 0x0100 /* Octets transmitted [31:0] */ #define GEM_OCTTXH 0x0104 /* Octets transmitted [47:32] */ @@ -159,6 +166,9 @@ #define GEM_DCFG6 0x0294 /* Design Config 6 */ #define GEM_DCFG7 0x0298 /* Design Config 7 */ +#define GEM_TXBDCTRL 0x04cc /* TX Buffer Descriptor control register */ +#define GEM_RXBDCTRL 0x04d0 /* RX Buffer Descriptor control register */ + #define GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2)) #define GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2)) #define GEM_TBQPH(hw_q) (0x04C8) @@ -195,6 +205,8 @@ #define MACB_TZQ_OFFSET 12 /* Transmit zero quantum pause frame */ #define MACB_TZQ_SIZE 1 #define MACB_SRTSM_OFFSET 15 +#define MACB_OSSMODE_OFFSET 24 /* Enable One Step Synchro Mode */ +#define MACB_OSSMODE_SIZE 1 /* Bitfields in NCFGR */ #define MACB_SPD_OFFSET 0 /* Speed */ @@ -362,6 +374,16 @@ #define MACB_PDRSFT_SIZE 1 #define MACB_SRI_OFFSET 26 /* TSU Seconds Register Increment */ #define MACB_SRI_SIZE 1 +#define MACB_TCI_OFFSET 29 /* TSU timer comparison interrupt */ +#define MACB_TCI_SIZE 1 +#define MACB_PTP_INT_MASK (MACB_BIT(DRQFR) \ + | MACB_BIT(SFR) \ + | MACB_BIT(DRQFT) \ + | MACB_BIT(SFT) \ + | MACB_BIT(PDRQFR) \ + | MACB_BIT(PDRSFR) \ + | MACB_BIT(PDRQFT) \ + | MACB_BIT(PDRSFT)) /* Timer increment fields */ #define MACB_TI_CNS_OFFSET 0 @@ -452,6 +474,52 @@ #define GEM_NSINCR_OFFSET 0 #define GEM_NSINCR_SIZE 8 +/* Bitfields in TSH */ +#define GEM_TSH_OFFSET 0 /* TSU timer value (s). MSB [47:32] of seconds timer count */ +#define GEM_TSH_SIZE 16 + +/* Bitfields in TSL */ +#define GEM_TSL_OFFSET 0 /* TSU timer value (s). LSB [31:0] of seconds timer count */ +#define GEM_TSL_SIZE 32 + +/* Bitfields in TN */ +#define GEM_TN_OFFSET 0 /* TSU timer value (ns) */ +#define GEM_TN_SIZE 30 + +/* Bitfields in TXBDCTRL */ +#define GEM_TXTSMODE_OFFSET 4 /* TX Descriptor Timestamp Insertion mode */ +#define GEM_TXTSMODE_SIZE 2 + +/* Bitfields in RXBDCTRL */ +#define GEM_RXTSMODE_OFFSET 4 /* RX Descriptor Timestamp Insertion mode */ +#define GEM_RXTSMODE_SIZE 2 + +/* Transmit DMA buffer descriptor Word 1 */ +#define GEM_DMA_TXVALID_OFFSET 23 /* timestamp has been captured in the Buffer Descriptor */ +#define GEM_DMA_TXVALID_SIZE 1 + +/* Receive DMA buffer descriptor Word 0 */ +#define GEM_DMA_RXVALID_OFFSET 2 /* indicates a valid timestamp in the Buffer Descriptor */ +#define GEM_DMA_RXVALID_SIZE 1 + +/* DMA buffer descriptor Word 2 (32 bit addressing) or Word 4 (64 bit addressing) */ +#define GEM_DMA_SECL_OFFSET 30 /* Timestamp seconds[1:0] */ +#define GEM_DMA_SECL_SIZE 2 +#define GEM_DMA_NSEC_OFFSET 0 /* Timestamp nanosecs [29:0] */ +#define GEM_DMA_NSEC_SIZE 30 + +/* DMA buffer descriptor Word 3 (32 bit addressing) or Word 5 (64 bit addressing) */ + +/* New hardware supports 12 bit precision of timestamp in DMA buffer descriptor. + * Old hardware supports only 6 bit precision but it is enough for PTP. + * Less accuracy is used always instead of checking hardware version. + */ +#define GEM_DMA_SECH_OFFSET 0 /* Timestamp seconds[5:2] */ +#define GEM_DMA_SECH_SIZE 4 +#define GEM_DMA_SEC_WIDTH (GEM_DMA_SECH_SIZE + GEM_DMA_SECL_SIZE) +#define GEM_DMA_SEC_TOP (1 << GEM_DMA_SEC_WIDTH) +#define GEM_DMA_SEC_MASK (GEM_DMA_SEC_TOP - 1) + /* Bitfields in ADJ */ #define GEM_ADDSUB_OFFSET 31 #define GEM_ADDSUB_SIZE 1 @@ -527,6 +595,8 @@ #define queue_readl(queue, reg) (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg) #define queue_writel(queue, reg, value) (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value)) +#define PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */ + /* Conditional GEM/MACB macros. These perform the operation to the correct * register dependent on whether the device is a GEM or a MACB. For registers * and bitfields that are common across both devices, use macb_{read,write}l @@ -889,6 +959,18 @@ struct macb_config { int jumbo_max_len; }; +#ifdef CONFIG_MACB_USE_HWSTAMP +struct tsu_incr { + u32 sub_ns; + u32 ns; +}; + +struct gem_tx_ts { + struct sk_buff *skb; + struct macb_dma_desc_ptp desc_ptp; +}; +#endif + struct macb_queue { struct macb *bp; int irq; @@ -905,6 +987,12 @@ struct macb_queue { struct macb_tx_skb *tx_skb; dma_addr_t tx_ring_dma; struct work_struct tx_error_task; + +#ifdef CONFIG_MACB_USE_HWSTAMP + struct work_struct tx_ts_task; + unsigned int tx_ts_head, tx_ts_tail; + struct gem_tx_ts tx_timestamps[PTP_TS_BUFFER_SIZE]; +#endif }; struct macb { @@ -978,9 +1066,61 @@ struct macb { #ifdef CONFIG_MACB_USE_HWSTAMP bool ptp_hw_support; + spinlock_t tsu_clk_lock; /* gem tsu clock locking */ + unsigned int tsu_rate; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + struct tsu_incr tsu_incr; + struct hwtstamp_config tstamp_config; #endif }; +#ifdef CONFIG_MACB_USE_HWSTAMP +#define GEM_TSEC_SIZE (GEM_TSH_SIZE + GEM_TSL_SIZE) +#define TSU_SEC_MAX_VAL (((u64)1 << GEM_TSEC_SIZE) - 1) +#define TSU_NSEC_MAX_VAL ((1 << GEM_TN_SIZE) - 1) + +enum macb_bd_control { + TSTAMP_DISABLED, + TSTAMP_FRAME_PTP_EVENT_ONLY, + TSTAMP_ALL_PTP_FRAMES, + TSTAMP_ALL_FRAMES, +}; + +void gem_ptp_init(struct net_device *ndev); +void gem_ptp_remove(struct net_device *ndev); +int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *des); +void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc); +void macb_ptp_int(struct macb_queue *queue, u32 status); +static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc) +{ + if (queue->bp->tstamp_config.tx_type == TSTAMP_DISABLED) + return -ENOTSUPP; + + return gem_ptp_txstamp(queue, skb, desc); +} + +static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) +{ + if (bp->tstamp_config.rx_filter == TSTAMP_DISABLED) + return; + + gem_ptp_rxstamp(bp, skb, desc); +} +int gem_get_hwtst(struct net_device *dev, struct ifreq *rq); +int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd); +#else +static inline void gem_ptp_init(struct net_device *ndev) { } +static inline void gem_ptp_remove(struct net_device *ndev) { } + +static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc) +{ + return 0; +} + +static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) { } +#endif + static inline bool macb_is_gem(struct macb *bp) { return !!(bp->caps & MACB_CAPS_MACB_IS_GEM); diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c new file mode 100755 index 0000000..72a79c4 --- /dev/null +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -0,0 +1,724 @@ +/** + * 1588 PTP support for Cadence GEM device. + * + * Copyright (C) 2017 Cadence Design Systems - http://www.cadence.com + * + * Authors: Rafal Ozieblo <rafalo@cadence.com> + * Bartosz Folta <bfolta@cadence.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/time64.h> +#include <linux/ptp_classify.h> +#include <linux/if_ether.h> +#include <linux/if_vlan.h> +#include <linux/net_tstamp.h> +#include <linux/circ_buf.h> +#include <linux/spinlock.h> + +#include "macb.h" + +#define GEM_PTP_TIMER_NAME "gem-ptp-timer" + +static struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp, + struct macb_dma_desc *desc) +{ + if (bp->hw_dma_cap == HW_DMA_CAP_PTP) + return (struct macb_dma_desc_ptp *) + ((u8 *)desc + sizeof(struct macb_dma_desc)); + if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP) + return (struct macb_dma_desc_ptp *) + ((u8 *)desc + sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_64)); + return NULL; +} + +static int gem_tsu_get_time(struct macb *bp, struct timespec64 *ts) +{ + long first, second; + u32 secl, sech; + unsigned long flags; + + if (!bp || !ts) + return -EINVAL; + + spin_lock_irqsave(&bp->tsu_clk_lock, flags); + first = gem_readl(bp, TN); + secl = gem_readl(bp, TSL); + sech = gem_readl(bp, TSH); + second = gem_readl(bp, TN); + + /* test for nsec rollover */ + if (first > second) { + /* if so, use later read & re-read seconds + * (assume all done within 1s) + */ + ts->tv_nsec = gem_readl(bp, TN); + secl = gem_readl(bp, TSL); + sech = gem_readl(bp, TSH); + } else + ts->tv_nsec = first; + + ts->tv_sec = (((u64)sech << GEM_TSL_SIZE) | secl) + & TSU_SEC_MAX_VAL; + + spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); + return 0; +} + +static int gem_tsu_set_time(struct macb *bp, const struct timespec64 *ts) +{ + u32 ns, sech, secl; + unsigned long flags; + + if (!bp || !ts) + return -EINVAL; + + secl = (u32)ts->tv_sec; + sech = (ts->tv_sec >> GEM_TSL_SIZE) & ((1 << GEM_TSH_SIZE) - 1); + ns = ts->tv_nsec; + + spin_lock_irqsave(&bp->tsu_clk_lock, flags); + + /* TSH doesn't latch the time and no atomicity! */ + gem_writel(bp, TN, 0); /* clear to avoid overflow */ + gem_writel(bp, TSH, sech); + /* write lower bits 2nd, for synchronized secs update */ + gem_writel(bp, TSL, secl); + gem_writel(bp, TN, ns); + + spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); + + return 0; +} + +static int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec) +{ + unsigned long flags; + + if (!bp || !incr_spec) + return -EINVAL; + + /* tsu_timer_incr register must be written after + * the tsu_timer_incr_sub_ns register and the write operation + * will cause the value written to the tsu_timer_incr_sub_ns register + * to take effect. + */ + spin_lock_irqsave(&bp->tsu_clk_lock, flags); + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, incr_spec->sub_ns)); + gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns)); + spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); + + return 0; +} + +static int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct tsu_incr incr_spec; + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); + u64 adj; + u32 word; + bool neg_adj = false; + + if (!ptp) + return -EINVAL; + + if (scaled_ppm < 0) { + neg_adj = true; + scaled_ppm = -scaled_ppm; + } + + /* Adjustment is relative to base frequency */ + incr_spec.sub_ns = bp->tsu_incr.sub_ns; + incr_spec.ns = bp->tsu_incr.ns; + + /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ + word = ((u64)incr_spec.ns << GEM_SUBNSINCR_SIZE) + incr_spec.sub_ns; + adj = (u64)scaled_ppm * word; + /* Divide with rounding, equivalent to floating dividing: + * (temp / USEC_PER_SEC) + 0.5 + */ + adj += (USEC_PER_SEC >> 1); + adj >>= GEM_SUBNSINCR_SIZE; /* remove fractions */ + adj = div_u64(adj, USEC_PER_SEC); + adj = neg_adj ? (word - adj) : (word + adj); + + incr_spec.ns = (adj >> GEM_SUBNSINCR_SIZE) + & ((1 << GEM_NSINCR_SIZE) - 1); + incr_spec.sub_ns = adj & ((1 << GEM_SUBNSINCR_SIZE) - 1); + gem_tsu_incr_set(bp, &incr_spec); + return 0; +} + +static int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct timespec64 now, then = ns_to_timespec64(delta); + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); + u32 adj, sign = 0; + + if (!ptp) + return -EINVAL; + + if (delta < 0) { + sign = 1; + delta = -delta; + } + + if (delta > TSU_NSEC_MAX_VAL) { + gem_tsu_get_time(bp, &now); + if (sign) + now = timespec64_sub(now, then); + else + now = timespec64_add(now, then); + + gem_tsu_set_time(bp, (const struct timespec64 *)&now); + } else { + adj = (sign << GEM_ADDSUB_OFFSET) | delta; + + gem_writel(bp, TA, adj); + } + + return 0; +} + +static int gem_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); + + if (!ptp || !ts) + return -EINVAL; + + gem_tsu_get_time(bp, ts); + return 0; +} + +static int gem_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); + + if (!ptp || !ts) + return -EINVAL; + + gem_tsu_set_time(bp, ts); + return 0; +} + +static int gem_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); + + if (!ptp || !rq) + return -EINVAL; + + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: /* Toggle TSU match interrupt */ + if (on) + macb_writel(bp, IER, MACB_BIT(TCI)); + else + macb_writel(bp, IDR, MACB_BIT(TCI)); + break; + case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */ + return -EOPNOTSUPP; + /* break; */ + case PTP_CLK_REQ_PPS: /* Toggle TSU periodic (second) interrupt */ + if (on) + macb_writel(bp, IER, MACB_BIT(SRI)); + else + macb_writel(bp, IDR, MACB_BIT(SRI)); + break; + default: + break; + } + return 0; +} + +static struct ptp_clock_info gem_ptp_caps_template = { + .owner = THIS_MODULE, + .name = GEM_PTP_TIMER_NAME, + .max_adj = 0, + .n_alarm = 1, + .n_ext_ts = 1, + .n_per_out = 0, + .n_pins = 0, + .pps = 1, + .adjfine = gem_ptp_adjfine, + .adjtime = gem_ptp_adjtime, + .gettime64 = gem_ptp_gettime, + .settime64 = gem_ptp_settime, + .enable = gem_ptp_enable, +}; + +static void gem_ptp_init_timer(struct macb *bp) +{ + u32 rem = 0; + + bp->tsu_incr.ns = div_u64_rem(NSEC_PER_SEC, bp->tsu_rate, &rem); + if (rem) { + u64 adj = rem; + + adj <<= GEM_SUBNSINCR_SIZE; + bp->tsu_incr.sub_ns = div_u64(adj, bp->tsu_rate); + } else { + bp->tsu_incr.sub_ns = 0; + } +} + +static void gem_ptp_init_tsu(struct macb *bp) +{ + struct timespec64 ts; + + /* 1. get current system time */ + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + + /* 2. set ptp timer */ + gem_tsu_set_time(bp, &ts); + + /* 3. set PTP timer increment value to BASE_INCREMENT */ + gem_tsu_incr_set(bp, &bp->tsu_incr); + + gem_writel(bp, TA, 0); +} + +static void gem_ptp_clear_timer(struct macb *bp) +{ + bp->tsu_incr.ns = 0; + bp->tsu_incr.sub_ns = 0; + + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, 0)); + gem_writel(bp, TI, GEM_BF(NSINCR, 0)); + gem_writel(bp, TA, 0); +} + +static int gem_hw_timestamp(struct macb *bp, + u32 dma_desc_ts_1, u32 dma_desc_ts_2, struct timespec64 *ts) +{ + struct timespec64 tsu; + + ts->tv_sec = (GEM_BFEXT(DMA_SECH, dma_desc_ts_2) << GEM_DMA_SECL_SIZE) | + GEM_BFEXT(DMA_SECL, dma_desc_ts_1); + ts->tv_nsec = GEM_BFEXT(DMA_NSEC, dma_desc_ts_1); + + /* TSU overlaping workaround + * The timestamp only contains lower few bits of seconds, + * so add value from 1588 timer + */ + gem_tsu_get_time(bp, &tsu); + + /* If the top bit is set in the timestamp, + * but not in 1588 timer, it has rolled over, + * so subtract max size + */ + if ((ts->tv_sec & (GEM_DMA_SEC_TOP >> 1)) && + !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1))) + ts->tv_sec -= GEM_DMA_SEC_TOP; + + ts->tv_sec += ((~GEM_DMA_SEC_MASK) & (tsu.tv_sec)); + + return 0; +} + +void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, + struct macb_dma_desc *desc) +{ + struct timespec64 ts; + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct macb_dma_desc_ptp *desc_ptp; + + if (GEM_BFEXT(DMA_RXVALID, desc->addr)) { + desc_ptp = macb_ptp_desc(bp, desc); + gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + } +} + +static void gem_tstamp_tx(struct macb *bp, struct sk_buff *skb, + struct macb_dma_desc_ptp *desc_ptp) +{ + struct skb_shared_hwtstamps shhwtstamps; + struct timespec64 ts; + + gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); +} + +int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, + struct macb_dma_desc *desc) +{ + struct gem_tx_ts *tx_timestamp; + struct macb_dma_desc_ptp *desc_ptp; + unsigned long head = queue->tx_ts_head; + unsigned long tail = READ_ONCE(queue->tx_ts_tail); + + if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) + return -EINVAL; + + if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0) + return -ENOMEM; + + desc_ptp = macb_ptp_desc(queue->bp, desc); + tx_timestamp = &queue->tx_timestamps[head]; + tx_timestamp->skb = skb; + tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1; + tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2; + /* move head */ + smp_store_release(&queue->tx_ts_head, + (head + 1) & (PTP_TS_BUFFER_SIZE - 1)); + return 0; +} + +static void gem_tx_timestamp_flush(struct work_struct *work) +{ + struct macb_queue *queue = + container_of(work, struct macb_queue, tx_ts_task); + struct gem_tx_ts *tx_ts; + unsigned long head, tail; + + /* take current head */ + head = smp_load_acquire(&queue->tx_ts_head); + tail = queue->tx_ts_tail; + + while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) { + tx_ts = &queue->tx_timestamps[tail]; + gem_tstamp_tx(queue->bp, tx_ts->skb, &tx_ts->desc_ptp); + /* cleanup */ + dev_kfree_skb_any(tx_ts->skb); + /* remove old tail */ + smp_store_release(&queue->tx_ts_tail, + (tail + 1) & (PTP_TS_BUFFER_SIZE - 1)); + tail = queue->tx_ts_tail; + } +} + +void gem_ptp_init(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + unsigned int q; + struct macb_queue *queue; + + bp->ptp_clock_info = gem_ptp_caps_template; + + /* nominal frequency and maximum adjustment in ppb */ + bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); + bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj(); + gem_ptp_init_timer(bp); + bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &dev->dev); + if (IS_ERR(&bp->ptp_clock)) { + bp->ptp_clock = NULL; + pr_err("ptp clock register failed\n"); + return; + } + + spin_lock_init(&bp->tsu_clk_lock); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + queue->tx_ts_head = 0; + queue->tx_ts_tail = 0; + INIT_WORK(&queue->tx_ts_task, gem_tx_timestamp_flush); + queue_writel(queue, IER, MACB_PTP_INT_MASK); + } + + gem_ptp_init_tsu(bp); + + dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", + GEM_PTP_TIMER_NAME); +} + +void gem_ptp_remove(struct net_device *ndev) +{ + struct macb *bp = netdev_priv(ndev); + + if (bp->ptp_clock) + ptp_clock_unregister(bp->ptp_clock); + + gem_ptp_clear_timer(bp); + + dev_info(&bp->pdev->dev, "%s ptp clock unregistered.\n", + GEM_PTP_TIMER_NAME); +} + +static int gem_ptp_set_ts_mode(struct macb *bp, + enum macb_bd_control tx_bd_control, + enum macb_bd_control rx_bd_control) +{ + if (!bp) + return -EINVAL; + + gem_writel(bp, TXBDCTRL, GEM_BF(TXTSMODE, tx_bd_control)); + gem_writel(bp, RXBDCTRL, GEM_BF(RXTSMODE, rx_bd_control)); + + return 0; +} + +int gem_get_hwtst(struct net_device *dev, struct ifreq *rq) +{ + struct macb *bp = netdev_priv(dev); + struct hwtstamp_config *tstamp_config = &bp->tstamp_config; + + if (!bp->ptp_hw_support) + return -EFAULT; + + if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) + return -EFAULT; + else + return 0; +} + +static int gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) +{ + u32 reg_val; + + if (!bp || enable > 1) + return -EINVAL; + + reg_val = macb_readl(bp, NCR); + + if (enable) + macb_writel(bp, NCR, reg_val | MACB_BIT(OSSMODE)); + else + macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE)); + + return 0; +} + +int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct macb *bp = netdev_priv(dev); + struct hwtstamp_config *tstamp_config = &bp->tstamp_config; + enum macb_bd_control tx_bd_control = TSTAMP_DISABLED; + enum macb_bd_control rx_bd_control = TSTAMP_DISABLED; + u32 regval; + + if (!bp->ptp_hw_support) + return -EFAULT; + + if (copy_from_user(tstamp_config, ifr->ifr_data, + sizeof(*tstamp_config))) + return -EFAULT; + + /* reserved for future extensions */ + if (tstamp_config->flags) + return -EINVAL; + + switch (tstamp_config->tx_type) { + case HWTSTAMP_TX_OFF: + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + if (gem_ptp_set_one_step_sync(bp, 1) != 0) + return -ERANGE; + case HWTSTAMP_TX_ON: + tx_bd_control = TSTAMP_ALL_FRAMES; + break; + default: + return -ERANGE; + } + + switch (tstamp_config->rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + break; + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + rx_bd_control = TSTAMP_ALL_PTP_FRAMES; + tstamp_config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + regval = macb_readl(bp, NCR); + macb_writel(bp, NCR, (regval | MACB_BIT(SRTSM))); + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_ALL: + rx_bd_control = TSTAMP_ALL_FRAMES; + tstamp_config->rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + tstamp_config->rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0) + return -ERANGE; + + if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config))) + return -EFAULT; + else + return 0; +} + +static int gem_ptp_time_peer_frame_tx_get(struct macb *bp, + struct timespec64 *ts) +{ + if (!bp || !ts) + return -EINVAL; + + ts->tv_sec = (((u64)gem_readl(bp, PEFTSH) << 32) | + gem_readl(bp, PEFTSL)) & TSU_SEC_MAX_VAL; + ts->tv_nsec = gem_readl(bp, PEFTN); + + return 0; +} + +static int gem_ptp_time_peer_frame_rx_get(struct macb *bp, + struct timespec64 *ts) +{ + if (!bp || !ts) + return -EINVAL; + + ts->tv_sec = (((u64)gem_readl(bp, PEFRSH) << 32) | + gem_readl(bp, PEFRSL)) & TSU_SEC_MAX_VAL; + ts->tv_nsec = gem_readl(bp, PEFRN); + + return 0; +} + +static int gem_ptp_time_frame_tx_get(struct macb *bp, struct timespec64 *ts) +{ + if (!bp || !ts) + return -EINVAL; + + ts->tv_sec = (((u64)gem_readl(bp, EFTSH) << 32) | + gem_readl(bp, EFTSL)) & TSU_SEC_MAX_VAL; + ts->tv_nsec = gem_readl(bp, EFTN); + + return 0; +} + +static int gem_ptp_time_frame_rx_get(struct macb *bp, struct timespec64 *ts) +{ + if (!bp || !ts) + return -EINVAL; + + ts->tv_sec = (((u64)gem_readl(bp, EFRSH) << 32) | + gem_readl(bp, EFRSL)) & TSU_SEC_MAX_VAL; + ts->tv_nsec = gem_readl(bp, EFRN); + + return 0; +} + +static int gem_ptp_event(struct macb *bp, struct timespec64 *ts) +{ + struct ptp_clock_event event; + + event.type = PTP_CLOCK_EXTTS; + event.index = 0; + event.timestamp = ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec; + + ptp_clock_event(bp->ptp_clock, &event); + + return 0; +} + +void macb_ptp_int(struct macb_queue *queue, u32 status) +{ + struct macb *bp = queue->bp; + struct timespec64 ts; + + if (status & MACB_BIT(DRQFR)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(DRQFR)); + if (gem_ptp_time_frame_rx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(SFR)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(SFR)); + if (gem_ptp_time_frame_rx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(DRQFT)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(DRQFT)); + if (gem_ptp_time_frame_tx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(SFT)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(SFT)); + if (gem_ptp_time_frame_tx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(PDRQFR)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(PDRQFR)); + if (gem_ptp_time_peer_frame_rx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(PDRSFR)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(PDRSFR)); + if (gem_ptp_time_peer_frame_rx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(PDRQFT)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, + MACB_BIT(PDRQFT)); + if (gem_ptp_time_peer_frame_tx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } + + if (status & MACB_BIT(PDRSFT)) { + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, + MACB_BIT(PDRSFT)); + if (gem_ptp_time_peer_frame_tx_get(bp, &ts) != 0) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + gem_ptp_event(bp, &ts); + } +} -- 2.4.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] net: macb: Add hardware PTP support 2017-04-13 13:39 ` [PATCH 3/4] net: macb: Add hardware PTP support Rafal Ozieblo @ 2017-04-14 6:03 ` kbuild test robot 2017-04-14 7:42 ` kbuild test robot 2017-04-14 18:28 ` Richard Cochran 2 siblings, 0 replies; 14+ messages in thread From: kbuild test robot @ 2017-04-14 6:03 UTC (permalink / raw) To: Rafal Ozieblo Cc: kbuild-all, David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica, Rafal Ozieblo [-- Attachment #1: Type: text/plain, Size: 1725 bytes --] Hi Rafal, [auto build test ERROR on net-next/master] [also build test ERROR on v4.11-rc6 next-20170413] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Rafal-Ozieblo/net-macb-Add-support-for-PTP-timestamps-in-DMA-descriptors/20170414-001330 config: arm-at91_dt_defconfig (attached as .config) compiler: arm-linux-gnueabi-gcc (Debian 6.1.1-9) 6.1.1 20160705 reproduce: wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm Note: the linux-review/Rafal-Ozieblo/net-macb-Add-support-for-PTP-timestamps-in-DMA-descriptors/20170414-001330 HEAD 3b878618e04f866388fd62f6c44752e50b15658a builds fine. It only hurts bisectibility. All errors (new ones prefixed by >>): drivers/built-in.o: In function `macb_interrupt': >> kfifo_buf.c:(.text+0xeabd8): undefined reference to `macb_ptp_int' >> kfifo_buf.c:(.text+0xeac6c): undefined reference to `gem_ptp_txstamp' drivers/built-in.o: In function `gem_rx': >> kfifo_buf.c:(.text+0xebbd8): undefined reference to `gem_ptp_rxstamp' >> drivers/built-in.o:(.data+0xa5e0): undefined reference to `gem_ptp_init' >> drivers/built-in.o:(.data+0xa5e4): undefined reference to `gem_ptp_remove' >> drivers/built-in.o:(.data+0xa5f4): undefined reference to `gem_get_hwtst' >> drivers/built-in.o:(.data+0xa5f8): undefined reference to `gem_set_hwtst' --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 22468 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] net: macb: Add hardware PTP support 2017-04-13 13:39 ` [PATCH 3/4] net: macb: Add hardware PTP support Rafal Ozieblo 2017-04-14 6:03 ` kbuild test robot @ 2017-04-14 7:42 ` kbuild test robot 2017-04-14 18:28 ` Richard Cochran 2 siblings, 0 replies; 14+ messages in thread From: kbuild test robot @ 2017-04-14 7:42 UTC (permalink / raw) To: Rafal Ozieblo Cc: kbuild-all, David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica, Rafal Ozieblo [-- Attachment #1: Type: text/plain, Size: 3090 bytes --] Hi Rafal, [auto build test ERROR on net-next/master] [also build test ERROR on v4.11-rc6 next-20170413] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Rafal-Ozieblo/net-macb-Add-support-for-PTP-timestamps-in-DMA-descriptors/20170414-001330 config: arm64-defconfig (attached as .config) compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705 reproduce: wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm64 Note: the linux-review/Rafal-Ozieblo/net-macb-Add-support-for-PTP-timestamps-in-DMA-descriptors/20170414-001330 HEAD 3b878618e04f866388fd62f6c44752e50b15658a builds fine. It only hurts bisectibility. All errors (new ones prefixed by >>): drivers/built-in.o: In function `gem_ptp_do_rxstamp': >> drivers/net/ethernet/cadence/macb.h:1108: undefined reference to `gem_ptp_rxstamp' drivers/net/ethernet/cadence/macb.h:1108:(.text+0x264604): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `gem_ptp_rxstamp' drivers/built-in.o: In function `gem_ptp_do_txstamp': >> drivers/net/ethernet/cadence/macb.h:1100: undefined reference to `gem_ptp_txstamp' drivers/net/ethernet/cadence/macb.h:1100:(.text+0x266564): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `gem_ptp_txstamp' drivers/built-in.o: In function `macb_interrupt': >> drivers/net/ethernet/cadence/macb.c:1332: undefined reference to `macb_ptp_int' drivers/net/ethernet/cadence/macb.c:1332:(.text+0x2666ac): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `macb_ptp_int' drivers/built-in.o:(.data+0x93fc8): undefined reference to `gem_ptp_init' drivers/built-in.o:(.data+0x93fd0): undefined reference to `gem_ptp_remove' drivers/built-in.o:(.data+0x93ff0): undefined reference to `gem_get_hwtst' drivers/built-in.o:(.data+0x93ff8): undefined reference to `gem_set_hwtst' vim +1108 drivers/net/ethernet/cadence/macb.h 1094 void macb_ptp_int(struct macb_queue *queue, u32 status); 1095 static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc) 1096 { 1097 if (queue->bp->tstamp_config.tx_type == TSTAMP_DISABLED) 1098 return -ENOTSUPP; 1099 > 1100 return gem_ptp_txstamp(queue, skb, desc); 1101 } 1102 1103 static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) 1104 { 1105 if (bp->tstamp_config.rx_filter == TSTAMP_DISABLED) 1106 return; 1107 > 1108 gem_ptp_rxstamp(bp, skb, desc); 1109 } 1110 int gem_get_hwtst(struct net_device *dev, struct ifreq *rq); 1111 int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd); --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 34537 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] net: macb: Add hardware PTP support 2017-04-13 13:39 ` [PATCH 3/4] net: macb: Add hardware PTP support Rafal Ozieblo 2017-04-14 6:03 ` kbuild test robot 2017-04-14 7:42 ` kbuild test robot @ 2017-04-14 18:28 ` Richard Cochran 2017-05-02 13:57 ` Rafal Ozieblo 2 siblings, 1 reply; 14+ messages in thread From: Richard Cochran @ 2017-04-14 18:28 UTC (permalink / raw) To: Rafal Ozieblo Cc: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, Andrei.Pistirica On Thu, Apr 13, 2017 at 02:39:23PM +0100, Rafal Ozieblo wrote: > This patch is based on original Harini's patch and Andrei's patch, > implemented in a separate file to ease the review/maintanance > and integration with other platforms. Please see if you can break this patch into 2 parts: 1. SO_TIMESTAMPING 2. PHC support > This driver does support GEM-GXL: "This driver supports GEM-GXL:" > - HW time stamp on the PTP Ethernet packets are received using the > SO_TIMESTAMPING API. Where timers are obtained from the dma buffer > descriptors This text is poor. No "timers" are obtained but rather time stamps. Also, second sentence is not a sentence. (An english sentence has a noun and a verb.) > diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c > index 59d459b..603bac1 100644 > --- a/drivers/net/ethernet/cadence/macb.c > +++ b/drivers/net/ethernet/cadence/macb.c > @@ -826,6 +826,15 @@ static void macb_tx_interrupt(struct macb_queue *queue) > > /* First, update TX stats if needed */ > if (skb) { > +#ifdef CONFIG_MACB_USE_HWSTAMP No need for ifdef here. Instead, let gem_ptp_do_txstamp() return -1. > + if (gem_ptp_do_txstamp(queue, skb, desc) == 0) { > + /* skb now belongs to timestamp buffer > + * and will be removed later > + */ > + tx_skb->skb = NULL; > + schedule_work(&queue->tx_ts_task); > + } > +#endif > netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n", > macb_tx_ring_wrap(bp, tail), > skb->data); > @@ -992,6 +1001,10 @@ static int gem_rx(struct macb *bp, int budget) > bp->dev->stats.rx_packets++; > bp->dev->stats.rx_bytes += skb->len; > > +#ifdef CONFIG_MACB_USE_HWSTAMP No ifdef needed. > + gem_ptp_do_rxstamp(bp, skb, desc); > +#endif > + > #if defined(DEBUG) && defined(VERBOSE_DEBUG) > netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", > skb->len, skb->csum); > @@ -1314,6 +1327,11 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) > queue_writel(queue, ISR, MACB_BIT(HRESP)); > } > > +#ifdef CONFIG_MACB_USE_HWSTAMP > + if (status & MACB_PTP_INT_MASK) Can't you use IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) here? > + macb_ptp_int(queue, status); > +#endif > + > status = queue_readl(queue, ISR); > } > > @@ -1643,8 +1661,10 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) > > /* Make newly initialized descriptor visible to hardware */ > wmb(); > - > - skb_tx_timestamp(skb); > +#ifdef CONFIG_MACB_USE_HWSTAMP > + if (!bp->ptp_hw_support) > +#endif > + skb_tx_timestamp(skb); This is wrong. You should call skb_tx_timestamp() unconditionally, but be sure to set SKBTX_IN_PROGRESS when appropriate. > diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h > index 2606970..5295045 100644 > --- a/drivers/net/ethernet/cadence/macb.h > +++ b/drivers/net/ethernet/cadence/macb.h > @@ -11,6 +11,9 @@ > #define _MACB_H > > #include <linux/phy.h> > +#include <linux/ptp_clock.h> You don't need to include ptp_clock.h. > +#include <linux/ptp_clock_kernel.h> > +#include <linux/net_tstamp.h> > > #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) > #define MACB_EXT_DESC ... > @@ -527,6 +595,8 @@ > #define queue_readl(queue, reg) (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg) > #define queue_writel(queue, reg, value) (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value)) > > +#define PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */ > + > /* Conditional GEM/MACB macros. These perform the operation to the correct > * register dependent on whether the device is a GEM or a MACB. For registers > * and bitfields that are common across both devices, use macb_{read,write}l > @@ -889,6 +959,18 @@ struct macb_config { > int jumbo_max_len; > }; > > +#ifdef CONFIG_MACB_USE_HWSTAMP No need for ifdef here. > +struct tsu_incr { > + u32 sub_ns; > + u32 ns; > +}; > + > +struct gem_tx_ts { > + struct sk_buff *skb; > + struct macb_dma_desc_ptp desc_ptp; > +}; > +#endif > + > struct macb_queue { > struct macb *bp; > int irq; ... > diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c > new file mode 100755 > index 0000000..72a79c4 > --- /dev/null > +++ b/drivers/net/ethernet/cadence/macb_ptp.c > @@ -0,0 +1,724 @@ > +/** > + * 1588 PTP support for Cadence GEM device. > + * > + * Copyright (C) 2017 Cadence Design Systems - http://www.cadence.com > + * > + * Authors: Rafal Ozieblo <rafalo@cadence.com> > + * Bartosz Folta <bfolta@cadence.com> > + * > + * This program is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 of > + * the License as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > +#include <linux/kernel.h> > +#include <linux/types.h> > +#include <linux/clk.h> > +#include <linux/device.h> > +#include <linux/etherdevice.h> > +#include <linux/platform_device.h> > +#include <linux/time64.h> > +#include <linux/ptp_classify.h> > +#include <linux/if_ether.h> > +#include <linux/if_vlan.h> > +#include <linux/net_tstamp.h> > +#include <linux/circ_buf.h> > +#include <linux/spinlock.h> > + > +#include "macb.h" > + > +#define GEM_PTP_TIMER_NAME "gem-ptp-timer" > + > +static struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp, > + struct macb_dma_desc *desc) > +{ > + if (bp->hw_dma_cap == HW_DMA_CAP_PTP) > + return (struct macb_dma_desc_ptp *) > + ((u8 *)desc + sizeof(struct macb_dma_desc)); > + if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP) > + return (struct macb_dma_desc_ptp *) > + ((u8 *)desc + sizeof(struct macb_dma_desc) > + + sizeof(struct macb_dma_desc_64)); > + return NULL; > +} > + > +static int gem_tsu_get_time(struct macb *bp, struct timespec64 *ts) > +{ > + long first, second; > + u32 secl, sech; > + unsigned long flags; > + > + if (!bp || !ts) > + return -EINVAL; Useless test. > + > + spin_lock_irqsave(&bp->tsu_clk_lock, flags); > + first = gem_readl(bp, TN); > + secl = gem_readl(bp, TSL); > + sech = gem_readl(bp, TSH); > + second = gem_readl(bp, TN); > + > + /* test for nsec rollover */ > + if (first > second) { > + /* if so, use later read & re-read seconds > + * (assume all done within 1s) > + */ > + ts->tv_nsec = gem_readl(bp, TN); > + secl = gem_readl(bp, TSL); > + sech = gem_readl(bp, TSH); > + } else > + ts->tv_nsec = first; CodingStyle. Also, this assignment does not need the lock... > + > + ts->tv_sec = (((u64)sech << GEM_TSL_SIZE) | secl) > + & TSU_SEC_MAX_VAL; ... nor this one. > + > + spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); > + return 0; > +} > + > +static int gem_tsu_set_time(struct macb *bp, const struct timespec64 *ts) > +{ > + u32 ns, sech, secl; > + unsigned long flags; > + > + if (!bp || !ts) > + return -EINVAL; Useless test. > + > + secl = (u32)ts->tv_sec; > + sech = (ts->tv_sec >> GEM_TSL_SIZE) & ((1 << GEM_TSH_SIZE) - 1); > + ns = ts->tv_nsec; > + > + spin_lock_irqsave(&bp->tsu_clk_lock, flags); > + > + /* TSH doesn't latch the time and no atomicity! */ > + gem_writel(bp, TN, 0); /* clear to avoid overflow */ > + gem_writel(bp, TSH, sech); > + /* write lower bits 2nd, for synchronized secs update */ > + gem_writel(bp, TSL, secl); > + gem_writel(bp, TN, ns); > + > + spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); > + > + return 0; > +} > + > +static int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec) > +{ > + unsigned long flags; > + > + if (!bp || !incr_spec) > + return -EINVAL; Useless test. > + > + /* tsu_timer_incr register must be written after > + * the tsu_timer_incr_sub_ns register and the write operation > + * will cause the value written to the tsu_timer_incr_sub_ns register > + * to take effect. > + */ > + spin_lock_irqsave(&bp->tsu_clk_lock, flags); > + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, incr_spec->sub_ns)); > + gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns)); > + spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); > + > + return 0; > +} > + > +static int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) > +{ > + struct tsu_incr incr_spec; > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > + u64 adj; > + u32 word; > + bool neg_adj = false; > + > + if (!ptp) > + return -EINVAL; Useless test (or can ptp be null?) > + > + if (scaled_ppm < 0) { > + neg_adj = true; > + scaled_ppm = -scaled_ppm; > + } > + > + /* Adjustment is relative to base frequency */ > + incr_spec.sub_ns = bp->tsu_incr.sub_ns; > + incr_spec.ns = bp->tsu_incr.ns; > + > + /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ > + word = ((u64)incr_spec.ns << GEM_SUBNSINCR_SIZE) + incr_spec.sub_ns; > + adj = (u64)scaled_ppm * word; > + /* Divide with rounding, equivalent to floating dividing: > + * (temp / USEC_PER_SEC) + 0.5 > + */ > + adj += (USEC_PER_SEC >> 1); > + adj >>= GEM_SUBNSINCR_SIZE; /* remove fractions */ > + adj = div_u64(adj, USEC_PER_SEC); > + adj = neg_adj ? (word - adj) : (word + adj); > + > + incr_spec.ns = (adj >> GEM_SUBNSINCR_SIZE) > + & ((1 << GEM_NSINCR_SIZE) - 1); > + incr_spec.sub_ns = adj & ((1 << GEM_SUBNSINCR_SIZE) - 1); > + gem_tsu_incr_set(bp, &incr_spec); > + return 0; > +} > + > +static int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) > +{ > + struct timespec64 now, then = ns_to_timespec64(delta); > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > + u32 adj, sign = 0; > + > + if (!ptp) > + return -EINVAL; Useless test. > + > + if (delta < 0) { > + sign = 1; > + delta = -delta; > + } > + > + if (delta > TSU_NSEC_MAX_VAL) { > + gem_tsu_get_time(bp, &now); > + if (sign) > + now = timespec64_sub(now, then); > + else > + now = timespec64_add(now, then); > + > + gem_tsu_set_time(bp, (const struct timespec64 *)&now); > + } else { > + adj = (sign << GEM_ADDSUB_OFFSET) | delta; > + > + gem_writel(bp, TA, adj); > + } > + > + return 0; > +} > + > +static int gem_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) > +{ > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > + > + if (!ptp || !ts) > + return -EINVAL; Useles test. What is the point of this wrapper function anyhow? Please remove it. > + > + gem_tsu_get_time(bp, ts); > + return 0; > +} > + > +static int gem_ptp_settime(struct ptp_clock_info *ptp, > + const struct timespec64 *ts) > +{ > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > + > + if (!ptp || !ts) > + return -EINVAL; Another useless function. > + gem_tsu_set_time(bp, ts); > + return 0; > +} > + > +static int gem_ptp_enable(struct ptp_clock_info *ptp, > + struct ptp_clock_request *rq, int on) > +{ > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > + > + if (!ptp || !rq) > + return -EINVAL; Sigh. > + > + switch (rq->type) { > + case PTP_CLK_REQ_EXTTS: /* Toggle TSU match interrupt */ > + if (on) > + macb_writel(bp, IER, MACB_BIT(TCI)); No locking to protect IER and IDE? > + else > + macb_writel(bp, IDR, MACB_BIT(TCI)); > + break; > + case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */ > + return -EOPNOTSUPP; > + /* break; */ > + case PTP_CLK_REQ_PPS: /* Toggle TSU periodic (second) interrupt */ > + if (on) > + macb_writel(bp, IER, MACB_BIT(SRI)); > + else > + macb_writel(bp, IDR, MACB_BIT(SRI)); > + break; > + default: > + break; > + } > + return 0; > +} > + > +static struct ptp_clock_info gem_ptp_caps_template = { > + .owner = THIS_MODULE, > + .name = GEM_PTP_TIMER_NAME, > + .max_adj = 0, > + .n_alarm = 1, You can't support alarms, because they are not implemented in the PHC subsystem at all. > + .n_ext_ts = 1, (see last 2 functions in this patch) > + .n_per_out = 0, > + .n_pins = 0, > + .pps = 1, > + .adjfine = gem_ptp_adjfine, > + .adjtime = gem_ptp_adjtime, > + .gettime64 = gem_ptp_gettime, > + .settime64 = gem_ptp_settime, > + .enable = gem_ptp_enable, > +}; > + > +static void gem_ptp_init_timer(struct macb *bp) > +{ > + u32 rem = 0; > + > + bp->tsu_incr.ns = div_u64_rem(NSEC_PER_SEC, bp->tsu_rate, &rem); > + if (rem) { > + u64 adj = rem; > + > + adj <<= GEM_SUBNSINCR_SIZE; > + bp->tsu_incr.sub_ns = div_u64(adj, bp->tsu_rate); > + } else { > + bp->tsu_incr.sub_ns = 0; > + } > +} > + > +static void gem_ptp_init_tsu(struct macb *bp) > +{ > + struct timespec64 ts; > + > + /* 1. get current system time */ > + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); > + > + /* 2. set ptp timer */ > + gem_tsu_set_time(bp, &ts); > + > + /* 3. set PTP timer increment value to BASE_INCREMENT */ > + gem_tsu_incr_set(bp, &bp->tsu_incr); > + > + gem_writel(bp, TA, 0); > +} > + > +static void gem_ptp_clear_timer(struct macb *bp) > +{ > + bp->tsu_incr.ns = 0; > + bp->tsu_incr.sub_ns = 0; > + > + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, 0)); > + gem_writel(bp, TI, GEM_BF(NSINCR, 0)); > + gem_writel(bp, TA, 0); > +} > + > +static int gem_hw_timestamp(struct macb *bp, > + u32 dma_desc_ts_1, u32 dma_desc_ts_2, struct timespec64 *ts) > +{ > + struct timespec64 tsu; > + > + ts->tv_sec = (GEM_BFEXT(DMA_SECH, dma_desc_ts_2) << GEM_DMA_SECL_SIZE) | > + GEM_BFEXT(DMA_SECL, dma_desc_ts_1); > + ts->tv_nsec = GEM_BFEXT(DMA_NSEC, dma_desc_ts_1); > + > + /* TSU overlaping workaround > + * The timestamp only contains lower few bits of seconds, > + * so add value from 1588 timer > + */ > + gem_tsu_get_time(bp, &tsu); > + > + /* If the top bit is set in the timestamp, > + * but not in 1588 timer, it has rolled over, > + * so subtract max size > + */ > + if ((ts->tv_sec & (GEM_DMA_SEC_TOP >> 1)) && > + !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1))) > + ts->tv_sec -= GEM_DMA_SEC_TOP; > + > + ts->tv_sec += ((~GEM_DMA_SEC_MASK) & (tsu.tv_sec)); > + > + return 0; > +} > + > +void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, > + struct macb_dma_desc *desc) > +{ > + struct timespec64 ts; > + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); > + struct macb_dma_desc_ptp *desc_ptp; > + > + if (GEM_BFEXT(DMA_RXVALID, desc->addr)) { > + desc_ptp = macb_ptp_desc(bp, desc); > + gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); > + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); > + shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); > + } > +} > + > +static void gem_tstamp_tx(struct macb *bp, struct sk_buff *skb, > + struct macb_dma_desc_ptp *desc_ptp) > +{ > + struct skb_shared_hwtstamps shhwtstamps; > + struct timespec64 ts; > + > + gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); > + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); > + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); > + skb_tstamp_tx(skb, &shhwtstamps); > +} > + > +int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, > + struct macb_dma_desc *desc) > +{ > + struct gem_tx_ts *tx_timestamp; > + struct macb_dma_desc_ptp *desc_ptp; > + unsigned long head = queue->tx_ts_head; > + unsigned long tail = READ_ONCE(queue->tx_ts_tail); > + > + if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) > + return -EINVAL; > + > + if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0) > + return -ENOMEM; > + > + desc_ptp = macb_ptp_desc(queue->bp, desc); > + tx_timestamp = &queue->tx_timestamps[head]; > + tx_timestamp->skb = skb; > + tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1; > + tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2; > + /* move head */ > + smp_store_release(&queue->tx_ts_head, > + (head + 1) & (PTP_TS_BUFFER_SIZE - 1)); > + return 0; > +} > + > +static void gem_tx_timestamp_flush(struct work_struct *work) > +{ > + struct macb_queue *queue = > + container_of(work, struct macb_queue, tx_ts_task); > + struct gem_tx_ts *tx_ts; > + unsigned long head, tail; > + > + /* take current head */ > + head = smp_load_acquire(&queue->tx_ts_head); > + tail = queue->tx_ts_tail; > + > + while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) { > + tx_ts = &queue->tx_timestamps[tail]; > + gem_tstamp_tx(queue->bp, tx_ts->skb, &tx_ts->desc_ptp); > + /* cleanup */ > + dev_kfree_skb_any(tx_ts->skb); > + /* remove old tail */ > + smp_store_release(&queue->tx_ts_tail, > + (tail + 1) & (PTP_TS_BUFFER_SIZE - 1)); > + tail = queue->tx_ts_tail; > + } > +} > + > +void gem_ptp_init(struct net_device *dev) > +{ > + struct macb *bp = netdev_priv(dev); > + unsigned int q; > + struct macb_queue *queue; > + > + bp->ptp_clock_info = gem_ptp_caps_template; > + > + /* nominal frequency and maximum adjustment in ppb */ > + bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); > + bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj(); > + gem_ptp_init_timer(bp); > + bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &dev->dev); > + if (IS_ERR(&bp->ptp_clock)) { > + bp->ptp_clock = NULL; > + pr_err("ptp clock register failed\n"); > + return; > + } > + > + spin_lock_init(&bp->tsu_clk_lock); > + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { > + queue->tx_ts_head = 0; > + queue->tx_ts_tail = 0; > + INIT_WORK(&queue->tx_ts_task, gem_tx_timestamp_flush); > + queue_writel(queue, IER, MACB_PTP_INT_MASK); > + } > + > + gem_ptp_init_tsu(bp); > + > + dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", > + GEM_PTP_TIMER_NAME); > +} > + > +void gem_ptp_remove(struct net_device *ndev) > +{ > + struct macb *bp = netdev_priv(ndev); > + > + if (bp->ptp_clock) > + ptp_clock_unregister(bp->ptp_clock); > + > + gem_ptp_clear_timer(bp); > + > + dev_info(&bp->pdev->dev, "%s ptp clock unregistered.\n", > + GEM_PTP_TIMER_NAME); > +} > + > +static int gem_ptp_set_ts_mode(struct macb *bp, > + enum macb_bd_control tx_bd_control, > + enum macb_bd_control rx_bd_control) > +{ > + if (!bp) > + return -EINVAL; Useless test. > + > + gem_writel(bp, TXBDCTRL, GEM_BF(TXTSMODE, tx_bd_control)); > + gem_writel(bp, RXBDCTRL, GEM_BF(RXTSMODE, rx_bd_control)); > + > + return 0; > +} > + > +int gem_get_hwtst(struct net_device *dev, struct ifreq *rq) > +{ > + struct macb *bp = netdev_priv(dev); > + struct hwtstamp_config *tstamp_config = &bp->tstamp_config; > + > + if (!bp->ptp_hw_support) > + return -EFAULT; > + > + if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) > + return -EFAULT; > + else > + return 0; > +} > + > +static int gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) > +{ > + u32 reg_val; > + > + if (!bp || enable > 1) > + return -EINVAL; Useless test. > + > + reg_val = macb_readl(bp, NCR); > + > + if (enable) > + macb_writel(bp, NCR, reg_val | MACB_BIT(OSSMODE)); > + else > + macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE)); > + > + return 0; > +} > + > +int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) > +{ > + struct macb *bp = netdev_priv(dev); > + struct hwtstamp_config *tstamp_config = &bp->tstamp_config; > + enum macb_bd_control tx_bd_control = TSTAMP_DISABLED; > + enum macb_bd_control rx_bd_control = TSTAMP_DISABLED; > + u32 regval; > + > + if (!bp->ptp_hw_support) > + return -EFAULT; > + > + if (copy_from_user(tstamp_config, ifr->ifr_data, > + sizeof(*tstamp_config))) > + return -EFAULT; > + > + /* reserved for future extensions */ > + if (tstamp_config->flags) > + return -EINVAL; > + > + switch (tstamp_config->tx_type) { > + case HWTSTAMP_TX_OFF: > + break; > + case HWTSTAMP_TX_ONESTEP_SYNC: > + if (gem_ptp_set_one_step_sync(bp, 1) != 0) > + return -ERANGE; > + case HWTSTAMP_TX_ON: > + tx_bd_control = TSTAMP_ALL_FRAMES; > + break; > + default: > + return -ERANGE; > + } > + > + switch (tstamp_config->rx_filter) { > + case HWTSTAMP_FILTER_NONE: > + break; > + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: > + break; > + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: > + break; > + case HWTSTAMP_FILTER_PTP_V2_EVENT: > + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: > + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: > + case HWTSTAMP_FILTER_PTP_V2_SYNC: > + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: > + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: > + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: > + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: > + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: > + rx_bd_control = TSTAMP_ALL_PTP_FRAMES; > + tstamp_config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; > + regval = macb_readl(bp, NCR); > + macb_writel(bp, NCR, (regval | MACB_BIT(SRTSM))); > + break; > + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: > + case HWTSTAMP_FILTER_ALL: > + rx_bd_control = TSTAMP_ALL_FRAMES; > + tstamp_config->rx_filter = HWTSTAMP_FILTER_ALL; > + break; > + default: > + tstamp_config->rx_filter = HWTSTAMP_FILTER_NONE; > + return -ERANGE; > + } > + > + if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0) > + return -ERANGE; > + > + if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config))) > + return -EFAULT; > + else > + return 0; > +} > + > +static int gem_ptp_time_peer_frame_tx_get(struct macb *bp, > + struct timespec64 *ts) > +{ > + if (!bp || !ts) > + return -EINVAL; > + > + ts->tv_sec = (((u64)gem_readl(bp, PEFTSH) << 32) | > + gem_readl(bp, PEFTSL)) & TSU_SEC_MAX_VAL; > + ts->tv_nsec = gem_readl(bp, PEFTN); > + > + return 0; > +} > + > +static int gem_ptp_time_peer_frame_rx_get(struct macb *bp, > + struct timespec64 *ts) > +{ > + if (!bp || !ts) > + return -EINVAL; > + > + ts->tv_sec = (((u64)gem_readl(bp, PEFRSH) << 32) | > + gem_readl(bp, PEFRSL)) & TSU_SEC_MAX_VAL; > + ts->tv_nsec = gem_readl(bp, PEFRN); > + > + return 0; > +} > + > +static int gem_ptp_time_frame_tx_get(struct macb *bp, struct timespec64 *ts) > +{ > + if (!bp || !ts) > + return -EINVAL; > + > + ts->tv_sec = (((u64)gem_readl(bp, EFTSH) << 32) | > + gem_readl(bp, EFTSL)) & TSU_SEC_MAX_VAL; > + ts->tv_nsec = gem_readl(bp, EFTN); > + > + return 0; > +} > + > +static int gem_ptp_time_frame_rx_get(struct macb *bp, struct timespec64 *ts) > +{ > + if (!bp || !ts) > + return -EINVAL; > + > + ts->tv_sec = (((u64)gem_readl(bp, EFRSH) << 32) | > + gem_readl(bp, EFRSL)) & TSU_SEC_MAX_VAL; > + ts->tv_nsec = gem_readl(bp, EFRN); > + > + return 0; > +} > + > +static int gem_ptp_event(struct macb *bp, struct timespec64 *ts) > +{ > + struct ptp_clock_event event; > + > + event.type = PTP_CLOCK_EXTTS; > + event.index = 0; > + event.timestamp = ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec; > + > + ptp_clock_event(bp->ptp_clock, &event); Here you produce time stamps on external input events, but you said that you have only one channel: .n_ext_ts = 1, So why do you call this function... > + > + return 0; > +} > + > +void macb_ptp_int(struct macb_queue *queue, u32 status) > +{ > + struct macb *bp = queue->bp; > + struct timespec64 ts; > + > + if (status & MACB_BIT(DRQFR)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, MACB_BIT(DRQFR)); > + if (gem_ptp_time_frame_rx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); One ... > + } > + > + if (status & MACB_BIT(SFR)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, MACB_BIT(SFR)); > + if (gem_ptp_time_frame_rx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); Two ... > + } > + > + if (status & MACB_BIT(DRQFT)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, MACB_BIT(DRQFT)); > + if (gem_ptp_time_frame_tx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); Three ... > + } > + > + if (status & MACB_BIT(SFT)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, MACB_BIT(SFT)); > + if (gem_ptp_time_frame_tx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); > + } > + > + if (status & MACB_BIT(PDRQFR)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, MACB_BIT(PDRQFR)); > + if (gem_ptp_time_peer_frame_rx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); > + } > + > + if (status & MACB_BIT(PDRSFR)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, MACB_BIT(PDRSFR)); > + if (gem_ptp_time_peer_frame_rx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); > + } > + > + if (status & MACB_BIT(PDRQFT)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, > + MACB_BIT(PDRQFT)); > + if (gem_ptp_time_peer_frame_tx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); > + } > + > + if (status & MACB_BIT(PDRSFT)) { > + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > + queue_writel(queue, ISR, > + MACB_BIT(PDRSFT)); > + if (gem_ptp_time_peer_frame_tx_get(bp, &ts) != 0) { > + ts.tv_sec = 0; > + ts.tv_nsec = 0; > + } > + gem_ptp_event(bp, &ts); .. eight times? > + } > +} > -- > 2.4.5 > Thanks, Richard ^ permalink raw reply [flat|nested] 14+ messages in thread
* RE: [PATCH 3/4] net: macb: Add hardware PTP support 2017-04-14 18:28 ` Richard Cochran @ 2017-05-02 13:57 ` Rafal Ozieblo 2017-05-03 9:43 ` Richard Cochran 0 siblings, 1 reply; 14+ messages in thread From: Rafal Ozieblo @ 2017-05-02 13:57 UTC (permalink / raw) To: Richard Cochran Cc: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, Andrei.Pistirica > From: Richard Cochran [mailto:richardcochran@gmail.com] > Sent: 14 kwietnia 2017 20:29 > To: Rafal Ozieblo <rafalo@cadence.com> > Cc: David Miller <davem@davemloft.net>; nicolas.ferre@atmel.com; > netdev@vger.kernel.org; linux-kernel@vger.kernel.org; > devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org; > harinikatakamlinux@gmail.com; harini.katakam@xilinx.com; > Andrei.Pistirica@microchip.com > Subject: Re: [PATCH 3/4] net: macb: Add hardware PTP support > > On Thu, Apr 13, 2017 at 02:39:23PM +0100, Rafal Ozieblo wrote: (...) > > +static int gem_tsu_get_time(struct macb *bp, struct timespec64 *ts) > > +{ > > + long first, second; > > + u32 secl, sech; > > + unsigned long flags; > > + > > + if (!bp || !ts) > > + return -EINVAL; > > Useless test. Sorry for me being too carefulness, I'll remove all tests. > (...) > > +static int gem_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 > *ts) > > +{ > > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > > + > > + if (!ptp || !ts) > > + return -EINVAL; > > Useles test. > > What is the point of this wrapper function anyhow? Please remove it. gem_ptp_gettime() is assigned in ptp_clock_info and it has to have ptp_clock_info pointer as first parameter. gem_tsu_get_time() is used in the source code but with macb pointer. Do you want me to do something like: gem_ptp_gettime(macb->ptp, ts); and first would be getting macb pointer from ptp ? struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > > > + > > + gem_tsu_get_time(bp, ts); > > + return 0; > > +} > > + > > +static int gem_ptp_settime(struct ptp_clock_info *ptp, > > + const struct timespec64 *ts) > > +{ > > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > > + > > + if (!ptp || !ts) > > + return -EINVAL; > > Another useless function. ditto > > > + gem_tsu_set_time(bp, ts); > > + return 0; > > +} > > + > > +static int gem_ptp_enable(struct ptp_clock_info *ptp, > > + struct ptp_clock_request *rq, int on) > > +{ > > + struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); > > + > > + if (!ptp || !rq) > > + return -EINVAL; > > Sigh. > > > + > > + switch (rq->type) { > > + case PTP_CLK_REQ_EXTTS: /* Toggle TSU match interrupt */ > > + if (on) > > + macb_writel(bp, IER, MACB_BIT(TCI)); > > No locking to protect IER and IDE? There is no need. > > > + else > > + macb_writel(bp, IDR, MACB_BIT(TCI)); > > + break; > > + case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */ > > + return -EOPNOTSUPP; > > + /* break; */ > > + case PTP_CLK_REQ_PPS: /* Toggle TSU periodic (second) > interrupt */ > > + if (on) > > + macb_writel(bp, IER, MACB_BIT(SRI)); > > + else > > + macb_writel(bp, IDR, MACB_BIT(SRI)); > > + break; > > + default: > > + break; > > + } > > + return 0; > > +} > > + (...) > > -- > > 2.4.5 > > > > Thanks, > Richard ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] net: macb: Add hardware PTP support 2017-05-02 13:57 ` Rafal Ozieblo @ 2017-05-03 9:43 ` Richard Cochran 0 siblings, 0 replies; 14+ messages in thread From: Richard Cochran @ 2017-05-03 9:43 UTC (permalink / raw) To: Rafal Ozieblo Cc: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, Andrei.Pistirica On Tue, May 02, 2017 at 01:57:15PM +0000, Rafal Ozieblo wrote: > > What is the point of this wrapper function anyhow? Please remove it. > gem_ptp_gettime() is assigned in ptp_clock_info and it has to have > ptp_clock_info pointer as first parameter. gem_tsu_get_time() is used in > the source code but with macb pointer. > Do you want me to do something like: > gem_ptp_gettime(macb->ptp, ts); > and first would be getting macb pointer from ptp ? > struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); Yes. Unless your sub-function is used in more than one place, then it is wasteful and confusing to wrap the functionality for no apparent reason. > > > + switch (rq->type) { > > > + case PTP_CLK_REQ_EXTTS: /* Toggle TSU match interrupt */ > > > + if (on) > > > + macb_writel(bp, IER, MACB_BIT(TCI)); > > > > No locking to protect IER and IDE? > There is no need. But what happens when the PTP_CLK_REQ_EXTTS and PTP_CLK_REQ_PPS ioctls are called at the same time? You need to ensure that IDR is consistent. If the bits are write only, then you should comment this fact. > > > + else > > > + macb_writel(bp, IDR, MACB_BIT(TCI)); > > > + break; > > > + case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */ > > > + return -EOPNOTSUPP; > > > + /* break; */ > > > + case PTP_CLK_REQ_PPS: /* Toggle TSU periodic (second) > > interrupt */ > > > + if (on) > > > + macb_writel(bp, IER, MACB_BIT(SRI)); > > > + else > > > + macb_writel(bp, IDR, MACB_BIT(SRI)); > > > + break; > > > + default: > > > + break; > > > + } > > > + return 0; > > > +} Thanks, Richard ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 4/4] net: macb: Add macb_ptp to compilation chain 2017-04-13 13:33 [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Rafal Ozieblo 2017-04-13 13:38 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo 2017-04-13 13:39 ` [PATCH 3/4] net: macb: Add hardware PTP support Rafal Ozieblo @ 2017-04-13 13:39 ` Rafal Ozieblo 2017-04-14 7:53 ` Richard Cochran 2017-04-14 7:43 ` [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Richard Cochran 3 siblings, 1 reply; 14+ messages in thread From: Rafal Ozieblo @ 2017-04-13 13:39 UTC (permalink / raw) To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, richardcochran, Andrei.Pistirica Cc: Rafal Ozieblo Add macb_ptp.c to Makefile. In case that macb is compiled as a module, it has been renamed to cadence-macb.ko to avoid naming confusion in Makefile. Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> --- drivers/net/ethernet/cadence/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/Makefile b/drivers/net/ethernet/cadence/Makefile index 4ba7559..a7f6e04 100644 --- a/drivers/net/ethernet/cadence/Makefile +++ b/drivers/net/ethernet/cadence/Makefile @@ -1,6 +1,11 @@ # # Makefile for the Atmel network device drivers. # +cadence-macb-y := macb.o -obj-$(CONFIG_MACB) += macb.o +ifeq ($(CONFIG_MACB_USE_HWSTAMP),y) +cadence-macb-y += macb_ptp.o +endif + +obj-$(CONFIG_MACB) += cadence-macb.o obj-$(CONFIG_MACB_PCI) += macb_pci.o -- 2.4.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 4/4] net: macb: Add macb_ptp to compilation chain 2017-04-13 13:39 ` [PATCH 4/4] net: macb: Add macb_ptp to compilation chain Rafal Ozieblo @ 2017-04-14 7:53 ` Richard Cochran 0 siblings, 0 replies; 14+ messages in thread From: Richard Cochran @ 2017-04-14 7:53 UTC (permalink / raw) To: Rafal Ozieblo Cc: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, Andrei.Pistirica On Thu, Apr 13, 2017 at 02:39:58PM +0100, Rafal Ozieblo wrote: > Add macb_ptp.c to Makefile. > In case that macb is compiled as a module, it has been renamed to > cadence-macb.ko to avoid naming confusion in Makefile. Renaming modules will break user's modpobe scripts. Why not keep macb.ko as the build product and rename macb.c to macb_main.c instead? Thanks, Richard ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors 2017-04-13 13:33 [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Rafal Ozieblo ` (2 preceding siblings ...) 2017-04-13 13:39 ` [PATCH 4/4] net: macb: Add macb_ptp to compilation chain Rafal Ozieblo @ 2017-04-14 7:43 ` Richard Cochran 3 siblings, 0 replies; 14+ messages in thread From: Richard Cochran @ 2017-04-14 7:43 UTC (permalink / raw) To: Rafal Ozieblo Cc: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree, linux-arm-kernel, harinikatakamlinux, harini.katakam, Andrei.Pistirica On Thu, Apr 13, 2017 at 02:33:59PM +0100, Rafal Ozieblo wrote: > @@ -1921,9 +1972,13 @@ static void macb_configure_dma(struct macb *bp) > dmacfg &= ~GEM_BIT(TXCOEN); > > #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - if (bp->hw_dma_cap == HW_DMA_CAP_64B) > + if (bp->hw_dma_cap & HW_DMA_CAP_64B) > dmacfg |= GEM_BIT(ADDR64); > #endif > +#ifdef CONFIG_MACB_USE_HWSTAMP > + if (bp->hw_dma_cap & HW_DMA_CAP_PTP) > + dmacfg |= GEM_BIT(RXEXT) | GEM_BIT(TXEXT); > +#endif > netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n", > dmacfg); > gem_writel(bp, DMACFG, dmacfg); > @@ -1971,14 +2026,15 @@ static void macb_init_hw(struct macb *bp) > /* Initialize TX and RX buffers */ > macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma)); > #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - if (bp->hw_dma_cap == HW_DMA_CAP_64B) > + if (bp->hw_dma_cap & HW_DMA_CAP_64B) > macb_writel(bp, RBQPH, upper_32_bits(bp->rx_ring_dma)); > #endif > for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { > queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma)); > #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - if (bp->hw_dma_cap == HW_DMA_CAP_64B) > - queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma)); > + if (bp->hw_dma_cap & HW_DMA_CAP_64B) > + queue_writel(queue, TBQPH, > + upper_32_bits(queue->tx_ring_dma)); Align arg3 with arg1 please. > #endif > > /* Enable interrupts */ > @@ -2579,6 +2635,18 @@ static void macb_configure_caps(struct macb *bp, > dcfg = gem_readl(bp, DCFG2); > if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0) > bp->caps |= MACB_CAPS_FIFO_MODE; > + /* if HWSTAMP is configure and gem has the capability */ This comment is redundant. We can see that clearly in the code already. > +#ifdef CONFIG_MACB_USE_HWSTAMP > + bp->ptp_hw_support = false; No need to clear this again. (The struct was cleared after allocation, right?) > + if (gem_has_ptp(bp)) { Why not drop the #idef: if (IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) && gem_has_ptp(bp)) ... > + if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) > + pr_err("GEM doesn't support hardware ptp.\n"); > + else { > + pr_emerg("rozieblo: ptp_hw_support = true"); pr_emerg? > + bp->ptp_hw_support = true; > + } Proper if/else CodingStyle please. > + } > +#endif > } > > dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); > @@ -2716,7 +2784,7 @@ static int macb_init(struct platform_device *pdev) > queue->IMR = GEM_IMR(hw_q - 1); > queue->TBQP = GEM_TBQP(hw_q - 1); > #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - if (bp->hw_dma_cap == HW_DMA_CAP_64B) > + if (bp->hw_dma_cap & HW_DMA_CAP_64B) > queue->TBQPH = GEM_TBQPH(hw_q - 1); > #endif > } else { > @@ -2727,7 +2795,7 @@ static int macb_init(struct platform_device *pdev) > queue->IMR = MACB_IMR; > queue->TBQP = MACB_TBQP; > #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - if (bp->hw_dma_cap == HW_DMA_CAP_64B) > + if (bp->hw_dma_cap & HW_DMA_CAP_64B) > queue->TBQPH = MACB_TBQPH; > #endif > } > @@ -3307,19 +3375,24 @@ static int macb_probe(struct platform_device *pdev) > bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; > device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); > > -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) { > - dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); > - bp->hw_dma_cap = HW_DMA_CAP_64B; > - } else > - bp->hw_dma_cap = HW_DMA_CAP_32B; > -#endif > - > spin_lock_init(&bp->lock); > > /* setup capabilities */ > macb_configure_caps(bp, macb_config); > > +#ifdef MACB_EXT_DESC > + bp->hw_dma_cap = HW_DMA_CAP_32B; > +#endif > +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > + if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) { > + dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); > + bp->hw_dma_cap |= HW_DMA_CAP_64B; > + } > +#endif > +#ifdef CONFIG_MACB_USE_HWSTAMP > + if (bp->ptp_hw_support) > + bp->hw_dma_cap |= HW_DMA_CAP_PTP; So bp->ptp_hw_support is a waste of storage. You can test for (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) directly here, or return a flag from macb_configure_caps(), or set the hw_dma_cap flag in that function, ... > +#endif > platform_set_drvdata(pdev, dev); > > dev->irq = platform_get_irq(pdev, 0); > @@ -954,8 +972,12 @@ struct macb { > u32 wol; > > struct macb_ptp_info *ptp_info; /* macb-ptp interface */ > -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > - enum macb_hw_dma_cap hw_dma_cap; > +#ifdef MACB_EXT_DESC > + uint8_t hw_dma_cap; > +#endif > + > +#ifdef CONFIG_MACB_USE_HWSTAMP > + bool ptp_hw_support; Remove this, please. Thanks, Richard > #endif > }; > > -- > 2.4.5 > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors @ 2017-05-09 9:24 Rafal Ozieblo 2017-05-09 9:26 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo 0 siblings, 1 reply; 14+ messages in thread From: Rafal Ozieblo @ 2017-05-09 9:24 UTC (permalink / raw) To: David Miller, nicolas.ferre, Richard Cochran, netdev, linux-kernel, devicetree, linux-arm-kernel, harini.katakam, andrei.pistirica Cc: Rafal Ozieblo This patch adds support for PTP timestamps in DMA buffer descriptors. It checks capability at runtime and uses appropriate buffer descriptor. Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> --- drivers/net/ethernet/cadence/Kconfig | 10 ++- drivers/net/ethernet/cadence/macb.c | 117 ++++++++++++++++++++++++++--------- drivers/net/ethernet/cadence/macb.h | 32 +++++++--- 3 files changed, 122 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index 608bea1..427d65a 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -29,7 +29,15 @@ config MACB support for the MACB/GEM chip. To compile this driver as a module, choose M here: the module - will be called macb. + will be macb. + +config MACB_USE_HWSTAMP + bool "Use IEEE 1588 hwstamp" + depends on MACB + default y + imply PTP_1588_CLOCK + ---help--- + Enable IEEE 1588 Precision Time Protocol (PTP) support for MACB. config MACB_PCI tristate "Cadence PCI MACB/GEM support" diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 91f7492..3151429 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -79,33 +79,84 @@ #define MACB_HALT_TIMEOUT 1230 /* DMA buffer descriptor might be different size - * depends on hardware configuration. + * depends on hardware configuration: + * + * 1. dma address width 32 bits: + * word 1: 32 bit address of Data Buffer + * word 2: control + * + * 2. dma address width 64 bits: + * word 1: 32 bit address of Data Buffer + * word 2: control + * word 3: upper 32 bit address of Data Buffer + * word 4: unused + * + * 3. dma address width 32 bits with hardware timestamping: + * word 1: 32 bit address of Data Buffer + * word 2: control + * word 3: timestamp word 1 + * word 4: timestamp word 2 + * + * 4. dma address width 64 bits with hardware timestamping: + * word 1: 32 bit address of Data Buffer + * word 2: control + * word 3: upper 32 bit address of Data Buffer + * word 4: unused + * word 5: timestamp word 1 + * word 6: timestamp word 2 */ static unsigned int macb_dma_desc_get_size(struct macb *bp) { -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) - return sizeof(struct macb_dma_desc) + sizeof(struct macb_dma_desc_64); +#ifdef MACB_EXT_DESC + unsigned int desc_size; + + switch (bp->hw_dma_cap) { + case HW_DMA_CAP_64B: + desc_size = sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_64); + break; + case HW_DMA_CAP_PTP: + desc_size = sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_ptp); + break; + case HW_DMA_CAP_64B_PTP: + desc_size = sizeof(struct macb_dma_desc) + + sizeof(struct macb_dma_desc_64) + + sizeof(struct macb_dma_desc_ptp); + break; + default: + desc_size = sizeof(struct macb_dma_desc); + } + return desc_size; #endif return sizeof(struct macb_dma_desc); } -static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int idx) +static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int desc_idx) { -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - /* Dma buffer descriptor is 4 words length (instead of 2 words) - * for 64b GEM. - */ - if (bp->hw_dma_cap == HW_DMA_CAP_64B) - idx <<= 1; +#ifdef MACB_EXT_DESC + switch (bp->hw_dma_cap) { + case HW_DMA_CAP_64B: + case HW_DMA_CAP_PTP: + desc_idx <<= 1; + break; + case HW_DMA_CAP_64B_PTP: + desc_idx *= 3; + break; + default: + break; + } + return desc_idx; #endif - return idx; + return desc_idx; } #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT static struct macb_dma_desc_64 *macb_64b_desc(struct macb *bp, struct macb_dma_desc *desc) { - return (struct macb_dma_desc_64 *)((void *)desc + sizeof(struct macb_dma_desc)); + if (bp->hw_dma_cap & HW_DMA_CAP_64B) + return (struct macb_dma_desc_64 *)((void *)desc + sizeof(struct macb_dma_desc)); + return NULL; } #endif @@ -602,7 +653,7 @@ static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, dma_addr_ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT struct macb_dma_desc_64 *desc_64; - if (bp->hw_dma_cap == HW_DMA_CAP_64B) { + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { desc_64 = macb_64b_desc(bp, desc); desc_64->addrh = upper_32_bits(addr); } @@ -616,7 +667,7 @@ static dma_addr_t macb_get_addr(struct macb *bp, struct macb_dma_desc *desc) #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT struct macb_dma_desc_64 *desc_64; - if (bp->hw_dma_cap == HW_DMA_CAP_64B) { + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { desc_64 = macb_64b_desc(bp, desc); addr = ((u64)(desc_64->addrh) << 32); } @@ -715,7 +766,7 @@ static void macb_tx_error_task(struct work_struct *work) /* Reinitialize the TX desc queue */ queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma)); #endif /* Make TX ring reflect state of hardware */ @@ -1923,9 +1974,13 @@ static void macb_configure_dma(struct macb *bp) dmacfg &= ~GEM_BIT(TXCOEN); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) dmacfg |= GEM_BIT(ADDR64); #endif +#ifdef CONFIG_MACB_USE_HWSTAMP + if (bp->hw_dma_cap & HW_DMA_CAP_PTP) + dmacfg |= GEM_BIT(RXEXT) | GEM_BIT(TXEXT); +#endif netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n", dmacfg); gem_writel(bp, DMACFG, dmacfg); @@ -1973,13 +2028,13 @@ static void macb_init_hw(struct macb *bp) /* Initialize TX and RX buffers */ macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) macb_writel(bp, RBQPH, upper_32_bits(bp->rx_ring_dma)); #endif for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma)); #endif @@ -2581,6 +2636,12 @@ static void macb_configure_caps(struct macb *bp, dcfg = gem_readl(bp, DCFG2); if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0) bp->caps |= MACB_CAPS_FIFO_MODE; + if (IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) && gem_has_ptp(bp)) { + if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) + pr_err("GEM doesn't support hardware ptp.\n"); + else + bp->hw_dma_cap |= HW_DMA_CAP_PTP; + } } dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); @@ -2718,7 +2779,7 @@ static int macb_init(struct platform_device *pdev) queue->IMR = GEM_IMR(hw_q - 1); queue->TBQP = GEM_TBQP(hw_q - 1); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue->TBQPH = GEM_TBQPH(hw_q - 1); #endif } else { @@ -2729,7 +2790,7 @@ static int macb_init(struct platform_device *pdev) queue->IMR = MACB_IMR; queue->TBQP = MACB_TBQP; #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap == HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) queue->TBQPH = MACB_TBQPH; #endif } @@ -3309,19 +3370,17 @@ static int macb_probe(struct platform_device *pdev) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) { - dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); - bp->hw_dma_cap = HW_DMA_CAP_64B; - } else - bp->hw_dma_cap = HW_DMA_CAP_32B; -#endif - spin_lock_init(&bp->lock); /* setup capabilities */ macb_configure_caps(bp, macb_config); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) { + dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); + bp->hw_dma_cap |= HW_DMA_CAP_64B; + } +#endif platform_set_drvdata(pdev, dev); dev->irq = platform_get_irq(pdev, 0); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index ec037b0..4359b08 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -12,6 +12,10 @@ #include <linux/phy.h> +#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) +#define MACB_EXT_DESC +#endif + #define MACB_GREGS_NBR 16 #define MACB_GREGS_VERSION 2 #define MACB_MAX_QUEUES 8 @@ -269,6 +273,10 @@ #define GEM_RXBS_SIZE 8 #define GEM_DDRP_OFFSET 24 /* disc_when_no_ahb */ #define GEM_DDRP_SIZE 1 +#define GEM_RXEXT_OFFSET 28 /* RX extended Buffer Descriptor mode */ +#define GEM_RXEXT_SIZE 1 +#define GEM_TXEXT_OFFSET 29 /* TX extended Buffer Descriptor mode */ +#define GEM_TXEXT_SIZE 1 #define GEM_ADDR64_OFFSET 30 /* Address bus width - 64b or 32b */ #define GEM_ADDR64_SIZE 1 @@ -425,6 +433,11 @@ #define GEM_TX_PKT_BUFF_OFFSET 21 #define GEM_TX_PKT_BUFF_SIZE 1 + +/* Bitfields in DCFG5. */ +#define GEM_TSU_OFFSET 8 +#define GEM_TSU_SIZE 1 + /* Bitfields in DCFG6. */ #define GEM_PBUF_LSO_OFFSET 27 #define GEM_PBUF_LSO_SIZE 1 @@ -546,16 +559,21 @@ struct macb_dma_desc { u32 ctrl; }; -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -enum macb_hw_dma_cap { - HW_DMA_CAP_32B, - HW_DMA_CAP_64B, -}; +#ifdef MACB_EXT_DESC +#define HW_DMA_CAP_32B 0 +#define HW_DMA_CAP_64B (1 << 0) +#define HW_DMA_CAP_PTP (1 << 1) +#define HW_DMA_CAP_64B_PTP (HW_DMA_CAP_64B | HW_DMA_CAP_PTP) struct macb_dma_desc_64 { u32 addrh; u32 resvd; }; + +struct macb_dma_desc_ptp { + u32 ts_1; + u32 ts_2; +}; #endif /* DMA descriptor bitfields */ @@ -954,8 +972,8 @@ struct macb { u32 wol; struct macb_ptp_info *ptp_info; /* macb-ptp interface */ -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - enum macb_hw_dma_cap hw_dma_cap; +#ifdef MACB_EXT_DESC + uint8_t hw_dma_cap; #endif }; -- 2.4.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/4] net: macb: Add tsu_clk to device tree 2017-05-09 9:24 Rafal Ozieblo @ 2017-05-09 9:26 ` Rafal Ozieblo 2017-05-12 15:12 ` Rob Herring 0 siblings, 1 reply; 14+ messages in thread From: Rafal Ozieblo @ 2017-05-09 9:26 UTC (permalink / raw) To: David Miller, nicolas.ferre, Richard Cochran, netdev, linux-kernel, devicetree, linux-arm-kernel, harini.katakam, andrei.pistirica Cc: Rafal Ozieblo Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> --- Documentation/devicetree/bindings/net/macb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index 1506e94..27966ae 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -22,6 +22,7 @@ Required properties: Required elements: 'pclk', 'hclk' Optional elements: 'tx_clk' Optional elements: 'rx_clk' applies to cdns,zynqmp-gem + Optional elements: 'tsu_clk' - clocks: Phandles to input clocks. Optional properties for PHY child node: -- 2.4.5 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/4] net: macb: Add tsu_clk to device tree 2017-05-09 9:26 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo @ 2017-05-12 15:12 ` Rob Herring 0 siblings, 0 replies; 14+ messages in thread From: Rob Herring @ 2017-05-12 15:12 UTC (permalink / raw) To: Rafal Ozieblo Cc: David Miller, nicolas.ferre, Richard Cochran, netdev, linux-kernel, devicetree, linux-arm-kernel, harini.katakam, andrei.pistirica On Tue, May 09, 2017 at 10:26:50AM +0100, Rafal Ozieblo wrote: > Signed-off-by: Rafal Ozieblo <rafalo@cadence.com> > --- > Documentation/devicetree/bindings/net/macb.txt | 1 + > 1 file changed, 1 insertion(+) I acked the last version, please add acks when posting new versions. Rob ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2017-05-12 15:12 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2017-04-13 13:33 [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Rafal Ozieblo 2017-04-13 13:38 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo 2017-04-19 21:57 ` Rob Herring 2017-04-13 13:39 ` [PATCH 3/4] net: macb: Add hardware PTP support Rafal Ozieblo 2017-04-14 6:03 ` kbuild test robot 2017-04-14 7:42 ` kbuild test robot 2017-04-14 18:28 ` Richard Cochran 2017-05-02 13:57 ` Rafal Ozieblo 2017-05-03 9:43 ` Richard Cochran 2017-04-13 13:39 ` [PATCH 4/4] net: macb: Add macb_ptp to compilation chain Rafal Ozieblo 2017-04-14 7:53 ` Richard Cochran 2017-04-14 7:43 ` [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors Richard Cochran 2017-05-09 9:24 Rafal Ozieblo 2017-05-09 9:26 ` [PATCH 2/4] net: macb: Add tsu_clk to device tree Rafal Ozieblo 2017-05-12 15:12 ` Rob Herring
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).