gianfar: Implement PAUSE frame generation support
authorMatei Pavaluca <matei.pavaluca@freescale.com>
Mon, 27 Oct 2014 08:42:44 +0000 (10:42 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Oct 2014 18:33:16 +0000 (14:33 -0400)
The hardware can automatically generate pause frames when the number
of free buffers drops under a certain threshold, but in order to do this,
the address of the last free buffer needs to be written to a specific
register for each RX queue.

This has to be done in 'gfar_clean_rx_ring' which is called for each
RX queue. In order not to impact performance, by adding a register write
for each incoming packet, this operation is done only when the PAUSE frame
transmission is enabled.

Whenever the link is readjusted, this capability is turned on or off.

Signed-off-by: Matei Pavaluca <matei.pavaluca@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar.h
drivers/net/ethernet/freescale/gianfar_ethtool.c

index 329efcad4fe03a7779895c0fd79d8616bf0f4f12..86dccb26fecc6a177731d6860cf21e95ea6e40ae 100644 (file)
@@ -173,10 +173,12 @@ static void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
 static int gfar_init_bds(struct net_device *ndev)
 {
        struct gfar_private *priv = netdev_priv(ndev);
+       struct gfar __iomem *regs = priv->gfargrp[0].regs;
        struct gfar_priv_tx_q *tx_queue = NULL;
        struct gfar_priv_rx_q *rx_queue = NULL;
        struct txbd8 *txbdp;
        struct rxbd8 *rxbdp;
+       u32 *rfbptr;
        int i, j;
 
        for (i = 0; i < priv->num_tx_queues; i++) {
@@ -201,6 +203,7 @@ static int gfar_init_bds(struct net_device *ndev)
                txbdp->status |= TXBD_WRAP;
        }
 
+       rfbptr = &regs->rfbptr0;
        for (i = 0; i < priv->num_rx_queues; i++) {
                rx_queue = priv->rx_queue[i];
                rx_queue->cur_rx = rx_queue->rx_bd_base;
@@ -227,6 +230,8 @@ static int gfar_init_bds(struct net_device *ndev)
                        rxbdp++;
                }
 
+               rx_queue->rfbptr = rfbptr;
+               rfbptr += 2;
        }
 
        return 0;
@@ -336,6 +341,20 @@ static void gfar_init_tx_rx_base(struct gfar_private *priv)
        }
 }
 
+static void gfar_init_rqprm(struct gfar_private *priv)
+{
+       struct gfar __iomem *regs = priv->gfargrp[0].regs;
+       u32 __iomem *baddr;
+       int i;
+
+       baddr = &regs->rqprm0;
+       for (i = 0; i < priv->num_rx_queues; i++) {
+               gfar_write(baddr, priv->rx_queue[i]->rx_ring_size |
+                          (DEFAULT_RX_LFC_THR << FBTHR_SHIFT));
+               baddr++;
+       }
+}
+
 static void gfar_rx_buff_size_config(struct gfar_private *priv)
 {
        int frame_size = priv->ndev->mtu + ETH_HLEN + ETH_FCS_LEN;
@@ -396,6 +415,13 @@ static void gfar_mac_rx_config(struct gfar_private *priv)
        if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
                rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
 
+       /* Clear the LFC bit */
+       gfar_write(&regs->rctrl, rctrl);
+       /* Init flow control threshold values */
+       gfar_init_rqprm(priv);
+       gfar_write(&regs->ptv, DEFAULT_LFC_PTVVAL);
+       rctrl |= RCTRL_LFC;
+
        /* Init rctrl based on our settings */
        gfar_write(&regs->rctrl, rctrl);
 }
@@ -2859,6 +2885,10 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
                /* Setup the new bdp */
                gfar_new_rxbdp(rx_queue, bdp, newskb);
 
+               /* Update Last Free RxBD pointer for LFC */
+               if (unlikely(rx_queue->rfbptr && priv->tx_actual_en))
+                       gfar_write(rx_queue->rfbptr, (u32)bdp);
+
                /* Update to the next pointer */
                bdp = next_bd(bdp, base, rx_queue->rx_ring_size);
 
