[SCSI] pm80xx: Firmware logging support.
authorAnand Kumar Santhanam <AnandKumar.Santhanam@pmcs.com>
Wed, 4 Sep 2013 07:27:00 +0000 (12:57 +0530)
committerJames Bottomley <JBottomley@Parallels.com>
Fri, 25 Oct 2013 08:58:16 +0000 (09:58 +0100)
Supports below logging facilities,
Inbound outbound queues dump.
Non fatal dump in case of IO failures.
Fatal dump in case of firmware failure.

[jejb: checkpatch spacing fixes]
Signed-off-by: Anandkumar.Santhanam@pmcs.com
Reviewed-by: Jack Wang <jinpu.wang@profitbricks.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/pm8001/pm8001_ctl.c
drivers/scsi/pm8001/pm8001_ctl.h
drivers/scsi/pm8001/pm8001_defs.h
drivers/scsi/pm8001/pm8001_hwi.c
drivers/scsi/pm8001/pm8001_hwi.h
drivers/scsi/pm8001/pm8001_init.c
drivers/scsi/pm8001/pm8001_sas.h
drivers/scsi/pm8001/pm80xx_hwi.c
drivers/scsi/pm8001/pm80xx_hwi.h

index 5a19e1930b4b48bd70e96557b2ba9f69ad72c374..a04b4ff8c7f627193c6829e2386c7c8e167632ee 100644 (file)
@@ -308,6 +308,84 @@ static ssize_t pm8001_ctl_aap_log_show(struct device *cdev,
        return str - buf;
 }
 static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL);
+/**
+ * pm8001_ctl_ib_queue_log_show - Out bound Queue log
+ * @cdev:pointer to embedded class device
+ * @buf: the buffer returned
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_ib_queue_log_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       int offset;
+       char *str = buf;
+       int start = 0;
+#define IB_MEMMAP(c)           \
+               (*(u32 *)((u8 *)pm8001_ha->             \
+               memoryMap.region[IB].virt_ptr +         \
+               pm8001_ha->evtlog_ib_offset + (c)))
+
+       for (offset = 0; offset < IB_OB_READ_TIMES; offset++) {
+               if (pm8001_ha->chip_id != chip_8001)
+                       str += sprintf(str, "0x%08x\n", IB_MEMMAP(start));
+               else
+                       str += sprintf(str, "0x%08x\n", IB_MEMMAP(start));
+               start = start + 4;
+       }
+       pm8001_ha->evtlog_ib_offset += SYSFS_OFFSET;
+       if ((((pm8001_ha->evtlog_ib_offset) % (PM80XX_IB_OB_QUEUE_SIZE)) == 0)
+               && (pm8001_ha->chip_id != chip_8001))
+               pm8001_ha->evtlog_ib_offset = 0;
+       if ((((pm8001_ha->evtlog_ib_offset) % (PM8001_IB_OB_QUEUE_SIZE)) == 0)
+               && (pm8001_ha->chip_id == chip_8001))
+               pm8001_ha->evtlog_ib_offset = 0;
+
+       return str - buf;
+}
+
+static DEVICE_ATTR(ib_log, S_IRUGO, pm8001_ctl_ib_queue_log_show, NULL);
+/**
+ * pm8001_ctl_ob_queue_log_show - Out bound Queue log
+ * @cdev:pointer to embedded class device
+ * @buf: the buffer returned
+ * A sysfs 'read-only' shost attribute.
+ */
+
+static ssize_t pm8001_ctl_ob_queue_log_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       int offset;
+       char *str = buf;
+       int start = 0;
+#define OB_MEMMAP(c)           \
+               (*(u32 *)((u8 *)pm8001_ha->             \
+               memoryMap.region[OB].virt_ptr +         \
+               pm8001_ha->evtlog_ob_offset + (c)))
+
+       for (offset = 0; offset < IB_OB_READ_TIMES; offset++) {
+               if (pm8001_ha->chip_id != chip_8001)
+                       str += sprintf(str, "0x%08x\n", OB_MEMMAP(start));
+               else
+                       str += sprintf(str, "0x%08x\n", OB_MEMMAP(start));
+               start = start + 4;
+       }
+       pm8001_ha->evtlog_ob_offset += SYSFS_OFFSET;
+       if ((((pm8001_ha->evtlog_ob_offset) % (PM80XX_IB_OB_QUEUE_SIZE)) == 0)
+                       && (pm8001_ha->chip_id != chip_8001))
+               pm8001_ha->evtlog_ob_offset = 0;
+       if ((((pm8001_ha->evtlog_ob_offset) % (PM8001_IB_OB_QUEUE_SIZE)) == 0)
+                       && (pm8001_ha->chip_id == chip_8001))
+               pm8001_ha->evtlog_ob_offset = 0;
+
+       return str - buf;
+}
+static DEVICE_ATTR(ob_log, S_IRUGO, pm8001_ctl_ob_queue_log_show, NULL);
 /**
  * pm8001_ctl_bios_version_show - Bios version Display
  * @cdev:pointer to embedded class device
@@ -377,6 +455,43 @@ static ssize_t pm8001_ctl_iop_log_show(struct device *cdev,
 }
 static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL);
 
+/**
+ ** pm8001_ctl_fatal_log_show - fatal error logging
+ ** @cdev:pointer to embedded class device
+ ** @buf: the buffer returned
+ **
+ ** A sysfs 'read-only' shost attribute.
+ **/
+
+static ssize_t pm8001_ctl_fatal_log_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       u32 count;
+
+       count = pm80xx_get_fatal_dump(cdev, attr, buf);
+       return count;
+}
+
+static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL);
+
+
+/**
+ ** pm8001_ctl_gsm_log_show - gsm dump collection
+ ** @cdev:pointer to embedded class device
+ ** @buf: the buffer returned
+ **A sysfs 'read-only' shost attribute.
+ **/
+static ssize_t pm8001_ctl_gsm_log_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       u32 count;
+
+       count = pm8001_get_gsm_dump(cdev, SYSFS_OFFSET, buf);
+       return count;
+}
+
+static DEVICE_ATTR(gsm_log, S_IRUGO, pm8001_ctl_gsm_log_show, NULL);
+
 #define FLASH_CMD_NONE      0x00
 #define FLASH_CMD_UPDATE    0x01
 #define FLASH_CMD_SET_NVMD    0x02
