Merge branch 'misc' into for-linus
[cascardo/linux.git] / drivers / scsi / bfa / bfa_ioc.c
index f78bcb6..65180e1 100644 (file)
@@ -21,6 +21,7 @@
 #include "bfi_reg.h"
 #include "bfa_defs.h"
 #include "bfa_defs_svc.h"
+#include "bfi.h"
 
 BFA_TRC_FILE(CNA, IOC);
 
@@ -45,6 +46,14 @@ BFA_TRC_FILE(CNA, IOC);
 
 #define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn))
 
+#define bfa_ioc_state_disabled(__sm)           \
+       (((__sm) == BFI_IOC_UNINIT) ||          \
+       ((__sm) == BFI_IOC_INITING) ||          \
+       ((__sm) == BFI_IOC_HWINIT) ||           \
+       ((__sm) == BFI_IOC_DISABLED) ||         \
+       ((__sm) == BFI_IOC_FAIL) ||             \
+       ((__sm) == BFI_IOC_CFG_DISABLED))
+
 /*
  * Asic specific macros : see bfa_hw_cb.c and bfa_hw_ct.c for details.
  */
@@ -102,6 +111,12 @@ static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc);
 static void bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc);
 static void bfa_ioc_fail_notify(struct bfa_ioc_s *ioc);
 static void bfa_ioc_pf_fwmismatch(struct bfa_ioc_s *ioc);
+static enum bfi_ioc_img_ver_cmp_e bfa_ioc_fw_ver_patch_cmp(
+                               struct bfi_ioc_image_hdr_s *base_fwhdr,
+                               struct bfi_ioc_image_hdr_s *fwhdr_to_cmp);
+static enum bfi_ioc_img_ver_cmp_e bfa_ioc_flash_fwver_cmp(
+                               struct bfa_ioc_s *ioc,
+                               struct bfi_ioc_image_hdr_s *base_fwhdr);
 
 /*
  * IOC state machine definitions/declarations
@@ -1454,28 +1469,42 @@ bfa_ioc_fwver_get(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
 }
 
 /*
- * Returns TRUE if same.
+ * Returns TRUE if driver is willing to work with current smem f/w version.
  */
 bfa_boolean_t
-bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
+bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc,
+               struct bfi_ioc_image_hdr_s *smem_fwhdr)
 {
        struct bfi_ioc_image_hdr_s *drv_fwhdr;
-       int i;
+       enum bfi_ioc_img_ver_cmp_e smem_flash_cmp, drv_smem_cmp;
 
        drv_fwhdr = (struct bfi_ioc_image_hdr_s *)
                bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
 
-       for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++) {
-               if (fwhdr->md5sum[i] != cpu_to_le32(drv_fwhdr->md5sum[i])) {
-                       bfa_trc(ioc, i);
-                       bfa_trc(ioc, fwhdr->md5sum[i]);
-                       bfa_trc(ioc, drv_fwhdr->md5sum[i]);
-                       return BFA_FALSE;
-               }
+       /*
+        * If smem is incompatible or old, driver should not work with it.
+        */
+       drv_smem_cmp = bfa_ioc_fw_ver_patch_cmp(drv_fwhdr, smem_fwhdr);
+       if (drv_smem_cmp == BFI_IOC_IMG_VER_INCOMP ||
+               drv_smem_cmp == BFI_IOC_IMG_VER_OLD) {
+               return BFA_FALSE;
        }
 
-       bfa_trc(ioc, fwhdr->md5sum[0]);
-       return BFA_TRUE;
+       /*
+        * IF Flash has a better F/W than smem do not work with smem.
+        * If smem f/w == flash f/w, as smem f/w not old | incmp, work with it.
+        * If Flash is old or incomp work with smem iff smem f/w == drv f/w.
+        */
+       smem_flash_cmp = bfa_ioc_flash_fwver_cmp(ioc, smem_fwhdr);
+
+       if (smem_flash_cmp == BFI_IOC_IMG_VER_BETTER) {
+               return BFA_FALSE;
+       } else if (smem_flash_cmp == BFI_IOC_IMG_VER_SAME) {
+               return BFA_TRUE;
+       } else {
+               return (drv_smem_cmp == BFI_IOC_IMG_VER_SAME) ?
+                       BFA_TRUE : BFA_FALSE;
+       }
 }
 
 /*
@@ -1485,17 +1514,9 @@ bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
 static bfa_boolean_t
 bfa_ioc_fwver_valid(struct bfa_ioc_s *ioc, u32 boot_env)
 {
-       struct bfi_ioc_image_hdr_s fwhdr, *drv_fwhdr;
+       struct bfi_ioc_image_hdr_s fwhdr;
 
        bfa_ioc_fwver_get(ioc, &fwhdr);
-       drv_fwhdr = (struct bfi_ioc_image_hdr_s *)
-               bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
-
-       if (fwhdr.signature != cpu_to_le32(drv_fwhdr->signature)) {
-               bfa_trc(ioc, fwhdr.signature);
-               bfa_trc(ioc, drv_fwhdr->signature);
-               return BFA_FALSE;
-       }
 
        if (swab32(fwhdr.bootenv) != boot_env) {
                bfa_trc(ioc, fwhdr.bootenv);
@@ -1506,6 +1527,168 @@ bfa_ioc_fwver_valid(struct bfa_ioc_s *ioc, u32 boot_env)
        return bfa_ioc_fwver_cmp(ioc, &fwhdr);
 }
 
+static bfa_boolean_t
+bfa_ioc_fwver_md5_check(struct bfi_ioc_image_hdr_s *fwhdr_1,
+                               struct bfi_ioc_image_hdr_s *fwhdr_2)
+{
+       int i;
+
+       for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++)
+               if (fwhdr_1->md5sum[i] != fwhdr_2->md5sum[i])
+                       return BFA_FALSE;
+
+       return BFA_TRUE;
+}
+
+/*
+ * Returns TRUE if major minor and maintainence are same.
+ * If patch versions are same, check for MD5 Checksum to be same.
+ */
+static bfa_boolean_t
+bfa_ioc_fw_ver_compatible(struct bfi_ioc_image_hdr_s *drv_fwhdr,
+                               struct bfi_ioc_image_hdr_s *fwhdr_to_cmp)
+{
+       if (drv_fwhdr->signature != fwhdr_to_cmp->signature)
+               return BFA_FALSE;
+
+       if (drv_fwhdr->fwver.major != fwhdr_to_cmp->fwver.major)
+               return BFA_FALSE;
+
+       if (drv_fwhdr->fwver.minor != fwhdr_to_cmp->fwver.minor)
+               return BFA_FALSE;
+
+       if (drv_fwhdr->fwver.maint != fwhdr_to_cmp->fwver.maint)
+               return BFA_FALSE;
+
+       if (drv_fwhdr->fwver.patch == fwhdr_to_cmp->fwver.patch &&
+               drv_fwhdr->fwver.phase == fwhdr_to_cmp->fwver.phase &&
+               drv_fwhdr->fwver.build == fwhdr_to_cmp->fwver.build) {
+               return bfa_ioc_fwver_md5_check(drv_fwhdr, fwhdr_to_cmp);
+       }
+
+       return BFA_TRUE;
+}
+
+static bfa_boolean_t
+bfa_ioc_flash_fwver_valid(struct bfi_ioc_image_hdr_s *flash_fwhdr)
+{
+       if (flash_fwhdr->fwver.major == 0 || flash_fwhdr->fwver.major == 0xFF)
+               return BFA_FALSE;
+
+       return BFA_TRUE;
+}
+
+static bfa_boolean_t fwhdr_is_ga(struct bfi_ioc_image_hdr_s *fwhdr)
+{
+       if (fwhdr->fwver.phase == 0 &&
+               fwhdr->fwver.build == 0)
+               return BFA_TRUE;
+
+       return BFA_FALSE;
+}
+
+/*
+ * Returns TRUE if both are compatible and patch of fwhdr_to_cmp is better.
+ */
+static enum bfi_ioc_img_ver_cmp_e
+bfa_ioc_fw_ver_patch_cmp(struct bfi_ioc_image_hdr_s *base_fwhdr,
+                               struct bfi_ioc_image_hdr_s *fwhdr_to_cmp)
+{
+       if (bfa_ioc_fw_ver_compatible(base_fwhdr, fwhdr_to_cmp) == BFA_FALSE)
+               return BFI_IOC_IMG_VER_INCOMP;
+
+       if (fwhdr_to_cmp->fwver.patch > base_fwhdr->fwver.patch)
+               return BFI_IOC_IMG_VER_BETTER;
+
+       else if (fwhdr_to_cmp->fwver.patch < base_fwhdr->fwver.patch)
+               return BFI_IOC_IMG_VER_OLD;
+
+       /*
+        * GA takes priority over internal builds of the same patch stream.
+        * At this point major minor maint and patch numbers are same.
+        */
+
+       if (fwhdr_is_ga(base_fwhdr) == BFA_TRUE) {
+               if (fwhdr_is_ga(fwhdr_to_cmp))
+                       return BFI_IOC_IMG_VER_SAME;
+               else
+                       return BFI_IOC_IMG_VER_OLD;
+       } else {
+               if (fwhdr_is_ga(fwhdr_to_cmp))
+                       return BFI_IOC_IMG_VER_BETTER;
+       }
+
+       if (fwhdr_to_cmp->fwver.phase > base_fwhdr->fwver.phase)
+               return BFI_IOC_IMG_VER_BETTER;
+       else if (fwhdr_to_cmp->fwver.phase < base_fwhdr->fwver.phase)
+               return BFI_IOC_IMG_VER_OLD;
+
+       if (fwhdr_to_cmp->fwver.build > base_fwhdr->fwver.build)
+               return BFI_IOC_IMG_VER_BETTER;
+       else if (fwhdr_to_cmp->fwver.build < base_fwhdr->fwver.build)
+               return BFI_IOC_IMG_VER_OLD;
+
+       /*
+        * All Version Numbers are equal.
+        * Md5 check to be done as a part of compatibility check.
+        */
+       return BFI_IOC_IMG_VER_SAME;
+}
+
+#define BFA_FLASH_PART_FWIMG_ADDR      0x100000 /* fw image address */
+
+bfa_status_t
+bfa_ioc_flash_img_get_chnk(struct bfa_ioc_s *ioc, u32 off,
+                               u32 *fwimg)
+{
+       return bfa_flash_raw_read(ioc->pcidev.pci_bar_kva,
+                       BFA_FLASH_PART_FWIMG_ADDR + (off * sizeof(u32)),
+                       (char *)fwimg, BFI_FLASH_CHUNK_SZ);
+}
+
+static enum bfi_ioc_img_ver_cmp_e
+bfa_ioc_flash_fwver_cmp(struct bfa_ioc_s *ioc,
+                       struct bfi_ioc_image_hdr_s *base_fwhdr)
+{
+       struct bfi_ioc_image_hdr_s *flash_fwhdr;
+       bfa_status_t status;
+       u32 fwimg[BFI_FLASH_CHUNK_SZ_WORDS];
+
+       status = bfa_ioc_flash_img_get_chnk(ioc, 0, fwimg);
+       if (status != BFA_STATUS_OK)
+               return BFI_IOC_IMG_VER_INCOMP;
+
+       flash_fwhdr = (struct bfi_ioc_image_hdr_s *) fwimg;
+       if (bfa_ioc_flash_fwver_valid(flash_fwhdr) == BFA_TRUE)
+               return bfa_ioc_fw_ver_patch_cmp(base_fwhdr, flash_fwhdr);
+       else
+               return BFI_IOC_IMG_VER_INCOMP;
+}
+
+
+/*
+ * Invalidate fwver signature
+ */
+bfa_status_t
+bfa_ioc_fwsig_invalidate(struct bfa_ioc_s *ioc)
+{
+
+       u32     pgnum, pgoff;
+       u32     loff = 0;
+       enum bfi_ioc_state ioc_fwstate;
+
+       ioc_fwstate = bfa_ioc_get_cur_ioc_fwstate(ioc);
+       if (!bfa_ioc_state_disabled(ioc_fwstate))
+               return BFA_STATUS_ADAPTER_ENABLED;
+
+       pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
+       pgoff = PSS_SMEM_PGOFF(loff);
+       writel(pgnum, ioc->ioc_regs.host_page_num_fn);
+       bfa_mem_write(ioc->ioc_regs.smem_page_start, loff, BFA_IOC_FW_INV_SIGN);
+
+       return BFA_STATUS_OK;
+}
+
 /*
  * Conditionally flush any pending message from firmware at start.
  */
