diff options
author | Kevin Barnett <kevin.barnett@microseim.com> | 2017-08-10 13:46:39 -0500 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2017-08-10 19:58:25 -0400 |
commit | 336b68193165b1215d21dd05619dc262340e404b (patch) | |
tree | efc10e7e5e80f8b0d53a3c716e9ed801300a68bf /drivers/scsi | |
parent | 0b7250f93f02a8aaa80146c34c08e4596f0a1e1d (diff) | |
download | linux-336b68193165b1215d21dd05619dc262340e404b.tar.bz2 |
scsi: smartpqi: add pqi reset quiesce support
Reviewed-by: Scott Benesh <scott.benesh@microsemi.com>
Signed-off-by: Kevin Barnett <kevin.barnett@microsemi.com>
Signed-off-by: Don Brace <don.brace@microsemi.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/smartpqi/smartpqi.h | 23 | ||||
-rw-r--r-- | drivers/scsi/smartpqi/smartpqi_init.c | 58 | ||||
-rw-r--r-- | drivers/scsi/smartpqi/smartpqi_sis.c | 30 | ||||
-rw-r--r-- | drivers/scsi/smartpqi/smartpqi_sis.h | 1 |
4 files changed, 98 insertions, 14 deletions
diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index e164ffade38a..6dd04491423b 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -688,6 +688,28 @@ struct pqi_config_table_heartbeat { __le32 heartbeat_counter; }; +union pqi_reset_register { + struct { + u32 reset_type : 3; + u32 reserved : 2; + u32 reset_action : 3; + u32 hold_in_pd1 : 1; + u32 reserved2 : 23; + } bits; + u32 all_bits; +}; + +#define PQI_RESET_ACTION_RESET 0x1 + +#define PQI_RESET_TYPE_NO_RESET 0x0 +#define PQI_RESET_TYPE_SOFT_RESET 0x1 +#define PQI_RESET_TYPE_FIRM_RESET 0x2 +#define PQI_RESET_TYPE_HARD_RESET 0x3 + +#define PQI_RESET_ACTION_COMPLETED 0x2 + +#define PQI_RESET_POLL_INTERVAL_MSECS 100 + #define PQI_MAX_OUTSTANDING_REQUESTS ((u32)~0) #define PQI_MAX_OUTSTANDING_REQUESTS_KDUMP 32 #define PQI_MAX_TRANSFER_SIZE (1024U * 1024U) @@ -995,6 +1017,7 @@ struct pqi_ctrl_info { u8 inbound_spanning_supported : 1; u8 outbound_spanning_supported : 1; u8 pqi_mode_enabled : 1; + u8 pqi_reset_quiesce_supported : 1; struct list_head scsi_device_list; spinlock_t scsi_device_list_lock; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index cb8f886e705c..ffdc32ba9178 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -5889,28 +5889,62 @@ static void pqi_unregister_scsi(struct pqi_ctrl_info *ctrl_info) scsi_host_put(shost); } -#define PQI_RESET_ACTION_RESET 0x1 +static int pqi_wait_for_pqi_reset_completion(struct pqi_ctrl_info *ctrl_info) +{ + int rc = 0; + struct pqi_device_registers __iomem *pqi_registers; + unsigned long timeout; + unsigned int timeout_msecs; + union pqi_reset_register reset_reg; + + pqi_registers = ctrl_info->pqi_registers; + timeout_msecs = readw(&pqi_registers->max_reset_timeout) * 100; + timeout = msecs_to_jiffies(timeout_msecs) + jiffies; + + while (1) { + msleep(PQI_RESET_POLL_INTERVAL_MSECS); + reset_reg.all_bits = readl(&pqi_registers->device_reset); + if (reset_reg.bits.reset_action == PQI_RESET_ACTION_COMPLETED) + break; + pqi_check_ctrl_health(ctrl_info); + if (pqi_ctrl_offline(ctrl_info)) { + rc = -ENXIO; + break; + } + if (time_after(jiffies, timeout)) { + rc = -ETIMEDOUT; + break; + } + } -#define PQI_RESET_TYPE_NO_RESET 0x0 -#define PQI_RESET_TYPE_SOFT_RESET 0x1 -#define PQI_RESET_TYPE_FIRM_RESET 0x2 -#define PQI_RESET_TYPE_HARD_RESET 0x3 + return rc; +} static int pqi_reset(struct pqi_ctrl_info *ctrl_info) { int rc; - u32 reset_params; + union pqi_reset_register reset_reg; - reset_params = (PQI_RESET_ACTION_RESET << 5) | - PQI_RESET_TYPE_HARD_RESET; + if (ctrl_info->pqi_reset_quiesce_supported) { + rc = sis_pqi_reset_quiesce(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "PQI reset failed during quiesce with error %d\n", + rc); + return rc; + } + } - writel(reset_params, - &ctrl_info->pqi_registers->device_reset); + reset_reg.all_bits = 0; + reset_reg.bits.reset_type = PQI_RESET_TYPE_HARD_RESET; + reset_reg.bits.reset_action = PQI_RESET_ACTION_RESET; - rc = pqi_wait_for_pqi_mode_ready(ctrl_info); + writel(reset_reg.all_bits, &ctrl_info->pqi_registers->device_reset); + + rc = pqi_wait_for_pqi_reset_completion(ctrl_info); if (rc) dev_err(&ctrl_info->pci_dev->dev, - "PQI reset failed\n"); + "PQI reset failed with error %d\n", rc); return rc; } diff --git a/drivers/scsi/smartpqi/smartpqi_sis.c b/drivers/scsi/smartpqi/smartpqi_sis.c index e55dfcf200e5..9abbaced4b33 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.c +++ b/drivers/scsi/smartpqi/smartpqi_sis.c @@ -36,6 +36,7 @@ #define SIS_ENABLE_INTX 0x80 #define SIS_SOFT_RESET 0x100 #define SIS_TRIGGER_SHUTDOWN 0x800000 +#define SIS_PQI_RESET_QUIESCE 0x1000000 #define SIS_CMD_READY 0x200 #define SIS_CMD_COMPLETE 0x1000 #define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000 @@ -47,6 +48,7 @@ #define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000 #define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2 #define SIS_PQI_MODE_SUPPORTED 0x4 +#define SIS_PQI_RESET_QUIESCE_SUPPORTED 0x8 #define SIS_REQUIRED_EXTENDED_PROPERTIES \ (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED) @@ -258,6 +260,9 @@ int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info) SIS_REQUIRED_EXTENDED_PROPERTIES) return -ENODEV; + if (extended_properties & SIS_PQI_RESET_QUIESCE_SUPPORTED) + ctrl_info->pqi_reset_quiesce_supported = true; + return 0; } @@ -336,9 +341,10 @@ out: #define SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS 30 -static void sis_wait_for_doorbell_bit_to_clear( +static int sis_wait_for_doorbell_bit_to_clear( struct pqi_ctrl_info *ctrl_info, u32 bit) { + int rc = 0; u32 doorbell_register; unsigned long timeout; @@ -350,16 +356,21 @@ static void sis_wait_for_doorbell_bit_to_clear( if ((doorbell_register & bit) == 0) break; if (readl(&ctrl_info->registers->sis_firmware_status) & - SIS_CTRL_KERNEL_PANIC) + SIS_CTRL_KERNEL_PANIC) { + rc = -ENODEV; break; + } if (time_after(jiffies, timeout)) { dev_err(&ctrl_info->pci_dev->dev, "doorbell register bit 0x%x not cleared\n", bit); + rc = -ETIMEDOUT; break; } usleep_range(1000, 2000); } + + return rc; } /* Enable MSI-X interrupts on the controller. */ @@ -434,6 +445,21 @@ void sis_shutdown_ctrl(struct pqi_ctrl_info *ctrl_info) &ctrl_info->registers->sis_host_to_ctrl_doorbell); } +int sis_pqi_reset_quiesce(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register |= SIS_PQI_RESET_QUIESCE; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); + + return sis_wait_for_doorbell_bit_to_clear(ctrl_info, + SIS_PQI_RESET_QUIESCE); +} + #define SIS_MODE_READY_TIMEOUT_SECS 30 int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) diff --git a/drivers/scsi/smartpqi/smartpqi_sis.h b/drivers/scsi/smartpqi/smartpqi_sis.h index 983184b69373..394b16eb4c77 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.h +++ b/drivers/scsi/smartpqi/smartpqi_sis.h @@ -32,6 +32,7 @@ void sis_enable_intx(struct pqi_ctrl_info *ctrl_info); void sis_disable_intx(struct pqi_ctrl_info *ctrl_info); void sis_soft_reset(struct pqi_ctrl_info *ctrl_info); void sis_shutdown_ctrl(struct pqi_ctrl_info *ctrl_info); +int sis_pqi_reset_quiesce(struct pqi_ctrl_info *ctrl_info); int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info); void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value); u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info); |