Signed-off-by: Bradley Grove <bgr...@attotech.com>
---
 drivers/scsi/esas2r/esas2r_flash.c  | 1516 +++++++++++++++++++++++++++++++++++
 drivers/scsi/esas2r/esas2r_targdb.c |  306 +++++++
 2 files changed, 1822 insertions(+)
 create mode 100644 drivers/scsi/esas2r/esas2r_flash.c
 create mode 100644 drivers/scsi/esas2r/esas2r_targdb.c

diff --git a/drivers/scsi/esas2r/esas2r_flash.c 
b/drivers/scsi/esas2r/esas2r_flash.c
new file mode 100644
index 0000000..61ef449
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_flash.c
@@ -0,0 +1,1514 @@
+
+/*
+ *  linux/drivers/scsi/esas2r/esas2r_flash.c
+ *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ *  Copyright (c) 2001-2013 ATTO Technology, Inc.
+ *  (mailto:linuxdriv...@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+/* local macro defs */
+#define esas2r_nvramcalc_cksum(n)     \
+       (esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \
+                               SASNVR_CKSUM_SEED))
+#define esas2r_nvramcalc_xor_cksum(n)  \
+       (esas2r_calc_byte_xor_cksum((u8 *)(n), \
+                                   sizeof(struct esas2r_sas_nvram), 0))
+
+#define ESAS2R_FS_DRVR_VER 2
+
+static struct esas2r_sas_nvram default_sas_nvram = {
+       { 'E',  'S',  'A',  'S'                  },  /* signature          */
+       SASNVR_VERSION,                              /* version            */
+       0,                                           /* checksum           */
+       31,                                          /* max_lun_for_target */
+       SASNVR_PCILAT_MAX,                           /* pci_latency        */
+       SASNVR1_BOOT_DRVR,                           /* options1           */
+       SASNVR2_HEARTBEAT   | SASNVR2_SINGLE_BUS     /* options2           */
+       | SASNVR2_SW_MUX_CTRL,
+       SASNVR_COAL_DIS,                             /* int_coalescing     */
+       SASNVR_CMDTHR_NONE,                          /* cmd_throttle       */
+       3,                                           /* dev_wait_time      */
+       1,                                           /* dev_wait_count     */
+       0,                                           /* spin_up_delay      */
+       0,                                           /* ssp_align_rate     */
+       { 0x50, 0x01, 0x08, 0x60,                    /* sas_addr           */
+         0x00, 0x00, 0x00, 0x00 },
+       { SASNVR_SPEED_AUTO },                       /* phy_speed          */
+       { SASNVR_MUX_DISABLED },                     /* SAS multiplexing   */
+       { 0 },                                       /* phy_flags          */
+       SASNVR_SORT_SAS_ADDR,                        /* sort_type          */
+       3,                                           /* dpm_reqcmd_lmt     */
+       3,                                           /* dpm_stndby_time    */
+       0,                                           /* dpm_active_time    */
+       { 0 },                                       /* phy_target_id      */
+       SASNVR_VSMH_DISABLED,                        /* virt_ses_mode      */
+       SASNVR_RWM_DEFAULT,                          /* read_write_mode    */
+       0,                                           /* link down timeout  */
+       { 0 }                                        /* reserved           */
+};
+
+static u8 cmd_to_fls_func[] = {
+       0xFF,
+       VDA_FLASH_READ,
+       VDA_FLASH_BEGINW,
+       VDA_FLASH_WRITE,
+       VDA_FLASH_COMMIT,
+       VDA_FLASH_CANCEL
+};
+
+static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed)
+{
+       u32 cksum = seed;
+       u8 *p = (u8 *)&cksum;
+
+       while (len) {
+               if (((uintptr_t)addr & 3) == 0)
+                       break;
+
+               cksum = cksum ^ *addr;
+               addr++;
+               len--;
+       }
+       while (len >= sizeof(u32)) {
+               cksum = cksum ^ *(u32 *)addr;
+               addr += 4;
+               len -= 4;
+       }
+       while (len--) {
+               cksum = cksum ^ *addr;
+               addr++;
+       }
+       return p[0] ^ p[1] ^ p[2] ^ p[3];
+}
+
+static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed)
+{
+       u8 *p = (u8 *)addr;
+       u8 cksum = seed;
+
+       while (len--)
+               cksum = cksum + p[len];
+       return cksum;
+}
+
+/* Interrupt callback to process FM API write requests. */
+static void esas2r_fmapi_callback(struct esas2r_adapter *a,
+                                 struct esas2r_request *rq)
+{
+       struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+       struct esas2r_flash_context *fc =
+               (struct esas2r_flash_context *)rq->interrupt_cx;
+
+       if (rq->req_stat == RS_SUCCESS) {
+               /* Last request was successful.  See what to do now. */
+               switch (vrq->sub_func) {
+               case VDA_FLASH_BEGINW:
+                       if (fc->sgc.cur_offset == NULL)
+                               goto commit;
+
+                       vrq->sub_func = VDA_FLASH_WRITE;
+                       rq->req_stat = RS_PENDING;
+                       break;
+
+               case VDA_FLASH_WRITE:
+commit:
+                       vrq->sub_func = VDA_FLASH_COMMIT;
+                       rq->req_stat = RS_PENDING;
+                       rq->interrupt_cb = fc->interrupt_cb;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (rq->req_stat != RS_PENDING)
+               /*
+                * All done. call the real callback to complete the FM API
+                * request.  We should only get here if a BEGINW or WRITE
+                * operation failed.
+                */
+               (*fc->interrupt_cb)(a, rq);
+}
+
+/*
+ * Build a flash request based on the flash context.  The request status
+ * is filled in on an error.
+ */
+static void build_flash_msg(struct esas2r_adapter *a,
+                           struct esas2r_request *rq)
+{
+       struct esas2r_flash_context *fc =
+               (struct esas2r_flash_context *)rq->interrupt_cx;
+       struct esas2r_sg_context *sgc = &fc->sgc;
+       u8 cksum = 0;
+
+       /* calculate the checksum */
+       if (fc->func == VDA_FLASH_BEGINW) {
+               if (sgc->cur_offset)
+                       cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset,
+                                                          sgc->length,
+                                                          0);
+               rq->interrupt_cb = esas2r_fmapi_callback;
+       } else {
+               rq->interrupt_cb = fc->interrupt_cb;
+       }
+       esas2r_build_flash_req(a,
+                              rq,
+                              fc->func,
+                              cksum,
+                              fc->flsh_addr,
+                              sgc->length);
+
+       esas2r_rq_free_sg_lists(rq, a);
+
+       /*
+        * remember the length we asked for.  we have to keep track of
+        * the current amount done so we know how much to compare when
+        * doing the verification phase.
+        */
+       fc->curr_len = fc->sgc.length;
+
+       if (sgc->cur_offset) {
+               /* setup the S/G context to build the S/G table  */
+               esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]);
+
+               if (!esas2r_build_sg_list(a, rq, sgc)) {
+                       rq->req_stat = RS_BUSY;
+                       return;
+               }
+       } else {
+               fc->sgc.length = 0;
+       }
+
+       /* update the flsh_addr to the next one to write to  */
+       fc->flsh_addr += fc->curr_len;
+}
+
+/* determine the method to process the flash request */
+static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+       /*
+        * assume we have more to do.  if we return with the status set to
+        * RS_PENDING, FM API tasks will continue.
+        */
+       rq->req_stat = RS_PENDING;
+       if (a->flags & AF_DEGRADED_MODE)
+               /* not suppported for now */;
+       else
+               build_flash_msg(a, rq);
+
+       return rq->req_stat == RS_PENDING;
+}
+
+/*  boot image fixer uppers called before downloading the image. */
+static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
+{
+       struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS];
+       struct esas2r_pc_image *pi;
+       struct esas2r_boot_header *bh;
+
+       pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset);
+       bh =
+               (struct esas2r_boot_header *)((u8 *)pi +
+                                             le16_to_cpu(pi->header_offset));
+       bh->device_id = cpu_to_le16(a->pcid->device);
+
+       /* Recalculate the checksum in the PNP header if there  */
+       if (pi->pnp_offset) {
+               u8 *pnp_header_bytes =
+                       ((u8 *)pi + le16_to_cpu(pi->pnp_offset));
+
+               /* Identifier - dword that starts at byte 10 */
+               *((u32 *)&pnp_header_bytes[10]) =
+                       cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor,
+                                             a->pcid->subsystem_device));
+
+               /* Checksum - byte 9 */
+               pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes,
+                                                             32, 0);
+       }
+
+       /* Recalculate the checksum needed by the PC */
+       pi->checksum = pi->checksum -
+                      esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0);
+}
+
+static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
+{
+       struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI];
+       u32 len = ch->length;
+       u32 offset = ch->image_offset;
+       struct esas2r_efi_image *ei;
+       struct esas2r_boot_header *bh;
+
+       while (len) {
+               u32 thislen;
+
+               ei = (struct esas2r_efi_image *)((u8 *)fi + offset);
+               bh = (struct esas2r_boot_header *)((u8 *)ei +
+                                                  le16_to_cpu(
+                                                          ei->header_offset));
+               bh->device_id = cpu_to_le16(a->pcid->device);
+               thislen = (u32)le16_to_cpu(bh->image_length) * 512;
+
+               if (thislen > len)
+                       break;
+
+               len -= thislen;
+               offset += thislen;
+       }
+}
+
+/* Complete a FM API request with the specified status. */
+static bool complete_fmapi_req(struct esas2r_adapter *a,
+                              struct esas2r_request *rq, u8 fi_stat)
+{
+       struct esas2r_flash_context *fc =
+               (struct esas2r_flash_context *)rq->interrupt_cx;
+       struct esas2r_flash_img *fi = fc->fi;
+
+       fi->status = fi_stat;
+       fi->driver_error = rq->req_stat;
+       rq->interrupt_cb = NULL;
+       rq->req_stat = RS_SUCCESS;
+
+       if (fi_stat != FI_STAT_IMG_VER)
+               memset(fc->scratch, 0, FM_BUF_SZ);
+
+       esas2r_enable_heartbeat(a);
+       esas2r_lock_clear_flags(&a->flags, AF_FLASH_LOCK);
+       return false;
+}
+
+/* Process each phase of the flash download process. */
+static void fw_download_proc(struct esas2r_adapter *a,
+                            struct esas2r_request *rq)
+{
+       struct esas2r_flash_context *fc =
+               (struct esas2r_flash_context *)rq->interrupt_cx;
+       struct esas2r_flash_img *fi = fc->fi;
+       struct esas2r_component_header *ch;
+       u32 len;
+       u8 *p, *q;
+
+       /* If the previous operation failed, just return. */
+       if (rq->req_stat != RS_SUCCESS)
+               goto error;
+
+       /*
+        * If an upload just completed and the compare length is non-zero,
+        * then we just read back part of the image we just wrote.  verify the
+        * section and continue reading until the entire image is verified.
+        */
+       if (fc->func == VDA_FLASH_READ
+           && fc->cmp_len) {
+               ch = &fi->cmp_hdr[fc->comp_typ];
+
+               p = fc->scratch;
+               q = (u8 *)fi                    /* start of the whole gob     */
+                   + ch->image_offset          /* start of the current image */
+                   + ch->length                /* end of the current image   */
+                   - fc->cmp_len;              /* where we are now           */
+
+               /*
+                * NOTE - curr_len is the exact count of bytes for the read
+                *        even when the end is read and its not a full buffer
+                */
+               for (len = fc->curr_len; len; len--)
+                       if (*p++ != *q++)
+                               goto error;
+
+               fc->cmp_len -= fc->curr_len; /* # left to compare    */
+
+               /* Update fc and determine the length for the next upload */
+               if (fc->cmp_len > FM_BUF_SZ)
+                       fc->sgc.length = FM_BUF_SZ;
+               else
+                       fc->sgc.length = fc->cmp_len;
+
+               fc->sgc.cur_offset = fc->sgc_offset +
+                                    ((u8 *)fc->scratch - (u8 *)fi);
+       }
+
+       /*
+        * This code uses a 'while' statement since the next component may
+        * have a length = zero.  This can happen since some components are
+        * not required.  At the end of this 'while' we set up the length
+        * for the next request and therefore sgc.length can be = 0.
+        */
+       while (fc->sgc.length == 0) {
+               ch = &fi->cmp_hdr[fc->comp_typ];
+
+               switch (fc->task) {
+               case FMTSK_ERASE_BOOT:
+                       /* the BIOS image is written next */
+                       ch = &fi->cmp_hdr[CH_IT_BIOS];
+                       if (ch->length == 0)
+                               goto no_bios;
+
+                       fc->task = FMTSK_WRTBIOS;
+                       fc->func = VDA_FLASH_BEGINW;
+                       fc->comp_typ = CH_IT_BIOS;
+                       fc->flsh_addr = FLS_OFFSET_BOOT;
+                       fc->sgc.length = ch->length;
+                       fc->sgc.cur_offset = fc->sgc_offset +
+                                            ch->image_offset;
+                       break;
+
+               case FMTSK_WRTBIOS:
+                       /*
+                        * The BIOS image has been written - read it and
+                        * verify it
+                        */
+                       fc->task = FMTSK_READBIOS;
+                       fc->func = VDA_FLASH_READ;
+                       fc->flsh_addr = FLS_OFFSET_BOOT;
+                       fc->cmp_len = ch->length;
+                       fc->sgc.length = FM_BUF_SZ;
+                       fc->sgc.cur_offset = fc->sgc_offset
+                                            + ((u8 *)fc->scratch -
+                                               (u8 *)fi);
+                       break;
+
+               case FMTSK_READBIOS:
+no_bios:
+                       /*
+                        * Mark the component header status for the image
+                        * completed
+                        */
+                       ch->status = CH_STAT_SUCCESS;
+
+                       /* The MAC image is written next */
+                       ch = &fi->cmp_hdr[CH_IT_MAC];
+                       if (ch->length == 0)
+                               goto no_mac;
+
+                       fc->task = FMTSK_WRTMAC;
+                       fc->func = VDA_FLASH_BEGINW;
+                       fc->comp_typ = CH_IT_MAC;
+                       fc->flsh_addr = FLS_OFFSET_BOOT
+                                       + fi->cmp_hdr[CH_IT_BIOS].length;
+                       fc->sgc.length = ch->length;
+                       fc->sgc.cur_offset = fc->sgc_offset +
+                                            ch->image_offset;
+                       break;
+
+               case FMTSK_WRTMAC:
+                       /* The MAC image has been written - read and verify */
+                       fc->task = FMTSK_READMAC;
+                       fc->func = VDA_FLASH_READ;
+                       fc->flsh_addr -= ch->length;
+                       fc->cmp_len = ch->length;
+                       fc->sgc.length = FM_BUF_SZ;
+                       fc->sgc.cur_offset = fc->sgc_offset
+                                            + ((u8 *)fc->scratch -
+                                               (u8 *)fi);
+                       break;
+
+               case FMTSK_READMAC:
+no_mac:
+                       /*
+                        * Mark the component header status for the image
+                        * completed
+                        */
+                       ch->status = CH_STAT_SUCCESS;
+
+                       /* The EFI image is written next */
+                       ch = &fi->cmp_hdr[CH_IT_EFI];
+                       if (ch->length == 0)
+                               goto no_efi;
+
+                       fc->task = FMTSK_WRTEFI;
+                       fc->func = VDA_FLASH_BEGINW;
+                       fc->comp_typ = CH_IT_EFI;
+                       fc->flsh_addr = FLS_OFFSET_BOOT
+                                       + fi->cmp_hdr[CH_IT_BIOS].length
+                                       + fi->cmp_hdr[CH_IT_MAC].length;
+                       fc->sgc.length = ch->length;
+                       fc->sgc.cur_offset = fc->sgc_offset +
+                                            ch->image_offset;
+                       break;
+
+               case FMTSK_WRTEFI:
+                       /* The EFI image has been written - read and verify */
+                       fc->task = FMTSK_READEFI;
+                       fc->func = VDA_FLASH_READ;
+                       fc->flsh_addr -= ch->length;
+                       fc->cmp_len = ch->length;
+                       fc->sgc.length = FM_BUF_SZ;
+                       fc->sgc.cur_offset = fc->sgc_offset
+                                            + ((u8 *)fc->scratch -
+                                               (u8 *)fi);
+                       break;
+
+               case FMTSK_READEFI:
+no_efi:
+                       /*
+                        * Mark the component header status for the image
+                        * completed
+                        */
+                       ch->status = CH_STAT_SUCCESS;
+
+                       /* The CFG image is written next */
+                       ch = &fi->cmp_hdr[CH_IT_CFG];
+
+                       if (ch->length == 0)
+                               goto no_cfg;
+                       fc->task = FMTSK_WRTCFG;
+                       fc->func = VDA_FLASH_BEGINW;
+                       fc->comp_typ = CH_IT_CFG;
+                       fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
+                       fc->sgc.length = ch->length;
+                       fc->sgc.cur_offset = fc->sgc_offset +
+                                            ch->image_offset;
+                       break;
+
+               case FMTSK_WRTCFG:
+                       /* The CFG image has been written - read and verify */
+                       fc->task = FMTSK_READCFG;
+                       fc->func = VDA_FLASH_READ;
+                       fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
+                       fc->cmp_len = ch->length;
+                       fc->sgc.length = FM_BUF_SZ;
+                       fc->sgc.cur_offset = fc->sgc_offset
+                                            + ((u8 *)fc->scratch -
+                                               (u8 *)fi);
+                       break;
+
+               case FMTSK_READCFG:
+no_cfg:
+                       /*
+                        * Mark the component header status for the image
+                        * completed
+                        */
+                       ch->status = CH_STAT_SUCCESS;
+
+                       /*
+                        * The download is complete.  If in degraded mode,
+                        * attempt a chip reset.
+                        */
+                       if (a->flags & AF_DEGRADED_MODE)
+                               esas2r_local_reset_adapter(a);
+
+                       a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version;
+                       esas2r_print_flash_rev(a);
+
+                       /* Update the type of boot image on the card */
+                       memcpy(a->image_type, fi->rel_version,
+                              sizeof(fi->rel_version));
+                       complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
+                       return;
+               }
+
+               /* If verifying, don't try reading more than what's there */
+               if (fc->func == VDA_FLASH_READ
+                   && fc->sgc.length > fc->cmp_len)
+                       fc->sgc.length = fc->cmp_len;
+       }
+
+       /* Build the request to perform the next action */
+       if (!load_image(a, rq)) {
+error:
+               if (fc->comp_typ < fi->num_comps) {
+                       ch = &fi->cmp_hdr[fc->comp_typ];
+                       ch->status = CH_STAT_FAILED;
+               }
+
+               complete_fmapi_req(a, rq, FI_STAT_FAILED);
+       }
+}
+
+/* Determine the flash image adaptyp for this adapter */
+static u8 get_fi_adap_type(struct esas2r_adapter *a)
+{
+       u8 type;
+
+       /* use the device ID to get the correct adap_typ for this HBA */
+       switch (a->pcid->device) {
+       case ATTO_DID_INTEL_IOP348:
+               type = FI_AT_SUN_LAKE;
+               break;
+
+       case ATTO_DID_MV_88RC9580:
+       case ATTO_DID_MV_88RC9580TS:
+       case ATTO_DID_MV_88RC9580TSE:
+       case ATTO_DID_MV_88RC9580TL:
+               type = FI_AT_MV_9580;
+               break;
+
+       default:
+               type = FI_AT_UNKNWN;
+               break;
+       }
+
+       return type;
+}
+
+/* Size of config + copyright + flash_ver images, 0 for failure. */
+static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver)
+{
+       u16 *pw = (u16 *)cfg - 1;
+       u32 sz = 0;
+       u32 len = length;
+
+       if (len == 0)
+               len = FM_BUF_SZ;
+
+       if (flash_ver)
+               *flash_ver = 0;
+
+       while (true) {
+               u16 type;
+               u16 size;
+
+               type = le16_to_cpu(*pw--);
+               size = le16_to_cpu(*pw--);
+
+               if (type != FBT_CPYR
+                   && type != FBT_SETUP
+                   && type != FBT_FLASH_VER)
+                       break;
+
+               if (type == FBT_FLASH_VER
+                   && flash_ver)
+                       *flash_ver = le32_to_cpu(*(u32 *)(pw - 1));
+
+               sz += size + (2 * sizeof(u16));
+               pw -= size / sizeof(u16);
+
+               if (sz > len - (2 * sizeof(u16)))
+                       break;
+       }
+
+       /* See if we are comparing the size to the specified length */
+       if (length && sz != length)
+               return 0;
+
+       return sz;
+}
+
+/* Verify that the boot image is valid */
+static u8 chk_boot(u8 *boot_img, u32 length)
+{
+       struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img;
+       u16 hdroffset = le16_to_cpu(bi->header_offset);
+       struct esas2r_boot_header *bh;
+
+       if (bi->signature != le16_to_cpu(0xaa55)
+           || (long)hdroffset >
+           (long)(65536L - sizeof(struct esas2r_boot_header))
+           || (hdroffset & 3)
+           || (hdroffset < sizeof(struct esas2r_boot_image))
+           || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length))
+               return 0xff;
+
+       bh = (struct esas2r_boot_header *)((char *)bi + hdroffset);
+
+       if (bh->signature[0] != 'P'
+           || bh->signature[1] != 'C'
+           || bh->signature[2] != 'I'
+           || bh->signature[3] != 'R'
+           || le16_to_cpu(bh->struct_length) <
+           (u16)sizeof(struct esas2r_boot_header)
+           || bh->class_code[2] != 0x01
+           || bh->class_code[1] != 0x04
+           || bh->class_code[0] != 0x00
+           || (bh->code_type != CODE_TYPE_PC
+               && bh->code_type != CODE_TYPE_OPEN
+               && bh->code_type != CODE_TYPE_EFI))
+               return 0xff;
+
+       return bh->code_type;
+}
+
+/* The sum of all the WORDS of the image */
+static u16 calc_fi_checksum(struct esas2r_flash_context *fc)
+{
+       struct esas2r_flash_img *fi = fc->fi;
+       u16 cksum;
+       u32 len;
+       u16 *pw;
+
+       for (len = (fi->length - fc->fi_hdr_len) / 2,
+            pw = (u16 *)((u8 *)fi + fc->fi_hdr_len),
+            cksum = 0;
+            len;
+            len--, pw++)
+               cksum = cksum + le16_to_cpu(*pw);
+
+       return cksum;
+}
+
+/*
+ * Verify the flash image structure.  The following verifications will
+ * be performed:
+ *              1)  verify the fi_version is correct
+ *              2)  verify the checksum of the entire image.
+ *              3)  validate the adap_typ, action and length fields.
+ *              4)  valdiate each component header. check the img_type and
+ *                  length fields
+ *              5)  valdiate each component image.  validate signatures and
+ *                  local checksums
+ */
+static bool verify_fi(struct esas2r_adapter *a,
+                     struct esas2r_flash_context *fc)
+{
+       struct esas2r_flash_img *fi = fc->fi;
+       u8 type;
+       bool imgerr;
+       u16 i;
+       u32 len;
+       struct esas2r_component_header *ch;
+
+       /* Verify the length - length must even since we do a word checksum */
+       len = fi->length;
+
+       if ((len & 1)
+           || len < fc->fi_hdr_len) {
+               fi->status = FI_STAT_LENGTH;
+               return false;
+       }
+
+       /* Get adapter type and verify type in flash image */
+       type = get_fi_adap_type(a);
+       if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) {
+               fi->status = FI_STAT_ADAPTYP;
+               return false;
+       }
+
+       /*
+        * Loop through each component and verify the img_type and length
+        * fields.  Keep a running count of the sizes sooze we can verify total
+        * size to additive size.
+        */
+       imgerr = false;
+
+       for (i = 0, len = 0, ch = fi->cmp_hdr;
+            i < fi->num_comps;
+            i++, ch++) {
+               bool cmperr = false;
+
+               /*
+                * Verify that the component header has the same index as the
+                * image type.  The headers must be ordered correctly
+                */
+               if (i != ch->img_type) {
+                       imgerr = true;
+                       ch->status = CH_STAT_INVALID;
+                       continue;
+               }
+
+               switch (ch->img_type) {
+               case CH_IT_BIOS:
+                       type = CODE_TYPE_PC;
+                       break;
+
+               case CH_IT_MAC:
+                       type = CODE_TYPE_OPEN;
+                       break;
+
+               case CH_IT_EFI:
+                       type = CODE_TYPE_EFI;
+                       break;
+               }
+
+               switch (ch->img_type) {
+               case CH_IT_FW:
+               case CH_IT_NVR:
+                       break;
+
+               case CH_IT_BIOS:
+               case CH_IT_MAC:
+               case CH_IT_EFI:
+                       if (ch->length & 0x1ff)
+                               cmperr = true;
+
+                       /* Test if component image is present  */
+                       if (ch->length == 0)
+                               break;
+
+                       /* Image is present - verify the image */
+                       if (chk_boot((u8 *)fi + ch->image_offset, ch->length)
+                           != type)
+                               cmperr = true;
+
+                       break;
+
+               case CH_IT_CFG:
+
+                       /* Test if component image is present */
+                       if (ch->length == 0) {
+                               cmperr = true;
+                               break;
+                       }
+
+                       /* Image is present - verify the image */
+                       if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length,
+                                    ch->length, NULL))
+                               cmperr = true;
+
+                       break;
+
+               default:
+
+                       fi->status = FI_STAT_UNKNOWN;
+                       return false;
+               }
+
+               if (cmperr) {
+                       imgerr = true;
+                       ch->status = CH_STAT_INVALID;
+               } else {
+                       ch->status = CH_STAT_PENDING;
+                       len += ch->length;
+               }
+       }
+
+       if (imgerr) {
+               fi->status = FI_STAT_MISSING;
+               return false;
+       }
+
+       /* Compare fi->length to the sum of ch->length fields */
+       if (len != fi->length - fc->fi_hdr_len) {
+               fi->status = FI_STAT_LENGTH;
+               return false;
+       }
+
+       /* Compute the checksum - it should come out zero */
+       if (fi->checksum != calc_fi_checksum(fc)) {
+               fi->status = FI_STAT_CHKSUM;
+               return false;
+       }
+
+       return true;
+}
+
+/* Fill in the FS IOCTL response data from a completed request. */
+static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a,
+                                    struct esas2r_request *rq)
+{
+       struct esas2r_ioctl_fs *fs =
+               (struct esas2r_ioctl_fs *)rq->interrupt_cx;
+
+       if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
+               esas2r_enable_heartbeat(a);
+
+       fs->driver_error = rq->req_stat;
+
+       if (fs->driver_error == RS_SUCCESS)
+               fs->status = ATTO_STS_SUCCESS;
+       else
+               fs->status = ATTO_STS_FAILED;
+}
+
+/* Prepare an FS IOCTL request to be sent to the firmware. */
+bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
+                            struct esas2r_ioctl_fs *fs,
+                            struct esas2r_request *rq,
+                            struct esas2r_sg_context *sgc)
+{
+       u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func);
+       struct esas2r_ioctlfs_command *fsc = &fs->command;
+       u8 func = 0;
+       u32 datalen;
+
+       fs->status = ATTO_STS_FAILED;
+       fs->driver_error = RS_PENDING;
+
+       if (fs->version > ESAS2R_FS_VER) {
+               fs->status = ATTO_STS_INV_VERSION;
+               return false;
+       }
+
+       func = cmd_to_fls_func[fsc->command];
+       if (fsc->command >= cmdcnt || func == 0xFF) {
+               fs->status = ATTO_STS_INV_FUNC;
+               return false;
+       }
+
+       if (fsc->command != ESAS2R_FS_CMD_CANCEL) {
+               if ((a->pcid->device != ATTO_DID_MV_88RC9580
+                    || fs->adap_type != ESAS2R_FS_AT_ESASRAID2)
+                   && (a->pcid->device != ATTO_DID_MV_88RC9580TS
+                       || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2)
+                   && (a->pcid->device != ATTO_DID_MV_88RC9580TSE
+                       || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E)
+                   && (a->pcid->device != ATTO_DID_MV_88RC9580TL
+                       || fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) {
+                       fs->status = ATTO_STS_INV_ADAPTER;
+                       return false;
+               }
+
+               if (fs->driver_ver > ESAS2R_FS_DRVR_VER) {
+                       fs->status = ATTO_STS_INV_DRVR_VER;
+                       return false;
+               }
+       }
+
+       if (a->flags & AF_DEGRADED_MODE) {
+               fs->status = ATTO_STS_DEGRADED;
+               return false;
+       }
+
+       rq->interrupt_cb = esas2r_complete_fs_ioctl;
+       rq->interrupt_cx = fs;
+       datalen = le32_to_cpu(fsc->length);
+       esas2r_build_flash_req(a,
+                              rq,
+                              func,
+                              fsc->checksum,
+                              le32_to_cpu(fsc->flash_addr),
+                              datalen);
+
+       if (func == VDA_FLASH_WRITE
+           || func == VDA_FLASH_READ) {
+               if (datalen == 0) {
+                       fs->status = ATTO_STS_INV_FUNC;
+                       return false;
+               }
+
+               esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge);
+               sgc->length = datalen;
+
+               if (!esas2r_build_sg_list(a, rq, sgc)) {
+                       fs->status = ATTO_STS_OUT_OF_RSRC;
+                       return false;
+               }
+       }
+
+       if (func == VDA_FLASH_COMMIT)
+               esas2r_disable_heartbeat(a);
+
+       esas2r_start_request(a, rq);
+
+       return true;
+}
+
+static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function)
+{
+       u32 starttime;
+       u32 timeout;
+       u32 time;
+       u32 delay;
+       u32 intstat;
+       u32 doorbell;
+
+       /* Disable chip interrupts awhile */
+       if (function == DRBL_FLASH_REQ)
+               esas2r_disable_chip_interrupts(a);
+
+       /* Issue the request to the firmware */
+       esas2r_write_register_dword(a, MU_DOORBELL_IN, function);
+
+       /* Now wait for the firmware to process it */
+       starttime = jiffies_to_msecs(jiffies);
+       timeout = a->flags &
+                 (AF_CHPRST_PENDING | AF_DISC_PENDING) ? 40000 : 5000;
+       delay = 10;
+
+       while (true) {
+               intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
+
+               if (intstat & MU_INTSTAT_DRBL) {
+                       /* Got a doorbell interrupt.  Check for the function */
+                       doorbell =
+                               esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+                       esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+                                                   doorbell);
+                       if (doorbell & function)
+                               break;
+               }
+
+               time = esas2r_stall_execution(a, starttime, &delay);
+               if ((time - starttime) > timeout) {
+                       /*
+                        * Iimeout.  If we were requesting flash access,
+                        * indicate we are done so the firmware knows we gave
+                        * up.  If this was a REQ, we also need to re-enable
+                        * chip interrupts.
+                        */
+                       if (function == DRBL_FLASH_REQ) {
+                               esas2r_hdebug("flash access timeout");
+                               esas2r_write_register_dword(a, MU_DOORBELL_IN,
+                                                           DRBL_FLASH_DONE);
+                               esas2r_enable_chip_interrupts(a);
+                       } else {
+                               esas2r_hdebug("flash release timeout");
+                       }
+
+                       return false;
+               }
+       }
+
+       /* if we're done, re-enable chip interrupts */
+       if (function == DRBL_FLASH_DONE)
+               esas2r_enable_chip_interrupts(a);
+
+       return true;
+}
+
+#define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)
+
+bool esas2r_read_flash_block(struct esas2r_adapter *a,
+                            void *to,
+                            u32 from,
+                            u32 size)
+{
+       u8 *end = (u8 *)to;
+
+       /* Try to acquire access to the flash */
+       if (!esas2r_flash_access(a, DRBL_FLASH_REQ))
+               return false;
+
+       while (size) {
+               u32 len;
+               u32 offset;
+               u32 iatvr;
+
+               if (a->flags2 & AF2_SERIAL_FLASH)
+                       iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE);
+               else
+                       iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE);
+
+               esas2r_map_data_window(a, iatvr);
+               offset = from & (WINDOW_SIZE - 1);
+               len = size;
+
+               if (len > WINDOW_SIZE - offset)
+                       len = WINDOW_SIZE - offset;
+
+               from += len;
+               size -= len;
+
+               while (len--) {
+                       *end++ = esas2r_read_data_byte(a, offset);
+                       offset++;
+               }
+       }
+
+       /* Release flash access */
+       esas2r_flash_access(a, DRBL_FLASH_DONE);
+       return true;
+}
+
+bool esas2r_read_flash_rev(struct esas2r_adapter *a)
+{
+       u8 bytes[256];
+       u16 *pw;
+       u16 *pwstart;
+       u16 type;
+       u16 size;
+       u32 sz;
+
+       sz = sizeof(bytes);
+       pw = (u16 *)(bytes + sz);
+       pwstart = (u16 *)bytes + 2;
+
+       if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz))
+               goto invalid_rev;
+
+       while (pw >= pwstart) {
+               pw--;
+               type = le16_to_cpu(*pw);
+               pw--;
+               size = le16_to_cpu(*pw);
+               pw -= size / 2;
+
+               if (type == FBT_CPYR
+                   || type == FBT_SETUP
+                   || pw < pwstart)
+                       continue;
+
+               if (type == FBT_FLASH_VER)
+                       a->flash_ver = le32_to_cpu(*(u32 *)pw);
+
+               break;
+       }
+
+invalid_rev:
+       return esas2r_print_flash_rev(a);
+}
+
+bool esas2r_print_flash_rev(struct esas2r_adapter *a)
+{
+       u16 year = LOWORD(a->flash_ver);
+       u8 day = LOBYTE(HIWORD(a->flash_ver));
+       u8 month = HIBYTE(HIWORD(a->flash_ver));
+
+       if (day == 0
+           || month == 0
+           || day > 31
+           || month > 12
+           || year < 2006
+           || year > 9999) {
+               strcpy(a->flash_rev, "not found");
+               a->flash_ver = 0;
+               return false;
+       }
+
+       sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year);
+       esas2r_hdebug("flash version: %s", a->flash_rev);
+       return true;
+}
+
+/*
+ * Find the type of boot image type that is currently in the flash.
+ * The chip only has a 64 KB PCI-e expansion ROM
+ * size so only one image can be flashed at a time.
+ */
+bool esas2r_read_image_type(struct esas2r_adapter *a)
+{
+       u8 bytes[256];
+       struct esas2r_boot_image *bi;
+       struct esas2r_boot_header *bh;
+       u32 sz;
+       u32 len;
+       u32 offset;
+
+       /* Start at the base of the boot images and look for a valid image */
+       sz = sizeof(bytes);
+       len = FLS_LENGTH_BOOT;
+       offset = 0;
+
+       while (true) {
+               if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT +
+                                            offset,
+                                            sz))
+                       goto invalid_rev;
+
+               bi = (struct esas2r_boot_image *)bytes;
+               bh = (struct esas2r_boot_header *)((u8 *)bi +
+                                                  le16_to_cpu(
+                                                          bi->header_offset));
+               if (bi->signature != cpu_to_le16(0xAA55))
+                       goto invalid_rev;
+
+               if (bh->code_type == CODE_TYPE_PC) {
+                       strcpy(a->image_type, "BIOS");
+
+                       return true;
+               } else if (bh->code_type == CODE_TYPE_EFI) {
+                       struct esas2r_efi_image *ei;
+
+                       /*
+                        * So we have an EFI image.  There are several types
+                        * so see which architecture we have.
+                        */
+                       ei = (struct esas2r_efi_image *)bytes;
+
+                       switch (le16_to_cpu(ei->machine_type)) {
+                       case EFI_MACHINE_IA32:
+                               strcpy(a->image_type, "EFI 32-bit");
+                               return true;
+
+                       case EFI_MACHINE_IA64:
+                               strcpy(a->image_type, "EFI itanium");
+                               return true;
+
+                       case EFI_MACHINE_X64:
+                               strcpy(a->image_type, "EFI 64-bit");
+                               return true;
+
+                       case EFI_MACHINE_EBC:
+                               strcpy(a->image_type, "EFI EBC");
+                               return true;
+
+                       default:
+                               goto invalid_rev;
+                       }
+               } else {
+                       u32 thislen;
+
+                       /* jump to the next image */
+                       thislen = (u32)le16_to_cpu(bh->image_length) * 512;
+                       if (thislen == 0
+                           || thislen + offset > len
+                           || bh->indicator == INDICATOR_LAST)
+                               break;
+
+                       offset += thislen;
+               }
+       }
+
+invalid_rev:
+       strcpy(a->image_type, "no boot images");
+       return false;
+}
+
+/*
+ *  Read and validate current NVRAM parameters by accessing
+ *  physical NVRAM directly.  if currently stored parameters are
+ *  invalid, use the defaults.
+ */
+bool esas2r_nvram_read_direct(struct esas2r_adapter *a)
+{
+       bool result;
+
+       if (down_interruptible(&a->nvram_semaphore))
+               return false;
+
+       if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR,
+                                    sizeof(struct esas2r_sas_nvram))) {
+               esas2r_hdebug("NVRAM read failed, using defaults");
+               return false;
+       }
+
+       result = esas2r_nvram_validate(a);
+
+       up(&a->nvram_semaphore);
+
+       return result;
+}
+
+/* Interrupt callback to process NVRAM completions. */
+static void esas2r_nvram_callback(struct esas2r_adapter *a,
+                                 struct esas2r_request *rq)
+{
+       struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+
+       if (rq->req_stat == RS_SUCCESS) {
+               /* last request was successful.  see what to do now. */
+
+               switch (vrq->sub_func) {
+               case VDA_FLASH_BEGINW:
+                       vrq->sub_func = VDA_FLASH_WRITE;
+                       rq->req_stat = RS_PENDING;
+                       break;
+
+               case VDA_FLASH_WRITE:
+                       vrq->sub_func = VDA_FLASH_COMMIT;
+                       rq->req_stat = RS_PENDING;
+                       break;
+
+               case VDA_FLASH_READ:
+                       esas2r_nvram_validate(a);
+                       break;
+
+               case VDA_FLASH_COMMIT:
+               default:
+                       break;
+               }
+       }
+
+       if (rq->req_stat != RS_PENDING) {
+               /* update the NVRAM state */
+               if (rq->req_stat == RS_SUCCESS)
+                       esas2r_lock_set_flags(&a->flags, AF_NVR_VALID);
+               else
+                       esas2r_lock_clear_flags(&a->flags, AF_NVR_VALID);
+
+               esas2r_enable_heartbeat(a);
+
+               up(&a->nvram_semaphore);
+       }
+}
+
+/*
+ * Write the contents of nvram to the adapter's physical NVRAM.
+ * The cached copy of the NVRAM is also updated.
+ */
+bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
+                       struct esas2r_sas_nvram *nvram)
+{
+       struct esas2r_sas_nvram *n = nvram;
+       u8 sas_address_bytes[8];
+       u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0];
+       struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+
+       if (a->flags & AF_DEGRADED_MODE)
+               return false;
+
+       if (down_interruptible(&a->nvram_semaphore))
+               return false;
+
+       if (n == NULL)
+               n = a->nvram;
+
+       /* check the validity of the settings */
+       if (n->version > SASNVR_VERSION) {
+               up(&a->nvram_semaphore);
+               return false;
+       }
+
+       memcpy(&sas_address_bytes[0], n->sas_addr, 8);
+
+       if (sas_address_bytes[0] != 0x50
+           || sas_address_bytes[1] != 0x01
+           || sas_address_bytes[2] != 0x08
+           || (sas_address_bytes[3] & 0xF0) != 0x60
+           || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) {
+               up(&a->nvram_semaphore);
+               return false;
+       }
+
+       if (n->spin_up_delay > SASNVR_SPINUP_MAX)
+               n->spin_up_delay = SASNVR_SPINUP_MAX;
+
+       n->version = SASNVR_VERSION;
+       n->checksum = n->checksum - esas2r_nvramcalc_cksum(n);
+       memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram));
+
+       /* write the NVRAM */
+       n = a->nvram;
+       esas2r_disable_heartbeat(a);
+
+       esas2r_build_flash_req(a,
+                              rq,
+                              VDA_FLASH_BEGINW,
+                              esas2r_nvramcalc_xor_cksum(n),
+                              FLS_OFFSET_NVR,
+                              sizeof(struct esas2r_sas_nvram));
+
+       if (a->flags & AF_LEGACY_SGE_MODE) {
+
+               vrq->data.sge[0].length =
+                       cpu_to_le32(SGE_LAST |
+                                   sizeof(struct esas2r_sas_nvram));
+               vrq->data.sge[0].address = cpu_to_le64(
+                       a->uncached_phys + (u64)((u8 *)n - a->uncached));
+       } else {
+               vrq->data.prde[0].ctl_len =
+                       cpu_to_le32(sizeof(struct esas2r_sas_nvram));
+               vrq->data.prde[0].address = cpu_to_le64(
+                       a->uncached_phys
+                       + (u64)((u8 *)n - a->uncached));
+       }
+       rq->interrupt_cb = esas2r_nvram_callback;
+       esas2r_start_request(a, rq);
+       return true;
+}
+
+/* Validate the cached NVRAM.  if the NVRAM is invalid, load the defaults. */
+bool esas2r_nvram_validate(struct esas2r_adapter *a)
+{
+       struct esas2r_sas_nvram *n = a->nvram;
+       bool rslt = false;
+
+       if (n->signature[0] != 'E'
+           || n->signature[1] != 'S'
+           || n->signature[2] != 'A'
+           || n->signature[3] != 'S') {
+               esas2r_hdebug("invalid NVRAM signature");
+       } else if (esas2r_nvramcalc_cksum(n)) {
+               esas2r_hdebug("invalid NVRAM checksum");
+       } else if (n->version > SASNVR_VERSION) {
+               esas2r_hdebug("invalid NVRAM version");
+       } else {
+               esas2r_lock_set_flags(&a->flags, AF_NVR_VALID);
+               rslt = true;
+       }
+
+       if (rslt == false) {
+               esas2r_hdebug("using defaults");
+               esas2r_nvram_set_defaults(a);
+       }
+
+       return rslt;
+}
+
+/*
+ * Set the cached NVRAM to defaults.  note that this function sets the default
+ * NVRAM when it has been determined that the physical NVRAM is invalid.
+ * In this case, the SAS address is fabricated.
+ */
+void esas2r_nvram_set_defaults(struct esas2r_adapter *a)
+{
+       struct esas2r_sas_nvram *n = a->nvram;
+       u32 time = jiffies_to_msecs(jiffies);
+
+       esas2r_lock_clear_flags(&a->flags, AF_NVR_VALID);
+       memcpy(n, &default_sas_nvram, sizeof(struct esas2r_sas_nvram));
+       n->sas_addr[3] |= 0x0F;
+       n->sas_addr[4] = HIBYTE(LOWORD(time));
+       n->sas_addr[5] = LOBYTE(LOWORD(time));
+       n->sas_addr[6] = a->pcid->bus->number;
+       n->sas_addr[7] = a->pcid->devfn;
+}
+
+void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
+                              struct esas2r_sas_nvram *nvram)
+{
+       u8 sas_addr[8];
+
+       /*
+        * in case we are copying the defaults into the adapter, copy the SAS
+        * address out first.
+        */
+       memcpy(&sas_addr[0], a->nvram->sas_addr, 8);
+       memcpy(nvram, &default_sas_nvram, sizeof(struct esas2r_sas_nvram));
+       memcpy(&nvram->sas_addr[0], &sas_addr[0], 8);
+}
+
+bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
+                  struct esas2r_request *rq, struct esas2r_sg_context *sgc)
+{
+       struct esas2r_flash_context *fc = &a->flash_context;
+       u8 j;
+       struct esas2r_component_header *ch;
+
+       if (esas2r_lock_set_flags(&a->flags, AF_FLASH_LOCK) & AF_FLASH_LOCK) {
+               /* flag was already set */
+               fi->status = FI_STAT_BUSY;
+               return false;
+       }
+
+       memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context));
+       sgc = &fc->sgc;
+       fc->fi = fi;
+       fc->sgc_offset = sgc->cur_offset;
+       rq->req_stat = RS_SUCCESS;
+       rq->interrupt_cx = fc;
+
+       switch (fi->fi_version) {
+       case FI_VERSION_1:
+               fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf;
+               fc->num_comps = FI_NUM_COMPS_V1;
+               fc->fi_hdr_len = sizeof(struct esas2r_flash_img);
+               break;
+
+       default:
+               return complete_fmapi_req(a, rq, FI_STAT_IMG_VER);
+       }
+
+       if (a->flags & AF_DEGRADED_MODE)
+               return complete_fmapi_req(a, rq, FI_STAT_DEGRADED);
+
+       switch (fi->action) {
+       case FI_ACT_DOWN: /* Download the components */
+               /* Verify the format of the flash image */
+               if (!verify_fi(a, fc))
+                       return complete_fmapi_req(a, rq, fi->status);
+
+               /* Adjust the BIOS fields that are dependent on the HBA */
+               ch = &fi->cmp_hdr[CH_IT_BIOS];
+
+               if (ch->length)
+                       fix_bios(a, fi);
+
+               /* Adjust the EFI fields that are dependent on the HBA */
+               ch = &fi->cmp_hdr[CH_IT_EFI];
+
+               if (ch->length)
+                       fix_efi(a, fi);
+
+               /*
+                * Since the image was just modified, compute the checksum on
+                * the modified image.  First update the CRC for the composite
+                * expansion ROM image.
+                */
+               fi->checksum = calc_fi_checksum(fc);
+
+               /* Disable the heartbeat */
+               esas2r_disable_heartbeat(a);
+
+               /* Now start up the download sequence */
+               fc->task = FMTSK_ERASE_BOOT;
+               fc->func = VDA_FLASH_BEGINW;
+               fc->comp_typ = CH_IT_CFG;
+               fc->flsh_addr = FLS_OFFSET_BOOT;
+               fc->sgc.length = FLS_LENGTH_BOOT;
+               fc->sgc.cur_offset = NULL;
+
+               /* Setup the callback address */
+               fc->interrupt_cb = fw_download_proc;
+               break;
+
+       case FI_ACT_UPSZ: /* Get upload sizes */
+               fi->adap_typ = get_fi_adap_type(a);
+               fi->flags = 0;
+               fi->num_comps = fc->num_comps;
+               fi->length = fc->fi_hdr_len;
+
+               /* Report the type of boot image in the rel_version string */
+               memcpy(fi->rel_version, a->image_type,
+                      sizeof(fi->rel_version));
+
+               /* Build the component headers */
+               for (j = 0, ch = fi->cmp_hdr;
+                    j < fi->num_comps;
+                    j++, ch++) {
+                       ch->img_type = j;
+                       ch->status = CH_STAT_PENDING;
+                       ch->length = 0;
+                       ch->version = 0xffffffff;
+                       ch->image_offset = 0;
+                       ch->pad[0] = 0;
+                       ch->pad[1] = 0;
+               }
+
+               if (a->flash_ver != 0) {
+                       fi->cmp_hdr[CH_IT_BIOS].version =
+                               fi->cmp_hdr[CH_IT_MAC].version =
+                                       fi->cmp_hdr[CH_IT_EFI].version =
+                                               fi->cmp_hdr[CH_IT_CFG].version
+                                                       = a->flash_ver;
+
+                       fi->cmp_hdr[CH_IT_BIOS].status =
+                               fi->cmp_hdr[CH_IT_MAC].status =
+                                       fi->cmp_hdr[CH_IT_EFI].status =
+                                               fi->cmp_hdr[CH_IT_CFG].status =
+                                                       CH_STAT_SUCCESS;
+
+                       return complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
+               }
+
+       /* fall through */
+
+       case FI_ACT_UP: /* Upload the components */
+       default:
+               return complete_fmapi_req(a, rq, FI_STAT_INVALID);
+       }
+
+       /*
+        * If we make it here, fc has been setup to do the first task.  Call
+        * load_image to format the request, start it, and get out.  The
+        * interrupt code will call the callback when the first message is
+        * complete.
+        */
+       if (!load_image(a, rq))
+               return complete_fmapi_req(a, rq, FI_STAT_FAILED);
+
+       esas2r_start_request(a, rq);
+
+       return true;
+}
diff --git a/drivers/scsi/esas2r/esas2r_targdb.c 
b/drivers/scsi/esas2r/esas2r_targdb.c
new file mode 100644
index 0000000..e540a2f
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_targdb.c
@@ -0,0 +1,306 @@
+/*
+ *  linux/drivers/scsi/esas2r/esas2r_targdb.c
+ *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ *  Copyright (c) 2001-2013 ATTO Technology, Inc.
+ *  (mailto:linuxdriv...@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+void esas2r_targ_db_initialize(struct esas2r_adapter *a)
+{
+       struct esas2r_target *t;
+
+       for (t = a->targetdb; t < a->targetdb_end; t++) {
+               memset(t, 0, sizeof(struct esas2r_target));
+
+               t->target_state = TS_NOT_PRESENT;
+               t->buffered_target_state = TS_NOT_PRESENT;
+               t->new_target_state = TS_INVALID;
+       }
+}
+
+void esas2r_targ_db_remove_all(struct esas2r_adapter *a, bool notify)
+{
+       struct esas2r_target *t;
+       unsigned long flags;
+
+       for (t = a->targetdb; t < a->targetdb_end; t++) {
+               if (t->target_state != TS_PRESENT)
+                       continue;
+
+               spin_lock_irqsave(&a->mem_lock, flags);
+               esas2r_targ_db_remove(a, t);
+               spin_unlock_irqrestore(&a->mem_lock, flags);
+
+               if (notify) {
+                       esas2r_trace("remove id:%d", esas2r_targ_get_id(t,
+                                                                       a));
+                       esas2r_target_state_changed(a, esas2r_targ_get_id(t,
+                                                                         a),
+                                                   TS_NOT_PRESENT);
+               }
+       }
+}
+
+void esas2r_targ_db_report_changes(struct esas2r_adapter *a)
+{
+       struct esas2r_target *t;
+       unsigned long flags;
+
+       esas2r_trace_enter();
+
+       if (a->flags & AF_DISC_PENDING) {
+               esas2r_trace_exit();
+               return;
+       }
+
+       for (t = a->targetdb; t < a->targetdb_end; t++) {
+               u8 state = TS_INVALID;
+
+               spin_lock_irqsave(&a->mem_lock, flags);
+               if (t->buffered_target_state != t->target_state)
+                       state = t->buffered_target_state = t->target_state;
+
+               spin_unlock_irqrestore(&a->mem_lock, flags);
+               if (state != TS_INVALID) {
+                       esas2r_trace("targ_db_report_changes:%d",
+                                    esas2r_targ_get_id(
+                                            t,
+                                            a));
+                       esas2r_trace("state:%d", state);
+
+                       esas2r_target_state_changed(a,
+                                                   esas2r_targ_get_id(t,
+                                                                      a),
+                                                   state);
+               }
+       }
+
+       esas2r_trace_exit();
+}
+
+struct esas2r_target *esas2r_targ_db_add_raid(struct esas2r_adapter *a,
+                                             struct esas2r_disc_context *
+                                             dc)
+{
+       struct esas2r_target *t;
+
+       esas2r_trace_enter();
+
+       if (dc->curr_virt_id >= ESAS2R_MAX_TARGETS) {
+               esas2r_bugon();
+               esas2r_trace_exit();
+               return NULL;
+       }
+
+       t = a->targetdb + dc->curr_virt_id;
+
+       if (t->target_state == TS_PRESENT) {
+               esas2r_trace_exit();
+               return NULL;
+       }
+
+       esas2r_hdebug("add RAID %s, T:%d", dc->raid_grp_name,
+                     esas2r_targ_get_id(
+                             t,
+                             a));
+
+       if (dc->interleave == 0
+           || dc->block_size  == 0) {
+               /* these are invalid values, don't create the target entry. */
+
+               esas2r_hdebug("invalid RAID group dimensions");
+
+               esas2r_trace_exit();
+
+               return NULL;
+       }
+
+       t->block_size = dc->block_size;
+       t->inter_byte = dc->interleave;
+       t->inter_block = dc->interleave / dc->block_size;
+       t->virt_targ_id = dc->curr_virt_id;
+       t->phys_targ_id = ESAS2R_TARG_ID_INV;
+
+       t->flags &= ~TF_PASS_THRU;
+       t->flags |= TF_USED;
+
+       t->identifier_len = 0;
+
+       t->target_state = TS_PRESENT;
+
+       return t;
+}
+
+struct esas2r_target *esas2r_targ_db_add_pthru(struct esas2r_adapter *a,
+                                              struct esas2r_disc_context *dc,
+                                              u8 *ident,
+                                              u8 ident_len)
+{
+       struct esas2r_target *t;
+
+       esas2r_trace_enter();
+
+       if (dc->curr_virt_id >= ESAS2R_MAX_TARGETS) {
+               esas2r_bugon();
+               esas2r_trace_exit();
+               return NULL;
+       }
+
+       /* see if we found this device before. */
+
+       t = esas2r_targ_db_find_by_ident(a, ident, ident_len);
+
+       if (t == NULL) {
+               t = a->targetdb + dc->curr_virt_id;
+
+               if (ident_len > sizeof(t->identifier)
+                   || t->target_state == TS_PRESENT) {
+                       esas2r_trace_exit();
+                       return NULL;
+               }
+       }
+
+       esas2r_hdebug("add PT; T:%d, V:%d, P:%d", esas2r_targ_get_id(t, a),
+                     dc->curr_virt_id,
+                     dc->curr_phys_id);
+
+       t->block_size = 0;
+       t->inter_byte = 0;
+       t->inter_block = 0;
+       t->virt_targ_id = dc->curr_virt_id;
+       t->phys_targ_id = dc->curr_phys_id;
+       t->identifier_len = ident_len;
+
+       memcpy(t->identifier, ident, ident_len);
+
+       t->flags |= TF_PASS_THRU | TF_USED;
+
+       t->target_state = TS_PRESENT;
+
+       return t;
+}
+
+void esas2r_targ_db_remove(struct esas2r_adapter *a, struct esas2r_target *t)
+{
+       esas2r_trace_enter();
+
+       t->target_state = TS_NOT_PRESENT;
+
+       esas2r_trace("remove id:%d", esas2r_targ_get_id(t, a));
+
+       esas2r_trace_exit();
+}
+
+struct esas2r_target *esas2r_targ_db_find_by_sas_addr(struct esas2r_adapter *a,
+                                                     u64 *sas_addr)
+{
+       struct esas2r_target *t;
+
+       for (t = a->targetdb; t < a->targetdb_end; t++)
+               if (t->sas_addr == *sas_addr)
+                       return t;
+
+       return NULL;
+}
+
+struct esas2r_target *esas2r_targ_db_find_by_ident(struct esas2r_adapter *a,
+                                                  void *identifier,
+                                                  u8 ident_len)
+{
+       struct esas2r_target *t;
+
+       for (t = a->targetdb; t < a->targetdb_end; t++) {
+               if (ident_len == t->identifier_len
+                   && memcmp(&t->identifier[0], identifier,
+                             ident_len) == 0)
+                       return t;
+       }
+
+       return NULL;
+}
+
+u16 esas2r_targ_db_find_next_present(struct esas2r_adapter *a, u16 target_id)
+{
+       u16 id = target_id + 1;
+
+       while (id < ESAS2R_MAX_TARGETS) {
+               struct esas2r_target *t = a->targetdb + id;
+
+               if (t->target_state == TS_PRESENT)
+                       break;
+
+               id++;
+       }
+
+       return id;
+}
+
+struct esas2r_target *esas2r_targ_db_find_by_virt_id(struct esas2r_adapter *a,
+                                                    u16 virt_id)
+{
+       struct esas2r_target *t;
+
+       for (t = a->targetdb; t < a->targetdb_end; t++) {
+               if (t->target_state != TS_PRESENT)
+                       continue;
+
+               if (t->virt_targ_id == virt_id)
+                       return t;
+       }
+
+       return NULL;
+}
+
+u16 esas2r_targ_db_get_tgt_cnt(struct esas2r_adapter *a)
+{
+       u16 devcnt = 0;
+       struct esas2r_target *t;
+       unsigned long flags;
+
+       spin_lock_irqsave(&a->mem_lock, flags);
+       for (t = a->targetdb; t < a->targetdb_end; t++)
+               if (t->target_state == TS_PRESENT)
+                       devcnt++;
+
+       spin_unlock_irqrestore(&a->mem_lock, flags);
+
+       return devcnt;
+}
-- 
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to