ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20];
                ha->fw_ddr_ram_start = (mcp->mb[23] << 16) | mcp->mb[22];
                ha->fw_ddr_ram_end = (mcp->mb[25] << 16) | mcp->mb[24];
+               if (IS_QLA28XX(ha)) {
+                       if (mcp->mb[16] & BIT_10) {
+                               ql_log(ql_log_info, vha, 0xffff,
+                                   "FW support secure flash updates\n");
+                               ha->flags.secure_fw = 1;
+                       }
+               }
        }
 
 failed:
        return rval;
 }
 
+int
+qla81xx_fac_semaphore_access(scsi_qla_host_t *vha, int lock)
+{
+       int rval = QLA_SUCCESS;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+       struct qla_hw_data *ha = vha->hw;
+
+       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
+           !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+               return rval;
+
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e2,
+           "Entered %s.\n", __func__);
+
+       mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
+       mcp->mb[1] = (lock ? FAC_OPT_CMD_LOCK_SEMAPHORE :
+           FAC_OPT_CMD_UNLOCK_SEMAPHORE);
+       mcp->out_mb = MBX_1|MBX_0;
+       mcp->in_mb = MBX_1|MBX_0;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_mbx, vha, 0x10e3,
+                   "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+                   rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+       } else {
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e4,
+                   "Done %s.\n", __func__);
+       }
+
+       return rval;
+}
+
 int
 qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
 {
 done:
        return rval;
 }
