liquidio: Add the features to show FEC settings and set FEC settings
authorWeilin Chang <weilin.chang@cavium.com>
Mon, 17 Sep 2018 05:43:32 +0000 (22:43 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Sep 2018 15:22:26 +0000 (08:22 -0700)
1. Add functions for get_fecparam and set_fecparam.
2. Modify lio_get_link_ksettings to display FEC setting.

Signed-off-by: Weilin Chang <weilin.chang@cavium.com>
Acked-by: Derek Chickles <derek.chickles@cavium.com>
Signed-off-by: Felix Manlunas <felix.manlunas@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/cavium/liquidio/lio_core.c
drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/cavium/liquidio/liquidio_common.h
drivers/net/ethernet/cavium/liquidio/octeon_device.h
drivers/net/ethernet/cavium/liquidio/octeon_network.h

index 52b32b8ad8c0511383500bac534391d23d182e3c..eb96b0613cf6d3ecc750d556e005baba7c2c88a6 100644 (file)
@@ -1654,3 +1654,151 @@ int liquidio_get_speed(struct lio *lio)
 
        return retval;
 }
+
+int liquidio_set_fec(struct lio *lio, int on_off)
+{
+       struct oct_nic_seapi_resp *resp;
+       struct octeon_soft_command *sc;
+       struct octeon_device *oct;
+       union octnet_cmd *ncmd;
+       int retval;
+       u32 var;
+
+       oct = lio->oct_dev;
+
+       if (oct->props[lio->ifidx].fec == on_off)
+               return 0;
+
+       if (!OCTEON_CN23XX_PF(oct)) {
+               dev_err(&oct->pci_dev->dev, "%s: SET FEC only for PF\n",
+                       __func__);
+               return -1;
+       }
+
+       if (oct->speed_boot != 25)  {
+               dev_err(&oct->pci_dev->dev,
+                       "Set FEC only when link speed is 25G during insmod\n");
+               return -1;
+       }
+
+       sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
+                                      sizeof(struct oct_nic_seapi_resp), 0);
+
+       ncmd = sc->virtdptr;
+       resp = sc->virtrptr;
+       memset(resp, 0, sizeof(struct oct_nic_seapi_resp));
+
+       init_completion(&sc->complete);
+       sc->sc_status = OCTEON_REQUEST_PENDING;
+
+       ncmd->u64 = 0;
+       ncmd->s.cmd = SEAPI_CMD_FEC_SET;
+       ncmd->s.param1 = on_off;
+       /* SEAPI_CMD_FEC_DISABLE(0) or SEAPI_CMD_FEC_RS(1) */
+
+       octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));
+
+       sc->iq_no = lio->linfo.txpciq[0].s.q_no;
+
+       octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
+                                   OPCODE_NIC_UBOOT_CTL, 0, 0, 0);
+
+       retval = octeon_send_soft_command(oct, sc);
+       if (retval == IQ_SEND_FAILED) {
+               dev_info(&oct->pci_dev->dev, "Failed to send soft command\n");
+               octeon_free_soft_command(oct, sc);
+               return -EIO;
+       }
+
+       retval = wait_for_sc_completion_timeout(oct, sc, 0);
+       if (retval)
+               return (-EIO);
+
+       var = be32_to_cpu(resp->fec_setting);
+       resp->fec_setting = var;
+       if (var != on_off) {
+               dev_err(&oct->pci_dev->dev,
+                       "Setting failed fec= %x, expect %x\n",
+                       var, on_off);
+               oct->props[lio->ifidx].fec = var;
+               if (resp->fec_setting == SEAPI_CMD_FEC_SET_RS)
+                       oct->props[lio->ifidx].fec = 1;
+               else
+                       oct->props[lio->ifidx].fec = 0;
+       }
+
+       WRITE_ONCE(sc->caller_is_done, true);
+
+       if (oct->props[lio->ifidx].fec !=
+           oct->props[lio->ifidx].fec_boot) {
+               dev_dbg(&oct->pci_dev->dev,
+                       "Reloade driver to chang fec to %s\n",
+                       oct->props[lio->ifidx].fec ? "on" : "off");
+       }
+
+       return retval;
+}
+
+int liquidio_get_fec(struct lio *lio)
+{
+       struct oct_nic_seapi_resp *resp;
+       struct octeon_soft_command *sc;
+       struct octeon_device *oct;
+       union octnet_cmd *ncmd;
+       int retval;
+       u32 var;
+
+       oct = lio->oct_dev;
+
+       sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
+                                      sizeof(struct oct_nic_seapi_resp), 0);
+       if (!sc)
+               return -ENOMEM;
+
+       ncmd = sc->virtdptr;
+       resp = sc->virtrptr;
+       memset(resp, 0, sizeof(struct oct_nic_seapi_resp));
+
+       init_completion(&sc->complete);
+       sc->sc_status = OCTEON_REQUEST_PENDING;
+
+       ncmd->u64 = 0;
+       ncmd->s.cmd = SEAPI_CMD_FEC_GET;
+
+       octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));
+
+       sc->iq_no = lio->linfo.txpciq[0].s.q_no;
+
+       octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
+                                   OPCODE_NIC_UBOOT_CTL, 0, 0, 0);
+
+       retval = octeon_send_soft_command(oct, sc);
+       if (retval == IQ_SEND_FAILED) {
+               dev_info(&oct->pci_dev->dev,
+                        "%s: Failed to send soft command\n", __func__);
+               octeon_free_soft_command(oct, sc);
+               return -EIO;
+       }
+
+       retval = wait_for_sc_completion_timeout(oct, sc, 0);
+       if (retval)
+               return retval;
+
+       var = be32_to_cpu(resp->fec_setting);
+       resp->fec_setting = var;
+       if (resp->fec_setting == SEAPI_CMD_FEC_SET_RS)
+               oct->props[lio->ifidx].fec = 1;
+       else
+               oct->props[lio->ifidx].fec = 0;
+
+       WRITE_ONCE(sc->caller_is_done, true);
+
+       if (oct->props[lio->ifidx].fec !=
+           oct->props[lio->ifidx].fec_boot) {
+               dev_dbg(&oct->pci_dev->dev,
+                       "Reloade driver to chang fec to %s\n",
+                       oct->props[lio->ifidx].fec ? "on" : "off");
+       }
+
+       return retval;
+}
index 9e53cdb9ec659b9f5bf909d815630757d68fc805..4c3925af53bccf0215f9fabd037adf8ab993ea46 100644 (file)
@@ -244,6 +244,7 @@ static int lio_get_link_ksettings(struct net_device *netdev,
                    linfo->link.s.if_mode == INTERFACE_MODE_XLAUI ||
                    linfo->link.s.if_mode == INTERFACE_MODE_XFI) {
                        dev_dbg(&oct->pci_dev->dev, "ecmd->base.transceiver is XCVR_EXTERNAL\n");
+                       ecmd->base.transceiver = XCVR_EXTERNAL;
                } else {
                        dev_err(&oct->pci_dev->dev, "Unknown link interface mode: %d\n",
                                linfo->link.s.if_mode);
@@ -277,10 +278,12 @@ static int lio_get_link_ksettings(struct net_device *netdev,
                                                 10000baseCR_Full);
                                }
 