@@ -1544,8 +1727,8 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
                BFA_FALSE : bfa_ioc_fwver_valid(ioc, boot_env);
 
        if (!fwvalid) {
-               bfa_ioc_boot(ioc, boot_type, boot_env);
-               bfa_ioc_poll_fwinit(ioc);
+               if (bfa_ioc_boot(ioc, boot_type, boot_env) == BFA_STATUS_OK)
+                       bfa_ioc_poll_fwinit(ioc);
                return;
        }
 
@@ -1580,8 +1763,8 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
        /*
         * Initialize the h/w for any other states.
         */
-       bfa_ioc_boot(ioc, boot_type, boot_env);
-       bfa_ioc_poll_fwinit(ioc);
+       if (bfa_ioc_boot(ioc, boot_type, boot_env) == BFA_STATUS_OK)
+               bfa_ioc_poll_fwinit(ioc);
 }
 
 static void
@@ -1684,7 +1867,7 @@ bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc)
 /*
  *     Initiate a full firmware download.
  */
-static void
+static bfa_status_t
 bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
                    u32 boot_env)
 {
@@ -1694,28 +1877,60 @@ bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
        u32 chunkno = 0;
        u32 i;
        u32 asicmode;
+       u32 fwimg_size;
+       u32 fwimg_buf[BFI_FLASH_CHUNK_SZ_WORDS];
+       bfa_status_t status;
+
+       if (boot_env == BFI_FWBOOT_ENV_OS &&
+               boot_type == BFI_FWBOOT_TYPE_FLASH) {
+               fwimg_size = BFI_FLASH_IMAGE_SZ/sizeof(u32);
+
+               status = bfa_ioc_flash_img_get_chnk(ioc,
+                       BFA_IOC_FLASH_CHUNK_ADDR(chunkno), fwimg_buf);
+               if (status != BFA_STATUS_OK)
+                       return status;
+
+               fwimg = fwimg_buf;
+       } else {
+               fwimg_size = bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc));
+               fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc),
+                                       BFA_IOC_FLASH_CHUNK_ADDR(chunkno));
+       }
+
+       bfa_trc(ioc, fwimg_size);
 