+
+int qla28xx_secure_flash_update(scsi_qla_host_t *vha, uint16_t opts,
+    uint16_t region, uint32_t len, dma_addr_t sfub_dma_addr,
+    uint32_t sfub_len)
+{
+       int             rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       mcp->mb[0] = MBC_SECURE_FLASH_UPDATE;
+       mcp->mb[1] = opts;
+       mcp->mb[2] = region;
+       mcp->mb[3] = MSW(len);
+       mcp->mb[4] = LSW(len);
+       mcp->mb[5] = MSW(sfub_dma_addr);
+       mcp->mb[6] = LSW(sfub_dma_addr);
+       mcp->mb[7] = MSW(MSD(sfub_dma_addr));
+       mcp->mb[8] = LSW(MSD(sfub_dma_addr));
+       mcp->mb[9] = sfub_len;
+       mcp->out_mb =
+           MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_2|MBX_1|MBX_0;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_mbx, vha, 0xffff, "%s(%ld): failed rval 0x%x, %x %x %x",
+                       __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1],
+                       mcp->mb[2]);
+       }
+
+       return rval;
+}
+
+int qla2xxx_write_remote_register(scsi_qla_host_t *vha, uint32_t addr,
+    uint32_t data)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e8,
+           "Entered %s.\n", __func__);
+
+       mcp->mb[0] = MBC_WRITE_REMOTE_REG;
+       mcp->mb[1] = LSW(addr);
+       mcp->mb[2] = MSW(addr);
+       mcp->mb[3] = LSW(data);
+       mcp->mb[4] = MSW(data);
+       mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_1|MBX_0;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_mbx, vha, 0x10e9,
+                   "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+       } else {
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea,
+                   "Done %s.\n", __func__);
+       }
+
+       return rval;
+}
+
+int qla2xxx_read_remote_register(scsi_qla_host_t *vha, uint32_t addr,
+    uint32_t *data)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e8,
+           "Entered %s.\n", __func__);
+
+       mcp->mb[0] = MBC_READ_REMOTE_REG;
+       mcp->mb[1] = LSW(addr);
+       mcp->mb[2] = MSW(addr);
+       mcp->out_mb = MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+
+       *data = (uint32_t)((((uint32_t)mcp->mb[4]) << 16) | mcp->mb[3]);
+
+       if (rval != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_mbx, vha, 0x10e9,
+                   "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+       } else {
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea,
+                   "Done %s.\n", __func__);
+       }
+
+       return rval;
+}
 
 static void
 qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
 {
-       const char *loc, *locations[] = { "DEF", "FLT" };
+       const char *locations[] = { "DEF", "FLT" }, *loc = locations[1];
        const uint32_t def_fw[] =
                { FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 };
        const uint32_t def_boot[] =
        const uint32_t fcp_prio_cfg1[] =
                { FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25,
                        0 };
-       uint32_t def;
-       uint16_t *wptr;
-       uint16_t cnt, chksum;
-       uint32_t start;
-       struct qla_flt_header *flt;
-       struct qla_flt_region *region;
-       struct qla_hw_data *ha = vha->hw;
-       struct req_que *req = ha->req_q_map[0];
 
-       def = 0;
-       if (IS_QLA25XX(ha))
-               def = 1;
-       else if (IS_QLA81XX(ha))
-               def = 2;
+       struct qla_hw_data *ha = vha->hw;
+       uint32_t def = IS_QLA81XX(ha) ? 2 : IS_QLA25XX(ha) ? 1 : 0;
+       struct qla_flt_header *flt = (void *)ha->flt;
+       struct qla_flt_region *region = (void *)&flt[1];
+       uint16_t *wptr, cnt, chksum;
+       uint32_t start;
 
        /* Assign FCP prio region since older adapters may not have FLT, or
           FCP prio region in it's FLT.
            fcp_prio_cfg0[def] : fcp_prio_cfg1[def];
 
        ha->flt_region_flt = flt_addr;
-       wptr = (uint16_t *)req->ring;
-       flt = (struct qla_flt_header *)req->ring;
-       region = (struct qla_flt_region *)&flt[1];
-       ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
-           flt_addr << 2, OPTROM_BURST_SIZE);
-       if (*wptr == cpu_to_le16(0xffff))
+       wptr = (uint16_t *)ha->flt;
+       qla24xx_read_flash_data(vha, (void *)flt, flt_addr,
+           (sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE) >> 2);
+
+       if (le16_to_cpu(*wptr) == 0xffff)
                goto no_flash_data;
        if (flt->version != cpu_to_le16(1)) {
                ql_log(ql_log_warn, vha, 0x0047,
                goto no_flash_data;
        }
 
-       cnt = (sizeof(struct qla_flt_header) + le16_to_cpu(flt->length)) >> 1;
+       cnt = (sizeof(*flt) + le16_to_cpu(flt->length)) / sizeof(*wptr);
        for (chksum = 0; cnt--; wptr++)
                chksum += le16_to_cpu(*wptr);
        if (chksum) {
                goto no_flash_data;
        }
 
-       loc = locations[1];
-       cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
+       cnt = le16_to_cpu(flt->length) / sizeof(*region);
        for ( ; cnt; cnt--, region++) {
                /* Store addresses as DWORD offsets. */
                start = le32_to_cpu(region->start) >> 2;
                ql_dbg(ql_dbg_init, vha, 0x0049,
                    "FLT[%#x]: start=%#x end=%#x size=%#x.\n",
-                   le16_to_cpu(region->code),
-                   start, le32_to_cpu(region->end) >> 2,
-                   le32_to_cpu(region->size));
+                   le16_to_cpu(region->code), start,
+                   le32_to_cpu(region->end) >> 2,
+                   le32_to_cpu(region->size) >> 2);
+               if (region->attribute)
+                       ql_log(ql_dbg_init, vha, 0xffff,
+                           "Region %x is secure\n", region->code);
 
                switch (le16_to_cpu(region->code)) {
                case FLT_REG_FCOE_FW:
        return buf;
 }
 
