net: thunderx: Pause frame support
authorSunil Goutham <sgoutham@cavium.com>
Thu, 24 Nov 2016 09:18:03 +0000 (14:48 +0530)
committerDavid S. Miller <davem@davemloft.net>
Sat, 26 Nov 2016 01:21:17 +0000 (20:21 -0500)
Enable pause frames on both Rx and Tx side, configure pause
interval e.t.c. Also support for enable/disable pause frames
on Rx/Tx via ethtool has been added.

Signed-off-by: Sunil Goutham <sgoutham@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/cavium/thunder/nic.h
drivers/net/ethernet/cavium/thunder/nic_main.c
drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
drivers/net/ethernet/cavium/thunder/nicvf_main.c
drivers/net/ethernet/cavium/thunder/thunder_bgx.c
drivers/net/ethernet/cavium/thunder/thunder_bgx.h

index be8404a9a325079508f1bc2b0f899b7ad5437208..e739c715356283553f4ace131a251bc4b30d6de2 100644 (file)
@@ -149,6 +149,12 @@ struct nicvf_rss_info {
        u64 key[RSS_HASH_KEY_SIZE];
 } ____cacheline_aligned_in_smp;
 
+struct nicvf_pfc {
+       u8    autoneg;
+       u8    fc_rx;
+       u8    fc_tx;
+};
+
 enum rx_stats_reg_offset {
        RX_OCTS = 0x0,
        RX_UCAST = 0x1,
@@ -298,6 +304,7 @@ struct nicvf {
        bool                    tns_mode;
        bool                    loopback_supported;
        struct nicvf_rss_info   rss_info;
+       struct nicvf_pfc        pfc;
        struct tasklet_struct   qs_err_task;
        struct work_struct      reset_task;
 
@@ -358,6 +365,7 @@ struct nicvf {
 #define        NIC_MBOX_MSG_SNICVF_PTR         0x15    /* Send sqet nicvf ptr to PVF */
 #define        NIC_MBOX_MSG_LOOPBACK           0x16    /* Set interface in loopback */
 #define        NIC_MBOX_MSG_RESET_STAT_COUNTER 0x17    /* Reset statistics counters */
+#define        NIC_MBOX_MSG_PFC                0x18    /* Pause frame control */
 #define        NIC_MBOX_MSG_CFG_DONE           0xF0    /* VF configuration done */
 #define        NIC_MBOX_MSG_SHUTDOWN           0xF1    /* VF is being shutdown */
 
@@ -500,6 +508,14 @@ struct reset_stat_cfg {
        u16   sq_stat_mask;
 };
 
+struct pfc {
+       u8    msg;
+       u8    get; /* Get or set PFC settings */
+       u8    autoneg;
+       u8    fc_rx;
+       u8    fc_tx;
+};
+
 /* 128 bit shared memory between PF and each VF */
 union nic_mbx {
        struct { u8 msg; }      msg;
@@ -518,6 +534,7 @@ union nic_mbx {
        struct nicvf_ptr        nicvf;
        struct set_loopback     lbk;
        struct reset_stat_cfg   reset_stat;
+       struct pfc              pfc;
 };
 
 #define NIC_NODE_ID_MASK       0x03
index 17490ec024281b00094f2b739b4ea0aead9e56b4..767234e2e8f94bb0520bee29e1813f8934cae7a8 100644 (file)
@@ -898,6 +898,30 @@ static void nic_enable_vf(struct nicpf *nic, int vf, bool enable)
        bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, enable);
 }
 
+static void nic_pause_frame(struct nicpf *nic, int vf, struct pfc *cfg)
+{
+       int bgx, lmac;
+       struct pfc pfc;
+       union nic_mbx mbx = {};
+
+       if (vf >= nic->num_vf_en)
+               return;
+       bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+       lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+
+       if (cfg->get) {
+               bgx_lmac_get_pfc(nic->node, bgx, lmac, &pfc);
+               mbx.pfc.msg = NIC_MBOX_MSG_PFC;
+               mbx.pfc.autoneg = pfc.autoneg;
+               mbx.pfc.fc_rx = pfc.fc_rx;
+               mbx.pfc.fc_tx = pfc.fc_tx;
+               nic_send_msg_to_vf(nic, vf, &mbx);
+       } else {
+               bgx_lmac_set_pfc(nic->node, bgx, lmac, cfg);
+               nic_mbx_send_ack(nic, vf);
+       }
+}
+
 /* Interrupt handler to handle mailbox messages from VFs */
 static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
 {
@@ -1037,6 +1061,9 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
        case NIC_MBOX_MSG_RESET_STAT_COUNTER:
                ret = nic_reset_stat_counters(nic, vf, &mbx.reset_stat);
                break;
+       case NIC_MBOX_MSG_PFC:
+               nic_pause_frame(nic, vf, &mbx.pfc);
+               goto unlock;
        default:
                dev_err(&nic->pdev->dev,
                        "Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg);
index d4d76a72b998a57bb5a320be819e6c7bea7988fe..b0482410052d6c2f459d91fea635abe7d03f7cfa 100644 (file)
@@ -720,6 +720,55 @@ static int nicvf_set_channels(struct net_device *dev,
        return err;
 }
 
+static void nicvf_get_pauseparam(struct net_device *dev,
+                                struct ethtool_pauseparam *pause)
+{
+       struct nicvf *nic = netdev_priv(dev);
+       union nic_mbx mbx = {};
+
+       /* Supported only for 10G/40G interfaces */
+       if ((nic->mac_type == BGX_MODE_SGMII) ||
+           (nic->mac_type == BGX_MODE_QSGMII) ||
+           (nic->mac_type == BGX_MODE_RGMII))
+               return;
+
+       mbx.pfc.msg = NIC_MBOX_MSG_PFC;
+       mbx.pfc.get = 1;
+       if (!nicvf_send_msg_to_pf(nic, &mbx)) {
+               pause->autoneg = nic->pfc.autoneg;
+               pause->rx_pause = nic->pfc.fc_rx;
+               pause->tx_pause = nic->pfc.fc_tx;
+       }
+}
+
+static int nicvf_set_pauseparam(struct net_device *dev,
+                               struct ethtool_pauseparam *pause)
+{
+       struct nicvf *nic = netdev_priv(dev);
+       union nic_mbx mbx = {};
+
+       /* Supported only for 10G/40G interfaces */
+       if ((nic->mac_type == BGX_MODE_SGMII) ||
+           (nic->mac_type == BGX_MODE_QSGMII) ||
+           (nic->mac_type == BGX_MODE_RGMII))
+               return -EOPNOTSUPP;
+
+       if (pause->autoneg)
+               return -EOPNOTSUPP;
+
+       mbx.pfc.msg = NIC_MBOX_MSG_PFC;
+       mbx.pfc.get = 0;
+       mbx.pfc.fc_rx = pause->rx_pause;
+       mbx.pfc.fc_tx = pause->tx_pause;
+       if (nicvf_send_msg_to_pf(nic, &mbx))
+               return -EAGAIN;
+
+       nic->pfc.fc_rx = pause->rx_pause;
+       nic->pfc.fc_tx = pause->tx_pause;
+
+       return 0;
+}
+
 static const struct ethtool_ops nicvf_ethtool_ops = {
        .get_settings           = nicvf_get_settings,
        .get_link               = nicvf_get_link,
@@ -741,6 +790,8 @@ static const struct ethtool_ops nicvf_ethtool_ops = {
        .set_rxfh               = nicvf_set_rxfh,
        .get_channels           = nicvf_get_channels,
        .set_channels           = nicvf_set_channels,
+       .get_pauseparam         = nicvf_get_pauseparam,
+       .set_pauseparam         = nicvf_set_pauseparam,
        .get_ts_info            = ethtool_op_get_ts_info,
 };
 
index c6c23033e6ebb28178359cfcdf0f3deea20aad88..1eacec85dac3ade1fa74c15d2662709043dccb8c 100644 (file)
@@ -256,6 +256,12 @@ static void  nicvf_handle_mbx_intr(struct nicvf *nic)
                nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf;
                nic->pf_acked = true;
                break;
+       case NIC_MBOX_MSG_PFC:
+               nic->pfc.autoneg = mbx.pfc.autoneg;
+               nic->pfc.fc_rx = mbx.pfc.fc_rx;
+               nic->pfc.fc_tx = mbx.pfc.fc_tx;
+               nic->pf_acked = true;
+               break;
        default:
                netdev_err(nic->netdev,
                           "Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
index 29c727fc887a8491e6a5518f05ec0a24d4492013..9211c750e0642660bfdd79bb023f9058a89c18af 100644 (file)
@@ -212,6 +212,47 @@ void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
 }
 EXPORT_SYMBOL(bgx_lmac_rx_tx_enable);
 
+void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause)
+{
+       struct pfc *pfc = (struct pfc *)pause;
+       struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
+       struct lmac *lmac;
+       u64 cfg;
+
+       if (!bgx)
+               return;
+       lmac = &bgx->lmac[lmacid];
+       if (lmac->is_sgmii)
+               return;
+
+       cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
+       pfc->fc_rx = cfg & RX_EN;
+       pfc->fc_tx = cfg & TX_EN;
+       pfc->autoneg = 0;
+}
+EXPORT_SYMBOL(bgx_lmac_get_pfc);
+
+void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause)
+{
+       struct pfc *pfc = (struct pfc *)pause;
+       struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
+       struct lmac *lmac;
+       u64 cfg;
+
+       if (!bgx)
+               return;
+       lmac = &bgx->lmac[lmacid];
+       if (lmac->is_sgmii)
+               return;
+
+       cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
+       cfg &= ~(RX_EN | TX_EN);
+       cfg |= (pfc->fc_rx ? RX_EN : 0x00);
+       cfg |= (pfc->fc_tx ? TX_EN : 0x00);
+       bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, cfg);
+}
+EXPORT_SYMBOL(bgx_lmac_set_pfc);
+
 static void bgx_sgmii_change_link_state(struct lmac *lmac)
 {
        struct bgx *bgx = lmac->bgx;
@@ -525,6 +566,18 @@ static int bgx_lmac_xaui_init(struct bgx *bgx, struct lmac *lmac)
        cfg |= SMU_TX_CTL_DIC_EN;
        bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg);
 
+       /* Enable receive and transmission of pause frames */
+       bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, ((0xffffULL << 32) |
+                     BCK_EN | DRP_EN | TX_EN | RX_EN));
+       /* Configure pause time and interval */
+       bgx_reg_write(bgx, lmacid,
+                     BGX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME);
+       cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL);
+       cfg &= ~0xFFFFull;
+       bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL,
+                     cfg | (DEFAULT_PAUSE_TIME - 0x1000));
+       bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_ZERO, 0x01);
+
        /* take lmac_count into account */
        bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1));
        /* max packet size */