@@ -636,6 +751,8 @@ struct device_attribute *pm8001_host_attrs[] = {
        &dev_attr_update_fw,
        &dev_attr_aap_log,
        &dev_attr_iop_log,
+       &dev_attr_fatal_log,
+       &dev_attr_gsm_log,
        &dev_attr_max_out_io,
        &dev_attr_max_devices,
        &dev_attr_max_sg_list,
@@ -643,6 +760,8 @@ struct device_attribute *pm8001_host_attrs[] = {
        &dev_attr_logging_level,
        &dev_attr_host_sas_address,
        &dev_attr_bios_version,
+       &dev_attr_ib_log,
+       &dev_attr_ob_log,
        NULL,
 };
 
index c6d8fdd3b9b9594679e7902a17a4a422ba7201d3..d0d43a250b9ed68b273eba9050661d98e07330da 100644 (file)
@@ -55,5 +55,9 @@
 #define FAIL_OUT_MEMORY                 0x000c00
 #define FLASH_IN_PROGRESS               0x001000
 
+#define IB_OB_READ_TIMES                256
+#define SYSFS_OFFSET                    1024
+#define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
+#define PM8001_IB_OB_QUEUE_SIZE         (16 * 1024)
 #endif /* PM8001_CTL_H_INCLUDED */
 
index 4bb304d379da2ca5471781b603dbbc9841de62fc..74a4bb9af07b61d23a4bfda39b5b341c04f7adfb 100644 (file)
@@ -102,7 +102,8 @@ enum memory_region_num {
        NVMD,       /* NVM device */
        DEV_MEM,    /* memory for devices */
        CCB_MEM,    /* memory for command control block */
-       FW_FLASH    /* memory for fw flash update */
+       FW_FLASH,    /* memory for fw flash update */
+       FORENSIC_MEM  /* memory for fw forensic data */
 };
 #define        PM8001_EVENT_LOG_SIZE    (128 * 1024)
 
index 9d1178bcf689df5d26acf6fce2e06294d25dd3ff..f16ece91b94ac73de979eda5496925c1d253b37b 100644 (file)
@@ -5001,6 +5001,89 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
        return rc;
 }
 
