From: Christina Jacob <cja...@marvell.com>

Add ethtool support to configure fec modes baser/rs and
support to fecth FEC stats from CGX as well PHY.

Configure fec mode
        - ethtool --set-fec eth0 encoding rs/baser/off/auto
Query fec mode
        - ethtool --show-fec eth0

Signed-off-by: Christina Jacob <cja...@marvell.com>
Signed-off-by: Sunil Goutham <sgout...@marvell.com>
Signed-off-by: Hariprasad Kelam <hke...@marvell.com>
---
 .../ethernet/marvell/octeontx2/nic/otx2_common.c   |  20 +++
 .../ethernet/marvell/octeontx2/nic/otx2_common.h   |   6 +
 .../ethernet/marvell/octeontx2/nic/otx2_ethtool.c  | 181 ++++++++++++++++++++-
 .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c   |   3 +
 4 files changed, 208 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c 
b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index 5ddedc3..1e67072 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -60,6 +60,19 @@ void otx2_update_lmac_stats(struct otx2_nic *pfvf)
        mutex_unlock(&pfvf->mbox.lock);
 }
 
+void otx2_update_lmac_fec_stats(struct otx2_nic *pfvf)
+{
+       struct msg_req *req;
+
+       if (!netif_running(pfvf->netdev))
+               return;
+       mutex_lock(&pfvf->mbox.lock);
+       req = otx2_mbox_alloc_msg_cgx_fec_stats(&pfvf->mbox);
+       if (req)
+               otx2_sync_mbox_msg(&pfvf->mbox);
+       mutex_unlock(&pfvf->mbox.lock);
+}
+
 int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx)
 {
        struct otx2_rcv_queue *rq = &pfvf->qset.rq[qidx];
@@ -1492,6 +1505,13 @@ void mbox_handler_cgx_stats(struct otx2_nic *pfvf,
                pfvf->hw.cgx_tx_stats[id] = rsp->tx_stats[id];
 }
 
+void mbox_handler_cgx_fec_stats(struct otx2_nic *pfvf,
+                               struct cgx_fec_stats_rsp *rsp)
+{
+       pfvf->hw.cgx_fec_corr_blks += rsp->fec_corr_blks;
+       pfvf->hw.cgx_fec_uncorr_blks += rsp->fec_uncorr_blks;
+}
+
 void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf,
                                  struct nix_txsch_alloc_rsp *rsp)
 {
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h 
b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index 143ae04..b3f3de9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -204,6 +204,8 @@ struct otx2_hw {
        struct otx2_drv_stats   drv_stats;
        u64                     cgx_rx_stats[CGX_RX_STATS_COUNT];
        u64                     cgx_tx_stats[CGX_TX_STATS_COUNT];
+       u64                     cgx_fec_corr_blks;
+       u64                     cgx_fec_uncorr_blks;
        u8                      cgx_links;  /* No. of CGX links present in HW */
        u8                      lbk_links;  /* No. of LBK links present in HW */
 };
@@ -660,6 +662,9 @@ void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf,
                                  struct nix_txsch_alloc_rsp *rsp);
 void mbox_handler_cgx_stats(struct otx2_nic *pfvf,
                            struct cgx_stats_rsp *rsp);
+void mbox_handler_cgx_fec_stats(struct otx2_nic *pfvf,
+                               struct cgx_fec_stats_rsp *rsp);
+void otx2_set_fec_stats_count(struct otx2_nic *pfvf);
 void mbox_handler_nix_bp_enable(struct otx2_nic *pfvf,
                                struct nix_bp_cfg_rsp *rsp);
 
@@ -668,6 +673,7 @@ void otx2_get_dev_stats(struct otx2_nic *pfvf);
 void otx2_get_stats64(struct net_device *netdev,
                      struct rtnl_link_stats64 *stats);
 void otx2_update_lmac_stats(struct otx2_nic *pfvf);
+void otx2_update_lmac_fec_stats(struct otx2_nic *pfvf);
 int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx);
 int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx);
 void otx2_set_ethtool_ops(struct net_device *netdev);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c 
b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
index e0199f0..e5b1a57 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
@@ -66,6 +66,8 @@ static const unsigned int otx2_n_dev_stats = 
ARRAY_SIZE(otx2_dev_stats);
 static const unsigned int otx2_n_drv_stats = ARRAY_SIZE(otx2_drv_stats);
 static const unsigned int otx2_n_queue_stats = ARRAY_SIZE(otx2_queue_stats);
 