@@ -3393,6 +3423,9 @@ static noinline void gfar_update_link_state(struct gfar_private *priv)
 {
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
        struct phy_device *phydev = priv->phydev;
+       struct gfar_priv_rx_q *rx_queue = NULL;
+       int i;
+       struct rxbd8 *bdp;
 
        if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
                return;
@@ -3401,6 +3434,7 @@ static noinline void gfar_update_link_state(struct gfar_private *priv)
                u32 tempval1 = gfar_read(&regs->maccfg1);
                u32 tempval = gfar_read(&regs->maccfg2);
                u32 ecntrl = gfar_read(&regs->ecntrl);
+               u32 tx_flow_oldval = (tempval & MACCFG1_TX_FLOW);
 
                if (phydev->duplex != priv->oldduplex) {
                        if (!(phydev->duplex))
@@ -3445,6 +3479,26 @@ static noinline void gfar_update_link_state(struct gfar_private *priv)
                tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
                tempval1 |= gfar_get_flowctrl_cfg(priv);
 
+               /* Turn last free buffer recording on */
+               if ((tempval1 & MACCFG1_TX_FLOW) && !tx_flow_oldval) {
+                       for (i = 0; i < priv->num_rx_queues; i++) {
+                               rx_queue = priv->rx_queue[i];
+                               bdp = rx_queue->cur_rx;
+                               /* skip to previous bd */
+                               bdp = skip_bd(bdp, rx_queue->rx_ring_size - 1,
+                                             rx_queue->rx_bd_base,
+                                             rx_queue->rx_ring_size);
+
+                               if (rx_queue->rfbptr)
+                                       gfar_write(rx_queue->rfbptr, (u32)bdp);
+                       }
+
+                       priv->tx_actual_en = 1;
+               }
+
+               if (unlikely(!(tempval1 & MACCFG1_TX_FLOW) && tx_flow_oldval))
+                       priv->tx_actual_en = 0;
+
                gfar_write(&regs->maccfg1, tempval1);
                gfar_write(&regs->maccfg2, tempval);
                gfar_write(&regs->ecntrl, ecntrl);
index 6b008685837cb217f28f2a6985aaa30e4268b6ba..b581b8823a2a98a0ff8bc82ba71e41da20d1abaf 100644 (file)
@@ -99,6 +99,10 @@ extern const char gfar_driver_version[];
 #define GFAR_MAX_FIFO_STARVE   511
 #define GFAR_MAX_FIFO_STARVE_OFF 511
 
+#define FBTHR_SHIFT        24
+#define DEFAULT_RX_LFC_THR  16
+#define DEFAULT_LFC_PTVVAL  4
+
 #define DEFAULT_RX_BUFFER_SIZE  1536
 #define TX_RING_MOD_MASK(size) (size-1)
 #define RX_RING_MOD_MASK(size) (size-1)
@@ -273,6 +277,7 @@ extern const char gfar_driver_version[];
 
 #define RCTRL_TS_ENABLE        0x01000000
 #define RCTRL_PAL_MASK         0x001f0000
+#define RCTRL_LFC              0x00004000
 #define RCTRL_VLEX             0x00002000
 #define RCTRL_FILREN           0x00001000
 #define RCTRL_GHTX             0x00000400
@@ -849,7 +854,32 @@ struct gfar {
        u8      res23c[248];
        u32     attr;           /* 0x.bf8 - Attributes Register */
        u32     attreli;        /* 0x.bfc - Attributes Extract Length and Extract Index Register */
-       u8      res24[688];
+       u32     rqprm0; /* 0x.c00 - Receive queue parameters register 0 */
+       u32     rqprm1; /* 0x.c04 - Receive queue parameters register 1 */
+       u32     rqprm2; /* 0x.c08 - Receive queue parameters register 2 */
+       u32     rqprm3; /* 0x.c0c - Receive queue parameters register 3 */
+       u32     rqprm4; /* 0x.c10 - Receive queue parameters register 4 */
+       u32     rqprm5; /* 0x.c14 - Receive queue parameters register 5 */
+       u32     rqprm6; /* 0x.c18 - Receive queue parameters register 6 */
+       u32     rqprm7; /* 0x.c1c - Receive queue parameters register 7 */
+       u8      res24[36];
+       u32     rfbptr0; /* 0x.c44 - Last free RxBD pointer for ring 0 */
+       u8      res24a[4];
+       u32     rfbptr1; /* 0x.c4c - Last free RxBD pointer for ring 1 */
+       u8      res24b[4];
+       u32     rfbptr2; /* 0x.c54 - Last free RxBD pointer for ring 2 */
+       u8      res24c[4];
+       u32     rfbptr3; /* 0x.c5c - Last free RxBD pointer for ring 3 */
+       u8      res24d[4];
+       u32     rfbptr4; /* 0x.c64 - Last free RxBD pointer for ring 4 */
+       u8      res24e[4];
+       u32     rfbptr5; /* 0x.c6c - Last free RxBD pointer for ring 5 */
+       u8      res24f[4];
+       u32     rfbptr6; /* 0x.c74 - Last free RxBD pointer for ring 6 */
+       u8      res24g[4];
+       u32     rfbptr7; /* 0x.c7c - Last free RxBD pointer for ring 7 */
+       u8      res24h[4];
+       u8      res24x[556];
        u32     isrg0;          /* 0x.eb0 - Interrupt steering group 0 register */
        u32     isrg1;          /* 0x.eb4 - Interrupt steering group 1 register */
        u32     isrg2;          /* 0x.eb8 - Interrupt steering group 2 register */
@@ -1009,6 +1039,7 @@ struct gfar_priv_rx_q {
        /* RX Coalescing values */
        unsigned char rxcoalescing;
        unsigned long rxic;
+       u32 *rfbptr;
 };
 
 enum gfar_irqinfo_id {
@@ -1099,6 +1130,7 @@ struct gfar_private {
        unsigned int num_tx_queues;
        unsigned int num_rx_queues;
        unsigned int num_grps;
+       int tx_actual_en;
 
        /* Network Statistics */
        struct gfar_extra_stats extra_stats;
index 76d70708f864af66b4a525e671f021e0d30a2b7d..3e1a9c1a67a95ffdaddbcca9739a9e37dee66eac 100644 (file)
@@ -579,8 +579,13 @@ static int gfar_spauseparam(struct net_device *dev,
                        u32 tempval;
                        tempval = gfar_read(&regs->maccfg1);
                        tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
-                       if (priv->tx_pause_en)
+
+                       priv->tx_actual_en = 0;
+                       if (priv->tx_pause_en) {
+                               priv->tx_actual_en = 1;
                                tempval |= MACCFG1_TX_FLOW;
+                       }
+
                        if (priv->rx_pause_en)
                                tempval |= MACCFG1_RX_FLOW;
                        gfar_write(&regs->maccfg1, tempval);