Message ID | 20240621045735.3031357-2-vineeth.karumanchi@amd.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 759cc793ebfc2d1a02f357ae97e5dcdcd63f758f |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | net: macb: WOL enhancements | expand |
On Fri, Jun 21, 2024 at 10:27:32AM +0530, Vineeth Karumanchi wrote: > When GEM is used as a wake device, it is not mandatory for the RX DMA > to be active. The RX engine in IP only needs to receive and identify > a wake packet through an interrupt. The wake packet is of no further > significance; hence, it is not required to be copied into memory. > By disabling RX DMA during suspend, we can avoid unnecessary DMA > processing of any incoming traffic. > > During suspend, perform either of the below operations: > > - tie-off/dummy descriptor: Disable unused queues by connecting > them to a looped descriptor chain without free slots. > > - queue disable: The newer IP version allows disabling individual queues. > > Co-developed-by: Harini Katakam <harini.katakam@amd.com> > Signed-off-by: Harini Katakam <harini.katakam@amd.com> > Signed-off-by: Vineeth Karumanchi <vineeth.karumanchi@amd.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Andrew
On 21.06.2024 07:57, Vineeth Karumanchi wrote: > When GEM is used as a wake device, it is not mandatory for the RX DMA > to be active. The RX engine in IP only needs to receive and identify > a wake packet through an interrupt. The wake packet is of no further > significance; hence, it is not required to be copied into memory. > By disabling RX DMA during suspend, we can avoid unnecessary DMA > processing of any incoming traffic. > > During suspend, perform either of the below operations: > > - tie-off/dummy descriptor: Disable unused queues by connecting > them to a looped descriptor chain without free slots. > > - queue disable: The newer IP version allows disabling individual queues. > > Co-developed-by: Harini Katakam <harini.katakam@amd.com> > Signed-off-by: Harini Katakam <harini.katakam@amd.com> > Signed-off-by: Vineeth Karumanchi <vineeth.karumanchi@amd.com> Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev> Tested-by: Claudiu Beznea <claudiu.beznea@tuxon.dev> # on SAMA7G5 > --- > drivers/net/ethernet/cadence/macb.h | 7 +++ > drivers/net/ethernet/cadence/macb_main.c | 60 ++++++++++++++++++++++-- > 2 files changed, 64 insertions(+), 3 deletions(-) > > diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h > index aa5700ac9c00..50cd35ef21ad 100644 > --- a/drivers/net/ethernet/cadence/macb.h > +++ b/drivers/net/ethernet/cadence/macb.h > @@ -645,6 +645,10 @@ > #define GEM_T2OFST_OFFSET 0 /* offset value */ > #define GEM_T2OFST_SIZE 7 > > +/* Bitfields in queue pointer registers */ > +#define MACB_QUEUE_DISABLE_OFFSET 0 /* disable queue */ > +#define MACB_QUEUE_DISABLE_SIZE 1 > + > /* Offset for screener type 2 compare values (T2CMPOFST). > * Note the offset is applied after the specified point, > * e.g. GEM_T2COMPOFST_ETYPE denotes the EtherType field, so an offset > @@ -733,6 +737,7 @@ > #define MACB_CAPS_NEEDS_RSTONUBR 0x00000100 > #define MACB_CAPS_MIIONRGMII 0x00000200 > #define MACB_CAPS_NEED_TSUCLK 0x00000400 > +#define MACB_CAPS_QUEUE_DISABLE 0x00000800 > #define MACB_CAPS_PCS 0x01000000 > #define MACB_CAPS_HIGH_SPEED 0x02000000 > #define MACB_CAPS_CLK_HW_CHG 0x04000000 > @@ -1254,6 +1259,8 @@ struct macb { > u32 (*macb_reg_readl)(struct macb *bp, int offset); > void (*macb_reg_writel)(struct macb *bp, int offset, u32 value); > > + struct macb_dma_desc *rx_ring_tieoff; > + dma_addr_t rx_ring_tieoff_dma; > size_t rx_buffer_size; > > unsigned int rx_ring_size; > diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c > index 241ce9a2fa99..9fc8c5a82bf8 100644 > --- a/drivers/net/ethernet/cadence/macb_main.c > +++ b/drivers/net/ethernet/cadence/macb_main.c > @@ -2477,6 +2477,12 @@ static void macb_free_consistent(struct macb *bp) > unsigned int q; > int size; > > + if (bp->rx_ring_tieoff) { > + dma_free_coherent(&bp->pdev->dev, macb_dma_desc_get_size(bp), > + bp->rx_ring_tieoff, bp->rx_ring_tieoff_dma); > + bp->rx_ring_tieoff = NULL; > + } > + > bp->macbgem_ops.mog_free_rx_buffers(bp); > > for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { > @@ -2568,6 +2574,16 @@ static int macb_alloc_consistent(struct macb *bp) > if (bp->macbgem_ops.mog_alloc_rx_buffers(bp)) > goto out_err; > > + /* Required for tie off descriptor for PM cases */ > + if (!(bp->caps & MACB_CAPS_QUEUE_DISABLE)) { > + bp->rx_ring_tieoff = dma_alloc_coherent(&bp->pdev->dev, > + macb_dma_desc_get_size(bp), > + &bp->rx_ring_tieoff_dma, > + GFP_KERNEL); > + if (!bp->rx_ring_tieoff) > + goto out_err; > + } > + > return 0; > > out_err: > @@ -2575,6 +2591,19 @@ static int macb_alloc_consistent(struct macb *bp) > return -ENOMEM; > } > > +static void macb_init_tieoff(struct macb *bp) > +{ > + struct macb_dma_desc *desc = bp->rx_ring_tieoff; > + > + if (bp->caps & MACB_CAPS_QUEUE_DISABLE) > + return; > + /* Setup a wrapping descriptor with no free slots > + * (WRAP and USED) to tie off/disable unused RX queues. > + */ > + macb_set_addr(bp, desc, MACB_BIT(RX_WRAP) | MACB_BIT(RX_USED)); > + desc->ctrl = 0; > +} > + > static void gem_init_rings(struct macb *bp) > { > struct macb_queue *queue; > @@ -2598,6 +2627,7 @@ static void gem_init_rings(struct macb *bp) > gem_rx_refill(queue); > } > > + macb_init_tieoff(bp); > } > > static void macb_init_rings(struct macb *bp) > @@ -2615,6 +2645,8 @@ static void macb_init_rings(struct macb *bp) > bp->queues[0].tx_head = 0; > bp->queues[0].tx_tail = 0; > desc->ctrl |= MACB_BIT(TX_WRAP); > + > + macb_init_tieoff(bp); > } > > static void macb_reset_hw(struct macb *bp) > @@ -5215,6 +5247,7 @@ static int __maybe_unused macb_suspend(struct device *dev) > unsigned long flags; > unsigned int q; > int err; > + u32 tmp; > > if (!device_may_wakeup(&bp->dev->dev)) > phy_exit(bp->sgmii_phy); > @@ -5224,17 +5257,38 @@ static int __maybe_unused macb_suspend(struct device *dev) > > if (bp->wol & MACB_WOL_ENABLED) { > spin_lock_irqsave(&bp->lock, flags); > - /* Flush all status bits */ > - macb_writel(bp, TSR, -1); > - macb_writel(bp, RSR, -1); > + > + /* Disable Tx and Rx engines before disabling the queues, > + * this is mandatory as per the IP spec sheet > + */ > + tmp = macb_readl(bp, NCR); > + macb_writel(bp, NCR, tmp & ~(MACB_BIT(TE) | MACB_BIT(RE))); > for (q = 0, queue = bp->queues; q < bp->num_queues; > ++q, ++queue) { > + /* Disable RX queues */ > + if (bp->caps & MACB_CAPS_QUEUE_DISABLE) { > + queue_writel(queue, RBQP, MACB_BIT(QUEUE_DISABLE)); > + } else { > + /* Tie off RX queues */ > + queue_writel(queue, RBQP, > + lower_32_bits(bp->rx_ring_tieoff_dma)); > +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT > + queue_writel(queue, RBQPH, > + upper_32_bits(bp->rx_ring_tieoff_dma)); > +#endif > + } > /* Disable all interrupts */ > queue_writel(queue, IDR, -1); > queue_readl(queue, ISR); > if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) > queue_writel(queue, ISR, -1); > } > + /* Enable Receive engine */ > + macb_writel(bp, NCR, tmp | MACB_BIT(RE)); > + /* Flush all status bits */ > + macb_writel(bp, TSR, -1); > + macb_writel(bp, RSR, -1); > + > /* Change interrupt handler and > * Enable WoL IRQ on queue 0 > */
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index aa5700ac9c00..50cd35ef21ad 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -645,6 +645,10 @@ #define GEM_T2OFST_OFFSET 0 /* offset value */ #define GEM_T2OFST_SIZE 7 +/* Bitfields in queue pointer registers */ +#define MACB_QUEUE_DISABLE_OFFSET 0 /* disable queue */ +#define MACB_QUEUE_DISABLE_SIZE 1 + /* Offset for screener type 2 compare values (T2CMPOFST). * Note the offset is applied after the specified point, * e.g. GEM_T2COMPOFST_ETYPE denotes the EtherType field, so an offset @@ -733,6 +737,7 @@ #define MACB_CAPS_NEEDS_RSTONUBR 0x00000100 #define MACB_CAPS_MIIONRGMII 0x00000200 #define MACB_CAPS_NEED_TSUCLK 0x00000400 +#define MACB_CAPS_QUEUE_DISABLE 0x00000800 #define MACB_CAPS_PCS 0x01000000 #define MACB_CAPS_HIGH_SPEED 0x02000000 #define MACB_CAPS_CLK_HW_CHG 0x04000000 @@ -1254,6 +1259,8 @@ struct macb { u32 (*macb_reg_readl)(struct macb *bp, int offset); void (*macb_reg_writel)(struct macb *bp, int offset, u32 value); + struct macb_dma_desc *rx_ring_tieoff; + dma_addr_t rx_ring_tieoff_dma; size_t rx_buffer_size; unsigned int rx_ring_size; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 241ce9a2fa99..9fc8c5a82bf8 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2477,6 +2477,12 @@ static void macb_free_consistent(struct macb *bp) unsigned int q; int size; + if (bp->rx_ring_tieoff) { + dma_free_coherent(&bp->pdev->dev, macb_dma_desc_get_size(bp), + bp->rx_ring_tieoff, bp->rx_ring_tieoff_dma); + bp->rx_ring_tieoff = NULL; + } + bp->macbgem_ops.mog_free_rx_buffers(bp); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { @@ -2568,6 +2574,16 @@ static int macb_alloc_consistent(struct macb *bp) if (bp->macbgem_ops.mog_alloc_rx_buffers(bp)) goto out_err; + /* Required for tie off descriptor for PM cases */ + if (!(bp->caps & MACB_CAPS_QUEUE_DISABLE)) { + bp->rx_ring_tieoff = dma_alloc_coherent(&bp->pdev->dev, + macb_dma_desc_get_size(bp), + &bp->rx_ring_tieoff_dma, + GFP_KERNEL); + if (!bp->rx_ring_tieoff) + goto out_err; + } + return 0; out_err: @@ -2575,6 +2591,19 @@ static int macb_alloc_consistent(struct macb *bp) return -ENOMEM; } +static void macb_init_tieoff(struct macb *bp) +{ + struct macb_dma_desc *desc = bp->rx_ring_tieoff; + + if (bp->caps & MACB_CAPS_QUEUE_DISABLE) + return; + /* Setup a wrapping descriptor with no free slots + * (WRAP and USED) to tie off/disable unused RX queues. + */ + macb_set_addr(bp, desc, MACB_BIT(RX_WRAP) | MACB_BIT(RX_USED)); + desc->ctrl = 0; +} + static void gem_init_rings(struct macb *bp) { struct macb_queue *queue; @@ -2598,6 +2627,7 @@ static void gem_init_rings(struct macb *bp) gem_rx_refill(queue); } + macb_init_tieoff(bp); } static void macb_init_rings(struct macb *bp) @@ -2615,6 +2645,8 @@ static void macb_init_rings(struct macb *bp) bp->queues[0].tx_head = 0; bp->queues[0].tx_tail = 0; desc->ctrl |= MACB_BIT(TX_WRAP); + + macb_init_tieoff(bp); } static void macb_reset_hw(struct macb *bp) @@ -5215,6 +5247,7 @@ static int __maybe_unused macb_suspend(struct device *dev) unsigned long flags; unsigned int q; int err; + u32 tmp; if (!device_may_wakeup(&bp->dev->dev)) phy_exit(bp->sgmii_phy); @@ -5224,17 +5257,38 @@ static int __maybe_unused macb_suspend(struct device *dev) if (bp->wol & MACB_WOL_ENABLED) { spin_lock_irqsave(&bp->lock, flags); - /* Flush all status bits */ - macb_writel(bp, TSR, -1); - macb_writel(bp, RSR, -1); + + /* Disable Tx and Rx engines before disabling the queues, + * this is mandatory as per the IP spec sheet + */ + tmp = macb_readl(bp, NCR); + macb_writel(bp, NCR, tmp & ~(MACB_BIT(TE) | MACB_BIT(RE))); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + /* Disable RX queues */ + if (bp->caps & MACB_CAPS_QUEUE_DISABLE) { + queue_writel(queue, RBQP, MACB_BIT(QUEUE_DISABLE)); + } else { + /* Tie off RX queues */ + queue_writel(queue, RBQP, + lower_32_bits(bp->rx_ring_tieoff_dma)); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + queue_writel(queue, RBQPH, + upper_32_bits(bp->rx_ring_tieoff_dma)); +#endif + } /* Disable all interrupts */ queue_writel(queue, IDR, -1); queue_readl(queue, ISR); if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) queue_writel(queue, ISR, -1); } + /* Enable Receive engine */ + macb_writel(bp, NCR, tmp | MACB_BIT(RE)); + /* Flush all status bits */ + macb_writel(bp, TSR, -1); + macb_writel(bp, RSR, -1); + /* Change interrupt handler and * Enable WoL IRQ on queue 0 */