scsi: megaraid_sas: add retry logic in megasas_readl
authorShivasharan S <shivasharan.srikanteshwara@broadcom.com>
Mon, 17 Dec 2018 08:47:40 +0000 (00:47 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 20 Dec 2018 02:37:15 +0000 (21:37 -0500)
Due to hardware errata in Aero controllers, reads to certain fusion
registers could intermittently return zero.  This behavior is
transient in nature and subsequent reads will return valid value.

For Aero controllers, any calls to readl to read from certain
registers will be retried for maximum three times, if read returns
zero.

Signed-off-by: Shivasharan S <shivasharan.srikanteshwara@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/megaraid/megaraid_sas_fusion.c

index d99390c..d0f4075 100644 (file)
@@ -220,6 +220,28 @@ megasas_free_ctrl_dma_buffers(struct megasas_instance *instance);
 static inline void
 megasas_init_ctrl_params(struct megasas_instance *instance);
 
+u32 megasas_readl(struct megasas_instance *instance,
+                 const volatile void __iomem *addr)
+{
+       u32 i = 0, ret_val;
+       /*
+        * Due to a HW errata in Aero controllers, reads to certain
+        * Fusion registers could intermittently return all zeroes.
+        * This behavior is transient in nature and subsequent reads will
+        * return valid value. As a workaround in driver, retry readl for
+        * upto three times until a non-zero value is read.
+        */
+       if (instance->adapter_type == AERO_SERIES) {
+               do {
+                       ret_val = readl(addr);
+                       i++;
+               } while (ret_val == 0 && i < 3);
+               return ret_val;
+       } else {
+               return readl(addr);
+       }
+}
+
 /**
  * megasas_set_dma_settings -  Populate DMA address, length and flags for DCMDs
  * @instance:                  Adapter soft state
@@ -3842,7 +3864,8 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
 
                                if (instance->adapter_type != MFI_SERIES) {
                                        for (i = 0; i < (10 * 1000); i += 20) {
-                                               if (readl(
+                                               if (megasas_readl(
+                                                           instance,
                                                            &instance->
                                                            reg_set->
                                                            doorbell) & 1)
@@ -5401,7 +5424,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
 
        if (instance->adapter_type >= VENTURA_SERIES) {
                scratch_pad_2 =
-                       readl(&instance->reg_set->outbound_scratch_pad_2);
+                       megasas_readl(instance,
+                                     &instance->reg_set->outbound_scratch_pad_2);
                instance->max_raid_mapsize = ((scratch_pad_2 >>
                        MR_MAX_RAID_MAP_SIZE_OFFSET_SHIFT) &
                        MR_MAX_RAID_MAP_SIZE_MASK);
@@ -5413,8 +5437,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
        if (msix_enable && !msix_disable) {
                int irq_flags = PCI_IRQ_MSIX;
 
-               scratch_pad_1 = readl
-                       (&instance->reg_set->outbound_scratch_pad_1);
+               scratch_pad_1 = megasas_readl
+                       (instance, &instance->reg_set->outbound_scratch_pad_1);
                /* Check max MSI-X vectors */
                if (fusion) {
                        if (instance->adapter_type == THUNDERBOLT_SERIES) {
@@ -5525,7 +5549,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
 
        if (instance->adapter_type >= VENTURA_SERIES) {
                scratch_pad_3 =
-                       readl(&instance->reg_set->outbound_scratch_pad_3);
+                       megasas_readl(instance,
+                                     &instance->reg_set->outbound_scratch_pad_3);
                if ((scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK) >=
                        MR_DEFAULT_NVME_PAGE_SHIFT)
                        instance->nvme_page_size =
@@ -6193,8 +6218,8 @@ megasas_set_dma_mask(struct megasas_instance *instance)
                         * If 32 bit DMA mask fails, then try for 64 bit mask
                         * for FW capable of handling 64 bit DMA.
                         */
-                       scratch_pad_1 = readl
-                               (&instance->reg_set->outbound_scratch_pad_1);
+                       scratch_pad_1 = megasas_readl
+                               (instance, &instance->reg_set->outbound_scratch_pad_1);
 
                        if (!(scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET))
                                goto fail_set_dma_mask;
index e4c3edc..50e2ed8 100644 (file)
@@ -95,6 +95,8 @@ static void megasas_free_reply_fusion(struct megasas_instance *instance);
 static inline
 void megasas_configure_queue_sizes(struct megasas_instance *instance);
 static void megasas_fusion_crash_dump(struct megasas_instance *instance);
+extern u32 megasas_readl(struct megasas_instance *instance,
+                        const volatile void __iomem *addr);
 
 /**
  * megasas_check_same_4gb_region -     check if allocation
@@ -267,7 +269,8 @@ megasas_fusion_update_can_queue(struct megasas_instance *instance, int fw_boot_c
        /* ventura FW does not fill outbound_scratch_pad_2 with queue depth */
        if (instance->adapter_type < VENTURA_SERIES)
                cur_max_fw_cmds =
-               readl(&instance->reg_set->outbound_scratch_pad_2) & 0x00FFFF;
+               megasas_readl(instance,
+                             &instance->reg_set->outbound_scratch_pad_2) & 0x00FFFF;
 
        if (dual_qdepth_disable || !cur_max_fw_cmds)
                cur_max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF;
@@ -984,8 +987,8 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
 
        cmd = fusion->ioc_init_cmd;
 
-       scratch_pad_1 = readl
-               (&instance->reg_set->outbound_scratch_pad_1);
+       scratch_pad_1 = megasas_readl
+               (instance, &instance->reg_set->outbound_scratch_pad_1);
 
        cur_rdpq_mode = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ? 1 : 0;
 
@@ -1104,7 +1107,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
        instance->instancet->disable_intr(instance);
 
        for (i = 0; i < (10 * 1000); i += 20) {
-               if (readl(&instance->reg_set->doorbell) & 1)
+               if (megasas_readl(instance, &instance->reg_set->doorbell) & 1)
                        msleep(20);
                else
                        break;
@@ -1653,7 +1656,8 @@ megasas_init_adapter_fusion(struct megasas_instance *instance)
 
        megasas_configure_queue_sizes(instance);
 
-       scratch_pad_1 = readl(&instance->reg_set->outbound_scratch_pad_1);
+       scratch_pad_1 = megasas_readl(instance,
+                                     &instance->reg_set->outbound_scratch_pad_1);
        /* If scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK is set,
         * Firmware support extended IO chain frame which is 4 times more than
         * legacy Firmware.
@@ -3731,7 +3735,7 @@ megasas_release_fusion(struct megasas_instance *instance)
 static u32
 megasas_read_fw_status_reg_fusion(struct megasas_instance *instance)
 {
-       return readl(&instance->reg_set->outbound_scratch_pad_0);
+       return megasas_readl(instance, &instance->reg_set->outbound_scratch_pad_0);
 }
 
 /**
@@ -3793,11 +3797,12 @@ megasas_adp_reset_fusion(struct megasas_instance *instance,
        writel(MPI2_WRSEQ_6TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
 
        /* Check that the diag write enable (DRWE) bit is on */
-       host_diag = readl(&instance->reg_set->fusion_host_diag);
+       host_diag = megasas_readl(instance, &instance->reg_set->fusion_host_diag);
        retry = 0;
        while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) {
                msleep(100);
-               host_diag = readl(&instance->reg_set->fusion_host_diag);
+               host_diag = megasas_readl(instance,
+                                         &instance->reg_set->fusion_host_diag);
                if (retry++ == 100) {
                        dev_warn(&instance->pdev->dev,
                                "Host diag unlock failed from %s %d\n",
@@ -3814,11 +3819,12 @@ megasas_adp_reset_fusion(struct megasas_instance *instance,
        msleep(3000);
 
        /* Make sure reset adapter bit is cleared */
-       host_diag = readl(&instance->reg_set->fusion_host_diag);
+       host_diag = megasas_readl(instance, &instance->reg_set->fusion_host_diag);
        retry = 0;
        while (host_diag & HOST_DIAG_RESET_ADAPTER) {
                msleep(100);
-               host_diag = readl(&instance->reg_set->fusion_host_diag);
+               host_diag = megasas_readl(instance,
+                                         &instance->reg_set->fusion_host_diag);
                if (retry++ == 1000) {
                        dev_warn(&instance->pdev->dev,
                                "Diag reset adapter never cleared %s %d\n",
@@ -4607,7 +4613,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason)
                dev_info(&instance->pdev->dev, "IO/DCMD timeout is detected, "
                        "forcibly FAULT Firmware\n");
                atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
-               status_reg = readl(&instance->reg_set->doorbell);
+               status_reg = megasas_readl(instance, &instance->reg_set->doorbell);
                writel(status_reg | MFI_STATE_FORCE_OCR,
                        &instance->reg_set->doorbell);
                readl(&instance->reg_set->doorbell);