drivers: net: xgene: Add flow control initialization
authorIyappan Subramanian <isubramanian@apm.com>
Fri, 2 Dec 2016 00:41:43 +0000 (16:41 -0800)
committerDavid S. Miller <davem@davemloft.net>
Sat, 3 Dec 2016 20:46:50 +0000 (15:46 -0500)
This patch adds flow control/pause frame initialization and
advertising capabilities.

Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
Signed-off-by: Quan Nguyen <qnguyen@apm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h

index 23a0175..06e6816 100644 (file)
@@ -577,6 +577,17 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata)
        /* Rtype should be copied from FP */
        xgene_enet_wr_csr(pdata, RSIF_RAM_DBG_REG0_ADDR, 0);
 
+       /* Configure HW pause frame generation */
+       xgene_enet_rd_mcx_csr(pdata, CSR_MULTI_DPF0_ADDR, &value);
+       value = (DEF_QUANTA << 16) | (value & 0xFFFF);
+       xgene_enet_wr_mcx_csr(pdata, CSR_MULTI_DPF0_ADDR, value);
+
+       xgene_enet_wr_csr(pdata, RXBUF_PAUSE_THRESH, DEF_PAUSE_THRES);
+       xgene_enet_wr_csr(pdata, RXBUF_PAUSE_OFF_THRESH, DEF_PAUSE_OFF_THRES);
+
+       xgene_gmac_flowctl_tx(pdata, pdata->tx_pause);
+       xgene_gmac_flowctl_rx(pdata, pdata->rx_pause);
+
        /* Rx-Tx traffic resume */
        xgene_enet_wr_csr(pdata, CFG_LINK_AGGR_RESUME_0_ADDR, TX_PORT0);
 
@@ -749,6 +760,48 @@ static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata)
        }
 }
 
+static u32 xgene_enet_flowctrl_cfg(struct net_device *ndev)
+{
+       struct xgene_enet_pdata *pdata = netdev_priv(ndev);
+       struct phy_device *phydev = ndev->phydev;
+       u16 lcladv, rmtadv = 0;
+       u32 rx_pause, tx_pause;
+       u8 flowctl = 0;
+
+       if (!phydev->duplex || !pdata->pause_autoneg)
+               return 0;
+
+       if (pdata->tx_pause)
+               flowctl |= FLOW_CTRL_TX;
+
+       if (pdata->rx_pause)
+               flowctl |= FLOW_CTRL_RX;
+
+       lcladv = mii_advertise_flowctrl(flowctl);
+
+       if (phydev->pause)
+               rmtadv = LPA_PAUSE_CAP;
+
+       if (phydev->asym_pause)
+               rmtadv |= LPA_PAUSE_ASYM;
+
+       flowctl = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+       tx_pause = !!(flowctl & FLOW_CTRL_TX);
+       rx_pause = !!(flowctl & FLOW_CTRL_RX);
+
+       if (tx_pause != pdata->tx_pause) {
+               pdata->tx_pause = tx_pause;
+               pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause);
+       }
+
+       if (rx_pause != pdata->rx_pause) {
+               pdata->rx_pause = rx_pause;
+               pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause);
+       }
+
+       return 0;
+}
+
 static void xgene_enet_adjust_link(struct net_device *ndev)
 {
        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
@@ -763,6 +816,8 @@ static void xgene_enet_adjust_link(struct net_device *ndev)
                        mac_ops->tx_enable(pdata);
                        phy_print_status(phydev);
                }
