xref: /openbmc/linux/drivers/scsi/smartpqi/smartpqi_sis.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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, &registers->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], &registers->sis_mailbox[i]);
1926c223761SKevin Barnett 
1936c223761SKevin Barnett 	/* Clear the command doorbell. */
1946c223761SKevin Barnett 	writel(SIS_CLEAR_CTRL_TO_HOST_DOORBELL,
1956c223761SKevin Barnett 		&registers->sis_ctrl_to_host_doorbell_clear);
1966c223761SKevin Barnett 
1976c223761SKevin Barnett 	/* Disable doorbell interrupts by masking all interrupts. */
1986c223761SKevin Barnett 	writel(~0, &registers->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(&registers->sis_interrupt_mask);
2066c223761SKevin Barnett 
2076c223761SKevin Barnett 	/* Submit the command to the controller. */
2086c223761SKevin Barnett 	writel(SIS_CMD_READY, &registers->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(&registers->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(&registers->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(&registers->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(&params, 0, sizeof(params));
2576c223761SKevin Barnett 
2586c223761SKevin Barnett 	rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_ADAPTER_PROPERTIES,
2596c223761SKevin Barnett 		&params);
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(&params, 0, sizeof(params));
2866c223761SKevin Barnett 
2876c223761SKevin Barnett 	rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_PQI_CAPABILITIES,
2886c223761SKevin Barnett 		&params);
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(&params, 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 		&params);
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