diff options
Diffstat (limited to 'drivers/scsi/smartpqi/smartpqi_sis.c')
-rw-r--r-- | drivers/scsi/smartpqi/smartpqi_sis.c | 100 |
1 files changed, 96 insertions, 4 deletions
diff --git a/drivers/scsi/smartpqi/smartpqi_sis.c b/drivers/scsi/smartpqi/smartpqi_sis.c index 71408f9e8f75..e55dfcf200e5 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.c +++ b/drivers/scsi/smartpqi/smartpqi_sis.c @@ -1,6 +1,6 @@ /* * driver for Microsemi PQI-based storage controllers - * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016-2017 Microsemi Corporation * Copyright (c) 2016 PMC-Sierra, Inc. * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,9 @@ /* for submission of legacy SIS commands */ #define SIS_REENABLE_SIS_MODE 0x1 #define SIS_ENABLE_MSIX 0x40 +#define SIS_ENABLE_INTX 0x80 #define SIS_SOFT_RESET 0x100 +#define SIS_TRIGGER_SHUTDOWN 0x800000 #define SIS_CMD_READY 0x200 #define SIS_CMD_COMPLETE 0x1000 #define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000 @@ -55,6 +57,7 @@ #define SIS_CTRL_KERNEL_UP 0x80 #define SIS_CTRL_KERNEL_PANIC 0x100 #define SIS_CTRL_READY_TIMEOUT_SECS 30 +#define SIS_CTRL_READY_RESUME_TIMEOUT_SECS 90 #define SIS_CTRL_READY_POLL_INTERVAL_MSECS 10 #pragma pack(1) @@ -78,12 +81,13 @@ struct sis_base_struct { #pragma pack() -int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) +static int sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info *ctrl_info, + unsigned int timeout_secs) { unsigned long timeout; u32 status; - timeout = (SIS_CTRL_READY_TIMEOUT_SECS * HZ) + jiffies; + timeout = (timeout_secs * HZ) + jiffies; while (1) { status = readl(&ctrl_info->registers->sis_firmware_status); @@ -98,14 +102,30 @@ int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) if (status & SIS_CTRL_KERNEL_UP) break; } - if (time_after(jiffies, timeout)) + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "controller not ready after %u seconds\n", + timeout_secs); return -ETIMEDOUT; + } msleep(SIS_CTRL_READY_POLL_INTERVAL_MSECS); } return 0; } +int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) +{ + return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, + SIS_CTRL_READY_TIMEOUT_SECS); +} + +int sis_wait_for_ctrl_ready_resume(struct pqi_ctrl_info *ctrl_info) +{ + return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, + SIS_CTRL_READY_RESUME_TIMEOUT_SECS); +} + bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) { bool running; @@ -126,6 +146,12 @@ bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) return running; } +bool sis_is_kernel_up(struct pqi_ctrl_info *ctrl_info) +{ + return readl(&ctrl_info->registers->sis_firmware_status) & + SIS_CTRL_KERNEL_UP; +} + /* used for passing command parameters/results when issuing SIS commands */ struct sis_sync_cmd_params { u32 mailbox[6]; /* mailboxes 0-5 */ @@ -308,6 +334,34 @@ out: return rc; } +#define SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS 30 + +static void sis_wait_for_doorbell_bit_to_clear( + struct pqi_ctrl_info *ctrl_info, u32 bit) +{ + u32 doorbell_register; + unsigned long timeout; + + timeout = (SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + if ((doorbell_register & bit) == 0) + break; + if (readl(&ctrl_info->registers->sis_firmware_status) & + SIS_CTRL_KERNEL_PANIC) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "doorbell register bit 0x%x not cleared\n", + bit); + break; + } + usleep_range(1000, 2000); + } +} + /* Enable MSI-X interrupts on the controller. */ void sis_enable_msix(struct pqi_ctrl_info *ctrl_info) @@ -320,6 +374,8 @@ void sis_enable_msix(struct pqi_ctrl_info *ctrl_info) writel(doorbell_register, &ctrl_info->registers->sis_host_to_ctrl_doorbell); + + sis_wait_for_doorbell_bit_to_clear(ctrl_info, SIS_ENABLE_MSIX); } /* Disable MSI-X interrupts on the controller. */ @@ -336,12 +392,48 @@ void sis_disable_msix(struct pqi_ctrl_info *ctrl_info) &ctrl_info->registers->sis_host_to_ctrl_doorbell); } +void sis_enable_intx(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register |= SIS_ENABLE_INTX; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); + + sis_wait_for_doorbell_bit_to_clear(ctrl_info, SIS_ENABLE_INTX); +} + +void sis_disable_intx(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register &= ~SIS_ENABLE_INTX; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + void sis_soft_reset(struct pqi_ctrl_info *ctrl_info) { writel(SIS_SOFT_RESET, &ctrl_info->registers->sis_host_to_ctrl_doorbell); } +void sis_shutdown_ctrl(struct pqi_ctrl_info *ctrl_info) +{ + if (readl(&ctrl_info->registers->sis_firmware_status) & + SIS_CTRL_KERNEL_PANIC) + return; + + writel(SIS_TRIGGER_SHUTDOWN, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + #define SIS_MODE_READY_TIMEOUT_SECS 30 int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) |