index 01cc7c8591313fca033a7e791bc57ccd9adb776b..c18ebfeb203919ea9b16e406bb678bd05a98a656 100644 (file)
@@ -27,6 +27,7 @@
 #define    MAX_BGX_CHANS_PER_LMAC              16
 #define    MAX_DMAC_PER_LMAC                   8
 #define    MAX_FRAME_SIZE                      9216
+#define    DEFAULT_PAUSE_TIME                  0xFFFF
 
 #define           BGX_ID_MASK                          0x3
 
 #define  SMU_RX_CTL_STATUS                     (3ull << 0)
 #define BGX_SMUX_TX_APPEND             0x20100
 #define  SMU_TX_APPEND_FCS_D                   BIT_ULL(2)
+#define BGX_SMUX_TX_PAUSE_PKT_TIME     0x20110
 #define BGX_SMUX_TX_MIN_PKT            0x20118
+#define BGX_SMUX_TX_PAUSE_PKT_INTERVAL 0x20120
+#define BGX_SMUX_TX_PAUSE_ZERO         0x20138
 #define BGX_SMUX_TX_INT                        0x20140
 #define BGX_SMUX_TX_CTL                        0x20178
 #define  SMU_TX_CTL_DIC_EN                     BIT_ULL(0)
 #define BGX_SMUX_CTL                   0x20200
 #define  SMU_CTL_RX_IDLE                       BIT_ULL(0)
 #define  SMU_CTL_TX_IDLE                       BIT_ULL(1)
+#define        BGX_SMUX_CBFC_CTL               0x20218
+#define        RX_EN                                   BIT_ULL(0)
+#define        TX_EN                                   BIT_ULL(1)
+#define        BCK_EN                                  BIT_ULL(2)
+#define        DRP_EN                                  BIT_ULL(3)
 
 #define BGX_GMP_PCS_MRX_CTL            0x30000
 #define         PCS_MRX_CTL_RST_AN                     BIT_ULL(9)
@@ -207,6 +216,9 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac);
 void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status);
 void bgx_lmac_internal_loopback(int node, int bgx_idx,
                                int lmac_idx, bool enable);
+void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause);
+void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause);
+
 void xcv_init_hw(void);
 void xcv_setup_link(bool link_up, int link_speed);