Skip to content

Commit

Permalink
scsi: megaraid_sas: add retry logic in megasas_readl
Browse files Browse the repository at this point in the history
ANBZ: torvalds#633

commit 272652f upstream.

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>
Reviewed-by: Xunlei Pang <xlpang@linux.alibaba.com>
Signed-off-by: Guixin Liu <kanie@linux.alibaba.com>
  • Loading branch information
shivasharan-s authored and guixinliu1995 committed Mar 10, 2022
1 parent fb08a5a commit 8789ddc
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 18 deletions.
2 changes: 2 additions & 0 deletions drivers/scsi/megaraid/megaraid_sas.h
Original file line number Diff line number Diff line change
Expand Up @@ -2545,6 +2545,8 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
void megasas_free_host_crash_buffer(struct megasas_instance *instance);
void megasas_fusion_crash_dump_wq(struct work_struct *work);

u32 megasas_readl(struct megasas_instance *instance,
const volatile void __iomem *addr);
void megasas_return_cmd_fusion(struct megasas_instance *instance,
struct megasas_cmd_fusion *cmd);
int megasas_issue_blocked_cmd(struct megasas_instance *instance,
Expand Down
39 changes: 32 additions & 7 deletions drivers/scsi/megaraid/megaraid_sas_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,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
Expand Down Expand Up @@ -3846,7 +3868,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)
Expand Down Expand Up @@ -5320,7 +5343,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);
Expand All @@ -5332,8 +5356,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) {
Expand Down Expand Up @@ -5444,7 +5468,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 =
Expand Down Expand Up @@ -6095,8 +6120,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;
Expand Down
26 changes: 15 additions & 11 deletions drivers/scsi/megaraid/megaraid_sas_fusion.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,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;
Expand Down Expand Up @@ -987,8 +988,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;

Expand Down Expand Up @@ -1109,7 +1110,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;
Expand Down Expand Up @@ -1661,7 +1662,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.
Expand Down Expand Up @@ -3696,7 +3698,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);
}

/**
Expand Down Expand Up @@ -3758,11 +3760,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",
Expand All @@ -3779,11 +3782,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",
Expand Down Expand Up @@ -4529,7 +4533,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);
Expand Down

0 comments on commit 8789ddc

Please sign in to comment.