-       bfa_trc(ioc, bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)));
-       fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), chunkno);
 
        pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
        pgoff = PSS_SMEM_PGOFF(loff);
 
        writel(pgnum, ioc->ioc_regs.host_page_num_fn);
 
-       for (i = 0; i < bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)); i++) {
+       for (i = 0; i < fwimg_size; i++) {
 
                if (BFA_IOC_FLASH_CHUNK_NO(i) != chunkno) {
                        chunkno = BFA_IOC_FLASH_CHUNK_NO(i);
-                       fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc),
+
+                       if (boot_env == BFI_FWBOOT_ENV_OS &&
+                               boot_type == BFI_FWBOOT_TYPE_FLASH) {
+                               status = bfa_ioc_flash_img_get_chnk(ioc,
+                                       BFA_IOC_FLASH_CHUNK_ADDR(chunkno),
+                                       fwimg_buf);
+                               if (status != BFA_STATUS_OK)
+                                       return status;
+
+                               fwimg = fwimg_buf;
+                       } else {
+                               fwimg = bfa_cb_image_get_chunk(
+                                       bfa_ioc_asic_gen(ioc),
                                        BFA_IOC_FLASH_CHUNK_ADDR(chunkno));
+                       }
                }
 
                /*
                 * write smem
                 */
                bfa_mem_write(ioc->ioc_regs.smem_page_start, loff,
-                       cpu_to_le32(fwimg[BFA_IOC_FLASH_OFFSET_IN_CHUNK(i)]));
+                             fwimg[BFA_IOC_FLASH_OFFSET_IN_CHUNK(i)]);
 
                loff += sizeof(u32);
 