+ssize_t
+pm8001_get_gsm_dump(struct device *cdev, u32 length, char *buf)
+{
+       u32 value, rem, offset = 0, bar = 0;
+       u32 index, work_offset, dw_length;
+       u32 shift_value, gsm_base, gsm_dump_offset;
+       char *direct_data;
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       direct_data = buf;
+       gsm_dump_offset = pm8001_ha->fatal_forensic_shift_offset;
+
+       /* check max is 1 Mbytes */
+       if ((length > 0x100000) || (gsm_dump_offset & 3) ||
+               ((gsm_dump_offset + length) > 0x1000000))
+                       return 1;
+
+       if (pm8001_ha->chip_id == chip_8001)
+               bar = 2;
+       else
+               bar = 1;
+
+       work_offset = gsm_dump_offset & 0xFFFF0000;
+       offset = gsm_dump_offset & 0x0000FFFF;
+       gsm_dump_offset = work_offset;
+       /* adjust length to dword boundary */
+       rem = length & 3;
+       dw_length = length >> 2;
+
+       for (index = 0; index < dw_length; index++) {
+               if ((work_offset + offset) & 0xFFFF0000) {
+                       if (pm8001_ha->chip_id == chip_8001)
+                               shift_value = ((gsm_dump_offset + offset) &
+                                               SHIFT_REG_64K_MASK);
+                       else
+                               shift_value = (((gsm_dump_offset + offset) &
+                                               SHIFT_REG_64K_MASK) >>
+                                               SHIFT_REG_BIT_SHIFT);
+
+                       if (pm8001_ha->chip_id == chip_8001) {
+                               gsm_base = GSM_BASE;
+                               if (-1 == pm8001_bar4_shift(pm8001_ha,
+                                               (gsm_base + shift_value)))
+                                       return 1;
+                       } else {
+                               gsm_base = 0;
+                               if (-1 == pm80xx_bar4_shift(pm8001_ha,
+                                               (gsm_base + shift_value)))
+                                       return 1;
+                       }
+                       gsm_dump_offset = (gsm_dump_offset + offset) &
+                                               0xFFFF0000;
+                       work_offset = 0;
+                       offset = offset & 0x0000FFFF;
+               }
+               value = pm8001_cr32(pm8001_ha, bar, (work_offset + offset) &
+                                               0x0000FFFF);
+               direct_data += sprintf(direct_data, "%08x ", value);
+               offset += 4;
+       }
+       if (rem != 0) {
+               value = pm8001_cr32(pm8001_ha, bar, (work_offset + offset) &
+                                               0x0000FFFF);
+               /* xfr for non_dw */
+               direct_data += sprintf(direct_data, "%08x ", value);
+       }
+       /* Shift back to BAR4 original address */
+       if (pm8001_ha->chip_id == chip_8001) {
+               if (-1 == pm8001_bar4_shift(pm8001_ha, 0))
+                       return 1;
+       } else {
+               if (-1 == pm80xx_bar4_shift(pm8001_ha, 0))
+                       return 1;
+       }
+       pm8001_ha->fatal_forensic_shift_offset += 1024;
+
+       if (pm8001_ha->fatal_forensic_shift_offset >= 0x100000)
+               pm8001_ha->fatal_forensic_shift_offset = 0;
+       return direct_data - buf;
+}
+
 int
 pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
        struct pm8001_device *pm8001_dev, u32 state)
