qla2xxx: Add support for online flash update for ISP27XX.
authorSawan Chandak <sawan.chandak@qlogic.com>
Wed, 27 Jan 2016 17:03:31 +0000 (12:03 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 24 Feb 2016 02:27:02 +0000 (21:27 -0500)
Signed-off-by: Sawan Chandak <sawan.chandak@qlogic.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_bsg.c
drivers/scsi/qla2xxx/qla_bsg.h
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_fw.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_sup.c

index 6992ebc50c87943c8e59015be6e41f82feb0ba9a..fef659a9835c2662a80a55b542a5403503d14660 100644 (file)
@@ -562,6 +562,7 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
        struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
            struct device, kobj)));
        struct qla_hw_data *ha = vha->hw;
+       uint32_t faddr;
 
        if (unlikely(pci_channel_offline(ha->pdev)))
                return -EAGAIN;
@@ -569,9 +570,16 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
        if (!capable(CAP_SYS_ADMIN))
                return -EINVAL;
 
-       if (IS_NOCACHE_VPD_TYPE(ha))
-               ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_vpd << 2,
+       if (IS_NOCACHE_VPD_TYPE(ha)) {
+               faddr = ha->flt_region_vpd << 2;
+
+               if (IS_QLA27XX(ha) &&
+                   qla27xx_find_valid_image(vha) == QLA27XX_SECONDARY_IMAGE)
+                       faddr = ha->flt_region_vpd_sec << 2;
+
+               ha->isp_ops->read_optrom(vha, ha->vpd, faddr,
                    ha->vpd_size);
+       }
        return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
 }
 
index c26acde797f0dda4395fe80a205131fdeb64d0c9..64fe17a72f01e39c8c40346186b54dd8327869bb 100644 (file)
@@ -2106,6 +2106,80 @@ qla8044_serdes_op(struct fc_bsg_job *bsg_job)
        return 0;
 }
 
+static int
+qla27xx_get_flash_upd_cap(struct fc_bsg_job *bsg_job)
+{
+       struct Scsi_Host *host = bsg_job->shost;
+       scsi_qla_host_t *vha = shost_priv(host);
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_flash_update_caps cap;
+
+       if (!(IS_QLA27XX(ha)))
+               return -EPERM;
+
+       memset(&cap, 0, sizeof(cap));
+       cap.capabilities = (uint64_t)ha->fw_attributes_ext[1] << 48 |
+                          (uint64_t)ha->fw_attributes_ext[0] << 32 |
+                          (uint64_t)ha->fw_attributes_h << 16 |
+                          (uint64_t)ha->fw_attributes;
+
+       sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+           bsg_job->reply_payload.sg_cnt, &cap, sizeof(cap));
+       bsg_job->reply->reply_payload_rcv_len = sizeof(cap);
+
+       bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+           EXT_STATUS_OK;
+
+       bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+       bsg_job->reply->result = DID_OK << 16;
+       bsg_job->job_done(bsg_job);
+       return 0;
+}
+
+static int
+qla27xx_set_flash_upd_cap(struct fc_bsg_job *bsg_job)
+{
+       struct Scsi_Host *host = bsg_job->shost;
+       scsi_qla_host_t *vha = shost_priv(host);
+       struct qla_hw_data *ha = vha->hw;
+       uint64_t online_fw_attr = 0;
+       struct qla_flash_update_caps cap;
+
+       if (!(IS_QLA27XX(ha)))
+               return -EPERM;
+
+       memset(&cap, 0, sizeof(cap));
+       sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+           bsg_job->request_payload.sg_cnt, &cap, sizeof(cap));
+
+       online_fw_attr = (uint64_t)ha->fw_attributes_ext[1] << 48 |
+                        (uint64_t)ha->fw_attributes_ext[0] << 32 |
+                        (uint64_t)ha->fw_attributes_h << 16 |
+                        (uint64_t)ha->fw_attributes;
+
+       if (online_fw_attr != cap.capabilities) {
+               bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+                   EXT_STATUS_INVALID_PARAM;
+               return -EINVAL;
+       }
+
+       if (cap.outage_duration < MAX_LOOP_TIMEOUT)  {
+               bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+                   EXT_STATUS_INVALID_PARAM;
+               return -EINVAL;
+       }
+
+       bsg_job->reply->reply_payload_rcv_len = 0;
+
+       bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+           EXT_STATUS_OK;
+
+       bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+       bsg_job->reply->result = DID_OK << 16;
+       bsg_job->job_done(bsg_job);
+       return 0;
+}
+
 static int
 qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
 {
@@ -2161,6 +2235,12 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
        case QL_VND_SERDES_OP_EX:
                return qla8044_serdes_op(bsg_job);
 
+       case QL_VND_GET_FLASH_UPDATE_CAPS:
+               return qla27xx_get_flash_upd_cap(bsg_job);
+
+       case QL_VND_SET_FLASH_UPDATE_CAPS:
+               return qla27xx_set_flash_upd_cap(bsg_job);
+
        default:
                return -ENOSYS;
        }