+static int
+qla28xx_extract_sfub_and_verify(struct scsi_qla_host *vha, uint32_t *buf,
+    uint32_t len, uint32_t buf_size_without_sfub, uint8_t *sfub_buf)
+{
+       uint32_t *p, check_sum = 0;
+       int i;
+
+       p = buf + buf_size_without_sfub;
+
+       /* Extract SFUB from end of file */
+       memcpy(sfub_buf, (uint8_t *)p,
+           sizeof(struct secure_flash_update_block));
+
+       for (i = 0; i < (sizeof(struct secure_flash_update_block) >> 2); i++)
+               check_sum += p[i];
+
+       check_sum = (~check_sum) + 1;
+
+       if (check_sum != p[i]) {
+               ql_log(ql_log_warn, vha, 0x7097,
+                   "SFUB checksum failed, 0x%x, 0x%x\n",
+                   check_sum, p[i]);
+               return QLA_COMMAND_ERROR;
+       }
+
+       return QLA_SUCCESS;
+}
+
+static int
+qla28xx_get_flash_region(struct scsi_qla_host *vha, uint32_t start,
+    struct qla_flt_region *region)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_flt_header *flt;
+       struct qla_flt_region *flt_reg;
+       uint16_t cnt;
+       int rval = QLA_FUNCTION_FAILED;
+
+       if (!ha->flt)
+               return QLA_FUNCTION_FAILED;
+
+       flt = (struct qla_flt_header *)ha->flt;
+       flt_reg = (struct qla_flt_region *)&flt[1];
+       cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
+
+       for (; cnt; cnt--, flt_reg++) {
+               if (flt_reg->start == start) {
+                       memcpy((uint8_t *)region, flt_reg,
+                           sizeof(struct qla_flt_region));
+                       rval = QLA_SUCCESS;
+                       break;
+               }
+       }
+
+       return rval;
+}
+
+static int
+qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
+    uint32_t dwords)
+{
+       struct qla_hw_data *ha = vha->hw;
+       ulong liter;
+       ulong dburst = OPTROM_BURST_DWORDS; /* burst size in dwords */
+       uint32_t sec_mask, rest_addr, fdata;
+       void *optrom = NULL;
+       dma_addr_t optrom_dma;
+       int rval;
+       struct secure_flash_update_block *sfub;
+       dma_addr_t sfub_dma;
+       uint32_t offset = faddr << 2;
+       uint32_t buf_size_without_sfub = 0;
+       struct qla_flt_region region;
+       bool reset_to_rom = false;
+       uint32_t risc_size, risc_attr = 0;
+       uint32_t *fw_array = NULL;
+
+       /* Retrieve region info - must be a start address passed in */
+       rval = qla28xx_get_flash_region(vha, offset, ®ion);
+
+       if (rval != QLA_SUCCESS) {
+               ql_log(ql_log_warn, vha, 0xffff,
+                   "Invalid address %x - not a region start address\n",
+                   offset);
+               goto done;
+       }
+
+       /* Allocate dma buffer for burst write */
+       optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+           &optrom_dma, GFP_KERNEL);
+       if (!optrom) {
+               ql_log(ql_log_warn, vha, 0x7095,
+                   "Failed allocate burst (%x bytes)\n", OPTROM_BURST_SIZE);
+               rval = QLA_COMMAND_ERROR;
+               goto done;
+       }
+
+       /*
+        * If adapter supports secure flash and region is secure
+        * extract secure flash update block (SFUB) and verify
+        */
+       if (ha->flags.secure_adapter && region.attribute) {
+
+               ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                   "Region %x is secure\n", region.code);
+
+               if (region.code == FLT_REG_FW ||
+                   region.code == FLT_REG_FW_SEC_27XX) {
+                       fw_array = dwptr;
+
+                       /* 1st fw array */
+                       risc_size = be32_to_cpu(fw_array[3]);
+                       risc_attr = be32_to_cpu(fw_array[9]);
+
+                       buf_size_without_sfub = risc_size;
+                       fw_array += risc_size;
+
+                       /* 2nd fw array */
+                       risc_size = be32_to_cpu(fw_array[3]);
+
+                       buf_size_without_sfub += risc_size;
+                       fw_array += risc_size;
+
+                       /* 1st dump template */
+                       risc_size = be32_to_cpu(fw_array[2]);
+
+                       /* skip header and ignore checksum */
+                       buf_size_without_sfub += risc_size;
+                       fw_array += risc_size;
+
+                       if (risc_attr & BIT_9) {
+                               /* 2nd dump template */
+                               risc_size = be32_to_cpu(fw_array[2]);
+
+                               /* skip header and ignore checksum */
+                               buf_size_without_sfub += risc_size;
+                               fw_array += risc_size;
+                       }
+               } else {
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                           "Secure region %x not supported\n",
+                           region.code);
+                       rval = QLA_COMMAND_ERROR;
+                       goto done;
+               }
+
+               sfub = dma_alloc_coherent(&ha->pdev->dev,
+                       sizeof(struct secure_flash_update_block), &sfub_dma,
+                       GFP_KERNEL);
+               if (!sfub) {
+                       ql_log(ql_log_warn, vha, 0xffff,
+                           "Unable to allocate memory for SFUB\n");
+                       rval = QLA_COMMAND_ERROR;
+                       goto done;
+               }
+
+               rval = qla28xx_extract_sfub_and_verify(vha, dwptr, dwords,
+                       buf_size_without_sfub, (uint8_t *)sfub);
+
+               if (rval != QLA_SUCCESS)
+                       goto done;
+
+               ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                   "SFUB extract and verify successful\n");
+       }
+
+       rest_addr = (ha->fdt_block_size >> 2) - 1;
+       sec_mask = ~rest_addr;
+
+       /* Lock semaphore */
+       rval = qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_LOCK);
+       if (rval != QLA_SUCCESS) {
+               ql_log(ql_log_warn, vha, 0xffff,
+                   "Unable to lock flash semaphore.");
+               goto done;
+       }
+
+       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+           "Unprotect flash...\n");
+       rval = qla24xx_unprotect_flash(vha);
+       if (rval) {
+               qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
+               ql_log(ql_log_warn, vha, 0x7096, "Failed unprotect flash\n");
+               goto done;
+       }
+
+       for (liter = 0; liter < dwords; liter++, faddr++) {
+               fdata = (faddr & sec_mask) << 2;
+
+               /* If start of sector */
+               if (!(faddr & rest_addr)) {
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+                           "Erase sector %#x...\n", faddr);
+                       rval = qla24xx_erase_sector(vha, fdata);
+                       if (rval) {
+                               ql_dbg(ql_dbg_user, vha, 0x7007,
+                                   "Failed erase sector %#x\n", faddr);
+                               goto write_protect;
+                       }
+               }
+       }
+
+       if (ha->flags.secure_adapter) {
+               /*
+                * If adapter supports secure flash but FW doesn't,
+                * disable write protect, release semaphore and reset
+                * chip to execute ROM code in order to update region securely
+                */
+               if (!ha->flags.secure_fw) {
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                           "Disable Write and Release Semaphore.");
+                       rval = qla24xx_protect_flash(vha);
+                       if (rval != QLA_SUCCESS) {
+                               qla81xx_fac_semaphore_access(vha,
+                                       FAC_SEMAPHORE_UNLOCK);
+                               ql_log(ql_log_warn, vha, 0xffff,
+                                   "Unable to protect flash.");
+                               goto done;
+                       }
+
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                           "Reset chip to ROM.");
+                       set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+                       set_bit(ISP_ABORT_TO_ROM, &vha->dpc_flags);
+                       qla2xxx_wake_dpc(vha);
+                       rval = qla2x00_wait_for_chip_reset(vha);
+                       if (rval != QLA_SUCCESS) {
+                               ql_log(ql_log_warn, vha, 0xffff,
+                                   "Unable to reset to ROM code.");
+                               goto done;
+                       }
+                       reset_to_rom = true;
+                       ha->flags.fac_supported = 0;
+
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                           "Lock Semaphore");
+                       rval = qla2xxx_write_remote_register(vha,
+                           FLASH_SEMAPHORE_REGISTER_ADDR, 0x00020002);
+                       if (rval != QLA_SUCCESS) {
+                               ql_log(ql_log_warn, vha, 0xffff,
+                                   "Unable to lock flash semaphore.");
+                               goto done;
+                       }
+
+                       /* Unprotect flash */
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                           "Enable Write.");
+                       rval = qla2x00_write_ram_word(vha, 0x7ffd0101, 0);
+                       if (rval) {
+                               ql_log(ql_log_warn, vha, 0x7096,
+                                   "Failed unprotect flash\n");
+                               goto done;
+                       }
+               }
+
+               /* If region is secure, send Secure Flash MB Cmd */
+               if (region.attribute && buf_size_without_sfub) {
+                       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
+                           "Sending Secure Flash MB Cmd\n");
+                       rval = qla28xx_secure_flash_update(vha, 0, region.code,
+                               buf_size_without_sfub, sfub_dma,
+                               sizeof(struct secure_flash_update_block));
+                       if (rval != QLA_SUCCESS) {
+                               ql_log(ql_log_warn, vha, 0xffff,
+                                   "Secure Flash MB Cmd failed %x.", rval);
+                               goto write_protect;
+                       }
+               }
+
+       }
+
+       /* re-init flash offset */
+       faddr = offset >> 2;
+
+       for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
+               fdata = (faddr & sec_mask) << 2;
+
+               /* If smaller than a burst remaining */
+               if (dwords - liter < dburst)
+                       dburst = dwords - liter;
+
+               /* Copy to dma buffer */
+               memcpy(optrom, dwptr, dburst << 2);
+
+               /* Burst write */
+               ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+                   "Write burst (%#lx dwords)...\n", dburst);
+               rval = qla2x00_load_ram(vha, optrom_dma,
+                   flash_data_addr(ha, faddr), dburst);
+               if (rval != QLA_SUCCESS) {
+                       ql_log(ql_log_warn, vha, 0x7097,
+                           "Failed burst write at %x (%p/%#llx)...\n",
+                           flash_data_addr(ha, faddr), optrom,
+                           (u64)optrom_dma);
+                       break;
+               }
+
+               liter += dburst - 1;
+               faddr += dburst - 1;
+               dwptr += dburst - 1;
+               continue;
+       }
+
+write_protect:
+       ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
+           "Protect flash...\n");
+       rval = qla24xx_protect_flash(vha);
+       if (rval) {
+               qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
+               ql_log(ql_log_warn, vha, 0x7099,
+                   "Failed protect flash\n");
+       }
+
+       if (reset_to_rom == true) {
+               /* Schedule DPC to restart the RISC */
+               set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+               qla2xxx_wake_dpc(vha);
+
+               rval = qla2x00_wait_for_hba_online(vha);
+               if (rval != QLA_SUCCESS)
+                       ql_log(ql_log_warn, vha, 0xffff,
+                           "Adapter did not come out of reset\n");
+       }
+
+done:
+       if (optrom)
+               dma_free_coherent(&ha->pdev->dev,
+                   OPTROM_BURST_SIZE, optrom, optrom_dma);
+
+       return rval;
+}
+
 int
 qla24xx_write_optrom_data(struct scsi_qla_host *vha, void *buf,
     uint32_t offset, uint32_t length)
        set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
 
        /* Go with write. */
-       rval = qla24xx_write_flash_data(vha, buf, offset >> 2,
-           length >> 2);
+       if (IS_QLA28XX(ha))
+               rval = qla28xx_write_flash_data(vha, (uint32_t *)buf,
+                   offset >> 2, length >> 2);
+       else
+               rval = qla24xx_write_flash_data(vha, (uint32_t *)buf,
+                   offset >> 2, length >> 2);
 
        clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
        scsi_unblock_requests(vha->host);
        memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
        faddr = ha->flt_region_fw;
        if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+               qla27xx_get_active_image(vha, &active_regions);
                if (active_regions.global == QLA27XX_SECONDARY_IMAGE)
                        faddr = ha->flt_region_fw_sec;
        }