From: Kevin Barnett <kevin.barnett@xxxxxxxxxxxxx> Reviewed-by: Scott Benesh <scott.benesh@xxxxxxxxxxxxx> Signed-off-by: Kevin Barnett <kevin.barnett@xxxxxxxxxxxxx> Signed-off-by: Don Brace <don.brace@xxxxxxxxxxxxx> --- drivers/scsi/smartpqi/smartpqi.h | 23 +++++++++++++ drivers/scsi/smartpqi/smartpqi_init.c | 58 ++++++++++++++++++++++++++------- drivers/scsi/smartpqi/smartpqi_sis.c | 30 ++++++++++++++++- 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 e164ffa..6dd0449 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 cb8f886..ffdc32b 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 e55dfcf..9abbace 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 @@ int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info) #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 983184b..394b16e 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);