+
+               xgene_enet_flowctrl_cfg(ndev);
        } else {
                mac_ops->rx_disable(pdata);
                mac_ops->tx_disable(pdata);
@@ -836,6 +891,8 @@ int xgene_enet_phy_connect(struct net_device *ndev)
        phy_dev->supported &= ~SUPPORTED_10baseT_Half &
                              ~SUPPORTED_100baseT_Half &
                              ~SUPPORTED_1000baseT_Half;
+       phy_dev->supported |= SUPPORTED_Pause |
+                             SUPPORTED_Asym_Pause;
        phy_dev->advertising = phy_dev->supported;
 
        return 0;
index 7ba649d..5f83037 100644 (file)
@@ -172,6 +172,13 @@ enum xgene_enet_rm {
 #define CFG_CLE_FPSEL0(val)            (((val) << 16) & GENMASK(19, 16))
 #define CSR_ECM_CFG_0_ADDR             0x0220
 #define CSR_ECM_CFG_1_ADDR             0x0224
+#define CSR_MULTI_DPF0_ADDR            0x0230
+#define RXBUF_PAUSE_THRESH             0x0534
+#define RXBUF_PAUSE_OFF_THRESH         0x0540
+#define DEF_PAUSE_THRES                        0x7d
+#define DEF_PAUSE_OFF_THRES            0x6d
+#define DEF_QUANTA                     0x8000
+#define NORM_PAUSE_OPCODE              0x0001
 #define PAUSE_XON_EN                   BIT(30)
 #define MULTI_DPF_AUTOCTRL             BIT(28)
 #define CFG_CLE_NXTFPSEL0(val)         (((val) << 20) & GENMASK(23, 20))
index 1c9b8ba..a8e063b 100644 (file)
@@ -393,9 +393,11 @@ static void xgene_sgmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable)
 
 static void xgene_sgmac_init(struct xgene_enet_pdata *p)
 {
+       u32 pause_thres_reg, pause_off_thres_reg;
        u32 enet_spare_cfg_reg, rsif_config_reg;
        u32 cfg_bypass_reg, rx_dv_gate_reg;
-       u32 data, offset;
+       u32 data, data1, data2, offset;
+       u32 multi_dpf_reg;
 
        if (!(p->enet_id == XGENE_ENET2 && p->mdio_driver))
                xgene_sgmac_reset(p);
@@ -431,6 +433,46 @@ static void xgene_sgmac_init(struct xgene_enet_pdata *p)
        data |= CFG_RSIF_FPBUFF_TIMEOUT_EN;
        xgene_enet_wr_csr(p, rsif_config_reg, data);
 
+       /* Configure HW pause frame generation */
+       multi_dpf_reg = (p->enet_id == XGENE_ENET1) ? CSR_MULTI_DPF0_ADDR :
+                        XG_MCX_MULTI_DPF0_ADDR;
+       data = xgene_enet_rd_mcx_csr(p, multi_dpf_reg);
+       data = (DEF_QUANTA << 16) | (data & 0xffff);
+       xgene_enet_wr_mcx_csr(p, multi_dpf_reg, data);
+
+       if (p->enet_id != XGENE_ENET1) {
+               data = xgene_enet_rd_mcx_csr(p, XG_MCX_MULTI_DPF1_ADDR);
+               data =  (NORM_PAUSE_OPCODE << 16) | (data & 0xFFFF);
+               xgene_enet_wr_mcx_csr(p, XG_MCX_MULTI_DPF1_ADDR, data);
+       }
+
+       pause_thres_reg = (p->enet_id == XGENE_ENET1) ? RXBUF_PAUSE_THRESH :
+                          XG_RXBUF_PAUSE_THRESH;
+       pause_off_thres_reg = (p->enet_id == XGENE_ENET1) ?
+                              RXBUF_PAUSE_OFF_THRESH : 0;
+
+       if (p->enet_id == XGENE_ENET1) {
+               data1 = xgene_enet_rd_csr(p, pause_thres_reg);
+               data2 = xgene_enet_rd_csr(p, pause_off_thres_reg);
+
+               if (!(p->port_id % 2)) {
+                       data1 = (data1 & 0xffff0000) | DEF_PAUSE_THRES;
+                       data2 = (data2 & 0xffff0000) | DEF_PAUSE_OFF_THRES;
+               } else {
+                       data1 = (data1 & 0xffff) | (DEF_PAUSE_THRES << 16);
+                       data2 = (data2 & 0xffff) | (DEF_PAUSE_OFF_THRES << 16);
+               }
+
+               xgene_enet_wr_csr(p, pause_thres_reg, data1);
+               xgene_enet_wr_csr(p, pause_off_thres_reg, data2);
+       } else {
+               data = (DEF_PAUSE_OFF_THRES << 16) | DEF_PAUSE_THRES;
+               xgene_enet_wr_csr(p, pause_thres_reg, data);
+       }
+
+       xgene_sgmac_flowctl_tx(p, p->tx_pause);
+       xgene_sgmac_flowctl_rx(p, p->rx_pause);
+
        /* Bypass traffic gating */
        xgene_enet_wr_csr(p, XG_ENET_SPARE_CFG_REG_1_ADDR, 0x84);
        xgene_enet_wr_csr(p, cfg_bypass_reg, RESUME_TX);
index 727ef4b..ece19e6 100644 (file)
@@ -349,6 +349,23 @@ static void xgene_xgmac_init(struct xgene_enet_pdata *pdata)
        xgene_enet_wr_csr(pdata, XG_ENET_SPARE_CFG_REG_1_ADDR, 0x82);
        xgene_enet_wr_csr(pdata, XGENET_RX_DV_GATE_REG_0_ADDR, 0);
        xgene_enet_wr_csr(pdata, XG_CFG_BYPASS_ADDR, RESUME_TX);
+
+       /* Configure HW pause frame generation */
+       xgene_enet_rd_axg_csr(pdata, XGENET_CSR_MULTI_DPF0_ADDR, &data);
+       data = (DEF_QUANTA << 16) | (data & 0xFFFF);
+       xgene_enet_wr_axg_csr(pdata, XGENET_CSR_MULTI_DPF0_ADDR, data);
+
+       if (pdata->enet_id != XGENE_ENET1) {
+               xgene_enet_rd_axg_csr(pdata, XGENET_CSR_MULTI_DPF1_ADDR, &data);
+               data = (NORM_PAUSE_OPCODE << 16) | (data & 0xFFFF);
+               xgene_enet_wr_axg_csr(pdata, XGENET_CSR_MULTI_DPF1_ADDR, data);
+       }
+
+       data = (XG_DEF_PAUSE_OFF_THRES << 16) | XG_DEF_PAUSE_THRES;
+       xgene_enet_wr_csr(pdata, XG_RXBUF_PAUSE_THRESH, data);
+
+       xgene_xgmac_flowctl_tx(pdata, pdata->tx_pause);
+       xgene_xgmac_flowctl_rx(pdata, pdata->rx_pause);
 }
 
 static void xgene_xgmac_rx_enable(struct xgene_enet_pdata *pdata)
index 9d75020..03b847a 100644 (file)
 
 #define XG_MCX_RX_DV_GATE_REG_0_ADDR   0x0004
 #define XG_MCX_ECM_CFG_0_ADDR          0x0074
+#define XG_MCX_MULTI_DPF0_ADDR         0x007c
+#define XG_MCX_MULTI_DPF1_ADDR         0x0080
+#define XG_DEF_PAUSE_THRES             0x390
+#define XG_DEF_PAUSE_OFF_THRES         0x2c0
 #define XG_RSIF_CONFIG_REG_ADDR                0x00a0
 #define XCLE_BYPASS_REG0_ADDR           0x0160
 #define XCLE_BYPASS_REG1_ADDR           0x0164
@@ -72,6 +76,9 @@
 #define XG_ENET_SPARE_CFG_REG_1_ADDR   0x0410
 #define XGENET_RX_DV_GATE_REG_0_ADDR   0x0804
 #define XGENET_CSR_ECM_CFG_0_ADDR      0x0880
+#define XGENET_CSR_MULTI_DPF0_ADDR     0x0888
+#define XGENET_CSR_MULTI_DPF1_ADDR     0x088c
+#define XG_RXBUF_PAUSE_THRESH          0x0020
 #define XG_MCX_ICM_CONFIG0_REG_0_ADDR  0x00e0
 #define XG_MCX_ICM_CONFIG2_REG_0_ADDR  0x00e8