12cc37b15SDon Brace // SPDX-License-Identifier: GPL-2.0
26c223761SKevin Barnett /*
3889653ecSKevin Barnett * driver for Microchip PQI-based storage controllers
4*49fd52d4SDon Brace * Copyright (c) 2019-2023 Microchip Technology Inc. and its subsidiaries
52f4c4b92SDon Brace * Copyright (c) 2016-2018 Microsemi Corporation
66c223761SKevin Barnett * Copyright (c) 2016 PMC-Sierra, Inc.
76c223761SKevin Barnett *
82f4c4b92SDon Brace * Questions/Comments/Bugfixes to storagedev@microchip.com
96c223761SKevin Barnett *
106c223761SKevin Barnett */
116c223761SKevin Barnett
126c223761SKevin Barnett #include <linux/module.h>
136c223761SKevin Barnett #include <linux/kernel.h>
146c223761SKevin Barnett #include <linux/delay.h>
156c223761SKevin Barnett #include <linux/pci.h>
166c223761SKevin Barnett #include <scsi/scsi_device.h>
176c223761SKevin Barnett #include <asm/unaligned.h>
186c223761SKevin Barnett #include "smartpqi.h"
196c223761SKevin Barnett #include "smartpqi_sis.h"
206c223761SKevin Barnett
216c223761SKevin Barnett /* legacy SIS interface commands */
226c223761SKevin Barnett #define SIS_CMD_GET_ADAPTER_PROPERTIES 0x19
236c223761SKevin Barnett #define SIS_CMD_INIT_BASE_STRUCT_ADDRESS 0x1b
246c223761SKevin Barnett #define SIS_CMD_GET_PQI_CAPABILITIES 0x3000
256c223761SKevin Barnett
266c223761SKevin Barnett /* for submission of legacy SIS commands */
276c223761SKevin Barnett #define SIS_REENABLE_SIS_MODE 0x1
286c223761SKevin Barnett #define SIS_ENABLE_MSIX 0x40
29061ef06aSKevin Barnett #define SIS_ENABLE_INTX 0x80
304fd22c13SMahesh Rajashekhara #define SIS_SOFT_RESET 0x100
314f078e24SKevin Barnett #define SIS_CMD_READY 0x200
325b0fba0fSKevin Barnett #define SIS_TRIGGER_SHUTDOWN 0x800000
33336b6819SKevin Barnett #define SIS_PQI_RESET_QUIESCE 0x1000000
344f078e24SKevin Barnett
356c223761SKevin Barnett #define SIS_CMD_COMPLETE 0x1000
366c223761SKevin Barnett #define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000
374f078e24SKevin Barnett
386c223761SKevin Barnett #define SIS_CMD_STATUS_SUCCESS 0x1
396c223761SKevin Barnett #define SIS_CMD_COMPLETE_TIMEOUT_SECS 30
406c223761SKevin Barnett #define SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS 10
416c223761SKevin Barnett
426c223761SKevin Barnett /* used with SIS_CMD_GET_ADAPTER_PROPERTIES command */
436c223761SKevin Barnett #define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000
446c223761SKevin Barnett #define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2
456c223761SKevin Barnett #define SIS_PQI_MODE_SUPPORTED 0x4
46336b6819SKevin Barnett #define SIS_PQI_RESET_QUIESCE_SUPPORTED 0x8
476c223761SKevin Barnett #define SIS_REQUIRED_EXTENDED_PROPERTIES \
486c223761SKevin Barnett (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED)
496c223761SKevin Barnett
506c223761SKevin Barnett /* used with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */
516c223761SKevin Barnett #define SIS_BASE_STRUCT_REVISION 9
526c223761SKevin Barnett #define SIS_BASE_STRUCT_ALIGNMENT 16
536c223761SKevin Barnett
549ee5d6e9SMahesh Rajashekhara #define SIS_CTRL_KERNEL_FW_TRIAGE 0x3
556c223761SKevin Barnett #define SIS_CTRL_KERNEL_UP 0x80
566c223761SKevin Barnett #define SIS_CTRL_KERNEL_PANIC 0x100
5765111785SMahesh Rajashekhara #define SIS_CTRL_READY_TIMEOUT_SECS 180
58061ef06aSKevin Barnett #define SIS_CTRL_READY_RESUME_TIMEOUT_SECS 90
596c223761SKevin Barnett #define SIS_CTRL_READY_POLL_INTERVAL_MSECS 10
606c223761SKevin Barnett
619ee5d6e9SMahesh Rajashekhara enum sis_fw_triage_status {
629ee5d6e9SMahesh Rajashekhara FW_TRIAGE_NOT_STARTED = 0,
639ee5d6e9SMahesh Rajashekhara FW_TRIAGE_STARTED,
649ee5d6e9SMahesh Rajashekhara FW_TRIAGE_COND_INVALID,
659ee5d6e9SMahesh Rajashekhara FW_TRIAGE_COMPLETED
669ee5d6e9SMahesh Rajashekhara };
679ee5d6e9SMahesh Rajashekhara
686c223761SKevin Barnett #pragma pack(1)
696c223761SKevin Barnett
706c223761SKevin Barnett /* for use with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */
716c223761SKevin Barnett struct sis_base_struct {
726c223761SKevin Barnett __le32 revision; /* revision of this structure */
736c223761SKevin Barnett __le32 flags; /* reserved */
746c223761SKevin Barnett __le32 error_buffer_paddr_low; /* lower 32 bits of physical memory */
756c223761SKevin Barnett /* buffer for PQI error response */
766c223761SKevin Barnett /* data */
776c223761SKevin Barnett __le32 error_buffer_paddr_high; /* upper 32 bits of physical */
786c223761SKevin Barnett /* memory buffer for PQI */
796c223761SKevin Barnett /* error response data */
806c223761SKevin Barnett __le32 error_buffer_element_length; /* length of each PQI error */
816c223761SKevin Barnett /* response buffer element */
826c223761SKevin Barnett /* in bytes */
836c223761SKevin Barnett __le32 error_buffer_num_elements; /* total number of PQI error */
846c223761SKevin Barnett /* response buffers available */
856c223761SKevin Barnett };
866c223761SKevin Barnett
876c223761SKevin Barnett #pragma pack()
886c223761SKevin Barnett
896d567dfeSKevin Barnett unsigned int sis_ctrl_ready_timeout_secs = SIS_CTRL_READY_TIMEOUT_SECS;
906d567dfeSKevin Barnett
sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info * ctrl_info,unsigned int timeout_secs)91061ef06aSKevin Barnett static int sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info *ctrl_info,
92061ef06aSKevin Barnett unsigned int timeout_secs)
936c223761SKevin Barnett {
946c223761SKevin Barnett unsigned long timeout;
956c223761SKevin Barnett u32 status;
966c223761SKevin Barnett
9742dc0426SBalsundar P timeout = (timeout_secs * HZ) + jiffies;
986c223761SKevin Barnett
996c223761SKevin Barnett while (1) {
1006c223761SKevin Barnett status = readl(&ctrl_info->registers->sis_firmware_status);
1016c223761SKevin Barnett if (status != ~0) {
1026c223761SKevin Barnett if (status & SIS_CTRL_KERNEL_PANIC) {
1036c223761SKevin Barnett dev_err(&ctrl_info->pci_dev->dev,
1046c223761SKevin Barnett "controller is offline: status code 0x%x\n",
1056c223761SKevin Barnett readl(
1066c223761SKevin Barnett &ctrl_info->registers->sis_mailbox[7]));
1076c223761SKevin Barnett return -ENODEV;
1086c223761SKevin Barnett }
1096c223761SKevin Barnett if (status & SIS_CTRL_KERNEL_UP)
1106c223761SKevin Barnett break;
1116c223761SKevin Barnett }
1128845fdfaSKevin Barnett if (time_after(jiffies, timeout)) {
1138845fdfaSKevin Barnett dev_err(&ctrl_info->pci_dev->dev,
114d87d5474SKevin Barnett "controller not ready after %u seconds\n",
115d87d5474SKevin Barnett timeout_secs);
1166c223761SKevin Barnett return -ETIMEDOUT;
1178845fdfaSKevin Barnett }
1186c223761SKevin Barnett msleep(SIS_CTRL_READY_POLL_INTERVAL_MSECS);
1196c223761SKevin Barnett }
1206c223761SKevin Barnett
1216c223761SKevin Barnett return 0;
1226c223761SKevin Barnett }
1236c223761SKevin Barnett
sis_wait_for_ctrl_ready(struct pqi_ctrl_info * ctrl_info)124061ef06aSKevin Barnett int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info)
125061ef06aSKevin Barnett {
126061ef06aSKevin Barnett return sis_wait_for_ctrl_ready_with_timeout(ctrl_info,
1276d567dfeSKevin Barnett sis_ctrl_ready_timeout_secs);
128061ef06aSKevin Barnett }
129061ef06aSKevin Barnett
sis_wait_for_ctrl_ready_resume(struct pqi_ctrl_info * ctrl_info)130061ef06aSKevin Barnett int sis_wait_for_ctrl_ready_resume(struct pqi_ctrl_info *ctrl_info)
131061ef06aSKevin Barnett {
132061ef06aSKevin Barnett return sis_wait_for_ctrl_ready_with_timeout(ctrl_info,
133061ef06aSKevin Barnett SIS_CTRL_READY_RESUME_TIMEOUT_SECS);
134061ef06aSKevin Barnett }
135061ef06aSKevin Barnett
sis_is_firmware_running(struct pqi_ctrl_info * ctrl_info)1366c223761SKevin Barnett bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info)
1376c223761SKevin Barnett {
1386c223761SKevin Barnett bool running;
1396c223761SKevin Barnett u32 status;
1406c223761SKevin Barnett
1416c223761SKevin Barnett status = readl(&ctrl_info->registers->sis_firmware_status);
1426c223761SKevin Barnett
143331f7e99SSagar Biradar if (status != ~0 && (status & SIS_CTRL_KERNEL_PANIC))
1446c223761SKevin Barnett running = false;
1456c223761SKevin Barnett else
1466c223761SKevin Barnett running = true;
1476c223761SKevin Barnett
1486c223761SKevin Barnett if (!running)
1496c223761SKevin Barnett dev_err(&ctrl_info->pci_dev->dev,
1506c223761SKevin Barnett "controller is offline: status code 0x%x\n",
1516c223761SKevin Barnett readl(&ctrl_info->registers->sis_mailbox[7]));
1526c223761SKevin Barnett
1536c223761SKevin Barnett return running;
1546c223761SKevin Barnett }
1556c223761SKevin Barnett
sis_is_kernel_up(struct pqi_ctrl_info * ctrl_info)156162d7753SKevin Barnett bool sis_is_kernel_up(struct pqi_ctrl_info *ctrl_info)
157162d7753SKevin Barnett {
158162d7753SKevin Barnett return readl(&ctrl_info->registers->sis_firmware_status) &
159162d7753SKevin Barnett SIS_CTRL_KERNEL_UP;
160162d7753SKevin Barnett }
161162d7753SKevin Barnett
sis_get_product_id(struct pqi_ctrl_info * ctrl_info)1622708a256SKevin Barnett u32 sis_get_product_id(struct pqi_ctrl_info *ctrl_info)
1632708a256SKevin Barnett {
1642708a256SKevin Barnett return readl(&ctrl_info->registers->sis_product_identifier);
1652708a256SKevin Barnett }
1662708a256SKevin Barnett
1676c223761SKevin Barnett /* used for passing command parameters/results when issuing SIS commands */
1686c223761SKevin Barnett struct sis_sync_cmd_params {
1696c223761SKevin Barnett u32 mailbox[6]; /* mailboxes 0-5 */
1706c223761SKevin Barnett };
1716c223761SKevin Barnett
sis_send_sync_cmd(struct pqi_ctrl_info * ctrl_info,u32 cmd,struct sis_sync_cmd_params * params)1726c223761SKevin Barnett static int sis_send_sync_cmd(struct pqi_ctrl_info *ctrl_info,
1736c223761SKevin Barnett u32 cmd, struct sis_sync_cmd_params *params)
1746c223761SKevin Barnett {
1756c223761SKevin Barnett struct pqi_ctrl_registers __iomem *registers;
1766c223761SKevin Barnett unsigned int i;
1776c223761SKevin Barnett unsigned long timeout;
1786c223761SKevin Barnett u32 doorbell;
1796c223761SKevin Barnett u32 cmd_status;
1806c223761SKevin Barnett
1816c223761SKevin Barnett registers = ctrl_info->registers;
1826c223761SKevin Barnett
1836c223761SKevin Barnett /* Write the command to mailbox 0. */
1846c223761SKevin Barnett writel(cmd, ®isters->sis_mailbox[0]);
1856c223761SKevin Barnett
1866c223761SKevin Barnett /*
1876c223761SKevin Barnett * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used
1886c223761SKevin Barnett * when sending a command to the controller).
1896c223761SKevin Barnett */
1906c223761SKevin Barnett for (i = 1; i <= 4; i++)
1916c223761SKevin Barnett writel(params->mailbox[i], ®isters->sis_mailbox[i]);
1926c223761SKevin Barnett
1936c223761SKevin Barnett /* Clear the command doorbell. */
1946c223761SKevin Barnett writel(SIS_CLEAR_CTRL_TO_HOST_DOORBELL,
1956c223761SKevin Barnett ®isters->sis_ctrl_to_host_doorbell_clear);
1966c223761SKevin Barnett
1976c223761SKevin Barnett /* Disable doorbell interrupts by masking all interrupts. */
1986c223761SKevin Barnett writel(~0, ®isters->sis_interrupt_mask);
199297bdc54SMike McGowen usleep_range(1000, 2000);
2006c223761SKevin Barnett
2016c223761SKevin Barnett /*
2026c223761SKevin Barnett * Force the completion of the interrupt mask register write before
2036c223761SKevin Barnett * submitting the command.
2046c223761SKevin Barnett */
2056c223761SKevin Barnett readl(®isters->sis_interrupt_mask);
2066c223761SKevin Barnett
2076c223761SKevin Barnett /* Submit the command to the controller. */
2086c223761SKevin Barnett writel(SIS_CMD_READY, ®isters->sis_host_to_ctrl_doorbell);
2096c223761SKevin Barnett
2106c223761SKevin Barnett /*
2116c223761SKevin Barnett * Poll for command completion. Note that the call to msleep() is at
2126c223761SKevin Barnett * the top of the loop in order to give the controller time to start
2136c223761SKevin Barnett * processing the command before we start polling.
2146c223761SKevin Barnett */
21542dc0426SBalsundar P timeout = (SIS_CMD_COMPLETE_TIMEOUT_SECS * HZ) + jiffies;
2166c223761SKevin Barnett while (1) {
2176c223761SKevin Barnett msleep(SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS);
2186c223761SKevin Barnett doorbell = readl(®isters->sis_ctrl_to_host_doorbell);
2196c223761SKevin Barnett if (doorbell & SIS_CMD_COMPLETE)
2206c223761SKevin Barnett break;
2216c223761SKevin Barnett if (time_after(jiffies, timeout))
2226c223761SKevin Barnett return -ETIMEDOUT;
2236c223761SKevin Barnett }
2246c223761SKevin Barnett
2256c223761SKevin Barnett /* Read the command status from mailbox 0. */
2266c223761SKevin Barnett cmd_status = readl(®isters->sis_mailbox[0]);
2276c223761SKevin Barnett if (cmd_status != SIS_CMD_STATUS_SUCCESS) {
2286c223761SKevin Barnett dev_err(&ctrl_info->pci_dev->dev,
2296c223761SKevin Barnett "SIS command failed for command 0x%x: status = 0x%x\n",
2306c223761SKevin Barnett cmd, cmd_status);
2316c223761SKevin Barnett return -EINVAL;
2326c223761SKevin Barnett }
2336c223761SKevin Barnett
2346c223761SKevin Barnett /*
2356c223761SKevin Barnett * The command completed successfully, so save the command status and
2366c223761SKevin Barnett * read the values returned in mailboxes 1-5.
2376c223761SKevin Barnett */
2386c223761SKevin Barnett params->mailbox[0] = cmd_status;
2396c223761SKevin Barnett for (i = 1; i < ARRAY_SIZE(params->mailbox); i++)
2406c223761SKevin Barnett params->mailbox[i] = readl(®isters->sis_mailbox[i]);
2416c223761SKevin Barnett
2426c223761SKevin Barnett return 0;
2436c223761SKevin Barnett }
2446c223761SKevin Barnett
2456c223761SKevin Barnett /*
2466c223761SKevin Barnett * This function verifies that we are talking to a controller that speaks PQI.
2476c223761SKevin Barnett */
2486c223761SKevin Barnett
sis_get_ctrl_properties(struct pqi_ctrl_info * ctrl_info)2496c223761SKevin Barnett int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info)
2506c223761SKevin Barnett {
2516c223761SKevin Barnett int rc;
2526c223761SKevin Barnett u32 properties;
2536c223761SKevin Barnett u32 extended_properties;
2546c223761SKevin Barnett struct sis_sync_cmd_params params;
2556c223761SKevin Barnett
2566c223761SKevin Barnett memset(¶ms, 0, sizeof(params));
2576c223761SKevin Barnett
2586c223761SKevin Barnett rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_ADAPTER_PROPERTIES,
2596c223761SKevin Barnett ¶ms);
2606c223761SKevin Barnett if (rc)
2616c223761SKevin Barnett return rc;
2626c223761SKevin Barnett
2636c223761SKevin Barnett properties = params.mailbox[1];
2646c223761SKevin Barnett
2656c223761SKevin Barnett if (!(properties & SIS_EXTENDED_PROPERTIES_SUPPORTED))
2666c223761SKevin Barnett return -ENODEV;
2676c223761SKevin Barnett
2686c223761SKevin Barnett extended_properties = params.mailbox[4];
2696c223761SKevin Barnett
2706c223761SKevin Barnett if ((extended_properties & SIS_REQUIRED_EXTENDED_PROPERTIES) !=
2716c223761SKevin Barnett SIS_REQUIRED_EXTENDED_PROPERTIES)
2726c223761SKevin Barnett return -ENODEV;
2736c223761SKevin Barnett
274336b6819SKevin Barnett if (extended_properties & SIS_PQI_RESET_QUIESCE_SUPPORTED)
275336b6819SKevin Barnett ctrl_info->pqi_reset_quiesce_supported = true;
276336b6819SKevin Barnett
2776c223761SKevin Barnett return 0;
2786c223761SKevin Barnett }
2796c223761SKevin Barnett
sis_get_pqi_capabilities(struct pqi_ctrl_info * ctrl_info)2806c223761SKevin Barnett int sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info)
2816c223761SKevin Barnett {
2826c223761SKevin Barnett int rc;
2836c223761SKevin Barnett struct sis_sync_cmd_params params;
2846c223761SKevin Barnett
2856c223761SKevin Barnett memset(¶ms, 0, sizeof(params));
2866c223761SKevin Barnett
2876c223761SKevin Barnett rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_PQI_CAPABILITIES,
2886c223761SKevin Barnett ¶ms);
2896c223761SKevin Barnett if (rc)
2906c223761SKevin Barnett return rc;
2916c223761SKevin Barnett
2926c223761SKevin Barnett ctrl_info->max_sg_entries = params.mailbox[1];
2936c223761SKevin Barnett ctrl_info->max_transfer_size = params.mailbox[2];
2946c223761SKevin Barnett ctrl_info->max_outstanding_requests = params.mailbox[3];
2956c223761SKevin Barnett ctrl_info->config_table_offset = params.mailbox[4];
2966c223761SKevin Barnett ctrl_info->config_table_length = params.mailbox[5];
2976c223761SKevin Barnett
2986c223761SKevin Barnett return 0;
2996c223761SKevin Barnett }
3006c223761SKevin Barnett
sis_init_base_struct_addr(struct pqi_ctrl_info * ctrl_info)3016c223761SKevin Barnett int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info)
3026c223761SKevin Barnett {
3036c223761SKevin Barnett int rc;
3046c223761SKevin Barnett void *base_struct_unaligned;
3056c223761SKevin Barnett struct sis_base_struct *base_struct;
3066c223761SKevin Barnett struct sis_sync_cmd_params params;
3076c223761SKevin Barnett unsigned long error_buffer_paddr;
3086c223761SKevin Barnett dma_addr_t bus_address;
3096c223761SKevin Barnett
3106c223761SKevin Barnett base_struct_unaligned = kzalloc(sizeof(*base_struct)
3116c223761SKevin Barnett + SIS_BASE_STRUCT_ALIGNMENT - 1, GFP_KERNEL);
3126c223761SKevin Barnett if (!base_struct_unaligned)
3136c223761SKevin Barnett return -ENOMEM;
3146c223761SKevin Barnett
3156c223761SKevin Barnett base_struct = PTR_ALIGN(base_struct_unaligned,
3166c223761SKevin Barnett SIS_BASE_STRUCT_ALIGNMENT);
3176c223761SKevin Barnett error_buffer_paddr = (unsigned long)ctrl_info->error_buffer_dma_handle;
3186c223761SKevin Barnett
3196c223761SKevin Barnett put_unaligned_le32(SIS_BASE_STRUCT_REVISION, &base_struct->revision);
3206c223761SKevin Barnett put_unaligned_le32(lower_32_bits(error_buffer_paddr),
3216c223761SKevin Barnett &base_struct->error_buffer_paddr_low);
3226c223761SKevin Barnett put_unaligned_le32(upper_32_bits(error_buffer_paddr),
3236c223761SKevin Barnett &base_struct->error_buffer_paddr_high);
3246c223761SKevin Barnett put_unaligned_le32(PQI_ERROR_BUFFER_ELEMENT_LENGTH,
3256c223761SKevin Barnett &base_struct->error_buffer_element_length);
3266c223761SKevin Barnett put_unaligned_le32(ctrl_info->max_io_slots,
3276c223761SKevin Barnett &base_struct->error_buffer_num_elements);
3286c223761SKevin Barnett
3296917a9ccSChristoph Hellwig bus_address = dma_map_single(&ctrl_info->pci_dev->dev, base_struct,
3306917a9ccSChristoph Hellwig sizeof(*base_struct), DMA_TO_DEVICE);
3316917a9ccSChristoph Hellwig if (dma_mapping_error(&ctrl_info->pci_dev->dev, bus_address)) {
3326c223761SKevin Barnett rc = -ENOMEM;
3336c223761SKevin Barnett goto out;
3346c223761SKevin Barnett }
3356c223761SKevin Barnett
3366c223761SKevin Barnett memset(¶ms, 0, sizeof(params));
3376c223761SKevin Barnett params.mailbox[1] = lower_32_bits((u64)bus_address);
3386c223761SKevin Barnett params.mailbox[2] = upper_32_bits((u64)bus_address);
3396c223761SKevin Barnett params.mailbox[3] = sizeof(*base_struct);
3406c223761SKevin Barnett
3416c223761SKevin Barnett rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_INIT_BASE_STRUCT_ADDRESS,
3426c223761SKevin Barnett ¶ms);
3436c223761SKevin Barnett
3446917a9ccSChristoph Hellwig dma_unmap_single(&ctrl_info->pci_dev->dev, bus_address,
3456917a9ccSChristoph Hellwig sizeof(*base_struct), DMA_TO_DEVICE);
3466c223761SKevin Barnett out:
3476c223761SKevin Barnett kfree(base_struct_unaligned);
3486c223761SKevin Barnett
3496c223761SKevin Barnett return rc;
3506c223761SKevin Barnett }
3516c223761SKevin Barnett
352061ef06aSKevin Barnett #define SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS 30
353061ef06aSKevin Barnett
sis_wait_for_doorbell_bit_to_clear(struct pqi_ctrl_info * ctrl_info,u32 bit)354336b6819SKevin Barnett static int sis_wait_for_doorbell_bit_to_clear(
355061ef06aSKevin Barnett struct pqi_ctrl_info *ctrl_info, u32 bit)
356061ef06aSKevin Barnett {
357336b6819SKevin Barnett int rc = 0;
358061ef06aSKevin Barnett u32 doorbell_register;
359061ef06aSKevin Barnett unsigned long timeout;
360061ef06aSKevin Barnett
36142dc0426SBalsundar P timeout = (SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS * HZ) + jiffies;
362061ef06aSKevin Barnett
363061ef06aSKevin Barnett while (1) {
364061ef06aSKevin Barnett doorbell_register =
365061ef06aSKevin Barnett readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell);
366061ef06aSKevin Barnett if ((doorbell_register & bit) == 0)
367061ef06aSKevin Barnett break;
368061ef06aSKevin Barnett if (readl(&ctrl_info->registers->sis_firmware_status) &
369336b6819SKevin Barnett SIS_CTRL_KERNEL_PANIC) {
370336b6819SKevin Barnett rc = -ENODEV;
371061ef06aSKevin Barnett break;
372336b6819SKevin Barnett }
373061ef06aSKevin Barnett if (time_after(jiffies, timeout)) {
374061ef06aSKevin Barnett dev_err(&ctrl_info->pci_dev->dev,
375061ef06aSKevin Barnett "doorbell register bit 0x%x not cleared\n",
376061ef06aSKevin Barnett bit);
377336b6819SKevin Barnett rc = -ETIMEDOUT;
378061ef06aSKevin Barnett break;
379061ef06aSKevin Barnett }
380061ef06aSKevin Barnett usleep_range(1000, 2000);
381061ef06aSKevin Barnett }
382336b6819SKevin Barnett
383336b6819SKevin Barnett return rc;
384061ef06aSKevin Barnett }
385061ef06aSKevin Barnett
sis_set_doorbell_bit(struct pqi_ctrl_info * ctrl_info,u32 bit)3864f078e24SKevin Barnett static inline int sis_set_doorbell_bit(struct pqi_ctrl_info *ctrl_info, u32 bit)
3874f078e24SKevin Barnett {
3884f078e24SKevin Barnett writel(bit, &ctrl_info->registers->sis_host_to_ctrl_doorbell);
389297bdc54SMike McGowen usleep_range(1000, 2000);
3904f078e24SKevin Barnett
3914f078e24SKevin Barnett return sis_wait_for_doorbell_bit_to_clear(ctrl_info, bit);
3924f078e24SKevin Barnett }
3936c223761SKevin Barnett
sis_enable_msix(struct pqi_ctrl_info * ctrl_info)3946c223761SKevin Barnett void sis_enable_msix(struct pqi_ctrl_info *ctrl_info)
3956c223761SKevin Barnett {
3964f078e24SKevin Barnett sis_set_doorbell_bit(ctrl_info, SIS_ENABLE_MSIX);
3976c223761SKevin Barnett }
3986c223761SKevin Barnett
sis_enable_intx(struct pqi_ctrl_info * ctrl_info)399061ef06aSKevin Barnett void sis_enable_intx(struct pqi_ctrl_info *ctrl_info)
400061ef06aSKevin Barnett {
4014f078e24SKevin Barnett sis_set_doorbell_bit(ctrl_info, SIS_ENABLE_INTX);
4026c223761SKevin Barnett }
4036c223761SKevin Barnett
sis_shutdown_ctrl(struct pqi_ctrl_info * ctrl_info,enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason)4045d1f03e6SMurthy Bhat void sis_shutdown_ctrl(struct pqi_ctrl_info *ctrl_info,
4055d1f03e6SMurthy Bhat enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason)
4065b0fba0fSKevin Barnett {
40798f87667SKevin Barnett if (readl(&ctrl_info->registers->sis_firmware_status) &
40898f87667SKevin Barnett SIS_CTRL_KERNEL_PANIC)
40998f87667SKevin Barnett return;
41098f87667SKevin Barnett
4115d1f03e6SMurthy Bhat if (ctrl_info->firmware_triage_supported)
4125d1f03e6SMurthy Bhat writel(ctrl_shutdown_reason, &ctrl_info->registers->sis_ctrl_shutdown_reason_code);
4135d1f03e6SMurthy Bhat
4145d1f03e6SMurthy Bhat writel(SIS_TRIGGER_SHUTDOWN, &ctrl_info->registers->sis_host_to_ctrl_doorbell);
4155b0fba0fSKevin Barnett }
4165b0fba0fSKevin Barnett
sis_pqi_reset_quiesce(struct pqi_ctrl_info * ctrl_info)417336b6819SKevin Barnett int sis_pqi_reset_quiesce(struct pqi_ctrl_info *ctrl_info)
418336b6819SKevin Barnett {
4194f078e24SKevin Barnett return sis_set_doorbell_bit(ctrl_info, SIS_PQI_RESET_QUIESCE);
420336b6819SKevin Barnett }
421336b6819SKevin Barnett
sis_reenable_sis_mode(struct pqi_ctrl_info * ctrl_info)4226c223761SKevin Barnett int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info)
4236c223761SKevin Barnett {
4244f078e24SKevin Barnett return sis_set_doorbell_bit(ctrl_info, SIS_REENABLE_SIS_MODE);
4256c223761SKevin Barnett }
4266c223761SKevin Barnett
sis_write_driver_scratch(struct pqi_ctrl_info * ctrl_info,u32 value)427ff6abb73SKevin Barnett void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value)
428ff6abb73SKevin Barnett {
429ff6abb73SKevin Barnett writel(value, &ctrl_info->registers->sis_driver_scratch);
430297bdc54SMike McGowen usleep_range(1000, 2000);
431ff6abb73SKevin Barnett }
432ff6abb73SKevin Barnett
sis_read_driver_scratch(struct pqi_ctrl_info * ctrl_info)433ff6abb73SKevin Barnett u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info)
434ff6abb73SKevin Barnett {
435ff6abb73SKevin Barnett return readl(&ctrl_info->registers->sis_driver_scratch);
436ff6abb73SKevin Barnett }
437ff6abb73SKevin Barnett
4389ee5d6e9SMahesh Rajashekhara static inline enum sis_fw_triage_status
sis_read_firmware_triage_status(struct pqi_ctrl_info * ctrl_info)4399ee5d6e9SMahesh Rajashekhara sis_read_firmware_triage_status(struct pqi_ctrl_info *ctrl_info)
4409ee5d6e9SMahesh Rajashekhara {
4419ee5d6e9SMahesh Rajashekhara return ((enum sis_fw_triage_status)(readl(&ctrl_info->registers->sis_firmware_status) &
4429ee5d6e9SMahesh Rajashekhara SIS_CTRL_KERNEL_FW_TRIAGE));
4439ee5d6e9SMahesh Rajashekhara }
4449ee5d6e9SMahesh Rajashekhara
sis_soft_reset(struct pqi_ctrl_info * ctrl_info)4454fd22c13SMahesh Rajashekhara void sis_soft_reset(struct pqi_ctrl_info *ctrl_info)
4464fd22c13SMahesh Rajashekhara {
4474fd22c13SMahesh Rajashekhara writel(SIS_SOFT_RESET,
4484fd22c13SMahesh Rajashekhara &ctrl_info->registers->sis_host_to_ctrl_doorbell);
4494fd22c13SMahesh Rajashekhara }
4504fd22c13SMahesh Rajashekhara
4519ee5d6e9SMahesh Rajashekhara #define SIS_FW_TRIAGE_STATUS_TIMEOUT_SECS 300
4529ee5d6e9SMahesh Rajashekhara #define SIS_FW_TRIAGE_STATUS_POLL_INTERVAL_SECS 1
4539ee5d6e9SMahesh Rajashekhara
sis_wait_for_fw_triage_completion(struct pqi_ctrl_info * ctrl_info)4549ee5d6e9SMahesh Rajashekhara int sis_wait_for_fw_triage_completion(struct pqi_ctrl_info *ctrl_info)
4559ee5d6e9SMahesh Rajashekhara {
4569ee5d6e9SMahesh Rajashekhara int rc;
4579ee5d6e9SMahesh Rajashekhara enum sis_fw_triage_status status;
4589ee5d6e9SMahesh Rajashekhara unsigned long timeout;
4599ee5d6e9SMahesh Rajashekhara
46042dc0426SBalsundar P timeout = (SIS_FW_TRIAGE_STATUS_TIMEOUT_SECS * HZ) + jiffies;
4619ee5d6e9SMahesh Rajashekhara while (1) {
4629ee5d6e9SMahesh Rajashekhara status = sis_read_firmware_triage_status(ctrl_info);
4639ee5d6e9SMahesh Rajashekhara if (status == FW_TRIAGE_COND_INVALID) {
4649ee5d6e9SMahesh Rajashekhara dev_err(&ctrl_info->pci_dev->dev,
4659ee5d6e9SMahesh Rajashekhara "firmware triage condition invalid\n");
4669ee5d6e9SMahesh Rajashekhara rc = -EINVAL;
4679ee5d6e9SMahesh Rajashekhara break;
4689ee5d6e9SMahesh Rajashekhara } else if (status == FW_TRIAGE_NOT_STARTED ||
4699ee5d6e9SMahesh Rajashekhara status == FW_TRIAGE_COMPLETED) {
4709ee5d6e9SMahesh Rajashekhara rc = 0;
4719ee5d6e9SMahesh Rajashekhara break;
4729ee5d6e9SMahesh Rajashekhara }
4739ee5d6e9SMahesh Rajashekhara
4749ee5d6e9SMahesh Rajashekhara if (time_after(jiffies, timeout)) {
4759ee5d6e9SMahesh Rajashekhara dev_err(&ctrl_info->pci_dev->dev,
4769ee5d6e9SMahesh Rajashekhara "timed out waiting for firmware triage status\n");
4779ee5d6e9SMahesh Rajashekhara rc = -ETIMEDOUT;
4789ee5d6e9SMahesh Rajashekhara break;
4799ee5d6e9SMahesh Rajashekhara }
4809ee5d6e9SMahesh Rajashekhara
4819ee5d6e9SMahesh Rajashekhara ssleep(SIS_FW_TRIAGE_STATUS_POLL_INTERVAL_SECS);
4829ee5d6e9SMahesh Rajashekhara }
4839ee5d6e9SMahesh Rajashekhara
4849ee5d6e9SMahesh Rajashekhara return rc;
4859ee5d6e9SMahesh Rajashekhara }
4869ee5d6e9SMahesh Rajashekhara
sis_verify_structures(void)4875e693586SMike McGowen void sis_verify_structures(void)
4886c223761SKevin Barnett {
4896c223761SKevin Barnett BUILD_BUG_ON(offsetof(struct sis_base_struct,
4906c223761SKevin Barnett revision) != 0x0);
4916c223761SKevin Barnett BUILD_BUG_ON(offsetof(struct sis_base_struct,
4926c223761SKevin Barnett flags) != 0x4);
4936c223761SKevin Barnett BUILD_BUG_ON(offsetof(struct sis_base_struct,
4946c223761SKevin Barnett error_buffer_paddr_low) != 0x8);
4956c223761SKevin Barnett BUILD_BUG_ON(offsetof(struct sis_base_struct,
4966c223761SKevin Barnett error_buffer_paddr_high) != 0xc);
4976c223761SKevin Barnett BUILD_BUG_ON(offsetof(struct sis_base_struct,
4986c223761SKevin Barnett error_buffer_element_length) != 0x10);
4996c223761SKevin Barnett BUILD_BUG_ON(offsetof(struct sis_base_struct,
5006c223761SKevin Barnett error_buffer_num_elements) != 0x14);
5016c223761SKevin Barnett BUILD_BUG_ON(sizeof(struct sis_base_struct) != 0x18);
5026c223761SKevin Barnett }
503