index d38f9efa56fa57e233804be207c869b87e43fe99..6c45bf4c9981362e314cf0d68eaba27aa9ae1b0e 100644 (file)
@@ -25,6 +25,8 @@
 #define QL_VND_FX00_MGMT_CMD   0x12
 #define QL_VND_SERDES_OP       0x13
 #define        QL_VND_SERDES_OP_EX     0x14
+#define QL_VND_GET_FLASH_UPDATE_CAPS    0x15
+#define QL_VND_SET_FLASH_UPDATE_CAPS    0x16
 
 /* BSG Vendor specific subcode returns */
 #define EXT_STATUS_OK                  0
@@ -232,4 +234,9 @@ struct qla_serdes_reg_ex {
        uint32_t val;
 } __packed;
 
+struct qla_flash_update_caps {
+       uint64_t  capabilities;
+       uint32_t  outage_duration;
+       uint8_t   reserved[20];
+} __packed;
 #endif
index 9e714cc5eb2a36a2a48c5c409186163c56e899df..493a3ea81e7933b78433dc76079bc350a861aa9b 100644 (file)
@@ -11,7 +11,7 @@
  * ----------------------------------------------------------------------
  * |             Level            |   Last Value Used  |     Holes     |
  * ----------------------------------------------------------------------
- * | Module Init and Probe        |       0x017f       | 0x0146         |
+ * | Module Init and Probe        |       0x018f       | 0x0146         |
  * |                              |                    | 0x015b-0x0160 |
  * |                              |                    | 0x016e-0x0170  |
  * | Mailbox commands             |       0x1192       |               |
