162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * driver for Microchip PQI-based storage controllers 462306a36Sopenharmony_ci * Copyright (c) 2019-2023 Microchip Technology Inc. and its subsidiaries 562306a36Sopenharmony_ci * Copyright (c) 2016-2018 Microsemi Corporation 662306a36Sopenharmony_ci * Copyright (c) 2016 PMC-Sierra, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Questions/Comments/Bugfixes to storagedev@microchip.com 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/pci.h> 1662306a36Sopenharmony_ci#include <scsi/scsi_device.h> 1762306a36Sopenharmony_ci#include <asm/unaligned.h> 1862306a36Sopenharmony_ci#include "smartpqi.h" 1962306a36Sopenharmony_ci#include "smartpqi_sis.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* legacy SIS interface commands */ 2262306a36Sopenharmony_ci#define SIS_CMD_GET_ADAPTER_PROPERTIES 0x19 2362306a36Sopenharmony_ci#define SIS_CMD_INIT_BASE_STRUCT_ADDRESS 0x1b 2462306a36Sopenharmony_ci#define SIS_CMD_GET_PQI_CAPABILITIES 0x3000 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* for submission of legacy SIS commands */ 2762306a36Sopenharmony_ci#define SIS_REENABLE_SIS_MODE 0x1 2862306a36Sopenharmony_ci#define SIS_ENABLE_MSIX 0x40 2962306a36Sopenharmony_ci#define SIS_ENABLE_INTX 0x80 3062306a36Sopenharmony_ci#define SIS_SOFT_RESET 0x100 3162306a36Sopenharmony_ci#define SIS_CMD_READY 0x200 3262306a36Sopenharmony_ci#define SIS_TRIGGER_SHUTDOWN 0x800000 3362306a36Sopenharmony_ci#define SIS_PQI_RESET_QUIESCE 0x1000000 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define SIS_CMD_COMPLETE 0x1000 3662306a36Sopenharmony_ci#define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define SIS_CMD_STATUS_SUCCESS 0x1 3962306a36Sopenharmony_ci#define SIS_CMD_COMPLETE_TIMEOUT_SECS 30 4062306a36Sopenharmony_ci#define SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS 10 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* used with SIS_CMD_GET_ADAPTER_PROPERTIES command */ 4362306a36Sopenharmony_ci#define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000 4462306a36Sopenharmony_ci#define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2 4562306a36Sopenharmony_ci#define SIS_PQI_MODE_SUPPORTED 0x4 4662306a36Sopenharmony_ci#define SIS_PQI_RESET_QUIESCE_SUPPORTED 0x8 4762306a36Sopenharmony_ci#define SIS_REQUIRED_EXTENDED_PROPERTIES \ 4862306a36Sopenharmony_ci (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* used with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ 5162306a36Sopenharmony_ci#define SIS_BASE_STRUCT_REVISION 9 5262306a36Sopenharmony_ci#define SIS_BASE_STRUCT_ALIGNMENT 16 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define SIS_CTRL_KERNEL_FW_TRIAGE 0x3 5562306a36Sopenharmony_ci#define SIS_CTRL_KERNEL_UP 0x80 5662306a36Sopenharmony_ci#define SIS_CTRL_KERNEL_PANIC 0x100 5762306a36Sopenharmony_ci#define SIS_CTRL_READY_TIMEOUT_SECS 180 5862306a36Sopenharmony_ci#define SIS_CTRL_READY_RESUME_TIMEOUT_SECS 90 5962306a36Sopenharmony_ci#define SIS_CTRL_READY_POLL_INTERVAL_MSECS 10 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cienum sis_fw_triage_status { 6262306a36Sopenharmony_ci FW_TRIAGE_NOT_STARTED = 0, 6362306a36Sopenharmony_ci FW_TRIAGE_STARTED, 6462306a36Sopenharmony_ci FW_TRIAGE_COND_INVALID, 6562306a36Sopenharmony_ci FW_TRIAGE_COMPLETED 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#pragma pack(1) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* for use with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ 7162306a36Sopenharmony_cistruct sis_base_struct { 7262306a36Sopenharmony_ci __le32 revision; /* revision of this structure */ 7362306a36Sopenharmony_ci __le32 flags; /* reserved */ 7462306a36Sopenharmony_ci __le32 error_buffer_paddr_low; /* lower 32 bits of physical memory */ 7562306a36Sopenharmony_ci /* buffer for PQI error response */ 7662306a36Sopenharmony_ci /* data */ 7762306a36Sopenharmony_ci __le32 error_buffer_paddr_high; /* upper 32 bits of physical */ 7862306a36Sopenharmony_ci /* memory buffer for PQI */ 7962306a36Sopenharmony_ci /* error response data */ 8062306a36Sopenharmony_ci __le32 error_buffer_element_length; /* length of each PQI error */ 8162306a36Sopenharmony_ci /* response buffer element */ 8262306a36Sopenharmony_ci /* in bytes */ 8362306a36Sopenharmony_ci __le32 error_buffer_num_elements; /* total number of PQI error */ 8462306a36Sopenharmony_ci /* response buffers available */ 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#pragma pack() 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciunsigned int sis_ctrl_ready_timeout_secs = SIS_CTRL_READY_TIMEOUT_SECS; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info *ctrl_info, 9262306a36Sopenharmony_ci unsigned int timeout_secs) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci unsigned long timeout; 9562306a36Sopenharmony_ci u32 status; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci timeout = (timeout_secs * HZ) + jiffies; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci while (1) { 10062306a36Sopenharmony_ci status = readl(&ctrl_info->registers->sis_firmware_status); 10162306a36Sopenharmony_ci if (status != ~0) { 10262306a36Sopenharmony_ci if (status & SIS_CTRL_KERNEL_PANIC) { 10362306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 10462306a36Sopenharmony_ci "controller is offline: status code 0x%x\n", 10562306a36Sopenharmony_ci readl( 10662306a36Sopenharmony_ci &ctrl_info->registers->sis_mailbox[7])); 10762306a36Sopenharmony_ci return -ENODEV; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci if (status & SIS_CTRL_KERNEL_UP) 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 11362306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 11462306a36Sopenharmony_ci "controller not ready after %u seconds\n", 11562306a36Sopenharmony_ci timeout_secs); 11662306a36Sopenharmony_ci return -ETIMEDOUT; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci msleep(SIS_CTRL_READY_POLL_INTERVAL_MSECS); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciint sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, 12762306a36Sopenharmony_ci sis_ctrl_ready_timeout_secs); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint sis_wait_for_ctrl_ready_resume(struct pqi_ctrl_info *ctrl_info) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, 13362306a36Sopenharmony_ci SIS_CTRL_READY_RESUME_TIMEOUT_SECS); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cibool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci bool running; 13962306a36Sopenharmony_ci u32 status; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci status = readl(&ctrl_info->registers->sis_firmware_status); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (status != ~0 && (status & SIS_CTRL_KERNEL_PANIC)) 14462306a36Sopenharmony_ci running = false; 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci running = true; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!running) 14962306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 15062306a36Sopenharmony_ci "controller is offline: status code 0x%x\n", 15162306a36Sopenharmony_ci readl(&ctrl_info->registers->sis_mailbox[7])); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return running; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cibool sis_is_kernel_up(struct pqi_ctrl_info *ctrl_info) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return readl(&ctrl_info->registers->sis_firmware_status) & 15962306a36Sopenharmony_ci SIS_CTRL_KERNEL_UP; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciu32 sis_get_product_id(struct pqi_ctrl_info *ctrl_info) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci return readl(&ctrl_info->registers->sis_product_identifier); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* used for passing command parameters/results when issuing SIS commands */ 16862306a36Sopenharmony_cistruct sis_sync_cmd_params { 16962306a36Sopenharmony_ci u32 mailbox[6]; /* mailboxes 0-5 */ 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int sis_send_sync_cmd(struct pqi_ctrl_info *ctrl_info, 17362306a36Sopenharmony_ci u32 cmd, struct sis_sync_cmd_params *params) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct pqi_ctrl_registers __iomem *registers; 17662306a36Sopenharmony_ci unsigned int i; 17762306a36Sopenharmony_ci unsigned long timeout; 17862306a36Sopenharmony_ci u32 doorbell; 17962306a36Sopenharmony_ci u32 cmd_status; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci registers = ctrl_info->registers; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Write the command to mailbox 0. */ 18462306a36Sopenharmony_ci writel(cmd, ®isters->sis_mailbox[0]); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* 18762306a36Sopenharmony_ci * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used 18862306a36Sopenharmony_ci * when sending a command to the controller). 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci for (i = 1; i <= 4; i++) 19162306a36Sopenharmony_ci writel(params->mailbox[i], ®isters->sis_mailbox[i]); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Clear the command doorbell. */ 19462306a36Sopenharmony_ci writel(SIS_CLEAR_CTRL_TO_HOST_DOORBELL, 19562306a36Sopenharmony_ci ®isters->sis_ctrl_to_host_doorbell_clear); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Disable doorbell interrupts by masking all interrupts. */ 19862306a36Sopenharmony_ci writel(~0, ®isters->sis_interrupt_mask); 19962306a36Sopenharmony_ci usleep_range(1000, 2000); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Force the completion of the interrupt mask register write before 20362306a36Sopenharmony_ci * submitting the command. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci readl(®isters->sis_interrupt_mask); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Submit the command to the controller. */ 20862306a36Sopenharmony_ci writel(SIS_CMD_READY, ®isters->sis_host_to_ctrl_doorbell); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Poll for command completion. Note that the call to msleep() is at 21262306a36Sopenharmony_ci * the top of the loop in order to give the controller time to start 21362306a36Sopenharmony_ci * processing the command before we start polling. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci timeout = (SIS_CMD_COMPLETE_TIMEOUT_SECS * HZ) + jiffies; 21662306a36Sopenharmony_ci while (1) { 21762306a36Sopenharmony_ci msleep(SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS); 21862306a36Sopenharmony_ci doorbell = readl(®isters->sis_ctrl_to_host_doorbell); 21962306a36Sopenharmony_ci if (doorbell & SIS_CMD_COMPLETE) 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 22262306a36Sopenharmony_ci return -ETIMEDOUT; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Read the command status from mailbox 0. */ 22662306a36Sopenharmony_ci cmd_status = readl(®isters->sis_mailbox[0]); 22762306a36Sopenharmony_ci if (cmd_status != SIS_CMD_STATUS_SUCCESS) { 22862306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 22962306a36Sopenharmony_ci "SIS command failed for command 0x%x: status = 0x%x\n", 23062306a36Sopenharmony_ci cmd, cmd_status); 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * The command completed successfully, so save the command status and 23662306a36Sopenharmony_ci * read the values returned in mailboxes 1-5. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci params->mailbox[0] = cmd_status; 23962306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(params->mailbox); i++) 24062306a36Sopenharmony_ci params->mailbox[i] = readl(®isters->sis_mailbox[i]); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* 24662306a36Sopenharmony_ci * This function verifies that we are talking to a controller that speaks PQI. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int rc; 25262306a36Sopenharmony_ci u32 properties; 25362306a36Sopenharmony_ci u32 extended_properties; 25462306a36Sopenharmony_ci struct sis_sync_cmd_params params; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_ADAPTER_PROPERTIES, 25962306a36Sopenharmony_ci ¶ms); 26062306a36Sopenharmony_ci if (rc) 26162306a36Sopenharmony_ci return rc; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci properties = params.mailbox[1]; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!(properties & SIS_EXTENDED_PROPERTIES_SUPPORTED)) 26662306a36Sopenharmony_ci return -ENODEV; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci extended_properties = params.mailbox[4]; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if ((extended_properties & SIS_REQUIRED_EXTENDED_PROPERTIES) != 27162306a36Sopenharmony_ci SIS_REQUIRED_EXTENDED_PROPERTIES) 27262306a36Sopenharmony_ci return -ENODEV; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (extended_properties & SIS_PQI_RESET_QUIESCE_SUPPORTED) 27562306a36Sopenharmony_ci ctrl_info->pqi_reset_quiesce_supported = true; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciint sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int rc; 28362306a36Sopenharmony_ci struct sis_sync_cmd_params params; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_PQI_CAPABILITIES, 28862306a36Sopenharmony_ci ¶ms); 28962306a36Sopenharmony_ci if (rc) 29062306a36Sopenharmony_ci return rc; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ctrl_info->max_sg_entries = params.mailbox[1]; 29362306a36Sopenharmony_ci ctrl_info->max_transfer_size = params.mailbox[2]; 29462306a36Sopenharmony_ci ctrl_info->max_outstanding_requests = params.mailbox[3]; 29562306a36Sopenharmony_ci ctrl_info->config_table_offset = params.mailbox[4]; 29662306a36Sopenharmony_ci ctrl_info->config_table_length = params.mailbox[5]; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ciint sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int rc; 30462306a36Sopenharmony_ci void *base_struct_unaligned; 30562306a36Sopenharmony_ci struct sis_base_struct *base_struct; 30662306a36Sopenharmony_ci struct sis_sync_cmd_params params; 30762306a36Sopenharmony_ci unsigned long error_buffer_paddr; 30862306a36Sopenharmony_ci dma_addr_t bus_address; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci base_struct_unaligned = kzalloc(sizeof(*base_struct) 31162306a36Sopenharmony_ci + SIS_BASE_STRUCT_ALIGNMENT - 1, GFP_KERNEL); 31262306a36Sopenharmony_ci if (!base_struct_unaligned) 31362306a36Sopenharmony_ci return -ENOMEM; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci base_struct = PTR_ALIGN(base_struct_unaligned, 31662306a36Sopenharmony_ci SIS_BASE_STRUCT_ALIGNMENT); 31762306a36Sopenharmony_ci error_buffer_paddr = (unsigned long)ctrl_info->error_buffer_dma_handle; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci put_unaligned_le32(SIS_BASE_STRUCT_REVISION, &base_struct->revision); 32062306a36Sopenharmony_ci put_unaligned_le32(lower_32_bits(error_buffer_paddr), 32162306a36Sopenharmony_ci &base_struct->error_buffer_paddr_low); 32262306a36Sopenharmony_ci put_unaligned_le32(upper_32_bits(error_buffer_paddr), 32362306a36Sopenharmony_ci &base_struct->error_buffer_paddr_high); 32462306a36Sopenharmony_ci put_unaligned_le32(PQI_ERROR_BUFFER_ELEMENT_LENGTH, 32562306a36Sopenharmony_ci &base_struct->error_buffer_element_length); 32662306a36Sopenharmony_ci put_unaligned_le32(ctrl_info->max_io_slots, 32762306a36Sopenharmony_ci &base_struct->error_buffer_num_elements); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci bus_address = dma_map_single(&ctrl_info->pci_dev->dev, base_struct, 33062306a36Sopenharmony_ci sizeof(*base_struct), DMA_TO_DEVICE); 33162306a36Sopenharmony_ci if (dma_mapping_error(&ctrl_info->pci_dev->dev, bus_address)) { 33262306a36Sopenharmony_ci rc = -ENOMEM; 33362306a36Sopenharmony_ci goto out; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 33762306a36Sopenharmony_ci params.mailbox[1] = lower_32_bits((u64)bus_address); 33862306a36Sopenharmony_ci params.mailbox[2] = upper_32_bits((u64)bus_address); 33962306a36Sopenharmony_ci params.mailbox[3] = sizeof(*base_struct); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, 34262306a36Sopenharmony_ci ¶ms); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci dma_unmap_single(&ctrl_info->pci_dev->dev, bus_address, 34562306a36Sopenharmony_ci sizeof(*base_struct), DMA_TO_DEVICE); 34662306a36Sopenharmony_ciout: 34762306a36Sopenharmony_ci kfree(base_struct_unaligned); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return rc; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci#define SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS 30 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int sis_wait_for_doorbell_bit_to_clear( 35562306a36Sopenharmony_ci struct pqi_ctrl_info *ctrl_info, u32 bit) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci int rc = 0; 35862306a36Sopenharmony_ci u32 doorbell_register; 35962306a36Sopenharmony_ci unsigned long timeout; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci timeout = (SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS * HZ) + jiffies; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci while (1) { 36462306a36Sopenharmony_ci doorbell_register = 36562306a36Sopenharmony_ci readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); 36662306a36Sopenharmony_ci if ((doorbell_register & bit) == 0) 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci if (readl(&ctrl_info->registers->sis_firmware_status) & 36962306a36Sopenharmony_ci SIS_CTRL_KERNEL_PANIC) { 37062306a36Sopenharmony_ci rc = -ENODEV; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 37462306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 37562306a36Sopenharmony_ci "doorbell register bit 0x%x not cleared\n", 37662306a36Sopenharmony_ci bit); 37762306a36Sopenharmony_ci rc = -ETIMEDOUT; 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci usleep_range(1000, 2000); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return rc; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic inline int sis_set_doorbell_bit(struct pqi_ctrl_info *ctrl_info, u32 bit) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci writel(bit, &ctrl_info->registers->sis_host_to_ctrl_doorbell); 38962306a36Sopenharmony_ci usleep_range(1000, 2000); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return sis_wait_for_doorbell_bit_to_clear(ctrl_info, bit); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_civoid sis_enable_msix(struct pqi_ctrl_info *ctrl_info) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci sis_set_doorbell_bit(ctrl_info, SIS_ENABLE_MSIX); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_civoid sis_enable_intx(struct pqi_ctrl_info *ctrl_info) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci sis_set_doorbell_bit(ctrl_info, SIS_ENABLE_INTX); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_civoid sis_shutdown_ctrl(struct pqi_ctrl_info *ctrl_info, 40562306a36Sopenharmony_ci enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci if (readl(&ctrl_info->registers->sis_firmware_status) & 40862306a36Sopenharmony_ci SIS_CTRL_KERNEL_PANIC) 40962306a36Sopenharmony_ci return; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (ctrl_info->firmware_triage_supported) 41262306a36Sopenharmony_ci writel(ctrl_shutdown_reason, &ctrl_info->registers->sis_ctrl_shutdown_reason_code); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci writel(SIS_TRIGGER_SHUTDOWN, &ctrl_info->registers->sis_host_to_ctrl_doorbell); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ciint sis_pqi_reset_quiesce(struct pqi_ctrl_info *ctrl_info) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci return sis_set_doorbell_bit(ctrl_info, SIS_PQI_RESET_QUIESCE); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ciint sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci return sis_set_doorbell_bit(ctrl_info, SIS_REENABLE_SIS_MODE); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_civoid sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci writel(value, &ctrl_info->registers->sis_driver_scratch); 43062306a36Sopenharmony_ci usleep_range(1000, 2000); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciu32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci return readl(&ctrl_info->registers->sis_driver_scratch); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic inline enum sis_fw_triage_status 43962306a36Sopenharmony_ci sis_read_firmware_triage_status(struct pqi_ctrl_info *ctrl_info) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci return ((enum sis_fw_triage_status)(readl(&ctrl_info->registers->sis_firmware_status) & 44262306a36Sopenharmony_ci SIS_CTRL_KERNEL_FW_TRIAGE)); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_civoid sis_soft_reset(struct pqi_ctrl_info *ctrl_info) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci writel(SIS_SOFT_RESET, 44862306a36Sopenharmony_ci &ctrl_info->registers->sis_host_to_ctrl_doorbell); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci#define SIS_FW_TRIAGE_STATUS_TIMEOUT_SECS 300 45262306a36Sopenharmony_ci#define SIS_FW_TRIAGE_STATUS_POLL_INTERVAL_SECS 1 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint sis_wait_for_fw_triage_completion(struct pqi_ctrl_info *ctrl_info) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int rc; 45762306a36Sopenharmony_ci enum sis_fw_triage_status status; 45862306a36Sopenharmony_ci unsigned long timeout; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci timeout = (SIS_FW_TRIAGE_STATUS_TIMEOUT_SECS * HZ) + jiffies; 46162306a36Sopenharmony_ci while (1) { 46262306a36Sopenharmony_ci status = sis_read_firmware_triage_status(ctrl_info); 46362306a36Sopenharmony_ci if (status == FW_TRIAGE_COND_INVALID) { 46462306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 46562306a36Sopenharmony_ci "firmware triage condition invalid\n"); 46662306a36Sopenharmony_ci rc = -EINVAL; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci } else if (status == FW_TRIAGE_NOT_STARTED || 46962306a36Sopenharmony_ci status == FW_TRIAGE_COMPLETED) { 47062306a36Sopenharmony_ci rc = 0; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 47562306a36Sopenharmony_ci dev_err(&ctrl_info->pci_dev->dev, 47662306a36Sopenharmony_ci "timed out waiting for firmware triage status\n"); 47762306a36Sopenharmony_ci rc = -ETIMEDOUT; 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ssleep(SIS_FW_TRIAGE_STATUS_POLL_INTERVAL_SECS); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return rc; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_civoid sis_verify_structures(void) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sis_base_struct, 49062306a36Sopenharmony_ci revision) != 0x0); 49162306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sis_base_struct, 49262306a36Sopenharmony_ci flags) != 0x4); 49362306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sis_base_struct, 49462306a36Sopenharmony_ci error_buffer_paddr_low) != 0x8); 49562306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sis_base_struct, 49662306a36Sopenharmony_ci error_buffer_paddr_high) != 0xc); 49762306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sis_base_struct, 49862306a36Sopenharmony_ci error_buffer_element_length) != 0x10); 49962306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct sis_base_struct, 50062306a36Sopenharmony_ci error_buffer_num_elements) != 0x14); 50162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct sis_base_struct) != 0x18); 50262306a36Sopenharmony_ci} 503