index d7c1e2034226ea7667201a2a1474400f52994b5d..6d91e2446542c78290dfba8f9e846380ccda3067 100644 (file)
@@ -1027,5 +1027,8 @@ struct set_dev_state_resp {
 #define DEVREG_FAILURE_PORT_NOT_VALID_STATE            0x06
 #define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID           0x07
 
+#define GSM_BASE                                       0x4F0000
+#define SHIFT_REG_64K_MASK                             0xffff0000
+#define SHIFT_REG_BIT_SHIFT                            8
 #endif
 
index 92a18c4d2ebb30670a17075ff963db316e929c45..662bf13c42f012720f114cdf6d4355a0446c5280 100644 (file)
@@ -347,6 +347,10 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha,
        /* Memory region for fw flash */
        pm8001_ha->memoryMap.region[FW_FLASH].total_len = 4096;
 
+       pm8001_ha->memoryMap.region[FORENSIC_MEM].num_elements = 1;
+       pm8001_ha->memoryMap.region[FORENSIC_MEM].total_len = 0x10000;
+       pm8001_ha->memoryMap.region[FORENSIC_MEM].element_size = 0x10000;
+       pm8001_ha->memoryMap.region[FORENSIC_MEM].alignment = 0x10000;
        for (i = 0; i < USI_MAX_MEMCNT; i++) {
                if (pm8001_mem_alloc(pm8001_ha->pdev,
                        &pm8001_ha->memoryMap.region[i].virt_ptr,
index cbde11a57a9279ec5b556291f6dd64c2ace763bd..6037d477a183241c0010d73a00580c3667f3aaa9 100644 (file)
@@ -132,6 +132,61 @@ struct pm8001_ioctl_payload {
        u8      *func_specific;
 };
 
+#define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF
+#define MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
+#define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
+#define MPI_FATAL_EDUMP_TABLE_HI_OFFSET            0x04     /* HNFBUFH */
+#define MPI_FATAL_EDUMP_TABLE_LENGTH               0x08     /* HNFBLEN */
+#define MPI_FATAL_EDUMP_TABLE_HANDSHAKE            0x0C     /* FDDHSHK */
+#define MPI_FATAL_EDUMP_TABLE_STATUS               0x10     /* FDDTSTAT */
+#define MPI_FATAL_EDUMP_TABLE_ACCUM_LEN            0x14     /* ACCDDLEN */
+#define MPI_FATAL_EDUMP_HANDSHAKE_RDY              0x1
+#define MPI_FATAL_EDUMP_HANDSHAKE_BUSY             0x0
+#define MPI_FATAL_EDUMP_TABLE_STAT_RSVD                 0x0
+#define MPI_FATAL_EDUMP_TABLE_STAT_DMA_FAILED           0x1
+#define MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_MORE_DATA 0x2
+#define MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE      0x3
+#define TYPE_GSM_SPACE        1
+#define TYPE_QUEUE            2
+#define TYPE_FATAL            3
+#define TYPE_NON_FATAL        4
+#define TYPE_INBOUND          1
+#define TYPE_OUTBOUND         2
+struct forensic_data {
+       u32  data_type;
+       union {
+               struct {
+                       u32  direct_len;
+                       u32  direct_offset;
+                       void  *direct_data;
+               } gsm_buf;
+               struct {
+                       u16  queue_type;
+                       u16  queue_index;
+                       u32  direct_len;
+                       void  *direct_data;
+               } queue_buf;
+               struct {
+                       u32  direct_len;
+                       u32  direct_offset;
+                       u32  read_len;
+                       void  *direct_data;
+               } data_buf;
+       };
+};
+
+/* bit31-26 - mask bar */
+#define SCRATCH_PAD0_BAR_MASK                    0xFC000000
+/* bit25-0  - offset mask */
+#define SCRATCH_PAD0_OFFSET_MASK                 0x03FFFFFF
+/* if AAP error state */
+#define SCRATCH_PAD0_AAPERR_MASK                 0xFFFFFFFF
+/* Inbound doorbell bit7 */
+#define SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP       0x80
+/* Inbound doorbell bit7 SPCV */
+#define SPCV_MSGU_CFG_TABLE_TRANSFER_DEBUG_INFO  0x80
+#define MAIN_MERRDCTO_MERRDCES                  0xA0/* DWORD 0x28) */
+
 struct pm8001_dispatch {
        char *name;
        int (*chip_init)(struct pm8001_hba_info *pm8001_ha);
@@ -346,6 +401,7 @@ union main_cfg_table {
        u32                     phy_attr_table_offset;
        u32                     port_recovery_timer;
        u32                     interrupt_reassertion_delay;
+       u32                     fatal_n_non_fatal_dump;         /* 0x28 */
        } pm80xx_tbl;
 };
 
@@ -420,6 +476,13 @@ struct pm8001_hba_info {
        struct pm8001_hba_memspace io_mem[6];
        struct mpi_mem_req      memoryMap;
        struct encrypt          encrypt_info; /* support encryption */
+       struct forensic_data    forensic_info;
+       u32                     fatal_bar_loc;
+       u32                     forensic_last_offset;
+       u32                     fatal_forensic_shift_offset;
+       u32                     forensic_fatal_step;
+       u32                     evtlog_ib_offset;
+       u32                     evtlog_ob_offset;
        void __iomem    *msg_unit_tbl_addr;/*Message Unit Table Addr*/
        void __iomem    *main_cfg_tbl_addr;/*Main Config Table Addr*/
        void __iomem    *general_stat_tbl_addr;/*General Status Table Addr*/
@@ -428,6 +491,7 @@ struct pm8001_hba_info {
        void __iomem    *pspa_q_tbl_addr;
                        /*MPI SAS PHY attributes Queue Config Table Addr*/
        void __iomem    *ivt_tbl_addr; /*MPI IVT Table Addr */
+       void __iomem    *fatal_tbl_addr; /*MPI IVT Table Addr */
        union main_cfg_table    main_cfg_tbl;
        union general_status_table      gs_tbl;
        struct inbound_queue_table      inbnd_q_tbl[PM8001_MAX_SPCV_INB_NUM];
@@ -634,6 +698,10 @@ int pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha);
 int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
 void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha,
        u32 length, u8 *buf);
+int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
+ssize_t pm80xx_get_fatal_dump(struct device *cdev,
+               struct device_attribute *attr, char *buf);
+ssize_t pm8001_get_gsm_dump(struct device *cdev, u32, char *buf);
 /* ctl shared API */
 extern struct device_attribute *pm8001_host_attrs[];
 
index 91cf4242a03d4e4742070cf6509cfd28d5790bf0..8987b1706216436ef36392eb329e56320547ab23 100644 (file)
 
 #define SMP_DIRECT 1
 #define SMP_INDIRECT 2
+
+
+int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shift_value)
+{
+       u32 reg_val;
+       unsigned long start;
+       pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, shift_value);
+       /* confirm the setting is written */
+       start = jiffies + HZ; /* 1 sec */
+       do {
+               reg_val = pm8001_cr32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER);
+       } while ((reg_val != shift_value) && time_before(jiffies, start));
+       if (reg_val != shift_value) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("TIMEOUT:MEMBASE_II_SHIFT_REGISTER"
+                       " = 0x%x\n", reg_val));
+               return -1;
+       }
+       return 0;
+}
+
+void pm80xx_pci_mem_copy(struct pm8001_hba_info  *pm8001_ha, u32 soffset,
+                               const void *destination,
+                               u32 dw_count, u32 bus_base_number)
+{
+       u32 index, value, offset;
+       u32 *destination1;
+       destination1 = (u32 *)destination;
+
+       for (index = 0; index < dw_count; index += 4, destination1++) {
+               offset = (soffset + index / 4);
+               if (offset < (64 * 1024)) {
+                       value = pm8001_cr32(pm8001_ha, bus_base_number, offset);
+                       *destination1 =  cpu_to_le32(value);
+               }
+       }
+       return;
+}
+
+ssize_t pm80xx_get_fatal_dump(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       void __iomem *fatal_table_address = pm8001_ha->fatal_tbl_addr;
+       u32 status = 1;
+       u32 accum_len , reg_val, index, *temp;
+       unsigned long start;
+       u8 *direct_data;
+       char *fatal_error_data = buf;
+
+       pm8001_ha->forensic_info.data_buf.direct_data = buf;
+       if (pm8001_ha->chip_id == chip_8001) {
+               pm8001_ha->forensic_info.data_buf.direct_data +=
+                       sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+                       "Not supported for SPC controller");
+               return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+                       (char *)buf;
+       }
+       if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) {
+               PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("forensic_info TYPE_NON_FATAL..............\n"));
+               direct_data = (u8 *)fatal_error_data;
+               pm8001_ha->forensic_info.data_type = TYPE_NON_FATAL;
+               pm8001_ha->forensic_info.data_buf.direct_len = SYSFS_OFFSET;
+               pm8001_ha->forensic_info.data_buf.direct_offset = 0;
+               pm8001_ha->forensic_info.data_buf.read_len = 0;
+
+               pm8001_ha->forensic_info.data_buf.direct_data = direct_data;
+       }
+
+       if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) {
+               /* start to get data */
+               /* Program the MEMBASE II Shifting Register with 0x00.*/
+               pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
+                               pm8001_ha->fatal_forensic_shift_offset);
+               pm8001_ha->forensic_last_offset = 0;
+               pm8001_ha->forensic_fatal_step = 0;
+               pm8001_ha->fatal_bar_loc = 0;
+       }
+       /* Read until accum_len is retrived */
+       accum_len = pm8001_mr32(fatal_table_address,
+                               MPI_FATAL_EDUMP_TABLE_ACCUM_LEN);
+       PM8001_IO_DBG(pm8001_ha, pm8001_printk("accum_len 0x%x\n",
+                                               accum_len));
+       if (accum_len == 0xFFFFFFFF) {
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("Possible PCI issue 0x%x not expected\n",
+                               accum_len));
+               return status;
+       }
+       if (accum_len == 0 || accum_len >= 0x100000) {
+               pm8001_ha->forensic_info.data_buf.direct_data +=
+                       sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+                               "%08x ", 0xFFFFFFFF);
+               return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+                       (char *)buf;
+       }
+       temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr;
+       if (pm8001_ha->forensic_fatal_step == 0) {
+moreData:
+               if (pm8001_ha->forensic_info.data_buf.direct_data) {
+                       /* Data is in bar, copy to host memory */
+                       pm80xx_pci_mem_copy(pm8001_ha, pm8001_ha->fatal_bar_loc,
+                        pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr,
+                               pm8001_ha->forensic_info.data_buf.direct_len ,
+                                       1);
+               }
+               pm8001_ha->fatal_bar_loc +=
+                       pm8001_ha->forensic_info.data_buf.direct_len;
+               pm8001_ha->forensic_info.data_buf.direct_offset +=
+                       pm8001_ha->forensic_info.data_buf.direct_len;
+               pm8001_ha->forensic_last_offset +=
+                       pm8001_ha->forensic_info.data_buf.direct_len;
+               pm8001_ha->forensic_info.data_buf.read_len =
+                       pm8001_ha->forensic_info.data_buf.direct_len;
+
+               if (pm8001_ha->forensic_last_offset  >= accum_len) {
+                       pm8001_ha->forensic_info.data_buf.direct_data +=
+                       sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+                               "%08x ", 3);
+                       for (index = 0; index < (SYSFS_OFFSET / 4); index++) {
+                               pm8001_ha->forensic_info.data_buf.direct_data +=
+                                       sprintf(pm8001_ha->
+                                        forensic_info.data_buf.direct_data,
+                                               "%08x ", *(temp + index));
+                       }
+
+                       pm8001_ha->fatal_bar_loc = 0;
+                       pm8001_ha->forensic_fatal_step = 1;
+                       pm8001_ha->fatal_forensic_shift_offset = 0;
+                       pm8001_ha->forensic_last_offset = 0;
+                       status = 0;
+                       return (char *)pm8001_ha->
+                               forensic_info.data_buf.direct_data -
+                               (char *)buf;
+               }
+               if (pm8001_ha->fatal_bar_loc < (64 * 1024)) {
+                       pm8001_ha->forensic_info.data_buf.direct_data +=
+                               sprintf(pm8001_ha->
+                                       forensic_info.data_buf.direct_data,
+                                       "%08x ", 2);
+                       for (index = 0; index < (SYSFS_OFFSET / 4); index++) {
+                               pm8001_ha->forensic_info.data_buf.direct_data +=
+                                       sprintf(pm8001_ha->
+                                       forensic_info.data_buf.direct_data,
+                                       "%08x ", *(temp + index));
+                       }
+                       status = 0;
+                       return (char *)pm8001_ha->
+                               forensic_info.data_buf.direct_data -
+                               (char *)buf;
+               }
+
+               /* Increment the MEMBASE II Shifting Register value by 0x100.*/
+               pm8001_ha->forensic_info.data_buf.direct_data +=
+                       sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+                               "%08x ", 2);
+               for (index = 0; index < 256; index++) {
+                       pm8001_ha->forensic_info.data_buf.direct_data +=
+                               sprintf(pm8001_ha->
+                                       forensic_info.data_buf.direct_data,
+                                               "%08x ", *(temp + index));
+               }
+               pm8001_ha->fatal_forensic_shift_offset += 0x100;
+               pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
+                       pm8001_ha->fatal_forensic_shift_offset);
+               pm8001_ha->fatal_bar_loc = 0;
+               status = 0;
+               return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+                       (char *)buf;
+       }
+       if (pm8001_ha->forensic_fatal_step == 1) {
+               pm8001_ha->fatal_forensic_shift_offset = 0;
+               /* Read 64K of the debug data. */
+               pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
+                       pm8001_ha->fatal_forensic_shift_offset);
+               pm8001_mw32(fatal_table_address,
+                       MPI_FATAL_EDUMP_TABLE_HANDSHAKE,
+                               MPI_FATAL_EDUMP_HANDSHAKE_RDY);
+
+               /* Poll FDDHSHK  until clear  */
+               start = jiffies + (2 * HZ); /* 2 sec */
+
+               do {
+                       reg_val = pm8001_mr32(fatal_table_address,
+                                       MPI_FATAL_EDUMP_TABLE_HANDSHAKE);
+               } while ((reg_val) && time_before(jiffies, start));
+
+               if (reg_val != 0) {
+                       PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("TIMEOUT:MEMBASE_II_SHIFT_REGISTER"
+                       " = 0x%x\n", reg_val));
+                       return -1;
+               }
+
+               /* Read the next 64K of the debug data. */
+               pm8001_ha->forensic_fatal_step = 0;
+               if (pm8001_mr32(fatal_table_address,
+                       MPI_FATAL_EDUMP_TABLE_STATUS) !=
+                               MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) {
+                       pm8001_mw32(fatal_table_address,
+                               MPI_FATAL_EDUMP_TABLE_HANDSHAKE, 0);
+                       goto moreData;
+               } else {
+                       pm8001_ha->forensic_info.data_buf.direct_data +=
+                               sprintf(pm8001_ha->
+                                       forensic_info.data_buf.direct_data,
+                                               "%08x ", 4);
+                       pm8001_ha->forensic_info.data_buf.read_len = 0xFFFFFFFF;
+                       pm8001_ha->forensic_info.data_buf.direct_len =  0;
+                       pm8001_ha->forensic_info.data_buf.direct_offset = 0;
+                       pm8001_ha->forensic_info.data_buf.read_len = 0;
+                       status = 0;
+               }
+       }
+
+       return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+               (char *)buf;
+}
+
 /**
  * read_main_config_table - read the configure table and save it.
  * @pm8001_ha: our hba card information
@@ -583,6 +805,9 @@ static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
        pm8001_ha->pspa_q_tbl_addr =
                base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x90) &
                                        0xFFFFFF);
+       pm8001_ha->fatal_tbl_addr =
+               base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0xA0) &
+                                       0xFFFFFF);
 
        PM8001_INIT_DBG(pm8001_ha,
                        pm8001_printk("GST OFFSET 0x%x\n",
index 872d5cff824f3a26b9553e046f3654488e345cf8..c86816bea4243354d8864a5422778996ebec215e 100644 (file)
@@ -1525,4 +1525,6 @@ typedef struct SASProtocolTimerConfig SASProtocolTimerConfig_t;
 #define DEVREG_FAILURE_PORT_NOT_VALID_STATE            0x06
 #define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID           0x07
 
+
+#define MEMBASE_II_SHIFT_REGISTER       0x1010
 #endif