index 9872f3429e53ae183d1c5667f21acd202461eebf..987480fb461282ea8d7b5a4401d76e4bac3fb4a8 100644 (file)
@@ -1060,6 +1060,12 @@ struct mbx_cmd_32 {
 #define FSTATE_FATAL_ERROR         4
 #define FSTATE_LOOP_BACK_CONN      5
 
+#define QLA27XX_IMG_STATUS_VER_MAJOR   0x01
+#define QLA27XX_IMG_STATUS_VER_MINOR    0x00
+#define QLA27XX_IMG_STATUS_SIGN   0xFACEFADE
+#define QLA27XX_PRIMARY_IMAGE  1
+#define QLA27XX_SECONDARY_IMAGE    2
+
 /*
  * Port Database structure definition
  * Little endian except where noted.
@@ -3433,14 +3439,20 @@ struct qla_hw_data {
        uint32_t        flt_region_flt;
        uint32_t        flt_region_fdt;
        uint32_t        flt_region_boot;
+       uint32_t        flt_region_boot_sec;
        uint32_t        flt_region_fw;
+       uint32_t        flt_region_fw_sec;
        uint32_t        flt_region_vpd_nvram;
        uint32_t        flt_region_vpd;
+       uint32_t        flt_region_vpd_sec;
        uint32_t        flt_region_nvram;
        uint32_t        flt_region_npiv_conf;
        uint32_t        flt_region_gold_fw;
        uint32_t        flt_region_fcp_prio;
        uint32_t        flt_region_bootload;
+       uint32_t        flt_region_img_status_pri;
+       uint32_t        flt_region_img_status_sec;
+       uint8_t         active_image;
 
        /* Needed for BEACON */
        uint16_t        beacon_blink_led;
@@ -3705,6 +3717,16 @@ typedef struct scsi_qla_host {
        struct qla_tgt_counters tgt_counters;
 } scsi_qla_host_t;
 
+struct qla27xx_image_status {
+       uint8_t image_status_mask;
+       uint16_t generation_number;
+       uint8_t reserved[3];
+       uint8_t ver_minor;
+       uint8_t ver_major;
+       uint32_t checksum;
+       uint32_t signature;
+} __packed;
+
 #define SET_VP_IDX     1
 #define SET_AL_PA      2
 #define RESET_VP_IDX   3
index 42bb357bf56b1dcd7d4e0391c1a39cfa2a164197..7f095e30bd1dc154b4b40b2cc9aa6f2ba97f3257 100644 (file)
@@ -1393,6 +1393,16 @@ struct qla_flt_header {
 #define FLT_REG_FCOE_NVRAM_0   0xAA
 #define FLT_REG_FCOE_NVRAM_1   0xAC
 
+/* 27xx */
+#define FLT_REG_IMG_PRI_27XX   0x95
+#define FLT_REG_IMG_SEC_27XX   0x96
+#define FLT_REG_FW_SEC_27XX    0x02
+#define FLT_REG_BOOTLOAD_SEC_27XX      0x9
+#define FLT_REG_VPD_SEC_27XX_0 0x50
+#define FLT_REG_VPD_SEC_27XX_1 0x52
+#define FLT_REG_VPD_SEC_27XX_2 0xD8
+#define FLT_REG_VPD_SEC_27XX_3 0xDA
+
 struct qla_flt_region {
        uint32_t code;
        uint32_t size;
index 1bfdcdf396787de44b9188e0ae206798f209e884..fe943772fe7beed0cd49f19a742e11a04f6d91df 100644 (file)
@@ -90,6 +90,7 @@ extern int qla2xxx_mctp_dump(scsi_qla_host_t *);
 extern int
 qla2x00_alloc_outstanding_cmds(struct qla_hw_data *, struct req_que *);
 extern int qla2x00_init_rings(scsi_qla_host_t *);
+extern uint8_t qla27xx_find_valid_image(struct scsi_qla_host *);
 
 /*
  * Global Data in qla_os.c source file.
index 7f6698a4cfe8d9b232784e1f908b7dd0c1569cc3..cad7c6cb336c41379045318b912a2e2e5da64633 100644 (file)
@@ -5348,6 +5348,93 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
        return (rval);
 }
 
+uint8_t qla27xx_find_valid_image(struct scsi_qla_host *vha)
+{
+       struct qla27xx_image_status pri_image_status, sec_image_status;
+       uint8_t valid_pri_image, valid_sec_image;
+       uint32_t *wptr;
+       uint32_t cnt, chksum, size;
+       struct qla_hw_data *ha = vha->hw;
+
+       valid_pri_image = valid_sec_image = 1;
+       ha->active_image = 0;
+       size = sizeof(struct qla27xx_image_status) / sizeof(uint32_t);
+
+       if (!ha->flt_region_img_status_pri) {
+               valid_pri_image = 0;
+               goto check_sec_image;
+       }
+
+       qla24xx_read_flash_data(vha, (uint32_t *)(&pri_image_status),
+           ha->flt_region_img_status_pri, size);
+
+       if (pri_image_status.signature != QLA27XX_IMG_STATUS_SIGN) {
+               ql_dbg(ql_dbg_init, vha, 0x018b,
+                   "Primary image signature (0x%x) not valid\n",
+                   pri_image_status.signature);
+               valid_pri_image = 0;
+               goto check_sec_image;
+       }
+
+       wptr = (uint32_t *)(&pri_image_status);
+       cnt = size;
+
+       for (chksum = 0; cnt; cnt--)
+               chksum += le32_to_cpu(*wptr++);
+       if (chksum) {
+               ql_dbg(ql_dbg_init, vha, 0x018c,
+                   "Checksum validation failed for primary image (0x%x)\n",
+                   chksum);
+               valid_pri_image = 0;
+       }
+
+check_sec_image:
+       if (!ha->flt_region_img_status_sec) {
+               valid_sec_image = 0;
+               goto check_valid_image;
+       }
+
+       qla24xx_read_flash_data(vha, (uint32_t *)(&sec_image_status),
+           ha->flt_region_img_status_sec, size);
+
+       if (sec_image_status.signature != QLA27XX_IMG_STATUS_SIGN) {
+               ql_dbg(ql_dbg_init, vha, 0x018d,
+                   "Secondary image signature(0x%x) not valid\n",
+                   sec_image_status.signature);
+               valid_sec_image = 0;
+               goto check_valid_image;
+       }
+
+       wptr = (uint32_t *)(&sec_image_status);
+       cnt = size;
+       for (chksum = 0; cnt; cnt--)
+               chksum += le32_to_cpu(*wptr++);
+       if (chksum) {
+               ql_dbg(ql_dbg_init, vha, 0x018e,
+                   "Checksum validation failed for secondary image (0x%x)\n",
+                   chksum);
+               valid_sec_image = 0;
+       }
+
+check_valid_image:
+       if (valid_pri_image && (pri_image_status.image_status_mask & 0x1))
+               ha->active_image = QLA27XX_PRIMARY_IMAGE;
+       if (valid_sec_image && (sec_image_status.image_status_mask & 0x1)) {
+               if (!ha->active_image ||
+                   pri_image_status.generation_number <
+                   sec_image_status.generation_number)
+                       ha->active_image = QLA27XX_SECONDARY_IMAGE;
+       }
+
+       ql_dbg(ql_dbg_init, vha, 0x018f, "%s image\n",
+           ha->active_image == 0 ? "default bootld and fw" :
+           ha->active_image == 1 ? "primary" :
+           ha->active_image == 2 ? "secondary" :
+           "Invalid");
+
+       return ha->active_image;
+}
+
 static int
 qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
     uint32_t faddr)
@@ -5370,6 +5457,10 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
        dcode = (uint32_t *)req->ring;
        *srisc_addr = 0;
 
+       if (IS_QLA27XX(ha) &&
+           qla27xx_find_valid_image(vha) == QLA27XX_SECONDARY_IMAGE)
+               faddr = ha->flt_region_fw_sec;
+
        /* Validate firmware image by checking version. */
        qla24xx_read_flash_data(vha, dcode, faddr + 4, 4);
        for (i = 0; i < 4; i++)
index 3272ed5bbcc7108d7aa245fe7d1e45068d54dc35..786ead292874d7a1f0c220a45b437b82e0be965a 100644 (file)
@@ -846,6 +846,38 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
                        if (ha->port_no == 1)
                                ha->flt_region_nvram = start;
                        break;
+               case FLT_REG_IMG_PRI_27XX:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_img_status_pri = start;
+                       break;
+               case FLT_REG_IMG_SEC_27XX:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_img_status_sec = start;
+                       break;
+               case FLT_REG_FW_SEC_27XX:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_fw_sec = start;
+                       break;
+               case FLT_REG_BOOTLOAD_SEC_27XX:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_boot_sec = start;
+                       break;
+               case FLT_REG_VPD_SEC_27XX_0:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_vpd_sec = start;
+                       break;
+               case FLT_REG_VPD_SEC_27XX_1:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_vpd_sec = start;
+                       break;
+               case FLT_REG_VPD_SEC_27XX_2:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_vpd_sec = start;
+                       break;
+               case FLT_REG_VPD_SEC_27XX_3:
+                       if (IS_QLA27XX(ha))
+                               ha->flt_region_vpd_sec = start;
+                       break;
                }
        }
        goto done;
@@ -2989,6 +3021,9 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
        uint8_t code_type, last_image;
        int i;
        struct qla_hw_data *ha = vha->hw;
+       uint32_t faddr = 0;
+
+       pcihdr = pcids = 0;
 
        if (IS_P3P_TYPE(ha))
                return ret;
@@ -3002,9 +3037,11 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
        memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
 
        dcode = mbuf;
-
-       /* Begin with first PCI expansion ROM header. */
        pcihdr = ha->flt_region_boot << 2;
+       if (IS_QLA27XX(ha) &&
+           qla27xx_find_valid_image(vha) == QLA27XX_SECONDARY_IMAGE)
+               pcihdr = ha->flt_region_boot_sec << 2;
+
        last_image = 1;
        do {
                /* Verify PCI expansion ROM header. */
@@ -3077,8 +3114,12 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
        /* Read firmware image information. */
        memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
        dcode = mbuf;
+       faddr = ha->flt_region_fw;
+       if (IS_QLA27XX(ha) &&
+           qla27xx_find_valid_image(vha) == QLA27XX_SECONDARY_IMAGE)
+               faddr = ha->flt_region_fw_sec;
 
-       qla24xx_read_flash_data(vha, dcode, ha->flt_region_fw + 4, 4);
+       qla24xx_read_flash_data(vha, dcode, faddr + 4, 4);
        for (i = 0; i < 4; i++)
                dcode[i] = be32_to_cpu(dcode[i]);