]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
stmmac: add Rx watchdog support to mitigate the DMA irqs
authorGiuseppe CAVALLARO <peppe.cavallaro@st.com>
Sun, 25 Nov 2012 23:10:43 +0000 (23:10 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 26 Nov 2012 22:22:12 +0000 (17:22 -0500)
GMAC devices newer than databook 3.40 has an embedded timer
that can be used for mitigating the number of interrupts.
So this patch adds this optimizations.

At any rate, the Rx watchdog can be disable (on bugged HW) by
passing from the platform the riwt_off field.

In this implementation the rx timer stored in the Reg9 is fixed
to the max value. This will be tuned by using ethtool.

V2: added a platform parameter to force to disable the rx-watchdog
for example on new core where it is bugged.

V3: do not disable NAPI when Rx watchdog is used.

V4: a new extra statistic field has been added to show the early
receive status in the interrupt handler.
This patch also adds an extra check to avoid to call
napi_schedule when the DMA_INTR_ENA_RIE bit is disabled in the
Interrupt Mask register.

Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
include/linux/stmmac.h

index 723e9035f275e7490b3ca35af015a128e3781ed5..186d1480612210d39d26b05305243d8fc71e1c16 100644 (file)
 #define CHIP_DBG(fmt, args...)  do { } while (0)
 #endif
 
+/* Synopsys Core versions */
+#define        DWMAC_CORE_3_40 0x34
+#define        DWMAC_CORE_3_50 0x35
+
 #undef FRAME_FILTER_DEBUG
 /* #define FRAME_FILTER_DEBUG */
 
@@ -81,7 +85,7 @@ struct stmmac_extra_stats {
        unsigned long rx_missed_cntr;
        unsigned long rx_overflow_cntr;
        unsigned long rx_vlan;
-       /* Tx/Rx IRQ errors */
+       /* Tx/Rx IRQ error info */
        unsigned long tx_undeflow_irq;
        unsigned long tx_process_stopped_irq;
        unsigned long tx_jabber_irq;
@@ -91,7 +95,8 @@ struct stmmac_extra_stats {
        unsigned long rx_watchdog_irq;
        unsigned long tx_early_irq;
        unsigned long fatal_bus_error_irq;
-       /* Extra info */
+       /* Tx/Rx IRQ Events */
+       unsigned long rx_early_irq;
        unsigned long threshold;
        unsigned long tx_pkt_n;
        unsigned long rx_pkt_n;
@@ -101,11 +106,12 @@ struct stmmac_extra_stats {
        unsigned long tx_normal_irq_n;
        unsigned long tx_clean;
        unsigned long tx_reset_ic_bit;
+       unsigned long irq_receive_pmt_irq_n;
+       /* MMC info */
        unsigned long mmc_tx_irq_n;
        unsigned long mmc_rx_irq_n;
        unsigned long mmc_rx_csum_offload_irq_n;
        /* EEE */
-       unsigned long irq_receive_pmt_irq_n;
        unsigned long irq_tx_path_in_lpi_mode_n;
        unsigned long irq_tx_path_exit_lpi_mode_n;
        unsigned long irq_rx_path_in_lpi_mode_n;
@@ -165,6 +171,9 @@ struct stmmac_extra_stats {
 #define DMA_HW_FEAT_ACTPHYIF   0x70000000 /* Active/selected PHY interface */
 #define DEFAULT_DMA_PBL                8
 
+/* Max/Min RI Watchdog Timer count value */
+#define MAX_DMA_RIWT           0xff
+#define MIN_DMA_RIWT           0x20
 /* Tx coalesce parameters */
 #define STMMAC_COAL_TX_TIMER   40000
 #define STMMAC_MAX_COAL_TX_TICK        100000
@@ -306,6 +315,8 @@ struct stmmac_dma_ops {
                              struct stmmac_extra_stats *x);
        /* If supported then get the optional core features */
        unsigned int (*get_hw_feature) (void __iomem *ioaddr);
+       /* Program the HW RX Watchdog */
+       void (*rx_watchdog) (void __iomem *ioaddr, u32 riwt);
 };
 
 struct stmmac_ops {
index 0e4cacedc1f0e0cf44fad3bb84c5bac000f8e6ee..7ad56afd63247a9e3c73c27c923109e48e5f638d 100644 (file)
@@ -230,8 +230,5 @@ enum rtc_control {
 #define GMAC_MMC_TX_INTR   0x108
 #define GMAC_MMC_RX_CSUM_OFFLOAD   0x208
 
-/* Synopsys Core versions */
-#define        DWMAC_CORE_3_40 0x34
-
 extern const struct stmmac_dma_ops dwmac1000_dma_ops;
 #endif /* __DWMAC1000_H__ */
index 033500090f552aedbbc7bddb99e6acd77395ef69..bf83c03bfd06f3699164de132968622690a10965 100644 (file)
@@ -174,6 +174,11 @@ static unsigned int dwmac1000_get_hw_feature(void __iomem *ioaddr)
        return readl(ioaddr + DMA_HW_FEATURE);
 }
 
+static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt)
+{
+       writel(riwt, ioaddr + DMA_RX_WATCHDOG);
+}
+
 const struct stmmac_dma_ops dwmac1000_dma_ops = {
        .init = dwmac1000_dma_init,
        .dump_regs = dwmac1000_dump_dma_regs,
@@ -187,4 +192,5 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = {
        .stop_rx = dwmac_dma_stop_rx,
        .dma_interrupt = dwmac_dma_interrupt,
        .get_hw_feature = dwmac1000_get_hw_feature,
+       .rx_watchdog = dwmac1000_rx_watchdog,
 };
index e49c9a0fd6ffe9a8ad52f19134e2e8c1aa7dcd2e..807f30371e49415593cb6bbff74ca733a9f02388 100644 (file)
 #define DMA_CONTROL            0x00001018      /* Ctrl (Operational Mode) */
 #define DMA_INTR_ENA           0x0000101c      /* Interrupt Enable */
 #define DMA_MISSED_FRAME_CTR   0x00001020      /* Missed Frame Counter */
-#define DMA_AXI_BUS_MODE       0x00001028      /* AXI Bus Mode */
+/* Rx watchdog register */
+#define DMA_RX_WATCHDOG                0x00001024
+/* AXI Bus Mode */
+#define DMA_AXI_BUS_MODE       0x00001028
 #define DMA_CUR_TX_BUF_ADDR    0x00001050      /* Current Host Tx Buffer */
 #define DMA_CUR_RX_BUF_ADDR    0x00001054      /* Current Host Rx Buffer */
 #define DMA_HW_FEATURE         0x00001058      /* HW Feature Register */
index 73766e655011f2b849cee011fdbd6e363165e9fb..491d7e9306037a119ae1cb5e4454935213cc01e8 100644 (file)
@@ -204,17 +204,28 @@ int dwmac_dma_interrupt(void __iomem *ioaddr,
                }
        }
        /* TX/RX NORMAL interrupts */
-       if (intr_status & DMA_STATUS_NIS) {
+       if (likely(intr_status & DMA_STATUS_NIS)) {
                x->normal_irq_n++;
-               if (likely(intr_status & DMA_STATUS_RI))
-                       ret |= handle_rx;
-               if (intr_status & (DMA_STATUS_TI))
+               if (likely(intr_status & DMA_STATUS_RI)) {
+                       u32 value = readl(ioaddr + DMA_INTR_ENA);
+                       /* to schedule NAPI on real RIE event. */
+                       if (likely(value & DMA_INTR_ENA_RIE)) {
+                               x->rx_normal_irq_n++;
+                               ret |= handle_rx;
+                       }
+               }
+               if (likely(intr_status & DMA_STATUS_TI)) {
+                       x->tx_normal_irq_n++;
                        ret |= handle_tx;
+               }
+               if (unlikely(intr_status & DMA_STATUS_ERI))
+                       x->rx_early_irq++;
        }
        /* Optional hardware blocks, interrupts should be disabled */
        if (unlikely(intr_status &
                     (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI)))
                pr_info("%s: unexpected status %08x\n", __func__, intr_status);
+
        /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
        writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS);
 
index 01b34517ca8ee8da418ada7a4110d3547d1789bb..fe0535cd86cded0fb3d7709352c76750b6df65f3 100644 (file)
@@ -91,6 +91,8 @@ struct stmmac_priv {
        u32 tx_count_frames;
        u32 tx_coal_frames;
        u32 tx_coal_timer;
+       int use_riwt;
+       u32 rx_riwt;
 };
 
 extern int phyaddr;
index 15cb6ee6ee41a61fdae9c834bbb5aa5879adb876..7f7ccd217e4d6d7296bdc170b2a865047879dc9a 100644 (file)
@@ -76,7 +76,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
        STMMAC_STAT(rx_missed_cntr),
        STMMAC_STAT(rx_overflow_cntr),
        STMMAC_STAT(rx_vlan),
-       /* Tx/Rx IRQ errors */
+       /* Tx/Rx IRQ error info */
        STMMAC_STAT(tx_undeflow_irq),
        STMMAC_STAT(tx_process_stopped_irq),
        STMMAC_STAT(tx_jabber_irq),
@@ -86,7 +86,8 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
        STMMAC_STAT(rx_watchdog_irq),
        STMMAC_STAT(tx_early_irq),
        STMMAC_STAT(fatal_bus_error_irq),
-       /* Extra info */
+       /* Tx/Rx IRQ Events */
+       STMMAC_STAT(rx_early_irq),
        STMMAC_STAT(threshold),
        STMMAC_STAT(tx_pkt_n),
        STMMAC_STAT(rx_pkt_n),
@@ -96,10 +97,12 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
        STMMAC_STAT(tx_normal_irq_n),
        STMMAC_STAT(tx_clean),
        STMMAC_STAT(tx_reset_ic_bit),
+       STMMAC_STAT(irq_receive_pmt_irq_n),
+       /* MMC info */
        STMMAC_STAT(mmc_tx_irq_n),
        STMMAC_STAT(mmc_rx_irq_n),
        STMMAC_STAT(mmc_rx_csum_offload_irq_n),
-       STMMAC_STAT(irq_receive_pmt_irq_n),
+       /* EEE */
        STMMAC_STAT(irq_tx_path_in_lpi_mode_n),
        STMMAC_STAT(irq_tx_path_exit_lpi_mode_n),
        STMMAC_STAT(irq_rx_path_in_lpi_mode_n),
index d9d68649bdaa2e78dc503644252945ffee445715..542edbcd92c715c3e0474f321f92de632725e173 100644 (file)
@@ -603,6 +603,8 @@ static void init_dma_desc_rings(struct net_device *dev)
        priv->dirty_tx = 0;
        priv->cur_tx = 0;
 
+       if (priv->use_riwt)
+               dis_ic = 1;
        /* Clear the Rx/Tx descriptors */
        priv->hw->desc->init_rx_desc(priv->dma_rx, rxsize, dis_ic);
        priv->hw->desc->init_tx_desc(priv->dma_tx, txsize);
@@ -1106,6 +1108,11 @@ static int stmmac_open(struct net_device *dev)
 
        stmmac_init_tx_coalesce(priv);
 
+       if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) {
+               priv->rx_riwt = MAX_DMA_RIWT;
+               priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT);
+       }
+
        napi_enable(&priv->napi);
        netif_start_queue(dev);
 
@@ -1423,14 +1430,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
 #endif
                        skb->protocol = eth_type_trans(skb, priv->dev);
 
-                       if (unlikely(!priv->plat->rx_coe)) {
-                               /* No RX COE for old mac10/100 devices */
+                       if (unlikely(!priv->plat->rx_coe))
                                skb_checksum_none_assert(skb);
-                               netif_receive_skb(skb);
-                       } else {
+                       else
                                skb->ip_summed = CHECKSUM_UNNECESSARY;
-                               napi_gro_receive(&priv->napi, skb);
-                       }
+
+                       napi_gro_receive(&priv->napi, skb);
 
                        priv->dev->stats.rx_packets++;
                        priv->dev->stats.rx_bytes += frame_len;
@@ -2001,6 +2006,16 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        if (flow_ctrl)
                priv->flow_ctrl = FLOW_AUTO;    /* RX/TX pause on */
 
+       /* Rx Watchdog is available in the COREs newer than the 3.40.
+        * In some case, for example on bugged HW this feature
+        * has to be disable and this can be done by passing the
+        * riwt_off field from the platform.
+        */
+       if ((priv->synopsys_id >= DWMAC_CORE_3_50) && (!priv->plat->riwt_off)) {
+               priv->use_riwt = 1;
+               pr_info(" Enable RX Mitigation via HW Watchdog Timer\n");
+       }
+
        netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);
 
        spin_lock_init(&priv->lock);
@@ -2092,6 +2107,9 @@ int stmmac_suspend(struct net_device *ndev)
        netif_device_detach(ndev);
        netif_stop_queue(ndev);
 
+       if (priv->use_riwt)
+               dis_ic = 1;
+
        napi_disable(&priv->napi);
 
        /* Stop TX/RX DMA */
index a1547ea3920d226cf56286b119c0e72d7a5b8712..de5b2f8176ce27d5c1dad489129585e3296d0d0a 100644 (file)
@@ -104,6 +104,7 @@ struct plat_stmmacenet_data {
        int bugged_jumbo;
        int pmt;
        int force_sf_dma_mode;
+       int riwt_off;
        void (*fix_mac_speed)(void *priv, unsigned int speed);
        void (*bus_setup)(void __iomem *ioaddr);
        int (*init)(struct platform_device *pdev);