-                               if (oct->no_speed_setting == 0)
+                               if (oct->no_speed_setting == 0) {
                                        liquidio_get_speed(lio);
-                               else
+                                       liquidio_get_fec(lio);
+                               } else {
                                        oct->speed_setting = 25;
+                               }
 
                                if (oct->speed_setting == 10) {
                                        ethtool_link_ksettings_add_link_mode
@@ -304,6 +307,24 @@ static int lio_get_link_ksettings(struct net_device *netdev,
                                                (ecmd, advertising,
                                                 25000baseCR_Full);
                                }
+
+                               if (oct->no_speed_setting)
+                                       break;
+
+                               ethtool_link_ksettings_add_link_mode
+                                       (ecmd, supported, FEC_RS);
+                               ethtool_link_ksettings_add_link_mode
+                                       (ecmd, supported, FEC_NONE);
+                                       /*FEC_OFF*/
+                               if (oct->props[lio->ifidx].fec == 1) {
+                                       /* ETHTOOL_FEC_RS */
+                                       ethtool_link_ksettings_add_link_mode
+                                               (ecmd, advertising, FEC_RS);
+                               } else {
+                                       /* ETHTOOL_FEC_OFF */
+                                       ethtool_link_ksettings_add_link_mode
+                                               (ecmd, advertising, FEC_NONE);
+                               }
                        } else { /* VF */
                                if (linfo->link.s.speed == 10000) {
                                        ethtool_link_ksettings_add_link_mode
@@ -3029,9 +3050,60 @@ static int lio_set_priv_flags(struct net_device *netdev, u32 flags)
        return 0;
 }
 
+static int lio_get_fecparam(struct net_device *netdev,
+                           struct ethtool_fecparam *fec)
+{
+       struct lio *lio = GET_LIO(netdev);
+       struct octeon_device *oct = lio->oct_dev;
+
+       fec->active_fec = ETHTOOL_FEC_NONE;
+       fec->fec = ETHTOOL_FEC_NONE;
+
+       if (oct->subsystem_id == OCTEON_CN2350_25GB_SUBSYS_ID ||
+           oct->subsystem_id == OCTEON_CN2360_25GB_SUBSYS_ID) {
+               if (oct->no_speed_setting == 1)
+                       return 0;
+
+               liquidio_get_fec(lio);
+               fec->fec = (ETHTOOL_FEC_RS | ETHTOOL_FEC_OFF);
+               if (oct->props[lio->ifidx].fec == 1)
+                       fec->active_fec = ETHTOOL_FEC_RS;
+               else
+                       fec->active_fec = ETHTOOL_FEC_OFF;
+       }
+
+       return 0;
+}
+
+static int lio_set_fecparam(struct net_device *netdev,
+                           struct ethtool_fecparam *fec)
+{
+       struct lio *lio = GET_LIO(netdev);
+       struct octeon_device *oct = lio->oct_dev;
+
+       if (oct->subsystem_id == OCTEON_CN2350_25GB_SUBSYS_ID ||
+           oct->subsystem_id == OCTEON_CN2360_25GB_SUBSYS_ID) {
+               if (oct->no_speed_setting == 1)
+                       return -EOPNOTSUPP;
+
+               if (fec->fec & ETHTOOL_FEC_OFF)
+                       liquidio_set_fec(lio, 0);
+               else if (fec->fec & ETHTOOL_FEC_RS)
+                       liquidio_set_fec(lio, 1);
+               else
+                       return -EOPNOTSUPP;
+       } else {
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
 static const struct ethtool_ops lio_ethtool_ops = {
        .get_link_ksettings     = lio_get_link_ksettings,
        .set_link_ksettings     = lio_set_link_ksettings,
+       .get_fecparam           = lio_get_fecparam,
+       .set_fecparam           = lio_set_fecparam,
        .get_link               = ethtool_op_get_link,
        .get_drvinfo            = lio_get_drvinfo,
        .get_ringparam          = lio_ethtool_get_ringparam,
index 0aba1f7ffefe7eb139f4650c799a764c5c3ac076..f42c1b0f4ac8fba41a80560e9eceb9e7791b2a53 100644 (file)
@@ -3761,6 +3761,14 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
                        octeon_dev->speed_setting = 10;
                }
                octeon_dev->speed_boot = octeon_dev->speed_setting;
+
+               /* don't read FEC setting if unsupported by f/w (see above) */
+               if (octeon_dev->speed_boot == 25 &&
+                   !octeon_dev->no_speed_setting) {
+                       liquidio_get_fec(lio);
+                       octeon_dev->props[lio->ifidx].fec_boot =
+                               octeon_dev->props[lio->ifidx].fec;
+               }
        }
 
        devlink = devlink_alloc(&liquidio_devlink_ops,
index 8fcb07d58303b1a0db45e0c4e22ad88f678fd752..a5e0e9f179590232472378c24d41331d99dcd59a 100644 (file)
@@ -260,6 +260,11 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry,
 
 #define   OCTNET_CMD_FAIL 0x1
 
+#define   SEAPI_CMD_FEC_SET             0x0
+#define   SEAPI_CMD_FEC_SET_DISABLE       0x0
+#define   SEAPI_CMD_FEC_SET_RS            0x1
+#define   SEAPI_CMD_FEC_GET             0x1
+
 #define   SEAPI_CMD_SPEED_SET           0x2
 #define   SEAPI_CMD_SPEED_GET           0x3
 
index 881e40da2c35459a74b4549725408aadef6f1011..3d01d3602d8f16aad7d0ea51fb42cc689aeb4dd1 100644 (file)
@@ -316,6 +316,8 @@ struct octdev_props {
         * device pointer (used for OS specific calls).
         */
        int    rx_on;
+       int    fec;
+       int    fec_boot;
        int    napi_enabled;
        int    gmxport;
        struct net_device *netdev;
index beb3eec1f7c98b929a893777c06b17dd6ae4a116..50201fc86dcf6fee134481204b2c3e46c8f2e6fc 100644 (file)
@@ -84,7 +84,10 @@ struct oct_nic_stats_ctrl {
 
 struct oct_nic_seapi_resp {
        u64 rh;
-       u32 speed;
+       union {
+               u32 fec_setting;
+               u32 speed;
+       };
        u64 status;
 };
 
@@ -236,6 +239,8 @@ int lio_setup_glists(struct octeon_device *oct, struct lio *lio, int num_qs);
 
 int liquidio_get_speed(struct lio *lio);
 int liquidio_set_speed(struct lio *lio, int speed);
+int liquidio_get_fec(struct lio *lio);
+int liquidio_set_fec(struct lio *lio, int on_off);
 
 /**
  * \brief Net device change_mtu