scsi: qla2xxx: Add support for setting port speed
authorAnil Gurumurthy <agurumurthy@marvell.com>
Fri, 15 Feb 2019 22:37:17 +0000 (14:37 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 19 Feb 2019 23:58:36 +0000 (18:58 -0500)
This patch adds sysfs node

1. There is a new sysfs node port_speed
2. The possible values are 2(Auto neg), 8, 16, 32
3. A value outside of the above defaults to Auto neg
4. Any update to the setting causes a link toggle
5. This feature is currently only for ISP27xx

Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Quinn Tran <qtran@marvell.com>
Signed-off-by: Himanshu Madhani <hmadhani@marvell.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_mbx.c

index 8b4dd72..ee9f943 100644 (file)
@@ -1632,6 +1632,92 @@ qla2x00_max_speed_sup_show(struct device *dev, struct device_attribute *attr,
            ha->max_speed_sup ? "32Gps" : "16Gps");
 }
 
+static ssize_t
+qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
+    const char *buf, size_t count)
+{
+       struct scsi_qla_host *vha = shost_priv(dev_to_shost(dev));
+       ulong type, speed;
+       int oldspeed, rval;
+       int mode = QLA_SET_DATA_RATE_LR;
+       struct qla_hw_data *ha = vha->hw;
+
+       if (!IS_QLA27XX(vha->hw)) {
+               ql_log(ql_log_warn, vha, 0x70d8,
+                   "Speed setting not supported \n");
+               return -EINVAL;
+       }
+
+       rval = kstrtol(buf, 10, &type);
+       speed = type;
+       if (type == 40 || type == 80 || type == 160 ||
+           type == 320) {
+               ql_dbg(ql_dbg_user, vha, 0x70d9,
+                   "Setting will be affected after a loss of sync\n");
+               type = type/10;
+               mode = QLA_SET_DATA_RATE_NOLR;
+       }
+
+       oldspeed = ha->set_data_rate;
+
+       switch (type) {
+       case 0:
+               ha->set_data_rate = PORT_SPEED_AUTO;
+               break;
+       case 4:
+               ha->set_data_rate = PORT_SPEED_4GB;
+               break;
+       case 8:
+               ha->set_data_rate = PORT_SPEED_8GB;
+               break;
+       case 16:
+               ha->set_data_rate = PORT_SPEED_16GB;
+               break;
+       case 32:
+               ha->set_data_rate = PORT_SPEED_32GB;
+               break;
+       default:
+               ql_log(ql_log_warn, vha, 0x1199,
+                   "Unrecognized speed setting:%lx. Setting Autoneg\n",
+                   speed);
+               ha->set_data_rate = PORT_SPEED_AUTO;
+       }
+
+       if (qla2x00_chip_is_down(vha) || (oldspeed == ha->set_data_rate))
+               return -EINVAL;
+
+       ql_log(ql_log_info, vha, 0x70da,
+           "Setting speed to %lx Gbps \n", type);
+
+       rval = qla2x00_set_data_rate(vha, mode);
+       if (rval != QLA_SUCCESS)
+               return -EIO;
+
+       return strlen(buf);
+}
+
+static ssize_t
+qla2x00_port_speed_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       struct scsi_qla_host *vha = shost_priv(dev_to_shost(dev));
+       struct qla_hw_data *ha = vha->hw;
+       ssize_t rval;
+       char *spd[7] = {"0", "0", "0", "4", "8", "16", "32"};
+
+       rval = qla2x00_get_data_rate(vha);
+       if (rval != QLA_SUCCESS) {
+               ql_log(ql_log_warn, vha, 0x70db,
+                   "Unable to get port speed rval:%zd\n", rval);
+               return -EINVAL;
+       }
+
+       ql_log(ql_log_info, vha, 0x70d6,
+           "port speed:%d\n", ha->link_data_rate);
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", spd[ha->link_data_rate]);
+}
+
 /* ----- */
 
 static ssize_t
@@ -2128,6 +2214,8 @@ static DEVICE_ATTR_RW(ql2xexchoffld);
 static DEVICE_ATTR_RW(ql2xiniexchg);
 static DEVICE_ATTR(dif_bundle_statistics, 0444,
     qla2x00_dif_bundle_statistics_show, NULL);