@@ -1733,8 +1948,12 @@ bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
                        ioc->ioc_regs.host_page_num_fn);
 
        /*
-        * Set boot type and device mode at the end.
+        * Set boot type, env and device mode at the end.
         */
+       if (boot_env == BFI_FWBOOT_ENV_OS &&
+               boot_type == BFI_FWBOOT_TYPE_FLASH) {
+               boot_type = BFI_FWBOOT_TYPE_NORMAL;
+       }
        asicmode = BFI_FWBOOT_DEVMODE(ioc->asic_gen, ioc->asic_mode,
                                ioc->port0_mode, ioc->port1_mode);
        bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_FWBOOT_DEVMODE_OFF,
@@ -1743,6 +1962,7 @@ bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
                        swab32(boot_type));
        bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_FWBOOT_ENV_OFF,
                        swab32(boot_env));
+       return BFA_STATUS_OK;
 }
 
 
@@ -2002,13 +2222,30 @@ bfa_ioc_pll_init(struct bfa_ioc_s *ioc)
  * Interface used by diag module to do firmware boot with memory test
  * as the entry vector.
  */
-void
+bfa_status_t
 bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_env)
 {
+       struct bfi_ioc_image_hdr_s *drv_fwhdr;
+       bfa_status_t status;
        bfa_ioc_stats(ioc, ioc_boots);
 
        if (bfa_ioc_pll_init(ioc) != BFA_STATUS_OK)
-               return;
+               return BFA_STATUS_FAILED;
+
+       if (boot_env == BFI_FWBOOT_ENV_OS &&
+               boot_type == BFI_FWBOOT_TYPE_NORMAL) {
+
+               drv_fwhdr = (struct bfi_ioc_image_hdr_s *)
+                       bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
+
+               /*
+                * Work with Flash iff flash f/w is better than driver f/w.
+                * Otherwise push drivers firmware.
+                */
+               if (bfa_ioc_flash_fwver_cmp(ioc, drv_fwhdr) ==
+                                               BFI_IOC_IMG_VER_BETTER)
+                       boot_type = BFI_FWBOOT_TYPE_FLASH;
+       }
 
        /*
         * Initialize IOC state of all functions on a chip reset.
@@ -2022,8 +2259,14 @@ bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_env)
        }
 
        bfa_ioc_msgflush(ioc);
-       bfa_ioc_download_fw(ioc, boot_type, boot_env);
-       bfa_ioc_lpu_start(ioc);
+       status = bfa_ioc_download_fw(ioc, boot_type, boot_env);
+       if (status == BFA_STATUS_OK)
+               bfa_ioc_lpu_start(ioc);
+       else {
+               WARN_ON(boot_type == BFI_FWBOOT_TYPE_MEMTEST);
+               bfa_iocpf_timeout(ioc);
+       }
+       return status;
 }
 
 /*
@@ -2419,14 +2662,6 @@ bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc)
                bfa_fsm_cmp_state(&ioc->iocpf, bfa_iocpf_sm_mismatch);
 }
 
-#define bfa_ioc_state_disabled(__sm)           \
-       (((__sm) == BFI_IOC_UNINIT) ||          \
-        ((__sm) == BFI_IOC_INITING) ||         \
-        ((__sm) == BFI_IOC_HWINIT) ||          \
-        ((__sm) == BFI_IOC_DISABLED) ||        \
-        ((__sm) == BFI_IOC_FAIL) ||            \
-        ((__sm) == BFI_IOC_CFG_DISABLED))
-
 /*
  * Check if adapter is disabled -- both IOCs should be in a disabled
  * state.
@@ -6423,3 +6658,407 @@ bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg)
                WARN_ON(1);
        }
 }
+
+/*
+ * register definitions
+ */
+#define FLI_CMD_REG                    0x0001d000
+#define FLI_RDDATA_REG                 0x0001d010
+#define FLI_ADDR_REG                   0x0001d004
+#define FLI_DEV_STATUS_REG             0x0001d014
+
+#define BFA_FLASH_FIFO_SIZE            128     /* fifo size */
+#define BFA_FLASH_CHECK_MAX            10000   /* max # of status check */
+#define BFA_FLASH_BLOCKING_OP_MAX      1000000 /* max # of blocking op check */
+#define BFA_FLASH_WIP_MASK             0x01    /* write in progress bit mask */
+
+enum bfa_flash_cmd {
+       BFA_FLASH_FAST_READ     = 0x0b, /* fast read */
+       BFA_FLASH_READ_STATUS   = 0x05, /* read status */
+};
+
+/**
+ * @brief hardware error definition
+ */
+enum bfa_flash_err {
+       BFA_FLASH_NOT_PRESENT   = -1,   /*!< flash not present */
+       BFA_FLASH_UNINIT        = -2,   /*!< flash not initialized */
+       BFA_FLASH_BAD           = -3,   /*!< flash bad */
+       BFA_FLASH_BUSY          = -4,   /*!< flash busy */
+       BFA_FLASH_ERR_CMD_ACT   = -5,   /*!< command active never cleared */
+       BFA_FLASH_ERR_FIFO_CNT  = -6,   /*!< fifo count never cleared */
+       BFA_FLASH_ERR_WIP       = -7,   /*!< write-in-progress never cleared */
+       BFA_FLASH_ERR_TIMEOUT   = -8,   /*!< fli timeout */
+       BFA_FLASH_ERR_LEN       = -9,   /*!< invalid length */
+};
+
+/**
+ * @brief flash command register data structure
+ */
+union bfa_flash_cmd_reg_u {
+       struct {
+#ifdef __BIG_ENDIAN
+               u32     act:1;
+               u32     rsv:1;
+               u32     write_cnt:9;
+               u32     read_cnt:9;
+               u32     addr_cnt:4;
+               u32     cmd:8;
+#else
+               u32     cmd:8;
+               u32     addr_cnt:4;
+               u32     read_cnt:9;
+               u32     write_cnt:9;
+               u32     rsv:1;
+               u32     act:1;
+#endif
+       } r;
+       u32     i;
+};
+
+/**
+ * @brief flash device status register data structure
+ */
+union bfa_flash_dev_status_reg_u {
+       struct {
+#ifdef __BIG_ENDIAN
+               u32     rsv:21;
+               u32     fifo_cnt:6;
+               u32     busy:1;
+               u32     init_status:1;
+               u32     present:1;
+               u32     bad:1;
+               u32     good:1;
+#else
+               u32     good:1;
+               u32     bad:1;
+               u32     present:1;
+               u32     init_status:1;
+               u32     busy:1;
+               u32     fifo_cnt:6;
+               u32     rsv:21;
+#endif
+       } r;
+       u32     i;
+};
+
+/**
+ * @brief flash address register data structure
+ */
+union bfa_flash_addr_reg_u {
+       struct {
+#ifdef __BIG_ENDIAN
+               u32     addr:24;
+               u32     dummy:8;
+#else
+               u32     dummy:8;
+               u32     addr:24;
+#endif
+       } r;
+       u32     i;
+};
+
+/**
+ * dg flash_raw_private Flash raw private functions
+ */
+static void
+bfa_flash_set_cmd(void __iomem *pci_bar, u8 wr_cnt,
+                 u8 rd_cnt, u8 ad_cnt, u8 op)
+{
+       union bfa_flash_cmd_reg_u cmd;
+
+       cmd.i = 0;
+       cmd.r.act = 1;
+       cmd.r.write_cnt = wr_cnt;
+       cmd.r.read_cnt = rd_cnt;
+       cmd.r.addr_cnt = ad_cnt;
+       cmd.r.cmd = op;
+       writel(cmd.i, (pci_bar + FLI_CMD_REG));
+}
+
+static void
+bfa_flash_set_addr(void __iomem *pci_bar, u32 address)
+{
+       union bfa_flash_addr_reg_u addr;
+
+       addr.r.addr = address & 0x00ffffff;
+       addr.r.dummy = 0;
+       writel(addr.i, (pci_bar + FLI_ADDR_REG));
+}
+
+static int
+bfa_flash_cmd_act_check(void __iomem *pci_bar)
+{
+       union bfa_flash_cmd_reg_u cmd;
+
+       cmd.i = readl(pci_bar + FLI_CMD_REG);
+
+       if (cmd.r.act)
+               return BFA_FLASH_ERR_CMD_ACT;
+
+       return 0;
+}
+
+/**
+ * @brief
+ * Flush FLI data fifo.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] dev_status - device status
+ *
+ * Return 0 on success, negative error number on error.
+ */
+static u32
+bfa_flash_fifo_flush(void __iomem *pci_bar)
+{
+       u32 i;
+       u32 t;
+       union bfa_flash_dev_status_reg_u dev_status;
+
+       dev_status.i = readl(pci_bar + FLI_DEV_STATUS_REG);
+
+       if (!dev_status.r.fifo_cnt)
+               return 0;
+
+       /* fifo counter in terms of words */
+       for (i = 0; i < dev_status.r.fifo_cnt; i++)
+               t = readl(pci_bar + FLI_RDDATA_REG);
+
+       /*
+        * Check the device status. It may take some time.
+        */
+       for (i = 0; i < BFA_FLASH_CHECK_MAX; i++) {
+               dev_status.i = readl(pci_bar + FLI_DEV_STATUS_REG);
+               if (!dev_status.r.fifo_cnt)
+                       break;
+       }
+
+       if (dev_status.r.fifo_cnt)
+               return BFA_FLASH_ERR_FIFO_CNT;
+
+       return 0;
+}
+
+/**
+ * @brief
+ * Read flash status.
+ *
+ * @param[in] pci_bar - pci bar address
+ *
+ * Return 0 on success, negative error number on error.
+*/
+static u32
+bfa_flash_status_read(void __iomem *pci_bar)
+{
+       union bfa_flash_dev_status_reg_u        dev_status;
+       u32                             status;
+       u32                     ret_status;
+       int                             i;
+
+       status = bfa_flash_fifo_flush(pci_bar);
+       if (status < 0)
+               return status;
+
+       bfa_flash_set_cmd(pci_bar, 0, 4, 0, BFA_FLASH_READ_STATUS);
+
+       for (i = 0; i < BFA_FLASH_CHECK_MAX; i++) {
+               status = bfa_flash_cmd_act_check(pci_bar);
+               if (!status)
+                       break;
+       }
+
+       if (status)
+               return status;
+
+       dev_status.i = readl(pci_bar + FLI_DEV_STATUS_REG);
+       if (!dev_status.r.fifo_cnt)
+               return BFA_FLASH_BUSY;
+
+       ret_status = readl(pci_bar + FLI_RDDATA_REG);
+       ret_status >>= 24;
+
+       status = bfa_flash_fifo_flush(pci_bar);
+       if (status < 0)
+               return status;
+
+       return ret_status;
+}
+
+/**
+ * @brief
+ * Start flash read operation.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] offset - flash address offset
+ * @param[in] len - read data length
+ * @param[in] buf - read data buffer
+ *
+ * Return 0 on success, negative error number on error.
+ */
+static u32
+bfa_flash_read_start(void __iomem *pci_bar, u32 offset, u32 len,
+                        char *buf)
+{
+       u32 status;
+
+       /*
+        * len must be mutiple of 4 and not exceeding fifo size
+        */
+       if (len == 0 || len > BFA_FLASH_FIFO_SIZE || (len & 0x03) != 0)
+               return BFA_FLASH_ERR_LEN;
+
+       /*
+        * check status
+        */
+       status = bfa_flash_status_read(pci_bar);
+       if (status == BFA_FLASH_BUSY)
+               status = bfa_flash_status_read(pci_bar);
+
+       if (status < 0)
+               return status;
+
+       /*
+        * check if write-in-progress bit is cleared
+        */
+       if (status & BFA_FLASH_WIP_MASK)
+               return BFA_FLASH_ERR_WIP;
+
+       bfa_flash_set_addr(pci_bar, offset);
+
+       bfa_flash_set_cmd(pci_bar, 0, (u8)len, 4, BFA_FLASH_FAST_READ);
+
+       return 0;
+}
+
+/**
+ * @brief
+ * Check flash read operation.
+ *
+ * @param[in] pci_bar - pci bar address
+ *
+ * Return flash device status, 1 if busy, 0 if not.
+ */
+static u32
+bfa_flash_read_check(void __iomem *pci_bar)
+{
+       if (bfa_flash_cmd_act_check(pci_bar))
+               return 1;
+
+       return 0;
+}
+/**
+ * @brief
+ * End flash read operation.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] len - read data length
+ * @param[in] buf - read data buffer
+ *
+ */
+static void
+bfa_flash_read_end(void __iomem *pci_bar, u32 len, char *buf)
+{
+
+       u32 i;
+
+       /*
+        * read data fifo up to 32 words
+        */
+       for (i = 0; i < len; i += 4) {
+               u32 w = readl(pci_bar + FLI_RDDATA_REG);
+               *((u32 *) (buf + i)) = swab32(w);
+       }
+
+       bfa_flash_fifo_flush(pci_bar);
+}
+
+/**
+ * @brief
+ * Perform flash raw read.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] offset - flash partition address offset
+ * @param[in] buf - read data buffer
+ * @param[in] len - read data length
+ *
+ * Return status.
+ */
+
+
+#define FLASH_BLOCKING_OP_MAX   500
+#define FLASH_SEM_LOCK_REG     0x18820
+
+static int
+bfa_raw_sem_get(void __iomem *bar)
+{
+       int     locked;
+
+       locked = readl((bar + FLASH_SEM_LOCK_REG));
+       return !locked;
+
+}
+
+bfa_status_t
+bfa_flash_sem_get(void __iomem *bar)
+{
+       u32 n = FLASH_BLOCKING_OP_MAX;
+
+       while (!bfa_raw_sem_get(bar)) {
+               if (--n <= 0)
+                       return BFA_STATUS_BADFLASH;
+               udelay(10000);
+       }
+       return BFA_STATUS_OK;
+}
+
+void
+bfa_flash_sem_put(void __iomem *bar)
+{
+       writel(0, (bar + FLASH_SEM_LOCK_REG));
+}
+
+bfa_status_t
+bfa_flash_raw_read(void __iomem *pci_bar, u32 offset, char *buf,
+                      u32 len)
+{
+       u32 n, status;
+       u32 off, l, s, residue, fifo_sz;
+
+       residue = len;
+       off = 0;
+       fifo_sz = BFA_FLASH_FIFO_SIZE;
+       status = bfa_flash_sem_get(pci_bar);
+       if (status != BFA_STATUS_OK)
+               return status;
+
+       while (residue) {
+               s = offset + off;
+               n = s / fifo_sz;
+               l = (n + 1) * fifo_sz - s;
+               if (l > residue)
+                       l = residue;
+
+               status = bfa_flash_read_start(pci_bar, offset + off, l,
+                                                               &buf[off]);
+               if (status < 0) {
+                       bfa_flash_sem_put(pci_bar);
+                       return BFA_STATUS_FAILED;
+               }
+
+               n = BFA_FLASH_BLOCKING_OP_MAX;
+               while (bfa_flash_read_check(pci_bar)) {
+                       if (--n <= 0) {
+                               bfa_flash_sem_put(pci_bar);
+                               return BFA_STATUS_FAILED;
+                       }
+               }
+
+               bfa_flash_read_end(pci_bar, l, &buf[off]);
+
+               residue -= l;
+               off += l;
+       }
+       bfa_flash_sem_put(pci_bar);
+
+       return BFA_STATUS_OK;
+}