+static struct cgx_fw_data *otx2_get_fwdata(struct otx2_nic *pfvf);
+
 static void otx2_get_drvinfo(struct net_device *netdev,
                             struct ethtool_drvinfo *info)
 {
@@ -128,6 +130,12 @@ static void otx2_get_strings(struct net_device *netdev, 
u32 sset, u8 *data)
 
        strcpy(data, "reset_count");
        data += ETH_GSTRING_LEN;
+       if (pfvf->linfo.fec) {
+               sprintf(data, "Fec Corrected Errors: ");
+               data += ETH_GSTRING_LEN;
+               sprintf(data, "Fec Uncorrected Errors: ");
+               data += ETH_GSTRING_LEN;
+       }
 }
 
 static void otx2_get_qset_stats(struct otx2_nic *pfvf,
@@ -160,11 +168,30 @@ static void otx2_get_qset_stats(struct otx2_nic *pfvf,
        }
 }
 
+static int otx2_get_phy_fec_stats(struct otx2_nic *pfvf)
+{
+       struct msg_req *req;
+       int rc = -ENOMEM;
+
+       mutex_lock(&pfvf->mbox.lock);
+       req = otx2_mbox_alloc_msg_cgx_get_phy_fec_stats(&pfvf->mbox);
+       if (!req)
+               goto end;
+
+       if (!otx2_sync_mbox_msg(&pfvf->mbox))
+               rc = 0;
+end:
+       mutex_unlock(&pfvf->mbox.lock);
+       return rc;
+}
+
 /* Get device and per queue statistics */
 static void otx2_get_ethtool_stats(struct net_device *netdev,
                                   struct ethtool_stats *stats, u64 *data)
 {
        struct otx2_nic *pfvf = netdev_priv(netdev);
+       u64 fec_corr_blks, fec_uncorr_blks;
+       struct cgx_fw_data *rsp;
        int stat;
 
        otx2_get_dev_stats(pfvf);
@@ -183,12 +210,43 @@ static void otx2_get_ethtool_stats(struct net_device 
*netdev,
        for (stat = 0; stat < CGX_TX_STATS_COUNT; stat++)
                *(data++) = pfvf->hw.cgx_tx_stats[stat];
        *(data++) = pfvf->reset_count;
+
+       /* Do not request fec stats if interface fec mode is none */
+       if (pfvf->linfo.fec == OTX2_FEC_NONE)
+               return;
+
+       fec_corr_blks = pfvf->hw.cgx_fec_corr_blks;
+       fec_uncorr_blks = pfvf->hw.cgx_fec_uncorr_blks;
+
+       rsp = otx2_get_fwdata(pfvf);
+       if (!IS_ERR(rsp) && rsp->fwdata.phy.misc.has_fec_stats &&
+           !otx2_get_phy_fec_stats(pfvf)) {
+               /* Fetch fwdata again because it's been recently populated with
+                * latest PHY FEC stats.
+                */
+               rsp = otx2_get_fwdata(pfvf);
+               if (!IS_ERR(rsp)) {
+                       struct fec_stats_s *p = &rsp->fwdata.phy.fec_stats;
+
+                       if (pfvf->linfo.fec == OTX2_FEC_BASER) {
+                               fec_corr_blks   = p->brfec_corr_blks;
+                               fec_uncorr_blks = p->brfec_uncorr_blks;
+                       } else {
+                               fec_corr_blks   = p->rsfec_corr_cws;
+                               fec_uncorr_blks = p->rsfec_uncorr_cws;
+                       }
+               }
+       }
+
+       *(data++) = fec_corr_blks;
+       *(data++) = fec_uncorr_blks;
 }
 
 static int otx2_get_sset_count(struct net_device *netdev, int sset)
 {
        struct otx2_nic *pfvf = netdev_priv(netdev);
-       int qstats_count;
+       int qstats_count, fec_stats_count = 0;
+       bool if_up = netif_running(netdev);
 
        if (sset != ETH_SS_STATS)
                return -EINVAL;
@@ -196,8 +254,16 @@ static int otx2_get_sset_count(struct net_device *netdev, 
int sset)
        qstats_count = otx2_n_queue_stats *
                       (pfvf->hw.rx_queues + pfvf->hw.tx_queues);
 
+       /* Do not show fec stats if interface fec mode is none */
+       if (!if_up || !pfvf->linfo.fec)
+               return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count +
+                       CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT + 1;
+
+       fec_stats_count = 2;
+       otx2_update_lmac_fec_stats(pfvf);
+
        return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count +
-               CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT + 1;
+              CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT + 1 + fec_stats_count;
 }
 
 /* Get no of queues device supports and current queue count */
@@ -859,6 +925,115 @@ static int otx2_get_ts_info(struct net_device *netdev,
        return 0;
 }
 
+static struct cgx_fw_data *otx2_get_fwdata(struct otx2_nic *pfvf)
+{
+       struct cgx_fw_data *rsp = NULL;
+       struct msg_req *req;
+       int err = 0;
+
+       mutex_lock(&pfvf->mbox.lock);
+       req = otx2_mbox_alloc_msg_cgx_get_aux_link_info(&pfvf->mbox);
+       if (!req) {
+               mutex_unlock(&pfvf->mbox.lock);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       err = otx2_sync_mbox_msg(&pfvf->mbox);
+       if (!err) {
+               rsp = (struct cgx_fw_data *)
+                       otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr);
+       } else {
+               rsp = ERR_PTR(err);
+       }
+
+       mutex_unlock(&pfvf->mbox.lock);
+       return rsp;
+}
+
+static int otx2_get_fecparam(struct net_device *netdev,
+                            struct ethtool_fecparam *fecparam)
+{
+       struct otx2_nic *pfvf = netdev_priv(netdev);
+       struct cgx_fw_data *rsp;
+       const int fec[] = {
+               ETHTOOL_FEC_OFF,
+               ETHTOOL_FEC_BASER,
+               ETHTOOL_FEC_RS,
+               ETHTOOL_FEC_BASER | ETHTOOL_FEC_RS};
+#define FEC_MAX_INDEX 3
+       if (pfvf->linfo.fec < FEC_MAX_INDEX)
+               fecparam->active_fec = fec[pfvf->linfo.fec];
+
+       rsp = otx2_get_fwdata(pfvf);
+       if (IS_ERR(rsp))
+               return PTR_ERR(rsp);
+
+       if (rsp->fwdata.supported_fec <= FEC_MAX_INDEX) {
+               if (!rsp->fwdata.supported_fec)
+                       fecparam->fec = ETHTOOL_FEC_NONE;
+               else
+                       fecparam->fec = fec[rsp->fwdata.supported_fec];
+       }
+       return 0;
+}
+
+static int otx2_set_fecparam(struct net_device *netdev,
+                            struct ethtool_fecparam *fecparam)
+{
+       struct otx2_nic *pfvf = netdev_priv(netdev);
+       struct mbox *mbox = &pfvf->mbox;
+       struct fec_mode *req, *rsp;
+       int err = 0, fec = 0;
+
+       switch (fecparam->fec) {
+       /* Firmware does not support AUTO mode consider it as FEC_NONE */
+       case ETHTOOL_FEC_OFF:
+       case ETHTOOL_FEC_AUTO:
+       case ETHTOOL_FEC_NONE:
+               fec = OTX2_FEC_NONE;
+               break;
+       case ETHTOOL_FEC_RS:
+               fec = OTX2_FEC_RS;
+               break;
+       case ETHTOOL_FEC_BASER:
+               fec = OTX2_FEC_BASER;
+               break;
+       default:
+               netdev_warn(pfvf->netdev, "Unsupported FEC mode: %d",
+                           fecparam->fec);
+               return -EINVAL;
+       }
+
+       if (fec == pfvf->linfo.fec)
+               return 0;
+
+       mutex_lock(&mbox->lock);
+       req = otx2_mbox_alloc_msg_cgx_set_fec_param(&pfvf->mbox);
+       if (!req) {
+               err = -ENOMEM;
+               goto end;
+       }
+       req->fec = fec;
+       err = otx2_sync_mbox_msg(&pfvf->mbox);
+       if (err)
+               goto end;
+
+       rsp = (struct fec_mode *)otx2_mbox_get_rsp(&pfvf->mbox.mbox,
+                                                  0, &req->hdr);
+       if (rsp->fec >= 0) {
+               pfvf->linfo.fec = rsp->fec;
+               /* clear stale counters */
+               pfvf->hw.cgx_fec_corr_blks = 0;
+               pfvf->hw.cgx_fec_uncorr_blks = 0;
+       } else {
+               err = rsp->fec;
+       }
+
+end:
+       mutex_unlock(&mbox->lock);
+       return err;
+}
+
 static const struct ethtool_ops otx2_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
                                     ETHTOOL_COALESCE_MAX_FRAMES,
@@ -886,6 +1061,8 @@ static const struct ethtool_ops otx2_ethtool_ops = {
        .get_pauseparam         = otx2_get_pauseparam,
        .set_pauseparam         = otx2_set_pauseparam,
        .get_ts_info            = otx2_get_ts_info,
+       .get_fecparam           = otx2_get_fecparam,
+       .set_fecparam           = otx2_set_fecparam,
 };
 
 void otx2_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c 
b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index 07ec85a..d024dac 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -779,6 +779,9 @@ static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf,
        case MBOX_MSG_CGX_STATS:
                mbox_handler_cgx_stats(pf, (struct cgx_stats_rsp *)msg);
                break;
+       case MBOX_MSG_CGX_FEC_STATS:
+               mbox_handler_cgx_fec_stats(pf, (struct cgx_fec_stats_rsp *)msg);
+               break;
        default:
                if (msg->rc)
                        dev_err(pf->dev,
-- 
2.7.4

Reply via email to