+static DEVICE_ATTR(port_speed, 0644, qla2x00_port_speed_show,
+    qla2x00_port_speed_store);
 
 
 struct device_attribute *qla2x00_host_attrs[] = {
@@ -2167,6 +2255,7 @@ struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_max_speed_sup,
        &dev_attr_zio_threshold,
        &dev_attr_dif_bundle_statistics,
+       &dev_attr_port_speed,
        NULL, /* reserve for qlini_mode */
        NULL, /* reserve for ql2xiniexchg */
        NULL, /* reserve for ql2xexchoffld */
index c256ba7..c0f7593 100644 (file)
@@ -3698,12 +3698,14 @@ struct qla_hw_data {
 #define PORT_SPEED_UNKNOWN 0xFFFF
 #define PORT_SPEED_1GB  0x00
 #define PORT_SPEED_2GB  0x01
+#define PORT_SPEED_AUTO 0x02
 #define PORT_SPEED_4GB  0x03
 #define PORT_SPEED_8GB  0x04
 #define PORT_SPEED_16GB 0x05
 #define PORT_SPEED_32GB 0x06
 #define PORT_SPEED_10GB        0x13
        uint16_t        link_data_rate;         /* F/W operating speed */
+       uint16_t        set_data_rate;          /* Set by user */
 
        uint8_t         current_topology;
        uint8_t         prev_topology;
@@ -4232,6 +4234,10 @@ struct qla_hw_data {
 #define FW_ABILITY_MAX_SPEED(ha)       \
        (ha->fw_ability_mask & FW_ABILITY_MAX_SPEED_MASK)
 
+#define QLA_GET_DATA_RATE      0
+#define QLA_SET_DATA_RATE_NOLR 1
+#define QLA_SET_DATA_RATE_LR   2 /* Set speed and initiate LR */
+
 /*
  * Qlogic scsi host structure
  */
index bcc17a7..3c59006 100644 (file)
@@ -899,5 +899,6 @@ void qlt_update_host_map(struct scsi_qla_host *, port_id_t);
 void qlt_remove_target_resources(struct qla_hw_data *);
 void qlt_clr_qp_table(struct scsi_qla_host *vha);
 void qlt_set_mode(struct scsi_qla_host *);
+int qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode);
 
 #endif /* _QLA_GBL_H */
index 633ad78..c319abf 100644 (file)
@@ -3882,8 +3882,17 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
                WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
                WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
        }
+
        qlt_24xx_config_rings(vha);
 
+       /* If the user has configured the speed, set it here */
+       if (ha->set_data_rate) {
+               ql_dbg(ql_dbg_init, vha, 0x00fd,
+                   "Speed set by user : %s Gbps \n",
+                   qla2x00_get_link_speed_str(ha, ha->set_data_rate));
+               icb->firmware_options_3 = (ha->set_data_rate << 13);
+       }
+
        /* PCI posting */
        RD_REG_DWORD(&ioreg->hccr);
 }
index 6c911f2..7ca3b76 100644 (file)
@@ -5250,6 +5250,66 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
        return rval;
 }
 
+/* Set the specified data rate */
+int
+qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+       struct qla_hw_data *ha = vha->hw;
+       uint16_t val;
+
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1106,
+           "Entered %s speed:0x%x mode:0x%x.\n", __func__, ha->set_data_rate,
+           mode);
+
+       if (!IS_FWI2_CAPABLE(ha))
+               return QLA_FUNCTION_FAILED;
+
+       memset(mcp, 0, sizeof(*mcp));
+       switch (ha->set_data_rate) {
+       case PORT_SPEED_AUTO:
+       case PORT_SPEED_4GB:
+       case PORT_SPEED_8GB:
+       case PORT_SPEED_16GB:
+       case PORT_SPEED_32GB:
+               val = ha->set_data_rate;
+               break;
+       default:
+               ql_log(ql_log_warn, vha, 0x1199,
+                   "Unrecognized speed setting:%d. Setting Autoneg\n",
+                   ha->set_data_rate);
+               val = ha->set_data_rate = PORT_SPEED_AUTO;
+               break;
+       }
+
+       mcp->mb[0] = MBC_DATA_RATE;
+       mcp->mb[1] = mode;
+       mcp->mb[2] = val;
+
+       mcp->out_mb = MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_2|MBX_1|MBX_0;
+       if (IS_QLA83XX(ha) || IS_QLA27XX(ha))
+               mcp->in_mb |= MBX_4|MBX_3;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+       if (rval != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_mbx, vha, 0x1107,
+                   "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+       } else {
+               if (mcp->mb[1] != 0x7)
+                       ql_dbg(ql_dbg_mbx, vha, 0x1179,
+                               "Speed set:0x%x\n", mcp->mb[1]);
+
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
+                   "Done %s.\n", __func__);
+       }
+
+       return rval;
+}
+
 int
 qla2x00_get_data_rate(scsi_qla_host_t *vha)
 {
@@ -5265,7 +5325,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
                return QLA_FUNCTION_FAILED;
 
        mcp->mb[0] = MBC_DATA_RATE;
-       mcp->mb[1] = 0;
+       mcp->mb[1] = QLA_GET_DATA_RATE;
        mcp->out_mb = MBX_1|MBX_0;
        mcp->in_mb = MBX_2|MBX_1|MBX_0;
        if (IS_QLA83XX(ha) || IS_QLA27XX(ha))