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>
This commit is contained in:
parent
0b7250f93f
commit
336b681931
|
@ -688,6 +688,28 @@ struct pqi_config_table_heartbeat {
|
||||||
__le32 heartbeat_counter;
|
__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 ((u32)~0)
|
||||||
#define PQI_MAX_OUTSTANDING_REQUESTS_KDUMP 32
|
#define PQI_MAX_OUTSTANDING_REQUESTS_KDUMP 32
|
||||||
#define PQI_MAX_TRANSFER_SIZE (1024U * 1024U)
|
#define PQI_MAX_TRANSFER_SIZE (1024U * 1024U)
|
||||||
|
@ -995,6 +1017,7 @@ struct pqi_ctrl_info {
|
||||||
u8 inbound_spanning_supported : 1;
|
u8 inbound_spanning_supported : 1;
|
||||||
u8 outbound_spanning_supported : 1;
|
u8 outbound_spanning_supported : 1;
|
||||||
u8 pqi_mode_enabled : 1;
|
u8 pqi_mode_enabled : 1;
|
||||||
|
u8 pqi_reset_quiesce_supported : 1;
|
||||||
|
|
||||||
struct list_head scsi_device_list;
|
struct list_head scsi_device_list;
|
||||||
spinlock_t scsi_device_list_lock;
|
spinlock_t scsi_device_list_lock;
|
||||||
|
|
|
@ -5889,28 +5889,62 @@ static void pqi_unregister_scsi(struct pqi_ctrl_info *ctrl_info)
|
||||||
scsi_host_put(shost);
|
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;
|
||||||
|
|
||||||
#define PQI_RESET_TYPE_NO_RESET 0x0
|
pqi_registers = ctrl_info->pqi_registers;
|
||||||
#define PQI_RESET_TYPE_SOFT_RESET 0x1
|
timeout_msecs = readw(&pqi_registers->max_reset_timeout) * 100;
|
||||||
#define PQI_RESET_TYPE_FIRM_RESET 0x2
|
timeout = msecs_to_jiffies(timeout_msecs) + jiffies;
|
||||||
#define PQI_RESET_TYPE_HARD_RESET 0x3
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static int pqi_reset(struct pqi_ctrl_info *ctrl_info)
|
static int pqi_reset(struct pqi_ctrl_info *ctrl_info)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u32 reset_params;
|
union pqi_reset_register reset_reg;
|
||||||
|
|
||||||
reset_params = (PQI_RESET_ACTION_RESET << 5) |
|
if (ctrl_info->pqi_reset_quiesce_supported) {
|
||||||
PQI_RESET_TYPE_HARD_RESET;
|
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,
|
reset_reg.all_bits = 0;
|
||||||
&ctrl_info->pqi_registers->device_reset);
|
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)
|
if (rc)
|
||||||
dev_err(&ctrl_info->pci_dev->dev,
|
dev_err(&ctrl_info->pci_dev->dev,
|
||||||
"PQI reset failed\n");
|
"PQI reset failed with error %d\n", rc);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#define SIS_ENABLE_INTX 0x80
|
#define SIS_ENABLE_INTX 0x80
|
||||||
#define SIS_SOFT_RESET 0x100
|
#define SIS_SOFT_RESET 0x100
|
||||||
#define SIS_TRIGGER_SHUTDOWN 0x800000
|
#define SIS_TRIGGER_SHUTDOWN 0x800000
|
||||||
|
#define SIS_PQI_RESET_QUIESCE 0x1000000
|
||||||
#define SIS_CMD_READY 0x200
|
#define SIS_CMD_READY 0x200
|
||||||
#define SIS_CMD_COMPLETE 0x1000
|
#define SIS_CMD_COMPLETE 0x1000
|
||||||
#define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000
|
#define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
#define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000
|
#define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000
|
||||||
#define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2
|
#define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2
|
||||||
#define SIS_PQI_MODE_SUPPORTED 0x4
|
#define SIS_PQI_MODE_SUPPORTED 0x4
|
||||||
|
#define SIS_PQI_RESET_QUIESCE_SUPPORTED 0x8
|
||||||
#define SIS_REQUIRED_EXTENDED_PROPERTIES \
|
#define SIS_REQUIRED_EXTENDED_PROPERTIES \
|
||||||
(SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED)
|
(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)
|
SIS_REQUIRED_EXTENDED_PROPERTIES)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (extended_properties & SIS_PQI_RESET_QUIESCE_SUPPORTED)
|
||||||
|
ctrl_info->pqi_reset_quiesce_supported = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,9 +341,10 @@ out:
|
||||||
|
|
||||||
#define SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS 30
|
#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)
|
struct pqi_ctrl_info *ctrl_info, u32 bit)
|
||||||
{
|
{
|
||||||
|
int rc = 0;
|
||||||
u32 doorbell_register;
|
u32 doorbell_register;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
|
@ -350,16 +356,21 @@ static void sis_wait_for_doorbell_bit_to_clear(
|
||||||
if ((doorbell_register & bit) == 0)
|
if ((doorbell_register & bit) == 0)
|
||||||
break;
|
break;
|
||||||
if (readl(&ctrl_info->registers->sis_firmware_status) &
|
if (readl(&ctrl_info->registers->sis_firmware_status) &
|
||||||
SIS_CTRL_KERNEL_PANIC)
|
SIS_CTRL_KERNEL_PANIC) {
|
||||||
|
rc = -ENODEV;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
if (time_after(jiffies, timeout)) {
|
if (time_after(jiffies, timeout)) {
|
||||||
dev_err(&ctrl_info->pci_dev->dev,
|
dev_err(&ctrl_info->pci_dev->dev,
|
||||||
"doorbell register bit 0x%x not cleared\n",
|
"doorbell register bit 0x%x not cleared\n",
|
||||||
bit);
|
bit);
|
||||||
|
rc = -ETIMEDOUT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
usleep_range(1000, 2000);
|
usleep_range(1000, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable MSI-X interrupts on the controller. */
|
/* 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);
|
&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
|
#define SIS_MODE_READY_TIMEOUT_SECS 30
|
||||||
|
|
||||||
int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info)
|
int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info)
|
||||||
|
|
|
@ -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_disable_intx(struct pqi_ctrl_info *ctrl_info);
|
||||||
void sis_soft_reset(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);
|
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);
|
int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info);
|
||||||
void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value);
|
void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value);
|
||||||
u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info);
|
u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info);
|
||||||
|
|
Loading…
Reference in New Issue