18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    driver for Microsemi PQI-based storage controllers
48c2ecf20Sopenharmony_ci *    Copyright (c) 2019-2020 Microchip Technology Inc. and its subsidiaries
58c2ecf20Sopenharmony_ci *    Copyright (c) 2016-2018 Microsemi Corporation
68c2ecf20Sopenharmony_ci *    Copyright (c) 2016 PMC-Sierra, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *    Questions/Comments/Bugfixes to storagedev@microchip.com
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/rtc.h>
198c2ecf20Sopenharmony_ci#include <linux/bcd.h>
208c2ecf20Sopenharmony_ci#include <linux/reboot.h>
218c2ecf20Sopenharmony_ci#include <linux/cciss_ioctl.h>
228c2ecf20Sopenharmony_ci#include <linux/blk-mq-pci.h>
238c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
248c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
258c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
268c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h>
278c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_sas.h>
288c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
298c2ecf20Sopenharmony_ci#include "smartpqi.h"
308c2ecf20Sopenharmony_ci#include "smartpqi_sis.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#if !defined(BUILD_TIMESTAMP)
338c2ecf20Sopenharmony_ci#define BUILD_TIMESTAMP
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define DRIVER_VERSION		"1.2.16-010"
378c2ecf20Sopenharmony_ci#define DRIVER_MAJOR		1
388c2ecf20Sopenharmony_ci#define DRIVER_MINOR		2
398c2ecf20Sopenharmony_ci#define DRIVER_RELEASE		16
408c2ecf20Sopenharmony_ci#define DRIVER_REVISION		10
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define DRIVER_NAME		"Microsemi PQI Driver (v" \
438c2ecf20Sopenharmony_ci				DRIVER_VERSION BUILD_TIMESTAMP ")"
448c2ecf20Sopenharmony_ci#define DRIVER_NAME_SHORT	"smartpqi"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define PQI_EXTRA_SGL_MEMORY	(12 * sizeof(struct pqi_sg_descriptor))
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Microsemi");
498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Microsemi Smart Family Controller version "
508c2ecf20Sopenharmony_ci	DRIVER_VERSION);
518c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Microsemi Smart Family Controllers");
528c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info);
568c2ecf20Sopenharmony_cistatic void pqi_ctrl_offline_worker(struct work_struct *work);
578c2ecf20Sopenharmony_cistatic void pqi_retry_raid_bypass_requests(struct pqi_ctrl_info *ctrl_info);
588c2ecf20Sopenharmony_cistatic int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info);
598c2ecf20Sopenharmony_cistatic void pqi_scan_start(struct Scsi_Host *shost);
608c2ecf20Sopenharmony_cistatic void pqi_start_io(struct pqi_ctrl_info *ctrl_info,
618c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group, enum pqi_io_path path,
628c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request);
638c2ecf20Sopenharmony_cistatic int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info,
648c2ecf20Sopenharmony_ci	struct pqi_iu_header *request, unsigned int flags,
658c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info, unsigned long timeout_msecs);
668c2ecf20Sopenharmony_cistatic int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info,
678c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb,
688c2ecf20Sopenharmony_ci	unsigned int cdb_length, struct pqi_queue_group *queue_group,
698c2ecf20Sopenharmony_ci	struct pqi_encryption_info *encryption_info, bool raid_bypass);
708c2ecf20Sopenharmony_cistatic void pqi_ofa_ctrl_quiesce(struct pqi_ctrl_info *ctrl_info);
718c2ecf20Sopenharmony_cistatic void pqi_ofa_ctrl_unquiesce(struct pqi_ctrl_info *ctrl_info);
728c2ecf20Sopenharmony_cistatic int pqi_ofa_ctrl_restart(struct pqi_ctrl_info *ctrl_info);
738c2ecf20Sopenharmony_cistatic void pqi_ofa_setup_host_buffer(struct pqi_ctrl_info *ctrl_info,
748c2ecf20Sopenharmony_ci	u32 bytes_requested);
758c2ecf20Sopenharmony_cistatic void pqi_ofa_free_host_buffer(struct pqi_ctrl_info *ctrl_info);
768c2ecf20Sopenharmony_cistatic int pqi_ofa_host_memory_update(struct pqi_ctrl_info *ctrl_info);
778c2ecf20Sopenharmony_cistatic int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info,
788c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, unsigned long timeout_secs);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* for flags argument to pqi_submit_raid_request_synchronous() */
818c2ecf20Sopenharmony_ci#define PQI_SYNC_FLAGS_INTERRUPTABLE	0x1
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct scsi_transport_template *pqi_sas_transport_template;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic atomic_t pqi_controller_count = ATOMIC_INIT(0);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cienum pqi_lockup_action {
888c2ecf20Sopenharmony_ci	NONE,
898c2ecf20Sopenharmony_ci	REBOOT,
908c2ecf20Sopenharmony_ci	PANIC
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic enum pqi_lockup_action pqi_lockup_action = NONE;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic struct {
968c2ecf20Sopenharmony_ci	enum pqi_lockup_action	action;
978c2ecf20Sopenharmony_ci	char			*name;
988c2ecf20Sopenharmony_ci} pqi_lockup_actions[] = {
998c2ecf20Sopenharmony_ci	{
1008c2ecf20Sopenharmony_ci		.action = NONE,
1018c2ecf20Sopenharmony_ci		.name = "none",
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci	{
1048c2ecf20Sopenharmony_ci		.action = REBOOT,
1058c2ecf20Sopenharmony_ci		.name = "reboot",
1068c2ecf20Sopenharmony_ci	},
1078c2ecf20Sopenharmony_ci	{
1088c2ecf20Sopenharmony_ci		.action = PANIC,
1098c2ecf20Sopenharmony_ci		.name = "panic",
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic unsigned int pqi_supported_event_types[] = {
1148c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_HOTPLUG,
1158c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_HARDWARE,
1168c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_PHYSICAL_DEVICE,
1178c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_LOGICAL_DEVICE,
1188c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_OFA,
1198c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_AIO_STATE_CHANGE,
1208c2ecf20Sopenharmony_ci	PQI_EVENT_TYPE_AIO_CONFIG_CHANGE,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int pqi_disable_device_id_wildcards;
1248c2ecf20Sopenharmony_cimodule_param_named(disable_device_id_wildcards,
1258c2ecf20Sopenharmony_ci	pqi_disable_device_id_wildcards, int, 0644);
1268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_device_id_wildcards,
1278c2ecf20Sopenharmony_ci	"Disable device ID wildcards.");
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int pqi_disable_heartbeat;
1308c2ecf20Sopenharmony_cimodule_param_named(disable_heartbeat,
1318c2ecf20Sopenharmony_ci	pqi_disable_heartbeat, int, 0644);
1328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_heartbeat,
1338c2ecf20Sopenharmony_ci	"Disable heartbeat.");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int pqi_disable_ctrl_shutdown;
1368c2ecf20Sopenharmony_cimodule_param_named(disable_ctrl_shutdown,
1378c2ecf20Sopenharmony_ci	pqi_disable_ctrl_shutdown, int, 0644);
1388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_ctrl_shutdown,
1398c2ecf20Sopenharmony_ci	"Disable controller shutdown when controller locked up.");
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic char *pqi_lockup_action_param;
1428c2ecf20Sopenharmony_cimodule_param_named(lockup_action,
1438c2ecf20Sopenharmony_ci	pqi_lockup_action_param, charp, 0644);
1448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(lockup_action, "Action to take when controller locked up.\n"
1458c2ecf20Sopenharmony_ci	"\t\tSupported: none, reboot, panic\n"
1468c2ecf20Sopenharmony_ci	"\t\tDefault: none");
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int pqi_expose_ld_first;
1498c2ecf20Sopenharmony_cimodule_param_named(expose_ld_first,
1508c2ecf20Sopenharmony_ci	pqi_expose_ld_first, int, 0644);
1518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(expose_ld_first,
1528c2ecf20Sopenharmony_ci	"Expose logical drives before physical drives.");
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int pqi_hide_vsep;
1558c2ecf20Sopenharmony_cimodule_param_named(hide_vsep,
1568c2ecf20Sopenharmony_ci	pqi_hide_vsep, int, 0644);
1578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hide_vsep,
1588c2ecf20Sopenharmony_ci	"Hide the virtual SEP for direct attached drives.");
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic char *raid_levels[] = {
1618c2ecf20Sopenharmony_ci	"RAID-0",
1628c2ecf20Sopenharmony_ci	"RAID-4",
1638c2ecf20Sopenharmony_ci	"RAID-1(1+0)",
1648c2ecf20Sopenharmony_ci	"RAID-5",
1658c2ecf20Sopenharmony_ci	"RAID-5+1",
1668c2ecf20Sopenharmony_ci	"RAID-ADG",
1678c2ecf20Sopenharmony_ci	"RAID-1(ADM)",
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic char *pqi_raid_level_to_string(u8 raid_level)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	if (raid_level < ARRAY_SIZE(raid_levels))
1738c2ecf20Sopenharmony_ci		return raid_levels[raid_level];
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return "RAID UNKNOWN";
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci#define SA_RAID_0		0
1798c2ecf20Sopenharmony_ci#define SA_RAID_4		1
1808c2ecf20Sopenharmony_ci#define SA_RAID_1		2	/* also used for RAID 10 */
1818c2ecf20Sopenharmony_ci#define SA_RAID_5		3	/* also used for RAID 50 */
1828c2ecf20Sopenharmony_ci#define SA_RAID_51		4
1838c2ecf20Sopenharmony_ci#define SA_RAID_6		5	/* also used for RAID 60 */
1848c2ecf20Sopenharmony_ci#define SA_RAID_ADM		6	/* also used for RAID 1+0 ADM */
1858c2ecf20Sopenharmony_ci#define SA_RAID_MAX		SA_RAID_ADM
1868c2ecf20Sopenharmony_ci#define SA_RAID_UNKNOWN		0xff
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic inline void pqi_scsi_done(struct scsi_cmnd *scmd)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	pqi_prep_for_scsi_done(scmd);
1918c2ecf20Sopenharmony_ci	scmd->scsi_done(scmd);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic inline void pqi_disable_write_same(struct scsi_device *sdev)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	sdev->no_write_same = 1;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic inline bool pqi_scsi3addr_equal(u8 *scsi3addr1, u8 *scsi3addr2)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	return memcmp(scsi3addr1, scsi3addr2, 8) == 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic inline bool pqi_is_logical_device(struct pqi_scsi_dev *device)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return !device->is_physical_device;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic inline bool pqi_is_external_raid_addr(u8 *scsi3addr)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	return scsi3addr[2] != 0;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic inline bool pqi_ctrl_offline(struct pqi_ctrl_info *ctrl_info)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return !ctrl_info->controller_online;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic inline void pqi_check_ctrl_health(struct pqi_ctrl_info *ctrl_info)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	if (ctrl_info->controller_online)
2228c2ecf20Sopenharmony_ci		if (!sis_is_firmware_running(ctrl_info))
2238c2ecf20Sopenharmony_ci			pqi_take_ctrl_offline(ctrl_info);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic inline bool pqi_is_hba_lunid(u8 *scsi3addr)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	return pqi_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic inline enum pqi_ctrl_mode pqi_get_ctrl_mode(
2328c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	return sis_read_driver_scratch(ctrl_info);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic inline void pqi_save_ctrl_mode(struct pqi_ctrl_info *ctrl_info,
2388c2ecf20Sopenharmony_ci	enum pqi_ctrl_mode mode)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	sis_write_driver_scratch(ctrl_info, mode);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_block_device_reset(struct pqi_ctrl_info *ctrl_info)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	ctrl_info->block_device_reset = true;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic inline bool pqi_device_reset_blocked(struct pqi_ctrl_info *ctrl_info)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	return ctrl_info->block_device_reset;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic inline bool pqi_ctrl_blocked(struct pqi_ctrl_info *ctrl_info)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	return ctrl_info->block_requests;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_block_requests(struct pqi_ctrl_info *ctrl_info)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	ctrl_info->block_requests = true;
2618c2ecf20Sopenharmony_ci	scsi_block_requests(ctrl_info->scsi_host);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_unblock_requests(struct pqi_ctrl_info *ctrl_info)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	ctrl_info->block_requests = false;
2678c2ecf20Sopenharmony_ci	wake_up_all(&ctrl_info->block_requests_wait);
2688c2ecf20Sopenharmony_ci	pqi_retry_raid_bypass_requests(ctrl_info);
2698c2ecf20Sopenharmony_ci	scsi_unblock_requests(ctrl_info->scsi_host);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic unsigned long pqi_wait_if_ctrl_blocked(struct pqi_ctrl_info *ctrl_info,
2738c2ecf20Sopenharmony_ci	unsigned long timeout_msecs)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	unsigned long remaining_msecs;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!pqi_ctrl_blocked(ctrl_info))
2788c2ecf20Sopenharmony_ci		return timeout_msecs;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	atomic_inc(&ctrl_info->num_blocked_threads);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (timeout_msecs == NO_TIMEOUT) {
2838c2ecf20Sopenharmony_ci		wait_event(ctrl_info->block_requests_wait,
2848c2ecf20Sopenharmony_ci			!pqi_ctrl_blocked(ctrl_info));
2858c2ecf20Sopenharmony_ci		remaining_msecs = timeout_msecs;
2868c2ecf20Sopenharmony_ci	} else {
2878c2ecf20Sopenharmony_ci		unsigned long remaining_jiffies;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		remaining_jiffies =
2908c2ecf20Sopenharmony_ci			wait_event_timeout(ctrl_info->block_requests_wait,
2918c2ecf20Sopenharmony_ci				!pqi_ctrl_blocked(ctrl_info),
2928c2ecf20Sopenharmony_ci				msecs_to_jiffies(timeout_msecs));
2938c2ecf20Sopenharmony_ci		remaining_msecs = jiffies_to_msecs(remaining_jiffies);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	atomic_dec(&ctrl_info->num_blocked_threads);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return remaining_msecs;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_wait_until_quiesced(struct pqi_ctrl_info *ctrl_info)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	while (atomic_read(&ctrl_info->num_busy_threads) >
3048c2ecf20Sopenharmony_ci		atomic_read(&ctrl_info->num_blocked_threads))
3058c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic inline bool pqi_device_offline(struct pqi_scsi_dev *device)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	return device->device_offline;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic inline void pqi_device_reset_start(struct pqi_scsi_dev *device)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	device->in_reset = true;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic inline void pqi_device_reset_done(struct pqi_scsi_dev *device)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	device->in_reset = false;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic inline bool pqi_device_in_reset(struct pqi_scsi_dev *device)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	return device->in_reset;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_ofa_start(struct pqi_ctrl_info *ctrl_info)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	ctrl_info->in_ofa = true;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_ofa_done(struct pqi_ctrl_info *ctrl_info)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	ctrl_info->in_ofa = false;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic inline bool pqi_ctrl_in_ofa(struct pqi_ctrl_info *ctrl_info)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	return ctrl_info->in_ofa;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic inline void pqi_device_remove_start(struct pqi_scsi_dev *device)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	device->in_remove = true;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic inline bool pqi_device_in_remove(struct pqi_ctrl_info *ctrl_info,
3498c2ecf20Sopenharmony_ci					struct pqi_scsi_dev *device)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	return device->in_remove && !ctrl_info->in_shutdown;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic inline void pqi_ctrl_shutdown_start(struct pqi_ctrl_info *ctrl_info)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	ctrl_info->in_shutdown = true;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic inline bool pqi_ctrl_in_shutdown(struct pqi_ctrl_info *ctrl_info)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	return ctrl_info->in_shutdown;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic inline void pqi_schedule_rescan_worker_with_delay(
3658c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info, unsigned long delay)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
3688c2ecf20Sopenharmony_ci		return;
3698c2ecf20Sopenharmony_ci	if (pqi_ctrl_in_ofa(ctrl_info))
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	schedule_delayed_work(&ctrl_info->rescan_work, delay);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic inline void pqi_schedule_rescan_worker(struct pqi_ctrl_info *ctrl_info)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	pqi_schedule_rescan_worker_with_delay(ctrl_info, 0);
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci#define PQI_RESCAN_WORK_DELAY	(10 * PQI_HZ)
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic inline void pqi_schedule_rescan_worker_delayed(
3838c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	pqi_schedule_rescan_worker_with_delay(ctrl_info, PQI_RESCAN_WORK_DELAY);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic inline void pqi_cancel_rescan_worker(struct pqi_ctrl_info *ctrl_info)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&ctrl_info->rescan_work);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic inline void pqi_cancel_event_worker(struct pqi_ctrl_info *ctrl_info)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	cancel_work_sync(&ctrl_info->event_work);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic inline u32 pqi_read_heartbeat_counter(struct pqi_ctrl_info *ctrl_info)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	if (!ctrl_info->heartbeat_counter)
4018c2ecf20Sopenharmony_ci		return 0;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return readl(ctrl_info->heartbeat_counter);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic inline u8 pqi_read_soft_reset_status(struct pqi_ctrl_info *ctrl_info)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	if (!ctrl_info->soft_reset_status)
4098c2ecf20Sopenharmony_ci		return 0;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return readb(ctrl_info->soft_reset_status);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic inline void pqi_clear_soft_reset_status(struct pqi_ctrl_info *ctrl_info,
4158c2ecf20Sopenharmony_ci	u8 clear)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	u8 status;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (!ctrl_info->soft_reset_status)
4208c2ecf20Sopenharmony_ci		return;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	status = pqi_read_soft_reset_status(ctrl_info);
4238c2ecf20Sopenharmony_ci	status &= ~clear;
4248c2ecf20Sopenharmony_ci	writeb(status, ctrl_info->soft_reset_status);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int pqi_map_single(struct pci_dev *pci_dev,
4288c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *sg_descriptor, void *buffer,
4298c2ecf20Sopenharmony_ci	size_t buffer_length, enum dma_data_direction data_direction)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	dma_addr_t bus_address;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (!buffer || buffer_length == 0 || data_direction == DMA_NONE)
4348c2ecf20Sopenharmony_ci		return 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	bus_address = dma_map_single(&pci_dev->dev, buffer, buffer_length,
4378c2ecf20Sopenharmony_ci		data_direction);
4388c2ecf20Sopenharmony_ci	if (dma_mapping_error(&pci_dev->dev, bus_address))
4398c2ecf20Sopenharmony_ci		return -ENOMEM;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)bus_address, &sg_descriptor->address);
4428c2ecf20Sopenharmony_ci	put_unaligned_le32(buffer_length, &sg_descriptor->length);
4438c2ecf20Sopenharmony_ci	put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return 0;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void pqi_pci_unmap(struct pci_dev *pci_dev,
4498c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *descriptors, int num_descriptors,
4508c2ecf20Sopenharmony_ci	enum dma_data_direction data_direction)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	int i;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (data_direction == DMA_NONE)
4558c2ecf20Sopenharmony_ci		return;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	for (i = 0; i < num_descriptors; i++)
4588c2ecf20Sopenharmony_ci		dma_unmap_single(&pci_dev->dev,
4598c2ecf20Sopenharmony_ci			(dma_addr_t)get_unaligned_le64(&descriptors[i].address),
4608c2ecf20Sopenharmony_ci			get_unaligned_le32(&descriptors[i].length),
4618c2ecf20Sopenharmony_ci			data_direction);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int pqi_build_raid_path_request(struct pqi_ctrl_info *ctrl_info,
4658c2ecf20Sopenharmony_ci	struct pqi_raid_path_request *request, u8 cmd,
4668c2ecf20Sopenharmony_ci	u8 *scsi3addr, void *buffer, size_t buffer_length,
4678c2ecf20Sopenharmony_ci	u16 vpd_page, enum dma_data_direction *dir)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	u8 *cdb;
4708c2ecf20Sopenharmony_ci	size_t cdb_length = buffer_length;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	memset(request, 0, sizeof(*request));
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
4758c2ecf20Sopenharmony_ci	put_unaligned_le16(offsetof(struct pqi_raid_path_request,
4768c2ecf20Sopenharmony_ci		sg_descriptors[1]) - PQI_REQUEST_HEADER_LENGTH,
4778c2ecf20Sopenharmony_ci		&request->header.iu_length);
4788c2ecf20Sopenharmony_ci	put_unaligned_le32(buffer_length, &request->buffer_length);
4798c2ecf20Sopenharmony_ci	memcpy(request->lun_number, scsi3addr, sizeof(request->lun_number));
4808c2ecf20Sopenharmony_ci	request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
4818c2ecf20Sopenharmony_ci	request->additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	cdb = request->cdb;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	switch (cmd) {
4868c2ecf20Sopenharmony_ci	case INQUIRY:
4878c2ecf20Sopenharmony_ci		request->data_direction = SOP_READ_FLAG;
4888c2ecf20Sopenharmony_ci		cdb[0] = INQUIRY;
4898c2ecf20Sopenharmony_ci		if (vpd_page & VPD_PAGE) {
4908c2ecf20Sopenharmony_ci			cdb[1] = 0x1;
4918c2ecf20Sopenharmony_ci			cdb[2] = (u8)vpd_page;
4928c2ecf20Sopenharmony_ci		}
4938c2ecf20Sopenharmony_ci		cdb[4] = (u8)cdb_length;
4948c2ecf20Sopenharmony_ci		break;
4958c2ecf20Sopenharmony_ci	case CISS_REPORT_LOG:
4968c2ecf20Sopenharmony_ci	case CISS_REPORT_PHYS:
4978c2ecf20Sopenharmony_ci		request->data_direction = SOP_READ_FLAG;
4988c2ecf20Sopenharmony_ci		cdb[0] = cmd;
4998c2ecf20Sopenharmony_ci		if (cmd == CISS_REPORT_PHYS)
5008c2ecf20Sopenharmony_ci			cdb[1] = CISS_REPORT_PHYS_FLAG_OTHER;
5018c2ecf20Sopenharmony_ci		else
5028c2ecf20Sopenharmony_ci			cdb[1] = CISS_REPORT_LOG_FLAG_UNIQUE_LUN_ID;
5038c2ecf20Sopenharmony_ci		put_unaligned_be32(cdb_length, &cdb[6]);
5048c2ecf20Sopenharmony_ci		break;
5058c2ecf20Sopenharmony_ci	case CISS_GET_RAID_MAP:
5068c2ecf20Sopenharmony_ci		request->data_direction = SOP_READ_FLAG;
5078c2ecf20Sopenharmony_ci		cdb[0] = CISS_READ;
5088c2ecf20Sopenharmony_ci		cdb[1] = CISS_GET_RAID_MAP;
5098c2ecf20Sopenharmony_ci		put_unaligned_be32(cdb_length, &cdb[6]);
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	case SA_FLUSH_CACHE:
5128c2ecf20Sopenharmony_ci		request->data_direction = SOP_WRITE_FLAG;
5138c2ecf20Sopenharmony_ci		cdb[0] = BMIC_WRITE;
5148c2ecf20Sopenharmony_ci		cdb[6] = BMIC_FLUSH_CACHE;
5158c2ecf20Sopenharmony_ci		put_unaligned_be16(cdb_length, &cdb[7]);
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci	case BMIC_SENSE_DIAG_OPTIONS:
5188c2ecf20Sopenharmony_ci		cdb_length = 0;
5198c2ecf20Sopenharmony_ci		fallthrough;
5208c2ecf20Sopenharmony_ci	case BMIC_IDENTIFY_CONTROLLER:
5218c2ecf20Sopenharmony_ci	case BMIC_IDENTIFY_PHYSICAL_DEVICE:
5228c2ecf20Sopenharmony_ci	case BMIC_SENSE_SUBSYSTEM_INFORMATION:
5238c2ecf20Sopenharmony_ci		request->data_direction = SOP_READ_FLAG;
5248c2ecf20Sopenharmony_ci		cdb[0] = BMIC_READ;
5258c2ecf20Sopenharmony_ci		cdb[6] = cmd;
5268c2ecf20Sopenharmony_ci		put_unaligned_be16(cdb_length, &cdb[7]);
5278c2ecf20Sopenharmony_ci		break;
5288c2ecf20Sopenharmony_ci	case BMIC_SET_DIAG_OPTIONS:
5298c2ecf20Sopenharmony_ci		cdb_length = 0;
5308c2ecf20Sopenharmony_ci		fallthrough;
5318c2ecf20Sopenharmony_ci	case BMIC_WRITE_HOST_WELLNESS:
5328c2ecf20Sopenharmony_ci		request->data_direction = SOP_WRITE_FLAG;
5338c2ecf20Sopenharmony_ci		cdb[0] = BMIC_WRITE;
5348c2ecf20Sopenharmony_ci		cdb[6] = cmd;
5358c2ecf20Sopenharmony_ci		put_unaligned_be16(cdb_length, &cdb[7]);
5368c2ecf20Sopenharmony_ci		break;
5378c2ecf20Sopenharmony_ci	case BMIC_CSMI_PASSTHRU:
5388c2ecf20Sopenharmony_ci		request->data_direction = SOP_BIDIRECTIONAL;
5398c2ecf20Sopenharmony_ci		cdb[0] = BMIC_WRITE;
5408c2ecf20Sopenharmony_ci		cdb[5] = CSMI_CC_SAS_SMP_PASSTHRU;
5418c2ecf20Sopenharmony_ci		cdb[6] = cmd;
5428c2ecf20Sopenharmony_ci		put_unaligned_be16(cdb_length, &cdb[7]);
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci	default:
5458c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev, "unknown command 0x%c\n", cmd);
5468c2ecf20Sopenharmony_ci		break;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	switch (request->data_direction) {
5508c2ecf20Sopenharmony_ci	case SOP_READ_FLAG:
5518c2ecf20Sopenharmony_ci		*dir = DMA_FROM_DEVICE;
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci	case SOP_WRITE_FLAG:
5548c2ecf20Sopenharmony_ci		*dir = DMA_TO_DEVICE;
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	case SOP_NO_DIRECTION_FLAG:
5578c2ecf20Sopenharmony_ci		*dir = DMA_NONE;
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci	default:
5608c2ecf20Sopenharmony_ci		*dir = DMA_BIDIRECTIONAL;
5618c2ecf20Sopenharmony_ci		break;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return pqi_map_single(ctrl_info->pci_dev, &request->sg_descriptors[0],
5658c2ecf20Sopenharmony_ci		buffer, buffer_length, *dir);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic inline void pqi_reinit_io_request(struct pqi_io_request *io_request)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	io_request->scmd = NULL;
5718c2ecf20Sopenharmony_ci	io_request->status = 0;
5728c2ecf20Sopenharmony_ci	io_request->error_info = NULL;
5738c2ecf20Sopenharmony_ci	io_request->raid_bypass = false;
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic struct pqi_io_request *pqi_alloc_io_request(
5778c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
5808c2ecf20Sopenharmony_ci	u16 i = ctrl_info->next_io_request_slot;	/* benignly racy */
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	while (1) {
5838c2ecf20Sopenharmony_ci		io_request = &ctrl_info->io_request_pool[i];
5848c2ecf20Sopenharmony_ci		if (atomic_inc_return(&io_request->refcount) == 1)
5858c2ecf20Sopenharmony_ci			break;
5868c2ecf20Sopenharmony_ci		atomic_dec(&io_request->refcount);
5878c2ecf20Sopenharmony_ci		i = (i + 1) % ctrl_info->max_io_slots;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* benignly racy */
5918c2ecf20Sopenharmony_ci	ctrl_info->next_io_request_slot = (i + 1) % ctrl_info->max_io_slots;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	pqi_reinit_io_request(io_request);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return io_request;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic void pqi_free_io_request(struct pqi_io_request *io_request)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	atomic_dec(&io_request->refcount);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic int pqi_send_scsi_raid_request(struct pqi_ctrl_info *ctrl_info, u8 cmd,
6048c2ecf20Sopenharmony_ci	u8 *scsi3addr, void *buffer, size_t buffer_length, u16 vpd_page,
6058c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info,	unsigned long timeout_msecs)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	int rc;
6088c2ecf20Sopenharmony_ci	struct pqi_raid_path_request request;
6098c2ecf20Sopenharmony_ci	enum dma_data_direction dir;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	rc = pqi_build_raid_path_request(ctrl_info, &request,
6128c2ecf20Sopenharmony_ci		cmd, scsi3addr, buffer,
6138c2ecf20Sopenharmony_ci		buffer_length, vpd_page, &dir);
6148c2ecf20Sopenharmony_ci	if (rc)
6158c2ecf20Sopenharmony_ci		return rc;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0,
6188c2ecf20Sopenharmony_ci		error_info, timeout_msecs);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, dir);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return rc;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci/* helper functions for pqi_send_scsi_raid_request */
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic inline int pqi_send_ctrl_raid_request(struct pqi_ctrl_info *ctrl_info,
6288c2ecf20Sopenharmony_ci	u8 cmd, void *buffer, size_t buffer_length)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	return pqi_send_scsi_raid_request(ctrl_info, cmd, RAID_CTLR_LUNID,
6318c2ecf20Sopenharmony_ci		buffer, buffer_length, 0, NULL, NO_TIMEOUT);
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic inline int pqi_send_ctrl_raid_with_error(struct pqi_ctrl_info *ctrl_info,
6358c2ecf20Sopenharmony_ci	u8 cmd, void *buffer, size_t buffer_length,
6368c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	return pqi_send_scsi_raid_request(ctrl_info, cmd, RAID_CTLR_LUNID,
6398c2ecf20Sopenharmony_ci		buffer, buffer_length, 0, error_info, NO_TIMEOUT);
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic inline int pqi_identify_controller(struct pqi_ctrl_info *ctrl_info,
6438c2ecf20Sopenharmony_ci	struct bmic_identify_controller *buffer)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	return pqi_send_ctrl_raid_request(ctrl_info, BMIC_IDENTIFY_CONTROLLER,
6468c2ecf20Sopenharmony_ci		buffer, sizeof(*buffer));
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic inline int pqi_sense_subsystem_info(struct  pqi_ctrl_info *ctrl_info,
6508c2ecf20Sopenharmony_ci	struct bmic_sense_subsystem_info *sense_info)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	return pqi_send_ctrl_raid_request(ctrl_info,
6538c2ecf20Sopenharmony_ci		BMIC_SENSE_SUBSYSTEM_INFORMATION, sense_info,
6548c2ecf20Sopenharmony_ci		sizeof(*sense_info));
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic inline int pqi_scsi_inquiry(struct pqi_ctrl_info *ctrl_info,
6588c2ecf20Sopenharmony_ci	u8 *scsi3addr, u16 vpd_page, void *buffer, size_t buffer_length)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	return pqi_send_scsi_raid_request(ctrl_info, INQUIRY, scsi3addr,
6618c2ecf20Sopenharmony_ci		buffer, buffer_length, vpd_page, NULL, NO_TIMEOUT);
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic int pqi_identify_physical_device(struct pqi_ctrl_info *ctrl_info,
6658c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device,
6668c2ecf20Sopenharmony_ci	struct bmic_identify_physical_device *buffer, size_t buffer_length)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	int rc;
6698c2ecf20Sopenharmony_ci	enum dma_data_direction dir;
6708c2ecf20Sopenharmony_ci	u16 bmic_device_index;
6718c2ecf20Sopenharmony_ci	struct pqi_raid_path_request request;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	rc = pqi_build_raid_path_request(ctrl_info, &request,
6748c2ecf20Sopenharmony_ci		BMIC_IDENTIFY_PHYSICAL_DEVICE, RAID_CTLR_LUNID, buffer,
6758c2ecf20Sopenharmony_ci		buffer_length, 0, &dir);
6768c2ecf20Sopenharmony_ci	if (rc)
6778c2ecf20Sopenharmony_ci		return rc;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	bmic_device_index = CISS_GET_DRIVE_NUMBER(device->scsi3addr);
6808c2ecf20Sopenharmony_ci	request.cdb[2] = (u8)bmic_device_index;
6818c2ecf20Sopenharmony_ci	request.cdb[9] = (u8)(bmic_device_index >> 8);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
6848c2ecf20Sopenharmony_ci		0, NULL, NO_TIMEOUT);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, dir);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	return rc;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic int pqi_flush_cache(struct pqi_ctrl_info *ctrl_info,
6928c2ecf20Sopenharmony_ci	enum bmic_flush_cache_shutdown_event shutdown_event)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	int rc;
6958c2ecf20Sopenharmony_ci	struct bmic_flush_cache *flush_cache;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/*
6988c2ecf20Sopenharmony_ci	 * Don't bother trying to flush the cache if the controller is
6998c2ecf20Sopenharmony_ci	 * locked up.
7008c2ecf20Sopenharmony_ci	 */
7018c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
7028c2ecf20Sopenharmony_ci		return -ENXIO;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	flush_cache = kzalloc(sizeof(*flush_cache), GFP_KERNEL);
7058c2ecf20Sopenharmony_ci	if (!flush_cache)
7068c2ecf20Sopenharmony_ci		return -ENOMEM;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	flush_cache->shutdown_event = shutdown_event;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	rc = pqi_send_ctrl_raid_request(ctrl_info, SA_FLUSH_CACHE, flush_cache,
7118c2ecf20Sopenharmony_ci		sizeof(*flush_cache));
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	kfree(flush_cache);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return rc;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ciint pqi_csmi_smp_passthru(struct pqi_ctrl_info *ctrl_info,
7198c2ecf20Sopenharmony_ci	struct bmic_csmi_smp_passthru_buffer *buffer, size_t buffer_length,
7208c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	return pqi_send_ctrl_raid_with_error(ctrl_info, BMIC_CSMI_PASSTHRU,
7238c2ecf20Sopenharmony_ci		buffer, buffer_length, error_info);
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci#define PQI_FETCH_PTRAID_DATA		(1 << 31)
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic int pqi_set_diag_rescan(struct pqi_ctrl_info *ctrl_info)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	int rc;
7318c2ecf20Sopenharmony_ci	struct bmic_diag_options *diag;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	diag = kzalloc(sizeof(*diag), GFP_KERNEL);
7348c2ecf20Sopenharmony_ci	if (!diag)
7358c2ecf20Sopenharmony_ci		return -ENOMEM;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	rc = pqi_send_ctrl_raid_request(ctrl_info, BMIC_SENSE_DIAG_OPTIONS,
7388c2ecf20Sopenharmony_ci		diag, sizeof(*diag));
7398c2ecf20Sopenharmony_ci	if (rc)
7408c2ecf20Sopenharmony_ci		goto out;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	diag->options |= cpu_to_le32(PQI_FETCH_PTRAID_DATA);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	rc = pqi_send_ctrl_raid_request(ctrl_info, BMIC_SET_DIAG_OPTIONS, diag,
7458c2ecf20Sopenharmony_ci		sizeof(*diag));
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ciout:
7488c2ecf20Sopenharmony_ci	kfree(diag);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	return rc;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic inline int pqi_write_host_wellness(struct pqi_ctrl_info *ctrl_info,
7548c2ecf20Sopenharmony_ci	void *buffer, size_t buffer_length)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	return pqi_send_ctrl_raid_request(ctrl_info, BMIC_WRITE_HOST_WELLNESS,
7578c2ecf20Sopenharmony_ci		buffer, buffer_length);
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci#pragma pack(1)
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistruct bmic_host_wellness_driver_version {
7638c2ecf20Sopenharmony_ci	u8	start_tag[4];
7648c2ecf20Sopenharmony_ci	u8	driver_version_tag[2];
7658c2ecf20Sopenharmony_ci	__le16	driver_version_length;
7668c2ecf20Sopenharmony_ci	char	driver_version[32];
7678c2ecf20Sopenharmony_ci	u8	dont_write_tag[2];
7688c2ecf20Sopenharmony_ci	u8	end_tag[2];
7698c2ecf20Sopenharmony_ci};
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci#pragma pack()
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic int pqi_write_driver_version_to_host_wellness(
7748c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	int rc;
7778c2ecf20Sopenharmony_ci	struct bmic_host_wellness_driver_version *buffer;
7788c2ecf20Sopenharmony_ci	size_t buffer_length;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	buffer_length = sizeof(*buffer);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	buffer = kmalloc(buffer_length, GFP_KERNEL);
7838c2ecf20Sopenharmony_ci	if (!buffer)
7848c2ecf20Sopenharmony_ci		return -ENOMEM;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	buffer->start_tag[0] = '<';
7878c2ecf20Sopenharmony_ci	buffer->start_tag[1] = 'H';
7888c2ecf20Sopenharmony_ci	buffer->start_tag[2] = 'W';
7898c2ecf20Sopenharmony_ci	buffer->start_tag[3] = '>';
7908c2ecf20Sopenharmony_ci	buffer->driver_version_tag[0] = 'D';
7918c2ecf20Sopenharmony_ci	buffer->driver_version_tag[1] = 'V';
7928c2ecf20Sopenharmony_ci	put_unaligned_le16(sizeof(buffer->driver_version),
7938c2ecf20Sopenharmony_ci		&buffer->driver_version_length);
7948c2ecf20Sopenharmony_ci	strncpy(buffer->driver_version, "Linux " DRIVER_VERSION,
7958c2ecf20Sopenharmony_ci		sizeof(buffer->driver_version) - 1);
7968c2ecf20Sopenharmony_ci	buffer->driver_version[sizeof(buffer->driver_version) - 1] = '\0';
7978c2ecf20Sopenharmony_ci	buffer->dont_write_tag[0] = 'D';
7988c2ecf20Sopenharmony_ci	buffer->dont_write_tag[1] = 'W';
7998c2ecf20Sopenharmony_ci	buffer->end_tag[0] = 'Z';
8008c2ecf20Sopenharmony_ci	buffer->end_tag[1] = 'Z';
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	kfree(buffer);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	return rc;
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci#pragma pack(1)
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistruct bmic_host_wellness_time {
8128c2ecf20Sopenharmony_ci	u8	start_tag[4];
8138c2ecf20Sopenharmony_ci	u8	time_tag[2];
8148c2ecf20Sopenharmony_ci	__le16	time_length;
8158c2ecf20Sopenharmony_ci	u8	time[8];
8168c2ecf20Sopenharmony_ci	u8	dont_write_tag[2];
8178c2ecf20Sopenharmony_ci	u8	end_tag[2];
8188c2ecf20Sopenharmony_ci};
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci#pragma pack()
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_cistatic int pqi_write_current_time_to_host_wellness(
8238c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	int rc;
8268c2ecf20Sopenharmony_ci	struct bmic_host_wellness_time *buffer;
8278c2ecf20Sopenharmony_ci	size_t buffer_length;
8288c2ecf20Sopenharmony_ci	time64_t local_time;
8298c2ecf20Sopenharmony_ci	unsigned int year;
8308c2ecf20Sopenharmony_ci	struct tm tm;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	buffer_length = sizeof(*buffer);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	buffer = kmalloc(buffer_length, GFP_KERNEL);
8358c2ecf20Sopenharmony_ci	if (!buffer)
8368c2ecf20Sopenharmony_ci		return -ENOMEM;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	buffer->start_tag[0] = '<';
8398c2ecf20Sopenharmony_ci	buffer->start_tag[1] = 'H';
8408c2ecf20Sopenharmony_ci	buffer->start_tag[2] = 'W';
8418c2ecf20Sopenharmony_ci	buffer->start_tag[3] = '>';
8428c2ecf20Sopenharmony_ci	buffer->time_tag[0] = 'T';
8438c2ecf20Sopenharmony_ci	buffer->time_tag[1] = 'D';
8448c2ecf20Sopenharmony_ci	put_unaligned_le16(sizeof(buffer->time),
8458c2ecf20Sopenharmony_ci		&buffer->time_length);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	local_time = ktime_get_real_seconds();
8488c2ecf20Sopenharmony_ci	time64_to_tm(local_time, -sys_tz.tz_minuteswest * 60, &tm);
8498c2ecf20Sopenharmony_ci	year = tm.tm_year + 1900;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	buffer->time[0] = bin2bcd(tm.tm_hour);
8528c2ecf20Sopenharmony_ci	buffer->time[1] = bin2bcd(tm.tm_min);
8538c2ecf20Sopenharmony_ci	buffer->time[2] = bin2bcd(tm.tm_sec);
8548c2ecf20Sopenharmony_ci	buffer->time[3] = 0;
8558c2ecf20Sopenharmony_ci	buffer->time[4] = bin2bcd(tm.tm_mon + 1);
8568c2ecf20Sopenharmony_ci	buffer->time[5] = bin2bcd(tm.tm_mday);
8578c2ecf20Sopenharmony_ci	buffer->time[6] = bin2bcd(year / 100);
8588c2ecf20Sopenharmony_ci	buffer->time[7] = bin2bcd(year % 100);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	buffer->dont_write_tag[0] = 'D';
8618c2ecf20Sopenharmony_ci	buffer->dont_write_tag[1] = 'W';
8628c2ecf20Sopenharmony_ci	buffer->end_tag[0] = 'Z';
8638c2ecf20Sopenharmony_ci	buffer->end_tag[1] = 'Z';
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	kfree(buffer);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return rc;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci#define PQI_UPDATE_TIME_WORK_INTERVAL	(24UL * 60 * 60 * PQI_HZ)
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic void pqi_update_time_worker(struct work_struct *work)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	int rc;
8778c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info,
8808c2ecf20Sopenharmony_ci		update_time_work);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
8838c2ecf20Sopenharmony_ci		return;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	rc = pqi_write_current_time_to_host_wellness(ctrl_info);
8868c2ecf20Sopenharmony_ci	if (rc)
8878c2ecf20Sopenharmony_ci		dev_warn(&ctrl_info->pci_dev->dev,
8888c2ecf20Sopenharmony_ci			"error updating time on controller\n");
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	schedule_delayed_work(&ctrl_info->update_time_work,
8918c2ecf20Sopenharmony_ci		PQI_UPDATE_TIME_WORK_INTERVAL);
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic inline void pqi_schedule_update_time_worker(
8958c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	schedule_delayed_work(&ctrl_info->update_time_work, 0);
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_cistatic inline void pqi_cancel_update_time_worker(
9018c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&ctrl_info->update_time_work);
9048c2ecf20Sopenharmony_ci}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic inline int pqi_report_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd,
9078c2ecf20Sopenharmony_ci	void *buffer, size_t buffer_length)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	return pqi_send_ctrl_raid_request(ctrl_info, cmd, buffer,
9108c2ecf20Sopenharmony_ci		buffer_length);
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_cistatic int pqi_report_phys_logical_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd,
9148c2ecf20Sopenharmony_ci	void **buffer)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	int rc;
9178c2ecf20Sopenharmony_ci	size_t lun_list_length;
9188c2ecf20Sopenharmony_ci	size_t lun_data_length;
9198c2ecf20Sopenharmony_ci	size_t new_lun_list_length;
9208c2ecf20Sopenharmony_ci	void *lun_data = NULL;
9218c2ecf20Sopenharmony_ci	struct report_lun_header *report_lun_header;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	report_lun_header = kmalloc(sizeof(*report_lun_header), GFP_KERNEL);
9248c2ecf20Sopenharmony_ci	if (!report_lun_header) {
9258c2ecf20Sopenharmony_ci		rc = -ENOMEM;
9268c2ecf20Sopenharmony_ci		goto out;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	rc = pqi_report_luns(ctrl_info, cmd, report_lun_header,
9308c2ecf20Sopenharmony_ci		sizeof(*report_lun_header));
9318c2ecf20Sopenharmony_ci	if (rc)
9328c2ecf20Sopenharmony_ci		goto out;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	lun_list_length = get_unaligned_be32(&report_lun_header->list_length);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ciagain:
9378c2ecf20Sopenharmony_ci	lun_data_length = sizeof(struct report_lun_header) + lun_list_length;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	lun_data = kmalloc(lun_data_length, GFP_KERNEL);
9408c2ecf20Sopenharmony_ci	if (!lun_data) {
9418c2ecf20Sopenharmony_ci		rc = -ENOMEM;
9428c2ecf20Sopenharmony_ci		goto out;
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	if (lun_list_length == 0) {
9468c2ecf20Sopenharmony_ci		memcpy(lun_data, report_lun_header, sizeof(*report_lun_header));
9478c2ecf20Sopenharmony_ci		goto out;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	rc = pqi_report_luns(ctrl_info, cmd, lun_data, lun_data_length);
9518c2ecf20Sopenharmony_ci	if (rc)
9528c2ecf20Sopenharmony_ci		goto out;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	new_lun_list_length = get_unaligned_be32(
9558c2ecf20Sopenharmony_ci		&((struct report_lun_header *)lun_data)->list_length);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (new_lun_list_length > lun_list_length) {
9588c2ecf20Sopenharmony_ci		lun_list_length = new_lun_list_length;
9598c2ecf20Sopenharmony_ci		kfree(lun_data);
9608c2ecf20Sopenharmony_ci		goto again;
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ciout:
9648c2ecf20Sopenharmony_ci	kfree(report_lun_header);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (rc) {
9678c2ecf20Sopenharmony_ci		kfree(lun_data);
9688c2ecf20Sopenharmony_ci		lun_data = NULL;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	*buffer = lun_data;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	return rc;
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic inline int pqi_report_phys_luns(struct pqi_ctrl_info *ctrl_info,
9778c2ecf20Sopenharmony_ci	void **buffer)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_PHYS,
9808c2ecf20Sopenharmony_ci		buffer);
9818c2ecf20Sopenharmony_ci}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic inline int pqi_report_logical_luns(struct pqi_ctrl_info *ctrl_info,
9848c2ecf20Sopenharmony_ci	void **buffer)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_LOG, buffer);
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic int pqi_get_device_lists(struct pqi_ctrl_info *ctrl_info,
9908c2ecf20Sopenharmony_ci	struct report_phys_lun_extended **physdev_list,
9918c2ecf20Sopenharmony_ci	struct report_log_lun_extended **logdev_list)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	int rc;
9948c2ecf20Sopenharmony_ci	size_t logdev_list_length;
9958c2ecf20Sopenharmony_ci	size_t logdev_data_length;
9968c2ecf20Sopenharmony_ci	struct report_log_lun_extended *internal_logdev_list;
9978c2ecf20Sopenharmony_ci	struct report_log_lun_extended *logdev_data;
9988c2ecf20Sopenharmony_ci	struct report_lun_header report_lun_header;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	rc = pqi_report_phys_luns(ctrl_info, (void **)physdev_list);
10018c2ecf20Sopenharmony_ci	if (rc)
10028c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
10038c2ecf20Sopenharmony_ci			"report physical LUNs failed\n");
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	rc = pqi_report_logical_luns(ctrl_info, (void **)logdev_list);
10068c2ecf20Sopenharmony_ci	if (rc)
10078c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
10088c2ecf20Sopenharmony_ci			"report logical LUNs failed\n");
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/*
10118c2ecf20Sopenharmony_ci	 * Tack the controller itself onto the end of the logical device list.
10128c2ecf20Sopenharmony_ci	 */
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	logdev_data = *logdev_list;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (logdev_data) {
10178c2ecf20Sopenharmony_ci		logdev_list_length =
10188c2ecf20Sopenharmony_ci			get_unaligned_be32(&logdev_data->header.list_length);
10198c2ecf20Sopenharmony_ci	} else {
10208c2ecf20Sopenharmony_ci		memset(&report_lun_header, 0, sizeof(report_lun_header));
10218c2ecf20Sopenharmony_ci		logdev_data =
10228c2ecf20Sopenharmony_ci			(struct report_log_lun_extended *)&report_lun_header;
10238c2ecf20Sopenharmony_ci		logdev_list_length = 0;
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	logdev_data_length = sizeof(struct report_lun_header) +
10278c2ecf20Sopenharmony_ci		logdev_list_length;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	internal_logdev_list = kmalloc(logdev_data_length +
10308c2ecf20Sopenharmony_ci		sizeof(struct report_log_lun_extended), GFP_KERNEL);
10318c2ecf20Sopenharmony_ci	if (!internal_logdev_list) {
10328c2ecf20Sopenharmony_ci		kfree(*logdev_list);
10338c2ecf20Sopenharmony_ci		*logdev_list = NULL;
10348c2ecf20Sopenharmony_ci		return -ENOMEM;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	memcpy(internal_logdev_list, logdev_data, logdev_data_length);
10388c2ecf20Sopenharmony_ci	memset((u8 *)internal_logdev_list + logdev_data_length, 0,
10398c2ecf20Sopenharmony_ci		sizeof(struct report_log_lun_extended_entry));
10408c2ecf20Sopenharmony_ci	put_unaligned_be32(logdev_list_length +
10418c2ecf20Sopenharmony_ci		sizeof(struct report_log_lun_extended_entry),
10428c2ecf20Sopenharmony_ci		&internal_logdev_list->header.list_length);
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	kfree(*logdev_list);
10458c2ecf20Sopenharmony_ci	*logdev_list = internal_logdev_list;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	return 0;
10488c2ecf20Sopenharmony_ci}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic inline void pqi_set_bus_target_lun(struct pqi_scsi_dev *device,
10518c2ecf20Sopenharmony_ci	int bus, int target, int lun)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	device->bus = bus;
10548c2ecf20Sopenharmony_ci	device->target = target;
10558c2ecf20Sopenharmony_ci	device->lun = lun;
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_cistatic void pqi_assign_bus_target_lun(struct pqi_scsi_dev *device)
10598c2ecf20Sopenharmony_ci{
10608c2ecf20Sopenharmony_ci	u8 *scsi3addr;
10618c2ecf20Sopenharmony_ci	u32 lunid;
10628c2ecf20Sopenharmony_ci	int bus;
10638c2ecf20Sopenharmony_ci	int target;
10648c2ecf20Sopenharmony_ci	int lun;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	scsi3addr = device->scsi3addr;
10678c2ecf20Sopenharmony_ci	lunid = get_unaligned_le32(scsi3addr);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (pqi_is_hba_lunid(scsi3addr)) {
10708c2ecf20Sopenharmony_ci		/* The specified device is the controller. */
10718c2ecf20Sopenharmony_ci		pqi_set_bus_target_lun(device, PQI_HBA_BUS, 0, lunid & 0x3fff);
10728c2ecf20Sopenharmony_ci		device->target_lun_valid = true;
10738c2ecf20Sopenharmony_ci		return;
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device)) {
10778c2ecf20Sopenharmony_ci		if (device->is_external_raid_device) {
10788c2ecf20Sopenharmony_ci			bus = PQI_EXTERNAL_RAID_VOLUME_BUS;
10798c2ecf20Sopenharmony_ci			target = (lunid >> 16) & 0x3fff;
10808c2ecf20Sopenharmony_ci			lun = lunid & 0xff;
10818c2ecf20Sopenharmony_ci		} else {
10828c2ecf20Sopenharmony_ci			bus = PQI_RAID_VOLUME_BUS;
10838c2ecf20Sopenharmony_ci			target = 0;
10848c2ecf20Sopenharmony_ci			lun = lunid & 0x3fff;
10858c2ecf20Sopenharmony_ci		}
10868c2ecf20Sopenharmony_ci		pqi_set_bus_target_lun(device, bus, target, lun);
10878c2ecf20Sopenharmony_ci		device->target_lun_valid = true;
10888c2ecf20Sopenharmony_ci		return;
10898c2ecf20Sopenharmony_ci	}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	/*
10928c2ecf20Sopenharmony_ci	 * Defer target and LUN assignment for non-controller physical devices
10938c2ecf20Sopenharmony_ci	 * because the SAS transport layer will make these assignments later.
10948c2ecf20Sopenharmony_ci	 */
10958c2ecf20Sopenharmony_ci	pqi_set_bus_target_lun(device, PQI_PHYSICAL_DEVICE_BUS, 0, 0);
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cistatic void pqi_get_raid_level(struct pqi_ctrl_info *ctrl_info,
10998c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	int rc;
11028c2ecf20Sopenharmony_ci	u8 raid_level;
11038c2ecf20Sopenharmony_ci	u8 *buffer;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	raid_level = SA_RAID_UNKNOWN;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	buffer = kmalloc(64, GFP_KERNEL);
11088c2ecf20Sopenharmony_ci	if (buffer) {
11098c2ecf20Sopenharmony_ci		rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr,
11108c2ecf20Sopenharmony_ci			VPD_PAGE | CISS_VPD_LV_DEVICE_GEOMETRY, buffer, 64);
11118c2ecf20Sopenharmony_ci		if (rc == 0) {
11128c2ecf20Sopenharmony_ci			raid_level = buffer[8];
11138c2ecf20Sopenharmony_ci			if (raid_level > SA_RAID_MAX)
11148c2ecf20Sopenharmony_ci				raid_level = SA_RAID_UNKNOWN;
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci		kfree(buffer);
11178c2ecf20Sopenharmony_ci	}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	device->raid_level = raid_level;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic int pqi_validate_raid_map(struct pqi_ctrl_info *ctrl_info,
11238c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, struct raid_map *raid_map)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	char *err_msg;
11268c2ecf20Sopenharmony_ci	u32 raid_map_size;
11278c2ecf20Sopenharmony_ci	u32 r5or6_blocks_per_row;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	raid_map_size = get_unaligned_le32(&raid_map->structure_size);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	if (raid_map_size < offsetof(struct raid_map, disk_data)) {
11328c2ecf20Sopenharmony_ci		err_msg = "RAID map too small";
11338c2ecf20Sopenharmony_ci		goto bad_raid_map;
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	if (device->raid_level == SA_RAID_1) {
11378c2ecf20Sopenharmony_ci		if (get_unaligned_le16(&raid_map->layout_map_count) != 2) {
11388c2ecf20Sopenharmony_ci			err_msg = "invalid RAID-1 map";
11398c2ecf20Sopenharmony_ci			goto bad_raid_map;
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci	} else if (device->raid_level == SA_RAID_ADM) {
11428c2ecf20Sopenharmony_ci		if (get_unaligned_le16(&raid_map->layout_map_count) != 3) {
11438c2ecf20Sopenharmony_ci			err_msg = "invalid RAID-1(ADM) map";
11448c2ecf20Sopenharmony_ci			goto bad_raid_map;
11458c2ecf20Sopenharmony_ci		}
11468c2ecf20Sopenharmony_ci	} else if ((device->raid_level == SA_RAID_5 ||
11478c2ecf20Sopenharmony_ci		device->raid_level == SA_RAID_6) &&
11488c2ecf20Sopenharmony_ci		get_unaligned_le16(&raid_map->layout_map_count) > 1) {
11498c2ecf20Sopenharmony_ci		/* RAID 50/60 */
11508c2ecf20Sopenharmony_ci		r5or6_blocks_per_row =
11518c2ecf20Sopenharmony_ci			get_unaligned_le16(&raid_map->strip_size) *
11528c2ecf20Sopenharmony_ci			get_unaligned_le16(&raid_map->data_disks_per_row);
11538c2ecf20Sopenharmony_ci		if (r5or6_blocks_per_row == 0) {
11548c2ecf20Sopenharmony_ci			err_msg = "invalid RAID-5 or RAID-6 map";
11558c2ecf20Sopenharmony_ci			goto bad_raid_map;
11568c2ecf20Sopenharmony_ci		}
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	return 0;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_cibad_raid_map:
11628c2ecf20Sopenharmony_ci	dev_warn(&ctrl_info->pci_dev->dev,
11638c2ecf20Sopenharmony_ci		"logical device %08x%08x %s\n",
11648c2ecf20Sopenharmony_ci		*((u32 *)&device->scsi3addr),
11658c2ecf20Sopenharmony_ci		*((u32 *)&device->scsi3addr[4]), err_msg);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	return -EINVAL;
11688c2ecf20Sopenharmony_ci}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_cistatic int pqi_get_raid_map(struct pqi_ctrl_info *ctrl_info,
11718c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
11728c2ecf20Sopenharmony_ci{
11738c2ecf20Sopenharmony_ci	int rc;
11748c2ecf20Sopenharmony_ci	u32 raid_map_size;
11758c2ecf20Sopenharmony_ci	struct raid_map *raid_map;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	raid_map = kmalloc(sizeof(*raid_map), GFP_KERNEL);
11788c2ecf20Sopenharmony_ci	if (!raid_map)
11798c2ecf20Sopenharmony_ci		return -ENOMEM;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	rc = pqi_send_scsi_raid_request(ctrl_info, CISS_GET_RAID_MAP,
11828c2ecf20Sopenharmony_ci		device->scsi3addr, raid_map, sizeof(*raid_map),
11838c2ecf20Sopenharmony_ci		0, NULL, NO_TIMEOUT);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	if (rc)
11868c2ecf20Sopenharmony_ci		goto error;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	raid_map_size = get_unaligned_le32(&raid_map->structure_size);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	if (raid_map_size > sizeof(*raid_map)) {
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci		kfree(raid_map);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci		raid_map = kmalloc(raid_map_size, GFP_KERNEL);
11958c2ecf20Sopenharmony_ci		if (!raid_map)
11968c2ecf20Sopenharmony_ci			return -ENOMEM;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci		rc = pqi_send_scsi_raid_request(ctrl_info, CISS_GET_RAID_MAP,
11998c2ecf20Sopenharmony_ci			device->scsi3addr, raid_map, raid_map_size,
12008c2ecf20Sopenharmony_ci			0, NULL, NO_TIMEOUT);
12018c2ecf20Sopenharmony_ci		if (rc)
12028c2ecf20Sopenharmony_ci			goto error;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci		if (get_unaligned_le32(&raid_map->structure_size)
12058c2ecf20Sopenharmony_ci			!= raid_map_size) {
12068c2ecf20Sopenharmony_ci			dev_warn(&ctrl_info->pci_dev->dev,
12078c2ecf20Sopenharmony_ci				"Requested %d bytes, received %d bytes",
12088c2ecf20Sopenharmony_ci				raid_map_size,
12098c2ecf20Sopenharmony_ci				get_unaligned_le32(&raid_map->structure_size));
12108c2ecf20Sopenharmony_ci			rc = -EINVAL;
12118c2ecf20Sopenharmony_ci			goto error;
12128c2ecf20Sopenharmony_ci		}
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	rc = pqi_validate_raid_map(ctrl_info, device, raid_map);
12168c2ecf20Sopenharmony_ci	if (rc)
12178c2ecf20Sopenharmony_ci		goto error;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	device->raid_map = raid_map;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	return 0;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_cierror:
12248c2ecf20Sopenharmony_ci	kfree(raid_map);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return rc;
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic void pqi_get_raid_bypass_status(struct pqi_ctrl_info *ctrl_info,
12308c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	int rc;
12338c2ecf20Sopenharmony_ci	u8 *buffer;
12348c2ecf20Sopenharmony_ci	u8 bypass_status;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	buffer = kmalloc(64, GFP_KERNEL);
12378c2ecf20Sopenharmony_ci	if (!buffer)
12388c2ecf20Sopenharmony_ci		return;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr,
12418c2ecf20Sopenharmony_ci		VPD_PAGE | CISS_VPD_LV_BYPASS_STATUS, buffer, 64);
12428c2ecf20Sopenharmony_ci	if (rc)
12438c2ecf20Sopenharmony_ci		goto out;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci#define RAID_BYPASS_STATUS		4
12468c2ecf20Sopenharmony_ci#define RAID_BYPASS_CONFIGURED		0x1
12478c2ecf20Sopenharmony_ci#define RAID_BYPASS_ENABLED		0x2
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	bypass_status = buffer[RAID_BYPASS_STATUS];
12508c2ecf20Sopenharmony_ci	device->raid_bypass_configured =
12518c2ecf20Sopenharmony_ci		(bypass_status & RAID_BYPASS_CONFIGURED) != 0;
12528c2ecf20Sopenharmony_ci	if (device->raid_bypass_configured &&
12538c2ecf20Sopenharmony_ci		(bypass_status & RAID_BYPASS_ENABLED) &&
12548c2ecf20Sopenharmony_ci		pqi_get_raid_map(ctrl_info, device) == 0)
12558c2ecf20Sopenharmony_ci		device->raid_bypass_enabled = true;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ciout:
12588c2ecf20Sopenharmony_ci	kfree(buffer);
12598c2ecf20Sopenharmony_ci}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci/*
12628c2ecf20Sopenharmony_ci * Use vendor-specific VPD to determine online/offline status of a volume.
12638c2ecf20Sopenharmony_ci */
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic void pqi_get_volume_status(struct pqi_ctrl_info *ctrl_info,
12668c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	int rc;
12698c2ecf20Sopenharmony_ci	size_t page_length;
12708c2ecf20Sopenharmony_ci	u8 volume_status = CISS_LV_STATUS_UNAVAILABLE;
12718c2ecf20Sopenharmony_ci	bool volume_offline = true;
12728c2ecf20Sopenharmony_ci	u32 volume_flags;
12738c2ecf20Sopenharmony_ci	struct ciss_vpd_logical_volume_status *vpd;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	vpd = kmalloc(sizeof(*vpd), GFP_KERNEL);
12768c2ecf20Sopenharmony_ci	if (!vpd)
12778c2ecf20Sopenharmony_ci		goto no_buffer;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr,
12808c2ecf20Sopenharmony_ci		VPD_PAGE | CISS_VPD_LV_STATUS, vpd, sizeof(*vpd));
12818c2ecf20Sopenharmony_ci	if (rc)
12828c2ecf20Sopenharmony_ci		goto out;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	if (vpd->page_code != CISS_VPD_LV_STATUS)
12858c2ecf20Sopenharmony_ci		goto out;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	page_length = offsetof(struct ciss_vpd_logical_volume_status,
12888c2ecf20Sopenharmony_ci		volume_status) + vpd->page_length;
12898c2ecf20Sopenharmony_ci	if (page_length < sizeof(*vpd))
12908c2ecf20Sopenharmony_ci		goto out;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	volume_status = vpd->volume_status;
12938c2ecf20Sopenharmony_ci	volume_flags = get_unaligned_be32(&vpd->flags);
12948c2ecf20Sopenharmony_ci	volume_offline = (volume_flags & CISS_LV_FLAGS_NO_HOST_IO) != 0;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ciout:
12978c2ecf20Sopenharmony_ci	kfree(vpd);
12988c2ecf20Sopenharmony_cino_buffer:
12998c2ecf20Sopenharmony_ci	device->volume_status = volume_status;
13008c2ecf20Sopenharmony_ci	device->volume_offline = volume_offline;
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic int pqi_get_physical_device_info(struct pqi_ctrl_info *ctrl_info,
13048c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device,
13058c2ecf20Sopenharmony_ci	struct bmic_identify_physical_device *id_phys)
13068c2ecf20Sopenharmony_ci{
13078c2ecf20Sopenharmony_ci	int rc;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	memset(id_phys, 0, sizeof(*id_phys));
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	rc = pqi_identify_physical_device(ctrl_info, device,
13128c2ecf20Sopenharmony_ci		id_phys, sizeof(*id_phys));
13138c2ecf20Sopenharmony_ci	if (rc) {
13148c2ecf20Sopenharmony_ci		device->queue_depth = PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH;
13158c2ecf20Sopenharmony_ci		return rc;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	scsi_sanitize_inquiry_string(&id_phys->model[0], 8);
13198c2ecf20Sopenharmony_ci	scsi_sanitize_inquiry_string(&id_phys->model[8], 16);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	memcpy(device->vendor, &id_phys->model[0], sizeof(device->vendor));
13228c2ecf20Sopenharmony_ci	memcpy(device->model, &id_phys->model[8], sizeof(device->model));
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	device->box_index = id_phys->box_index;
13258c2ecf20Sopenharmony_ci	device->phys_box_on_bus = id_phys->phys_box_on_bus;
13268c2ecf20Sopenharmony_ci	device->phy_connected_dev_type = id_phys->phy_connected_dev_type[0];
13278c2ecf20Sopenharmony_ci	device->queue_depth =
13288c2ecf20Sopenharmony_ci		get_unaligned_le16(&id_phys->current_queue_depth_limit);
13298c2ecf20Sopenharmony_ci	device->active_path_index = id_phys->active_path_number;
13308c2ecf20Sopenharmony_ci	device->path_map = id_phys->redundant_path_present_map;
13318c2ecf20Sopenharmony_ci	memcpy(&device->box,
13328c2ecf20Sopenharmony_ci		&id_phys->alternate_paths_phys_box_on_port,
13338c2ecf20Sopenharmony_ci		sizeof(device->box));
13348c2ecf20Sopenharmony_ci	memcpy(&device->phys_connector,
13358c2ecf20Sopenharmony_ci		&id_phys->alternate_paths_phys_connector,
13368c2ecf20Sopenharmony_ci		sizeof(device->phys_connector));
13378c2ecf20Sopenharmony_ci	device->bay = id_phys->phys_bay_in_box;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	return 0;
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic int pqi_get_logical_device_info(struct pqi_ctrl_info *ctrl_info,
13438c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
13448c2ecf20Sopenharmony_ci{
13458c2ecf20Sopenharmony_ci	int rc;
13468c2ecf20Sopenharmony_ci	u8 *buffer;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	buffer = kmalloc(64, GFP_KERNEL);
13498c2ecf20Sopenharmony_ci	if (!buffer)
13508c2ecf20Sopenharmony_ci		return -ENOMEM;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	/* Send an inquiry to the device to see what it is. */
13538c2ecf20Sopenharmony_ci	rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, 0, buffer, 64);
13548c2ecf20Sopenharmony_ci	if (rc)
13558c2ecf20Sopenharmony_ci		goto out;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	scsi_sanitize_inquiry_string(&buffer[8], 8);
13588c2ecf20Sopenharmony_ci	scsi_sanitize_inquiry_string(&buffer[16], 16);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	device->devtype = buffer[0] & 0x1f;
13618c2ecf20Sopenharmony_ci	memcpy(device->vendor, &buffer[8], sizeof(device->vendor));
13628c2ecf20Sopenharmony_ci	memcpy(device->model, &buffer[16], sizeof(device->model));
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	if (device->devtype == TYPE_DISK) {
13658c2ecf20Sopenharmony_ci		if (device->is_external_raid_device) {
13668c2ecf20Sopenharmony_ci			device->raid_level = SA_RAID_UNKNOWN;
13678c2ecf20Sopenharmony_ci			device->volume_status = CISS_LV_OK;
13688c2ecf20Sopenharmony_ci			device->volume_offline = false;
13698c2ecf20Sopenharmony_ci		} else {
13708c2ecf20Sopenharmony_ci			pqi_get_raid_level(ctrl_info, device);
13718c2ecf20Sopenharmony_ci			pqi_get_raid_bypass_status(ctrl_info, device);
13728c2ecf20Sopenharmony_ci			pqi_get_volume_status(ctrl_info, device);
13738c2ecf20Sopenharmony_ci		}
13748c2ecf20Sopenharmony_ci	}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ciout:
13778c2ecf20Sopenharmony_ci	kfree(buffer);
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	return rc;
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_cistatic int pqi_get_device_info(struct pqi_ctrl_info *ctrl_info,
13838c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device,
13848c2ecf20Sopenharmony_ci	struct bmic_identify_physical_device *id_phys)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	int rc;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	if (device->is_expander_smp_device)
13898c2ecf20Sopenharmony_ci		return 0;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device))
13928c2ecf20Sopenharmony_ci		rc = pqi_get_logical_device_info(ctrl_info, device);
13938c2ecf20Sopenharmony_ci	else
13948c2ecf20Sopenharmony_ci		rc = pqi_get_physical_device_info(ctrl_info, device, id_phys);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	return rc;
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic void pqi_show_volume_status(struct pqi_ctrl_info *ctrl_info,
14008c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
14018c2ecf20Sopenharmony_ci{
14028c2ecf20Sopenharmony_ci	char *status;
14038c2ecf20Sopenharmony_ci	static const char unknown_state_str[] =
14048c2ecf20Sopenharmony_ci		"Volume is in an unknown state (%u)";
14058c2ecf20Sopenharmony_ci	char unknown_state_buffer[sizeof(unknown_state_str) + 10];
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	switch (device->volume_status) {
14088c2ecf20Sopenharmony_ci	case CISS_LV_OK:
14098c2ecf20Sopenharmony_ci		status = "Volume online";
14108c2ecf20Sopenharmony_ci		break;
14118c2ecf20Sopenharmony_ci	case CISS_LV_FAILED:
14128c2ecf20Sopenharmony_ci		status = "Volume failed";
14138c2ecf20Sopenharmony_ci		break;
14148c2ecf20Sopenharmony_ci	case CISS_LV_NOT_CONFIGURED:
14158c2ecf20Sopenharmony_ci		status = "Volume not configured";
14168c2ecf20Sopenharmony_ci		break;
14178c2ecf20Sopenharmony_ci	case CISS_LV_DEGRADED:
14188c2ecf20Sopenharmony_ci		status = "Volume degraded";
14198c2ecf20Sopenharmony_ci		break;
14208c2ecf20Sopenharmony_ci	case CISS_LV_READY_FOR_RECOVERY:
14218c2ecf20Sopenharmony_ci		status = "Volume ready for recovery operation";
14228c2ecf20Sopenharmony_ci		break;
14238c2ecf20Sopenharmony_ci	case CISS_LV_UNDERGOING_RECOVERY:
14248c2ecf20Sopenharmony_ci		status = "Volume undergoing recovery";
14258c2ecf20Sopenharmony_ci		break;
14268c2ecf20Sopenharmony_ci	case CISS_LV_WRONG_PHYSICAL_DRIVE_REPLACED:
14278c2ecf20Sopenharmony_ci		status = "Wrong physical drive was replaced";
14288c2ecf20Sopenharmony_ci		break;
14298c2ecf20Sopenharmony_ci	case CISS_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM:
14308c2ecf20Sopenharmony_ci		status = "A physical drive not properly connected";
14318c2ecf20Sopenharmony_ci		break;
14328c2ecf20Sopenharmony_ci	case CISS_LV_HARDWARE_OVERHEATING:
14338c2ecf20Sopenharmony_ci		status = "Hardware is overheating";
14348c2ecf20Sopenharmony_ci		break;
14358c2ecf20Sopenharmony_ci	case CISS_LV_HARDWARE_HAS_OVERHEATED:
14368c2ecf20Sopenharmony_ci		status = "Hardware has overheated";
14378c2ecf20Sopenharmony_ci		break;
14388c2ecf20Sopenharmony_ci	case CISS_LV_UNDERGOING_EXPANSION:
14398c2ecf20Sopenharmony_ci		status = "Volume undergoing expansion";
14408c2ecf20Sopenharmony_ci		break;
14418c2ecf20Sopenharmony_ci	case CISS_LV_NOT_AVAILABLE:
14428c2ecf20Sopenharmony_ci		status = "Volume waiting for transforming volume";
14438c2ecf20Sopenharmony_ci		break;
14448c2ecf20Sopenharmony_ci	case CISS_LV_QUEUED_FOR_EXPANSION:
14458c2ecf20Sopenharmony_ci		status = "Volume queued for expansion";
14468c2ecf20Sopenharmony_ci		break;
14478c2ecf20Sopenharmony_ci	case CISS_LV_DISABLED_SCSI_ID_CONFLICT:
14488c2ecf20Sopenharmony_ci		status = "Volume disabled due to SCSI ID conflict";
14498c2ecf20Sopenharmony_ci		break;
14508c2ecf20Sopenharmony_ci	case CISS_LV_EJECTED:
14518c2ecf20Sopenharmony_ci		status = "Volume has been ejected";
14528c2ecf20Sopenharmony_ci		break;
14538c2ecf20Sopenharmony_ci	case CISS_LV_UNDERGOING_ERASE:
14548c2ecf20Sopenharmony_ci		status = "Volume undergoing background erase";
14558c2ecf20Sopenharmony_ci		break;
14568c2ecf20Sopenharmony_ci	case CISS_LV_READY_FOR_PREDICTIVE_SPARE_REBUILD:
14578c2ecf20Sopenharmony_ci		status = "Volume ready for predictive spare rebuild";
14588c2ecf20Sopenharmony_ci		break;
14598c2ecf20Sopenharmony_ci	case CISS_LV_UNDERGOING_RPI:
14608c2ecf20Sopenharmony_ci		status = "Volume undergoing rapid parity initialization";
14618c2ecf20Sopenharmony_ci		break;
14628c2ecf20Sopenharmony_ci	case CISS_LV_PENDING_RPI:
14638c2ecf20Sopenharmony_ci		status = "Volume queued for rapid parity initialization";
14648c2ecf20Sopenharmony_ci		break;
14658c2ecf20Sopenharmony_ci	case CISS_LV_ENCRYPTED_NO_KEY:
14668c2ecf20Sopenharmony_ci		status = "Encrypted volume inaccessible - key not present";
14678c2ecf20Sopenharmony_ci		break;
14688c2ecf20Sopenharmony_ci	case CISS_LV_UNDERGOING_ENCRYPTION:
14698c2ecf20Sopenharmony_ci		status = "Volume undergoing encryption process";
14708c2ecf20Sopenharmony_ci		break;
14718c2ecf20Sopenharmony_ci	case CISS_LV_UNDERGOING_ENCRYPTION_REKEYING:
14728c2ecf20Sopenharmony_ci		status = "Volume undergoing encryption re-keying process";
14738c2ecf20Sopenharmony_ci		break;
14748c2ecf20Sopenharmony_ci	case CISS_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER:
14758c2ecf20Sopenharmony_ci		status = "Volume encrypted but encryption is disabled";
14768c2ecf20Sopenharmony_ci		break;
14778c2ecf20Sopenharmony_ci	case CISS_LV_PENDING_ENCRYPTION:
14788c2ecf20Sopenharmony_ci		status = "Volume pending migration to encrypted state";
14798c2ecf20Sopenharmony_ci		break;
14808c2ecf20Sopenharmony_ci	case CISS_LV_PENDING_ENCRYPTION_REKEYING:
14818c2ecf20Sopenharmony_ci		status = "Volume pending encryption rekeying";
14828c2ecf20Sopenharmony_ci		break;
14838c2ecf20Sopenharmony_ci	case CISS_LV_NOT_SUPPORTED:
14848c2ecf20Sopenharmony_ci		status = "Volume not supported on this controller";
14858c2ecf20Sopenharmony_ci		break;
14868c2ecf20Sopenharmony_ci	case CISS_LV_STATUS_UNAVAILABLE:
14878c2ecf20Sopenharmony_ci		status = "Volume status not available";
14888c2ecf20Sopenharmony_ci		break;
14898c2ecf20Sopenharmony_ci	default:
14908c2ecf20Sopenharmony_ci		snprintf(unknown_state_buffer, sizeof(unknown_state_buffer),
14918c2ecf20Sopenharmony_ci			unknown_state_str, device->volume_status);
14928c2ecf20Sopenharmony_ci		status = unknown_state_buffer;
14938c2ecf20Sopenharmony_ci		break;
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	dev_info(&ctrl_info->pci_dev->dev,
14978c2ecf20Sopenharmony_ci		"scsi %d:%d:%d:%d %s\n",
14988c2ecf20Sopenharmony_ci		ctrl_info->scsi_host->host_no,
14998c2ecf20Sopenharmony_ci		device->bus, device->target, device->lun, status);
15008c2ecf20Sopenharmony_ci}
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_cistatic void pqi_rescan_worker(struct work_struct *work)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info,
15078c2ecf20Sopenharmony_ci		rescan_work);
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	pqi_scan_scsi_devices(ctrl_info);
15108c2ecf20Sopenharmony_ci}
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_cistatic int pqi_add_device(struct pqi_ctrl_info *ctrl_info,
15138c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
15148c2ecf20Sopenharmony_ci{
15158c2ecf20Sopenharmony_ci	int rc;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device))
15188c2ecf20Sopenharmony_ci		rc = scsi_add_device(ctrl_info->scsi_host, device->bus,
15198c2ecf20Sopenharmony_ci			device->target, device->lun);
15208c2ecf20Sopenharmony_ci	else
15218c2ecf20Sopenharmony_ci		rc = pqi_add_sas_device(ctrl_info->sas_host, device);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	return rc;
15248c2ecf20Sopenharmony_ci}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci#define PQI_PENDING_IO_TIMEOUT_SECS	20
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_cistatic inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info,
15298c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
15308c2ecf20Sopenharmony_ci{
15318c2ecf20Sopenharmony_ci	int rc;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	pqi_device_remove_start(device);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	rc = pqi_device_wait_for_pending_io(ctrl_info, device, PQI_PENDING_IO_TIMEOUT_SECS);
15368c2ecf20Sopenharmony_ci	if (rc)
15378c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
15388c2ecf20Sopenharmony_ci			"scsi %d:%d:%d:%d removing device with %d outstanding command(s)\n",
15398c2ecf20Sopenharmony_ci			ctrl_info->scsi_host->host_no, device->bus,
15408c2ecf20Sopenharmony_ci			device->target, device->lun,
15418c2ecf20Sopenharmony_ci			atomic_read(&device->scsi_cmds_outstanding));
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device))
15448c2ecf20Sopenharmony_ci		scsi_remove_device(device->sdev);
15458c2ecf20Sopenharmony_ci	else
15468c2ecf20Sopenharmony_ci		pqi_remove_sas_device(device);
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci/* Assumes the SCSI device list lock is held. */
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_cistatic struct pqi_scsi_dev *pqi_find_scsi_dev(struct pqi_ctrl_info *ctrl_info,
15528c2ecf20Sopenharmony_ci	int bus, int target, int lun)
15538c2ecf20Sopenharmony_ci{
15548c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	list_for_each_entry(device, &ctrl_info->scsi_device_list, scsi_device_list_entry)
15578c2ecf20Sopenharmony_ci		if (device->bus == bus && device->target == target && device->lun == lun)
15588c2ecf20Sopenharmony_ci			return device;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	return NULL;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic inline bool pqi_device_equal(struct pqi_scsi_dev *dev1,
15648c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *dev2)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	if (dev1->is_physical_device != dev2->is_physical_device)
15678c2ecf20Sopenharmony_ci		return false;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	if (dev1->is_physical_device)
15708c2ecf20Sopenharmony_ci		return dev1->wwid == dev2->wwid;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	return memcmp(dev1->volume_id, dev2->volume_id,
15738c2ecf20Sopenharmony_ci		sizeof(dev1->volume_id)) == 0;
15748c2ecf20Sopenharmony_ci}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_cienum pqi_find_result {
15778c2ecf20Sopenharmony_ci	DEVICE_NOT_FOUND,
15788c2ecf20Sopenharmony_ci	DEVICE_CHANGED,
15798c2ecf20Sopenharmony_ci	DEVICE_SAME,
15808c2ecf20Sopenharmony_ci};
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cistatic enum pqi_find_result pqi_scsi_find_entry(struct pqi_ctrl_info *ctrl_info,
15838c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device_to_find, struct pqi_scsi_dev **matching_device)
15848c2ecf20Sopenharmony_ci{
15858c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	list_for_each_entry(device, &ctrl_info->scsi_device_list, scsi_device_list_entry) {
15888c2ecf20Sopenharmony_ci		if (pqi_scsi3addr_equal(device_to_find->scsi3addr, device->scsi3addr)) {
15898c2ecf20Sopenharmony_ci			*matching_device = device;
15908c2ecf20Sopenharmony_ci			if (pqi_device_equal(device_to_find, device)) {
15918c2ecf20Sopenharmony_ci				if (device_to_find->volume_offline)
15928c2ecf20Sopenharmony_ci					return DEVICE_CHANGED;
15938c2ecf20Sopenharmony_ci				return DEVICE_SAME;
15948c2ecf20Sopenharmony_ci			}
15958c2ecf20Sopenharmony_ci			return DEVICE_CHANGED;
15968c2ecf20Sopenharmony_ci		}
15978c2ecf20Sopenharmony_ci	}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	return DEVICE_NOT_FOUND;
16008c2ecf20Sopenharmony_ci}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_cistatic inline const char *pqi_device_type(struct pqi_scsi_dev *device)
16038c2ecf20Sopenharmony_ci{
16048c2ecf20Sopenharmony_ci	if (device->is_expander_smp_device)
16058c2ecf20Sopenharmony_ci		return "Enclosure SMP    ";
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	return scsi_device_type(device->devtype);
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci#define PQI_DEV_INFO_BUFFER_LENGTH	128
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_cistatic void pqi_dev_info(struct pqi_ctrl_info *ctrl_info,
16138c2ecf20Sopenharmony_ci	char *action, struct pqi_scsi_dev *device)
16148c2ecf20Sopenharmony_ci{
16158c2ecf20Sopenharmony_ci	ssize_t count;
16168c2ecf20Sopenharmony_ci	char buffer[PQI_DEV_INFO_BUFFER_LENGTH];
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	count = snprintf(buffer, PQI_DEV_INFO_BUFFER_LENGTH,
16198c2ecf20Sopenharmony_ci		"%d:%d:", ctrl_info->scsi_host->host_no, device->bus);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	if (device->target_lun_valid)
16228c2ecf20Sopenharmony_ci		count += scnprintf(buffer + count,
16238c2ecf20Sopenharmony_ci			PQI_DEV_INFO_BUFFER_LENGTH - count,
16248c2ecf20Sopenharmony_ci			"%d:%d",
16258c2ecf20Sopenharmony_ci			device->target,
16268c2ecf20Sopenharmony_ci			device->lun);
16278c2ecf20Sopenharmony_ci	else
16288c2ecf20Sopenharmony_ci		count += scnprintf(buffer + count,
16298c2ecf20Sopenharmony_ci			PQI_DEV_INFO_BUFFER_LENGTH - count,
16308c2ecf20Sopenharmony_ci			"-:-");
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device))
16338c2ecf20Sopenharmony_ci		count += scnprintf(buffer + count,
16348c2ecf20Sopenharmony_ci			PQI_DEV_INFO_BUFFER_LENGTH - count,
16358c2ecf20Sopenharmony_ci			" %08x%08x",
16368c2ecf20Sopenharmony_ci			*((u32 *)&device->scsi3addr),
16378c2ecf20Sopenharmony_ci			*((u32 *)&device->scsi3addr[4]));
16388c2ecf20Sopenharmony_ci	else
16398c2ecf20Sopenharmony_ci		count += scnprintf(buffer + count,
16408c2ecf20Sopenharmony_ci			PQI_DEV_INFO_BUFFER_LENGTH - count,
16418c2ecf20Sopenharmony_ci			" %016llx", device->sas_address);
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	count += scnprintf(buffer + count, PQI_DEV_INFO_BUFFER_LENGTH - count,
16448c2ecf20Sopenharmony_ci		" %s %.8s %.16s ",
16458c2ecf20Sopenharmony_ci		pqi_device_type(device),
16468c2ecf20Sopenharmony_ci		device->vendor,
16478c2ecf20Sopenharmony_ci		device->model);
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device)) {
16508c2ecf20Sopenharmony_ci		if (device->devtype == TYPE_DISK)
16518c2ecf20Sopenharmony_ci			count += scnprintf(buffer + count,
16528c2ecf20Sopenharmony_ci				PQI_DEV_INFO_BUFFER_LENGTH - count,
16538c2ecf20Sopenharmony_ci				"SSDSmartPathCap%c En%c %-12s",
16548c2ecf20Sopenharmony_ci				device->raid_bypass_configured ? '+' : '-',
16558c2ecf20Sopenharmony_ci				device->raid_bypass_enabled ? '+' : '-',
16568c2ecf20Sopenharmony_ci				pqi_raid_level_to_string(device->raid_level));
16578c2ecf20Sopenharmony_ci	} else {
16588c2ecf20Sopenharmony_ci		count += scnprintf(buffer + count,
16598c2ecf20Sopenharmony_ci			PQI_DEV_INFO_BUFFER_LENGTH - count,
16608c2ecf20Sopenharmony_ci			"AIO%c", device->aio_enabled ? '+' : '-');
16618c2ecf20Sopenharmony_ci		if (device->devtype == TYPE_DISK ||
16628c2ecf20Sopenharmony_ci			device->devtype == TYPE_ZBC)
16638c2ecf20Sopenharmony_ci			count += scnprintf(buffer + count,
16648c2ecf20Sopenharmony_ci				PQI_DEV_INFO_BUFFER_LENGTH - count,
16658c2ecf20Sopenharmony_ci				" qd=%-6d", device->queue_depth);
16668c2ecf20Sopenharmony_ci	}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	dev_info(&ctrl_info->pci_dev->dev, "%s %s\n", action, buffer);
16698c2ecf20Sopenharmony_ci}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci/* Assumes the SCSI device list lock is held. */
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_cistatic void pqi_scsi_update_device(struct pqi_scsi_dev *existing_device,
16748c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *new_device)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	existing_device->devtype = new_device->devtype;
16778c2ecf20Sopenharmony_ci	existing_device->device_type = new_device->device_type;
16788c2ecf20Sopenharmony_ci	existing_device->bus = new_device->bus;
16798c2ecf20Sopenharmony_ci	if (new_device->target_lun_valid) {
16808c2ecf20Sopenharmony_ci		existing_device->target = new_device->target;
16818c2ecf20Sopenharmony_ci		existing_device->lun = new_device->lun;
16828c2ecf20Sopenharmony_ci		existing_device->target_lun_valid = true;
16838c2ecf20Sopenharmony_ci	}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	if ((existing_device->volume_status == CISS_LV_QUEUED_FOR_EXPANSION ||
16868c2ecf20Sopenharmony_ci		existing_device->volume_status == CISS_LV_UNDERGOING_EXPANSION) &&
16878c2ecf20Sopenharmony_ci		new_device->volume_status == CISS_LV_OK)
16888c2ecf20Sopenharmony_ci		existing_device->rescan = true;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	/* By definition, the scsi3addr and wwid fields are already the same. */
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	existing_device->is_physical_device = new_device->is_physical_device;
16938c2ecf20Sopenharmony_ci	existing_device->is_external_raid_device =
16948c2ecf20Sopenharmony_ci		new_device->is_external_raid_device;
16958c2ecf20Sopenharmony_ci	existing_device->is_expander_smp_device =
16968c2ecf20Sopenharmony_ci		new_device->is_expander_smp_device;
16978c2ecf20Sopenharmony_ci	existing_device->aio_enabled = new_device->aio_enabled;
16988c2ecf20Sopenharmony_ci	memcpy(existing_device->vendor, new_device->vendor,
16998c2ecf20Sopenharmony_ci		sizeof(existing_device->vendor));
17008c2ecf20Sopenharmony_ci	memcpy(existing_device->model, new_device->model,
17018c2ecf20Sopenharmony_ci		sizeof(existing_device->model));
17028c2ecf20Sopenharmony_ci	existing_device->sas_address = new_device->sas_address;
17038c2ecf20Sopenharmony_ci	existing_device->raid_level = new_device->raid_level;
17048c2ecf20Sopenharmony_ci	existing_device->queue_depth = new_device->queue_depth;
17058c2ecf20Sopenharmony_ci	existing_device->aio_handle = new_device->aio_handle;
17068c2ecf20Sopenharmony_ci	existing_device->volume_status = new_device->volume_status;
17078c2ecf20Sopenharmony_ci	existing_device->active_path_index = new_device->active_path_index;
17088c2ecf20Sopenharmony_ci	existing_device->path_map = new_device->path_map;
17098c2ecf20Sopenharmony_ci	existing_device->bay = new_device->bay;
17108c2ecf20Sopenharmony_ci	existing_device->box_index = new_device->box_index;
17118c2ecf20Sopenharmony_ci	existing_device->phys_box_on_bus = new_device->phys_box_on_bus;
17128c2ecf20Sopenharmony_ci	existing_device->phy_connected_dev_type =
17138c2ecf20Sopenharmony_ci		new_device->phy_connected_dev_type;
17148c2ecf20Sopenharmony_ci	memcpy(existing_device->box, new_device->box,
17158c2ecf20Sopenharmony_ci		sizeof(existing_device->box));
17168c2ecf20Sopenharmony_ci	memcpy(existing_device->phys_connector, new_device->phys_connector,
17178c2ecf20Sopenharmony_ci		sizeof(existing_device->phys_connector));
17188c2ecf20Sopenharmony_ci	existing_device->offload_to_mirror = 0;
17198c2ecf20Sopenharmony_ci	kfree(existing_device->raid_map);
17208c2ecf20Sopenharmony_ci	existing_device->raid_map = new_device->raid_map;
17218c2ecf20Sopenharmony_ci	existing_device->raid_bypass_configured =
17228c2ecf20Sopenharmony_ci		new_device->raid_bypass_configured;
17238c2ecf20Sopenharmony_ci	existing_device->raid_bypass_enabled =
17248c2ecf20Sopenharmony_ci		new_device->raid_bypass_enabled;
17258c2ecf20Sopenharmony_ci	existing_device->device_offline = false;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	/* To prevent this from being freed later. */
17288c2ecf20Sopenharmony_ci	new_device->raid_map = NULL;
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_cistatic inline void pqi_free_device(struct pqi_scsi_dev *device)
17328c2ecf20Sopenharmony_ci{
17338c2ecf20Sopenharmony_ci	if (device) {
17348c2ecf20Sopenharmony_ci		kfree(device->raid_map);
17358c2ecf20Sopenharmony_ci		kfree(device);
17368c2ecf20Sopenharmony_ci	}
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci/*
17408c2ecf20Sopenharmony_ci * Called when exposing a new device to the OS fails in order to re-adjust
17418c2ecf20Sopenharmony_ci * our internal SCSI device list to match the SCSI ML's view.
17428c2ecf20Sopenharmony_ci */
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cistatic inline void pqi_fixup_botched_add(struct pqi_ctrl_info *ctrl_info,
17458c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
17468c2ecf20Sopenharmony_ci{
17478c2ecf20Sopenharmony_ci	unsigned long flags;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
17508c2ecf20Sopenharmony_ci	list_del(&device->scsi_device_list_entry);
17518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	/* Allow the device structure to be freed later. */
17548c2ecf20Sopenharmony_ci	device->keep_device = false;
17558c2ecf20Sopenharmony_ci}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cistatic inline bool pqi_is_device_added(struct pqi_scsi_dev *device)
17588c2ecf20Sopenharmony_ci{
17598c2ecf20Sopenharmony_ci	if (device->is_expander_smp_device)
17608c2ecf20Sopenharmony_ci		return device->sas_port != NULL;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	return device->sdev != NULL;
17638c2ecf20Sopenharmony_ci}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_cistatic void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info,
17668c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *new_device_list[], unsigned int num_new_devices)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	int rc;
17698c2ecf20Sopenharmony_ci	unsigned int i;
17708c2ecf20Sopenharmony_ci	unsigned long flags;
17718c2ecf20Sopenharmony_ci	enum pqi_find_result find_result;
17728c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
17738c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *next;
17748c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *matching_device;
17758c2ecf20Sopenharmony_ci	LIST_HEAD(add_list);
17768c2ecf20Sopenharmony_ci	LIST_HEAD(delete_list);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	/*
17798c2ecf20Sopenharmony_ci	 * The idea here is to do as little work as possible while holding the
17808c2ecf20Sopenharmony_ci	 * spinlock.  That's why we go to great pains to defer anything other
17818c2ecf20Sopenharmony_ci	 * than updating the internal device list until after we release the
17828c2ecf20Sopenharmony_ci	 * spinlock.
17838c2ecf20Sopenharmony_ci	 */
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	/* Assume that all devices in the existing list have gone away. */
17888c2ecf20Sopenharmony_ci	list_for_each_entry(device, &ctrl_info->scsi_device_list, scsi_device_list_entry)
17898c2ecf20Sopenharmony_ci		device->device_gone = true;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	for (i = 0; i < num_new_devices; i++) {
17928c2ecf20Sopenharmony_ci		device = new_device_list[i];
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci		find_result = pqi_scsi_find_entry(ctrl_info, device,
17958c2ecf20Sopenharmony_ci			&matching_device);
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci		switch (find_result) {
17988c2ecf20Sopenharmony_ci		case DEVICE_SAME:
17998c2ecf20Sopenharmony_ci			/*
18008c2ecf20Sopenharmony_ci			 * The newly found device is already in the existing
18018c2ecf20Sopenharmony_ci			 * device list.
18028c2ecf20Sopenharmony_ci			 */
18038c2ecf20Sopenharmony_ci			device->new_device = false;
18048c2ecf20Sopenharmony_ci			matching_device->device_gone = false;
18058c2ecf20Sopenharmony_ci			pqi_scsi_update_device(matching_device, device);
18068c2ecf20Sopenharmony_ci			break;
18078c2ecf20Sopenharmony_ci		case DEVICE_NOT_FOUND:
18088c2ecf20Sopenharmony_ci			/*
18098c2ecf20Sopenharmony_ci			 * The newly found device is NOT in the existing device
18108c2ecf20Sopenharmony_ci			 * list.
18118c2ecf20Sopenharmony_ci			 */
18128c2ecf20Sopenharmony_ci			device->new_device = true;
18138c2ecf20Sopenharmony_ci			break;
18148c2ecf20Sopenharmony_ci		case DEVICE_CHANGED:
18158c2ecf20Sopenharmony_ci			/*
18168c2ecf20Sopenharmony_ci			 * The original device has gone away and we need to add
18178c2ecf20Sopenharmony_ci			 * the new device.
18188c2ecf20Sopenharmony_ci			 */
18198c2ecf20Sopenharmony_ci			device->new_device = true;
18208c2ecf20Sopenharmony_ci			break;
18218c2ecf20Sopenharmony_ci		}
18228c2ecf20Sopenharmony_ci	}
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	/* Process all devices that have gone away. */
18258c2ecf20Sopenharmony_ci	list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list,
18268c2ecf20Sopenharmony_ci		scsi_device_list_entry) {
18278c2ecf20Sopenharmony_ci		if (device->device_gone) {
18288c2ecf20Sopenharmony_ci			list_del_init(&device->scsi_device_list_entry);
18298c2ecf20Sopenharmony_ci			list_add_tail(&device->delete_list_entry, &delete_list);
18308c2ecf20Sopenharmony_ci		}
18318c2ecf20Sopenharmony_ci	}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	/* Process all new devices. */
18348c2ecf20Sopenharmony_ci	for (i = 0; i < num_new_devices; i++) {
18358c2ecf20Sopenharmony_ci		device = new_device_list[i];
18368c2ecf20Sopenharmony_ci		if (!device->new_device)
18378c2ecf20Sopenharmony_ci			continue;
18388c2ecf20Sopenharmony_ci		if (device->volume_offline)
18398c2ecf20Sopenharmony_ci			continue;
18408c2ecf20Sopenharmony_ci		list_add_tail(&device->scsi_device_list_entry,
18418c2ecf20Sopenharmony_ci			&ctrl_info->scsi_device_list);
18428c2ecf20Sopenharmony_ci		list_add_tail(&device->add_list_entry, &add_list);
18438c2ecf20Sopenharmony_ci		/* To prevent this device structure from being freed later. */
18448c2ecf20Sopenharmony_ci		device->keep_device = true;
18458c2ecf20Sopenharmony_ci	}
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	if (pqi_ctrl_in_ofa(ctrl_info))
18508c2ecf20Sopenharmony_ci		pqi_ctrl_ofa_done(ctrl_info);
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	/* Remove all devices that have gone away. */
18538c2ecf20Sopenharmony_ci	list_for_each_entry_safe(device, next, &delete_list, delete_list_entry) {
18548c2ecf20Sopenharmony_ci		if (device->volume_offline) {
18558c2ecf20Sopenharmony_ci			pqi_dev_info(ctrl_info, "offline", device);
18568c2ecf20Sopenharmony_ci			pqi_show_volume_status(ctrl_info, device);
18578c2ecf20Sopenharmony_ci		}
18588c2ecf20Sopenharmony_ci		list_del(&device->delete_list_entry);
18598c2ecf20Sopenharmony_ci		if (pqi_is_device_added(device)) {
18608c2ecf20Sopenharmony_ci			pqi_remove_device(ctrl_info, device);
18618c2ecf20Sopenharmony_ci		} else {
18628c2ecf20Sopenharmony_ci			if (!device->volume_offline)
18638c2ecf20Sopenharmony_ci				pqi_dev_info(ctrl_info, "removed", device);
18648c2ecf20Sopenharmony_ci			pqi_free_device(device);
18658c2ecf20Sopenharmony_ci		}
18668c2ecf20Sopenharmony_ci	}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	/*
18698c2ecf20Sopenharmony_ci	 * Notify the SCSI ML if the queue depth of any existing device has
18708c2ecf20Sopenharmony_ci	 * changed.
18718c2ecf20Sopenharmony_ci	 */
18728c2ecf20Sopenharmony_ci	list_for_each_entry(device, &ctrl_info->scsi_device_list,
18738c2ecf20Sopenharmony_ci		scsi_device_list_entry) {
18748c2ecf20Sopenharmony_ci		if (device->sdev) {
18758c2ecf20Sopenharmony_ci			if (device->queue_depth !=
18768c2ecf20Sopenharmony_ci				device->advertised_queue_depth) {
18778c2ecf20Sopenharmony_ci				device->advertised_queue_depth = device->queue_depth;
18788c2ecf20Sopenharmony_ci				scsi_change_queue_depth(device->sdev,
18798c2ecf20Sopenharmony_ci					device->advertised_queue_depth);
18808c2ecf20Sopenharmony_ci			}
18818c2ecf20Sopenharmony_ci			if (device->rescan) {
18828c2ecf20Sopenharmony_ci				scsi_rescan_device(&device->sdev->sdev_gendev);
18838c2ecf20Sopenharmony_ci				device->rescan = false;
18848c2ecf20Sopenharmony_ci			}
18858c2ecf20Sopenharmony_ci		}
18868c2ecf20Sopenharmony_ci	}
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci	/* Expose any new devices. */
18898c2ecf20Sopenharmony_ci	list_for_each_entry_safe(device, next, &add_list, add_list_entry) {
18908c2ecf20Sopenharmony_ci		if (!pqi_is_device_added(device)) {
18918c2ecf20Sopenharmony_ci			rc = pqi_add_device(ctrl_info, device);
18928c2ecf20Sopenharmony_ci			if (rc == 0) {
18938c2ecf20Sopenharmony_ci				pqi_dev_info(ctrl_info, "added", device);
18948c2ecf20Sopenharmony_ci			} else {
18958c2ecf20Sopenharmony_ci				dev_warn(&ctrl_info->pci_dev->dev,
18968c2ecf20Sopenharmony_ci					"scsi %d:%d:%d:%d addition failed, device not added\n",
18978c2ecf20Sopenharmony_ci					ctrl_info->scsi_host->host_no,
18988c2ecf20Sopenharmony_ci					device->bus, device->target,
18998c2ecf20Sopenharmony_ci					device->lun);
19008c2ecf20Sopenharmony_ci				pqi_fixup_botched_add(ctrl_info, device);
19018c2ecf20Sopenharmony_ci			}
19028c2ecf20Sopenharmony_ci		}
19038c2ecf20Sopenharmony_ci	}
19048c2ecf20Sopenharmony_ci}
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_cistatic inline bool pqi_is_supported_device(struct pqi_scsi_dev *device)
19078c2ecf20Sopenharmony_ci{
19088c2ecf20Sopenharmony_ci	/*
19098c2ecf20Sopenharmony_ci	 * Only support the HBA controller itself as a RAID
19108c2ecf20Sopenharmony_ci	 * controller.  If it's a RAID controller other than
19118c2ecf20Sopenharmony_ci	 * the HBA itself (an external RAID controller, for
19128c2ecf20Sopenharmony_ci	 * example), we don't support it.
19138c2ecf20Sopenharmony_ci	 */
19148c2ecf20Sopenharmony_ci	if (device->device_type == SA_DEVICE_TYPE_CONTROLLER &&
19158c2ecf20Sopenharmony_ci		!pqi_is_hba_lunid(device->scsi3addr))
19168c2ecf20Sopenharmony_ci		return false;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	return true;
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_cistatic inline bool pqi_skip_device(u8 *scsi3addr)
19228c2ecf20Sopenharmony_ci{
19238c2ecf20Sopenharmony_ci	/* Ignore all masked devices. */
19248c2ecf20Sopenharmony_ci	if (MASKED_DEVICE(scsi3addr))
19258c2ecf20Sopenharmony_ci		return true;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	return false;
19288c2ecf20Sopenharmony_ci}
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_cistatic inline void pqi_mask_device(u8 *scsi3addr)
19318c2ecf20Sopenharmony_ci{
19328c2ecf20Sopenharmony_ci	scsi3addr[3] |= 0xc0;
19338c2ecf20Sopenharmony_ci}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_cistatic inline bool pqi_is_device_with_sas_address(struct pqi_scsi_dev *device)
19368c2ecf20Sopenharmony_ci{
19378c2ecf20Sopenharmony_ci	switch (device->device_type) {
19388c2ecf20Sopenharmony_ci	case SA_DEVICE_TYPE_SAS:
19398c2ecf20Sopenharmony_ci	case SA_DEVICE_TYPE_EXPANDER_SMP:
19408c2ecf20Sopenharmony_ci	case SA_DEVICE_TYPE_SES:
19418c2ecf20Sopenharmony_ci		return true;
19428c2ecf20Sopenharmony_ci	}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	return false;
19458c2ecf20Sopenharmony_ci}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_cistatic inline bool pqi_expose_device(struct pqi_scsi_dev *device)
19488c2ecf20Sopenharmony_ci{
19498c2ecf20Sopenharmony_ci	return !device->is_physical_device ||
19508c2ecf20Sopenharmony_ci		!pqi_skip_device(device->scsi3addr);
19518c2ecf20Sopenharmony_ci}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_cistatic int pqi_update_scsi_devices(struct pqi_ctrl_info *ctrl_info)
19548c2ecf20Sopenharmony_ci{
19558c2ecf20Sopenharmony_ci	int i;
19568c2ecf20Sopenharmony_ci	int rc;
19578c2ecf20Sopenharmony_ci	LIST_HEAD(new_device_list_head);
19588c2ecf20Sopenharmony_ci	struct report_phys_lun_extended *physdev_list = NULL;
19598c2ecf20Sopenharmony_ci	struct report_log_lun_extended *logdev_list = NULL;
19608c2ecf20Sopenharmony_ci	struct report_phys_lun_extended_entry *phys_lun_ext_entry;
19618c2ecf20Sopenharmony_ci	struct report_log_lun_extended_entry *log_lun_ext_entry;
19628c2ecf20Sopenharmony_ci	struct bmic_identify_physical_device *id_phys = NULL;
19638c2ecf20Sopenharmony_ci	u32 num_physicals;
19648c2ecf20Sopenharmony_ci	u32 num_logicals;
19658c2ecf20Sopenharmony_ci	struct pqi_scsi_dev **new_device_list = NULL;
19668c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
19678c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *next;
19688c2ecf20Sopenharmony_ci	unsigned int num_new_devices;
19698c2ecf20Sopenharmony_ci	unsigned int num_valid_devices;
19708c2ecf20Sopenharmony_ci	bool is_physical_device;
19718c2ecf20Sopenharmony_ci	u8 *scsi3addr;
19728c2ecf20Sopenharmony_ci	unsigned int physical_index;
19738c2ecf20Sopenharmony_ci	unsigned int logical_index;
19748c2ecf20Sopenharmony_ci	static char *out_of_memory_msg =
19758c2ecf20Sopenharmony_ci		"failed to allocate memory, device discovery stopped";
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	rc = pqi_get_device_lists(ctrl_info, &physdev_list, &logdev_list);
19788c2ecf20Sopenharmony_ci	if (rc)
19798c2ecf20Sopenharmony_ci		goto out;
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	if (physdev_list)
19828c2ecf20Sopenharmony_ci		num_physicals =
19838c2ecf20Sopenharmony_ci			get_unaligned_be32(&physdev_list->header.list_length)
19848c2ecf20Sopenharmony_ci				/ sizeof(physdev_list->lun_entries[0]);
19858c2ecf20Sopenharmony_ci	else
19868c2ecf20Sopenharmony_ci		num_physicals = 0;
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	if (logdev_list)
19898c2ecf20Sopenharmony_ci		num_logicals =
19908c2ecf20Sopenharmony_ci			get_unaligned_be32(&logdev_list->header.list_length)
19918c2ecf20Sopenharmony_ci				/ sizeof(logdev_list->lun_entries[0]);
19928c2ecf20Sopenharmony_ci	else
19938c2ecf20Sopenharmony_ci		num_logicals = 0;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (num_physicals) {
19968c2ecf20Sopenharmony_ci		/*
19978c2ecf20Sopenharmony_ci		 * We need this buffer for calls to pqi_get_physical_disk_info()
19988c2ecf20Sopenharmony_ci		 * below.  We allocate it here instead of inside
19998c2ecf20Sopenharmony_ci		 * pqi_get_physical_disk_info() because it's a fairly large
20008c2ecf20Sopenharmony_ci		 * buffer.
20018c2ecf20Sopenharmony_ci		 */
20028c2ecf20Sopenharmony_ci		id_phys = kmalloc(sizeof(*id_phys), GFP_KERNEL);
20038c2ecf20Sopenharmony_ci		if (!id_phys) {
20048c2ecf20Sopenharmony_ci			dev_warn(&ctrl_info->pci_dev->dev, "%s\n",
20058c2ecf20Sopenharmony_ci				out_of_memory_msg);
20068c2ecf20Sopenharmony_ci			rc = -ENOMEM;
20078c2ecf20Sopenharmony_ci			goto out;
20088c2ecf20Sopenharmony_ci		}
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci		if (pqi_hide_vsep) {
20118c2ecf20Sopenharmony_ci			for (i = num_physicals - 1; i >= 0; i--) {
20128c2ecf20Sopenharmony_ci				phys_lun_ext_entry =
20138c2ecf20Sopenharmony_ci						&physdev_list->lun_entries[i];
20148c2ecf20Sopenharmony_ci				if (CISS_GET_DRIVE_NUMBER(
20158c2ecf20Sopenharmony_ci					phys_lun_ext_entry->lunid) ==
20168c2ecf20Sopenharmony_ci						PQI_VSEP_CISS_BTL) {
20178c2ecf20Sopenharmony_ci					pqi_mask_device(
20188c2ecf20Sopenharmony_ci						phys_lun_ext_entry->lunid);
20198c2ecf20Sopenharmony_ci					break;
20208c2ecf20Sopenharmony_ci				}
20218c2ecf20Sopenharmony_ci			}
20228c2ecf20Sopenharmony_ci		}
20238c2ecf20Sopenharmony_ci	}
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ci	num_new_devices = num_physicals + num_logicals;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	new_device_list = kmalloc_array(num_new_devices,
20288c2ecf20Sopenharmony_ci					sizeof(*new_device_list),
20298c2ecf20Sopenharmony_ci					GFP_KERNEL);
20308c2ecf20Sopenharmony_ci	if (!new_device_list) {
20318c2ecf20Sopenharmony_ci		dev_warn(&ctrl_info->pci_dev->dev, "%s\n", out_of_memory_msg);
20328c2ecf20Sopenharmony_ci		rc = -ENOMEM;
20338c2ecf20Sopenharmony_ci		goto out;
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	for (i = 0; i < num_new_devices; i++) {
20378c2ecf20Sopenharmony_ci		device = kzalloc(sizeof(*device), GFP_KERNEL);
20388c2ecf20Sopenharmony_ci		if (!device) {
20398c2ecf20Sopenharmony_ci			dev_warn(&ctrl_info->pci_dev->dev, "%s\n",
20408c2ecf20Sopenharmony_ci				out_of_memory_msg);
20418c2ecf20Sopenharmony_ci			rc = -ENOMEM;
20428c2ecf20Sopenharmony_ci			goto out;
20438c2ecf20Sopenharmony_ci		}
20448c2ecf20Sopenharmony_ci		list_add_tail(&device->new_device_list_entry,
20458c2ecf20Sopenharmony_ci			&new_device_list_head);
20468c2ecf20Sopenharmony_ci	}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	device = NULL;
20498c2ecf20Sopenharmony_ci	num_valid_devices = 0;
20508c2ecf20Sopenharmony_ci	physical_index = 0;
20518c2ecf20Sopenharmony_ci	logical_index = 0;
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci	for (i = 0; i < num_new_devices; i++) {
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci		if ((!pqi_expose_ld_first && i < num_physicals) ||
20568c2ecf20Sopenharmony_ci			(pqi_expose_ld_first && i >= num_logicals)) {
20578c2ecf20Sopenharmony_ci			is_physical_device = true;
20588c2ecf20Sopenharmony_ci			phys_lun_ext_entry =
20598c2ecf20Sopenharmony_ci				&physdev_list->lun_entries[physical_index++];
20608c2ecf20Sopenharmony_ci			log_lun_ext_entry = NULL;
20618c2ecf20Sopenharmony_ci			scsi3addr = phys_lun_ext_entry->lunid;
20628c2ecf20Sopenharmony_ci		} else {
20638c2ecf20Sopenharmony_ci			is_physical_device = false;
20648c2ecf20Sopenharmony_ci			phys_lun_ext_entry = NULL;
20658c2ecf20Sopenharmony_ci			log_lun_ext_entry =
20668c2ecf20Sopenharmony_ci				&logdev_list->lun_entries[logical_index++];
20678c2ecf20Sopenharmony_ci			scsi3addr = log_lun_ext_entry->lunid;
20688c2ecf20Sopenharmony_ci		}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci		if (is_physical_device && pqi_skip_device(scsi3addr))
20718c2ecf20Sopenharmony_ci			continue;
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci		if (device)
20748c2ecf20Sopenharmony_ci			device = list_next_entry(device, new_device_list_entry);
20758c2ecf20Sopenharmony_ci		else
20768c2ecf20Sopenharmony_ci			device = list_first_entry(&new_device_list_head,
20778c2ecf20Sopenharmony_ci				struct pqi_scsi_dev, new_device_list_entry);
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci		memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr));
20808c2ecf20Sopenharmony_ci		device->is_physical_device = is_physical_device;
20818c2ecf20Sopenharmony_ci		if (is_physical_device) {
20828c2ecf20Sopenharmony_ci			device->device_type = phys_lun_ext_entry->device_type;
20838c2ecf20Sopenharmony_ci			if (device->device_type == SA_DEVICE_TYPE_EXPANDER_SMP)
20848c2ecf20Sopenharmony_ci				device->is_expander_smp_device = true;
20858c2ecf20Sopenharmony_ci		} else {
20868c2ecf20Sopenharmony_ci			device->is_external_raid_device =
20878c2ecf20Sopenharmony_ci				pqi_is_external_raid_addr(scsi3addr);
20888c2ecf20Sopenharmony_ci		}
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci		if (!pqi_is_supported_device(device))
20918c2ecf20Sopenharmony_ci			continue;
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci		/* Gather information about the device. */
20948c2ecf20Sopenharmony_ci		rc = pqi_get_device_info(ctrl_info, device, id_phys);
20958c2ecf20Sopenharmony_ci		if (rc == -ENOMEM) {
20968c2ecf20Sopenharmony_ci			dev_warn(&ctrl_info->pci_dev->dev, "%s\n",
20978c2ecf20Sopenharmony_ci				out_of_memory_msg);
20988c2ecf20Sopenharmony_ci			goto out;
20998c2ecf20Sopenharmony_ci		}
21008c2ecf20Sopenharmony_ci		if (rc) {
21018c2ecf20Sopenharmony_ci			if (device->is_physical_device)
21028c2ecf20Sopenharmony_ci				dev_warn(&ctrl_info->pci_dev->dev,
21038c2ecf20Sopenharmony_ci					"obtaining device info failed, skipping physical device %016llx\n",
21048c2ecf20Sopenharmony_ci					get_unaligned_be64(
21058c2ecf20Sopenharmony_ci						&phys_lun_ext_entry->wwid));
21068c2ecf20Sopenharmony_ci			else
21078c2ecf20Sopenharmony_ci				dev_warn(&ctrl_info->pci_dev->dev,
21088c2ecf20Sopenharmony_ci					"obtaining device info failed, skipping logical device %08x%08x\n",
21098c2ecf20Sopenharmony_ci					*((u32 *)&device->scsi3addr),
21108c2ecf20Sopenharmony_ci					*((u32 *)&device->scsi3addr[4]));
21118c2ecf20Sopenharmony_ci			rc = 0;
21128c2ecf20Sopenharmony_ci			continue;
21138c2ecf20Sopenharmony_ci		}
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci		pqi_assign_bus_target_lun(device);
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci		if (device->is_physical_device) {
21188c2ecf20Sopenharmony_ci			device->wwid = phys_lun_ext_entry->wwid;
21198c2ecf20Sopenharmony_ci			if ((phys_lun_ext_entry->device_flags &
21208c2ecf20Sopenharmony_ci				CISS_REPORT_PHYS_DEV_FLAG_AIO_ENABLED) &&
21218c2ecf20Sopenharmony_ci				phys_lun_ext_entry->aio_handle) {
21228c2ecf20Sopenharmony_ci				device->aio_enabled = true;
21238c2ecf20Sopenharmony_ci				device->aio_handle =
21248c2ecf20Sopenharmony_ci					phys_lun_ext_entry->aio_handle;
21258c2ecf20Sopenharmony_ci			}
21268c2ecf20Sopenharmony_ci		} else {
21278c2ecf20Sopenharmony_ci			memcpy(device->volume_id, log_lun_ext_entry->volume_id,
21288c2ecf20Sopenharmony_ci				sizeof(device->volume_id));
21298c2ecf20Sopenharmony_ci		}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci		if (pqi_is_device_with_sas_address(device))
21328c2ecf20Sopenharmony_ci			device->sas_address = get_unaligned_be64(&device->wwid);
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci		new_device_list[num_valid_devices++] = device;
21358c2ecf20Sopenharmony_ci	}
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	pqi_update_device_list(ctrl_info, new_device_list, num_valid_devices);
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ciout:
21408c2ecf20Sopenharmony_ci	list_for_each_entry_safe(device, next, &new_device_list_head,
21418c2ecf20Sopenharmony_ci		new_device_list_entry) {
21428c2ecf20Sopenharmony_ci		if (device->keep_device)
21438c2ecf20Sopenharmony_ci			continue;
21448c2ecf20Sopenharmony_ci		list_del(&device->new_device_list_entry);
21458c2ecf20Sopenharmony_ci		pqi_free_device(device);
21468c2ecf20Sopenharmony_ci	}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	kfree(new_device_list);
21498c2ecf20Sopenharmony_ci	kfree(physdev_list);
21508c2ecf20Sopenharmony_ci	kfree(logdev_list);
21518c2ecf20Sopenharmony_ci	kfree(id_phys);
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	return rc;
21548c2ecf20Sopenharmony_ci}
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_cistatic int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info)
21578c2ecf20Sopenharmony_ci{
21588c2ecf20Sopenharmony_ci	int rc = 0;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
21618c2ecf20Sopenharmony_ci		return -ENXIO;
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	if (!mutex_trylock(&ctrl_info->scan_mutex)) {
21648c2ecf20Sopenharmony_ci		pqi_schedule_rescan_worker_delayed(ctrl_info);
21658c2ecf20Sopenharmony_ci		rc = -EINPROGRESS;
21668c2ecf20Sopenharmony_ci	} else {
21678c2ecf20Sopenharmony_ci		rc = pqi_update_scsi_devices(ctrl_info);
21688c2ecf20Sopenharmony_ci		if (rc)
21698c2ecf20Sopenharmony_ci			pqi_schedule_rescan_worker_delayed(ctrl_info);
21708c2ecf20Sopenharmony_ci		mutex_unlock(&ctrl_info->scan_mutex);
21718c2ecf20Sopenharmony_ci	}
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	return rc;
21748c2ecf20Sopenharmony_ci}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_cistatic void pqi_scan_start(struct Scsi_Host *shost)
21778c2ecf20Sopenharmony_ci{
21788c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
21818c2ecf20Sopenharmony_ci	if (pqi_ctrl_in_ofa(ctrl_info))
21828c2ecf20Sopenharmony_ci		return;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	pqi_scan_scsi_devices(ctrl_info);
21858c2ecf20Sopenharmony_ci}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci/* Returns TRUE if scan is finished. */
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_cistatic int pqi_scan_finished(struct Scsi_Host *shost,
21908c2ecf20Sopenharmony_ci	unsigned long elapsed_time)
21918c2ecf20Sopenharmony_ci{
21928c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	ctrl_info = shost_priv(shost);
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	return !mutex_is_locked(&ctrl_info->scan_mutex);
21978c2ecf20Sopenharmony_ci}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_cistatic void pqi_wait_until_scan_finished(struct pqi_ctrl_info *ctrl_info)
22008c2ecf20Sopenharmony_ci{
22018c2ecf20Sopenharmony_ci	mutex_lock(&ctrl_info->scan_mutex);
22028c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl_info->scan_mutex);
22038c2ecf20Sopenharmony_ci}
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_cistatic void pqi_wait_until_lun_reset_finished(struct pqi_ctrl_info *ctrl_info)
22068c2ecf20Sopenharmony_ci{
22078c2ecf20Sopenharmony_ci	mutex_lock(&ctrl_info->lun_reset_mutex);
22088c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl_info->lun_reset_mutex);
22098c2ecf20Sopenharmony_ci}
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_cistatic void pqi_wait_until_ofa_finished(struct pqi_ctrl_info *ctrl_info)
22128c2ecf20Sopenharmony_ci{
22138c2ecf20Sopenharmony_ci	mutex_lock(&ctrl_info->ofa_mutex);
22148c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl_info->ofa_mutex);
22158c2ecf20Sopenharmony_ci}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_cistatic inline void pqi_set_encryption_info(
22188c2ecf20Sopenharmony_ci	struct pqi_encryption_info *encryption_info, struct raid_map *raid_map,
22198c2ecf20Sopenharmony_ci	u64 first_block)
22208c2ecf20Sopenharmony_ci{
22218c2ecf20Sopenharmony_ci	u32 volume_blk_size;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	/*
22248c2ecf20Sopenharmony_ci	 * Set the encryption tweak values based on logical block address.
22258c2ecf20Sopenharmony_ci	 * If the block size is 512, the tweak value is equal to the LBA.
22268c2ecf20Sopenharmony_ci	 * For other block sizes, tweak value is (LBA * block size) / 512.
22278c2ecf20Sopenharmony_ci	 */
22288c2ecf20Sopenharmony_ci	volume_blk_size = get_unaligned_le32(&raid_map->volume_blk_size);
22298c2ecf20Sopenharmony_ci	if (volume_blk_size != 512)
22308c2ecf20Sopenharmony_ci		first_block = (first_block * volume_blk_size) / 512;
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci	encryption_info->data_encryption_key_index =
22338c2ecf20Sopenharmony_ci		get_unaligned_le16(&raid_map->data_encryption_key_index);
22348c2ecf20Sopenharmony_ci	encryption_info->encrypt_tweak_lower = lower_32_bits(first_block);
22358c2ecf20Sopenharmony_ci	encryption_info->encrypt_tweak_upper = upper_32_bits(first_block);
22368c2ecf20Sopenharmony_ci}
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci/*
22398c2ecf20Sopenharmony_ci * Attempt to perform RAID bypass mapping for a logical volume I/O.
22408c2ecf20Sopenharmony_ci */
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci#define PQI_RAID_BYPASS_INELIGIBLE	1
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_cistatic int pqi_raid_bypass_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info,
22458c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, struct scsi_cmnd *scmd,
22468c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group)
22478c2ecf20Sopenharmony_ci{
22488c2ecf20Sopenharmony_ci	struct raid_map *raid_map;
22498c2ecf20Sopenharmony_ci	bool is_write = false;
22508c2ecf20Sopenharmony_ci	u32 map_index;
22518c2ecf20Sopenharmony_ci	u64 first_block;
22528c2ecf20Sopenharmony_ci	u64 last_block;
22538c2ecf20Sopenharmony_ci	u32 block_cnt;
22548c2ecf20Sopenharmony_ci	u32 blocks_per_row;
22558c2ecf20Sopenharmony_ci	u64 first_row;
22568c2ecf20Sopenharmony_ci	u64 last_row;
22578c2ecf20Sopenharmony_ci	u32 first_row_offset;
22588c2ecf20Sopenharmony_ci	u32 last_row_offset;
22598c2ecf20Sopenharmony_ci	u32 first_column;
22608c2ecf20Sopenharmony_ci	u32 last_column;
22618c2ecf20Sopenharmony_ci	u64 r0_first_row;
22628c2ecf20Sopenharmony_ci	u64 r0_last_row;
22638c2ecf20Sopenharmony_ci	u32 r5or6_blocks_per_row;
22648c2ecf20Sopenharmony_ci	u64 r5or6_first_row;
22658c2ecf20Sopenharmony_ci	u64 r5or6_last_row;
22668c2ecf20Sopenharmony_ci	u32 r5or6_first_row_offset;
22678c2ecf20Sopenharmony_ci	u32 r5or6_last_row_offset;
22688c2ecf20Sopenharmony_ci	u32 r5or6_first_column;
22698c2ecf20Sopenharmony_ci	u32 r5or6_last_column;
22708c2ecf20Sopenharmony_ci	u16 data_disks_per_row;
22718c2ecf20Sopenharmony_ci	u32 total_disks_per_row;
22728c2ecf20Sopenharmony_ci	u16 layout_map_count;
22738c2ecf20Sopenharmony_ci	u32 stripesize;
22748c2ecf20Sopenharmony_ci	u16 strip_size;
22758c2ecf20Sopenharmony_ci	u32 first_group;
22768c2ecf20Sopenharmony_ci	u32 last_group;
22778c2ecf20Sopenharmony_ci	u32 current_group;
22788c2ecf20Sopenharmony_ci	u32 map_row;
22798c2ecf20Sopenharmony_ci	u32 aio_handle;
22808c2ecf20Sopenharmony_ci	u64 disk_block;
22818c2ecf20Sopenharmony_ci	u32 disk_block_cnt;
22828c2ecf20Sopenharmony_ci	u8 cdb[16];
22838c2ecf20Sopenharmony_ci	u8 cdb_length;
22848c2ecf20Sopenharmony_ci	int offload_to_mirror;
22858c2ecf20Sopenharmony_ci	struct pqi_encryption_info *encryption_info_ptr;
22868c2ecf20Sopenharmony_ci	struct pqi_encryption_info encryption_info;
22878c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 32
22888c2ecf20Sopenharmony_ci	u64 tmpdiv;
22898c2ecf20Sopenharmony_ci#endif
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	/* Check for valid opcode, get LBA and block count. */
22928c2ecf20Sopenharmony_ci	switch (scmd->cmnd[0]) {
22938c2ecf20Sopenharmony_ci	case WRITE_6:
22948c2ecf20Sopenharmony_ci		is_write = true;
22958c2ecf20Sopenharmony_ci		fallthrough;
22968c2ecf20Sopenharmony_ci	case READ_6:
22978c2ecf20Sopenharmony_ci		first_block = (u64)(((scmd->cmnd[1] & 0x1f) << 16) |
22988c2ecf20Sopenharmony_ci			(scmd->cmnd[2] << 8) | scmd->cmnd[3]);
22998c2ecf20Sopenharmony_ci		block_cnt = (u32)scmd->cmnd[4];
23008c2ecf20Sopenharmony_ci		if (block_cnt == 0)
23018c2ecf20Sopenharmony_ci			block_cnt = 256;
23028c2ecf20Sopenharmony_ci		break;
23038c2ecf20Sopenharmony_ci	case WRITE_10:
23048c2ecf20Sopenharmony_ci		is_write = true;
23058c2ecf20Sopenharmony_ci		fallthrough;
23068c2ecf20Sopenharmony_ci	case READ_10:
23078c2ecf20Sopenharmony_ci		first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]);
23088c2ecf20Sopenharmony_ci		block_cnt = (u32)get_unaligned_be16(&scmd->cmnd[7]);
23098c2ecf20Sopenharmony_ci		break;
23108c2ecf20Sopenharmony_ci	case WRITE_12:
23118c2ecf20Sopenharmony_ci		is_write = true;
23128c2ecf20Sopenharmony_ci		fallthrough;
23138c2ecf20Sopenharmony_ci	case READ_12:
23148c2ecf20Sopenharmony_ci		first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]);
23158c2ecf20Sopenharmony_ci		block_cnt = get_unaligned_be32(&scmd->cmnd[6]);
23168c2ecf20Sopenharmony_ci		break;
23178c2ecf20Sopenharmony_ci	case WRITE_16:
23188c2ecf20Sopenharmony_ci		is_write = true;
23198c2ecf20Sopenharmony_ci		fallthrough;
23208c2ecf20Sopenharmony_ci	case READ_16:
23218c2ecf20Sopenharmony_ci		first_block = get_unaligned_be64(&scmd->cmnd[2]);
23228c2ecf20Sopenharmony_ci		block_cnt = get_unaligned_be32(&scmd->cmnd[10]);
23238c2ecf20Sopenharmony_ci		break;
23248c2ecf20Sopenharmony_ci	default:
23258c2ecf20Sopenharmony_ci		/* Process via normal I/O path. */
23268c2ecf20Sopenharmony_ci		return PQI_RAID_BYPASS_INELIGIBLE;
23278c2ecf20Sopenharmony_ci	}
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	/* Check for write to non-RAID-0. */
23308c2ecf20Sopenharmony_ci	if (is_write && device->raid_level != SA_RAID_0)
23318c2ecf20Sopenharmony_ci		return PQI_RAID_BYPASS_INELIGIBLE;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	if (unlikely(block_cnt == 0))
23348c2ecf20Sopenharmony_ci		return PQI_RAID_BYPASS_INELIGIBLE;
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	last_block = first_block + block_cnt - 1;
23378c2ecf20Sopenharmony_ci	raid_map = device->raid_map;
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	/* Check for invalid block or wraparound. */
23408c2ecf20Sopenharmony_ci	if (last_block >= get_unaligned_le64(&raid_map->volume_blk_cnt) ||
23418c2ecf20Sopenharmony_ci		last_block < first_block)
23428c2ecf20Sopenharmony_ci		return PQI_RAID_BYPASS_INELIGIBLE;
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	data_disks_per_row = get_unaligned_le16(&raid_map->data_disks_per_row);
23458c2ecf20Sopenharmony_ci	strip_size = get_unaligned_le16(&raid_map->strip_size);
23468c2ecf20Sopenharmony_ci	layout_map_count = get_unaligned_le16(&raid_map->layout_map_count);
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	/* Calculate stripe information for the request. */
23498c2ecf20Sopenharmony_ci	blocks_per_row = data_disks_per_row * strip_size;
23508c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 32
23518c2ecf20Sopenharmony_ci	tmpdiv = first_block;
23528c2ecf20Sopenharmony_ci	do_div(tmpdiv, blocks_per_row);
23538c2ecf20Sopenharmony_ci	first_row = tmpdiv;
23548c2ecf20Sopenharmony_ci	tmpdiv = last_block;
23558c2ecf20Sopenharmony_ci	do_div(tmpdiv, blocks_per_row);
23568c2ecf20Sopenharmony_ci	last_row = tmpdiv;
23578c2ecf20Sopenharmony_ci	first_row_offset = (u32)(first_block - (first_row * blocks_per_row));
23588c2ecf20Sopenharmony_ci	last_row_offset = (u32)(last_block - (last_row * blocks_per_row));
23598c2ecf20Sopenharmony_ci	tmpdiv = first_row_offset;
23608c2ecf20Sopenharmony_ci	do_div(tmpdiv, strip_size);
23618c2ecf20Sopenharmony_ci	first_column = tmpdiv;
23628c2ecf20Sopenharmony_ci	tmpdiv = last_row_offset;
23638c2ecf20Sopenharmony_ci	do_div(tmpdiv, strip_size);
23648c2ecf20Sopenharmony_ci	last_column = tmpdiv;
23658c2ecf20Sopenharmony_ci#else
23668c2ecf20Sopenharmony_ci	first_row = first_block / blocks_per_row;
23678c2ecf20Sopenharmony_ci	last_row = last_block / blocks_per_row;
23688c2ecf20Sopenharmony_ci	first_row_offset = (u32)(first_block - (first_row * blocks_per_row));
23698c2ecf20Sopenharmony_ci	last_row_offset = (u32)(last_block - (last_row * blocks_per_row));
23708c2ecf20Sopenharmony_ci	first_column = first_row_offset / strip_size;
23718c2ecf20Sopenharmony_ci	last_column = last_row_offset / strip_size;
23728c2ecf20Sopenharmony_ci#endif
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	/* If this isn't a single row/column then give to the controller. */
23758c2ecf20Sopenharmony_ci	if (first_row != last_row || first_column != last_column)
23768c2ecf20Sopenharmony_ci		return PQI_RAID_BYPASS_INELIGIBLE;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	/* Proceeding with driver mapping. */
23798c2ecf20Sopenharmony_ci	total_disks_per_row = data_disks_per_row +
23808c2ecf20Sopenharmony_ci		get_unaligned_le16(&raid_map->metadata_disks_per_row);
23818c2ecf20Sopenharmony_ci	map_row = ((u32)(first_row >> raid_map->parity_rotation_shift)) %
23828c2ecf20Sopenharmony_ci		get_unaligned_le16(&raid_map->row_cnt);
23838c2ecf20Sopenharmony_ci	map_index = (map_row * total_disks_per_row) + first_column;
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_ci	/* RAID 1 */
23868c2ecf20Sopenharmony_ci	if (device->raid_level == SA_RAID_1) {
23878c2ecf20Sopenharmony_ci		if (device->offload_to_mirror)
23888c2ecf20Sopenharmony_ci			map_index += data_disks_per_row;
23898c2ecf20Sopenharmony_ci		device->offload_to_mirror = !device->offload_to_mirror;
23908c2ecf20Sopenharmony_ci	} else if (device->raid_level == SA_RAID_ADM) {
23918c2ecf20Sopenharmony_ci		/* RAID ADM */
23928c2ecf20Sopenharmony_ci		/*
23938c2ecf20Sopenharmony_ci		 * Handles N-way mirrors  (R1-ADM) and R10 with # of drives
23948c2ecf20Sopenharmony_ci		 * divisible by 3.
23958c2ecf20Sopenharmony_ci		 */
23968c2ecf20Sopenharmony_ci		offload_to_mirror = device->offload_to_mirror;
23978c2ecf20Sopenharmony_ci		if (offload_to_mirror == 0)  {
23988c2ecf20Sopenharmony_ci			/* use physical disk in the first mirrored group. */
23998c2ecf20Sopenharmony_ci			map_index %= data_disks_per_row;
24008c2ecf20Sopenharmony_ci		} else {
24018c2ecf20Sopenharmony_ci			do {
24028c2ecf20Sopenharmony_ci				/*
24038c2ecf20Sopenharmony_ci				 * Determine mirror group that map_index
24048c2ecf20Sopenharmony_ci				 * indicates.
24058c2ecf20Sopenharmony_ci				 */
24068c2ecf20Sopenharmony_ci				current_group = map_index / data_disks_per_row;
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci				if (offload_to_mirror != current_group) {
24098c2ecf20Sopenharmony_ci					if (current_group <
24108c2ecf20Sopenharmony_ci						layout_map_count - 1) {
24118c2ecf20Sopenharmony_ci						/*
24128c2ecf20Sopenharmony_ci						 * Select raid index from
24138c2ecf20Sopenharmony_ci						 * next group.
24148c2ecf20Sopenharmony_ci						 */
24158c2ecf20Sopenharmony_ci						map_index += data_disks_per_row;
24168c2ecf20Sopenharmony_ci						current_group++;
24178c2ecf20Sopenharmony_ci					} else {
24188c2ecf20Sopenharmony_ci						/*
24198c2ecf20Sopenharmony_ci						 * Select raid index from first
24208c2ecf20Sopenharmony_ci						 * group.
24218c2ecf20Sopenharmony_ci						 */
24228c2ecf20Sopenharmony_ci						map_index %= data_disks_per_row;
24238c2ecf20Sopenharmony_ci						current_group = 0;
24248c2ecf20Sopenharmony_ci					}
24258c2ecf20Sopenharmony_ci				}
24268c2ecf20Sopenharmony_ci			} while (offload_to_mirror != current_group);
24278c2ecf20Sopenharmony_ci		}
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci		/* Set mirror group to use next time. */
24308c2ecf20Sopenharmony_ci		offload_to_mirror =
24318c2ecf20Sopenharmony_ci			(offload_to_mirror >= layout_map_count - 1) ?
24328c2ecf20Sopenharmony_ci				0 : offload_to_mirror + 1;
24338c2ecf20Sopenharmony_ci		device->offload_to_mirror = offload_to_mirror;
24348c2ecf20Sopenharmony_ci		/*
24358c2ecf20Sopenharmony_ci		 * Avoid direct use of device->offload_to_mirror within this
24368c2ecf20Sopenharmony_ci		 * function since multiple threads might simultaneously
24378c2ecf20Sopenharmony_ci		 * increment it beyond the range of device->layout_map_count -1.
24388c2ecf20Sopenharmony_ci		 */
24398c2ecf20Sopenharmony_ci	} else if ((device->raid_level == SA_RAID_5 ||
24408c2ecf20Sopenharmony_ci		device->raid_level == SA_RAID_6) && layout_map_count > 1) {
24418c2ecf20Sopenharmony_ci		/* RAID 50/60 */
24428c2ecf20Sopenharmony_ci		/* Verify first and last block are in same RAID group */
24438c2ecf20Sopenharmony_ci		r5or6_blocks_per_row = strip_size * data_disks_per_row;
24448c2ecf20Sopenharmony_ci		stripesize = r5or6_blocks_per_row * layout_map_count;
24458c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 32
24468c2ecf20Sopenharmony_ci		tmpdiv = first_block;
24478c2ecf20Sopenharmony_ci		first_group = do_div(tmpdiv, stripesize);
24488c2ecf20Sopenharmony_ci		tmpdiv = first_group;
24498c2ecf20Sopenharmony_ci		do_div(tmpdiv, r5or6_blocks_per_row);
24508c2ecf20Sopenharmony_ci		first_group = tmpdiv;
24518c2ecf20Sopenharmony_ci		tmpdiv = last_block;
24528c2ecf20Sopenharmony_ci		last_group = do_div(tmpdiv, stripesize);
24538c2ecf20Sopenharmony_ci		tmpdiv = last_group;
24548c2ecf20Sopenharmony_ci		do_div(tmpdiv, r5or6_blocks_per_row);
24558c2ecf20Sopenharmony_ci		last_group = tmpdiv;
24568c2ecf20Sopenharmony_ci#else
24578c2ecf20Sopenharmony_ci		first_group = (first_block % stripesize) / r5or6_blocks_per_row;
24588c2ecf20Sopenharmony_ci		last_group = (last_block % stripesize) / r5or6_blocks_per_row;
24598c2ecf20Sopenharmony_ci#endif
24608c2ecf20Sopenharmony_ci		if (first_group != last_group)
24618c2ecf20Sopenharmony_ci			return PQI_RAID_BYPASS_INELIGIBLE;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci		/* Verify request is in a single row of RAID 5/6 */
24648c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 32
24658c2ecf20Sopenharmony_ci		tmpdiv = first_block;
24668c2ecf20Sopenharmony_ci		do_div(tmpdiv, stripesize);
24678c2ecf20Sopenharmony_ci		first_row = r5or6_first_row = r0_first_row = tmpdiv;
24688c2ecf20Sopenharmony_ci		tmpdiv = last_block;
24698c2ecf20Sopenharmony_ci		do_div(tmpdiv, stripesize);
24708c2ecf20Sopenharmony_ci		r5or6_last_row = r0_last_row = tmpdiv;
24718c2ecf20Sopenharmony_ci#else
24728c2ecf20Sopenharmony_ci		first_row = r5or6_first_row = r0_first_row =
24738c2ecf20Sopenharmony_ci			first_block / stripesize;
24748c2ecf20Sopenharmony_ci		r5or6_last_row = r0_last_row = last_block / stripesize;
24758c2ecf20Sopenharmony_ci#endif
24768c2ecf20Sopenharmony_ci		if (r5or6_first_row != r5or6_last_row)
24778c2ecf20Sopenharmony_ci			return PQI_RAID_BYPASS_INELIGIBLE;
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci		/* Verify request is in a single column */
24808c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 32
24818c2ecf20Sopenharmony_ci		tmpdiv = first_block;
24828c2ecf20Sopenharmony_ci		first_row_offset = do_div(tmpdiv, stripesize);
24838c2ecf20Sopenharmony_ci		tmpdiv = first_row_offset;
24848c2ecf20Sopenharmony_ci		first_row_offset = (u32)do_div(tmpdiv, r5or6_blocks_per_row);
24858c2ecf20Sopenharmony_ci		r5or6_first_row_offset = first_row_offset;
24868c2ecf20Sopenharmony_ci		tmpdiv = last_block;
24878c2ecf20Sopenharmony_ci		r5or6_last_row_offset = do_div(tmpdiv, stripesize);
24888c2ecf20Sopenharmony_ci		tmpdiv = r5or6_last_row_offset;
24898c2ecf20Sopenharmony_ci		r5or6_last_row_offset = do_div(tmpdiv, r5or6_blocks_per_row);
24908c2ecf20Sopenharmony_ci		tmpdiv = r5or6_first_row_offset;
24918c2ecf20Sopenharmony_ci		do_div(tmpdiv, strip_size);
24928c2ecf20Sopenharmony_ci		first_column = r5or6_first_column = tmpdiv;
24938c2ecf20Sopenharmony_ci		tmpdiv = r5or6_last_row_offset;
24948c2ecf20Sopenharmony_ci		do_div(tmpdiv, strip_size);
24958c2ecf20Sopenharmony_ci		r5or6_last_column = tmpdiv;
24968c2ecf20Sopenharmony_ci#else
24978c2ecf20Sopenharmony_ci		first_row_offset = r5or6_first_row_offset =
24988c2ecf20Sopenharmony_ci			(u32)((first_block % stripesize) %
24998c2ecf20Sopenharmony_ci			r5or6_blocks_per_row);
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci		r5or6_last_row_offset =
25028c2ecf20Sopenharmony_ci			(u32)((last_block % stripesize) %
25038c2ecf20Sopenharmony_ci			r5or6_blocks_per_row);
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci		first_column = r5or6_first_row_offset / strip_size;
25068c2ecf20Sopenharmony_ci		r5or6_first_column = first_column;
25078c2ecf20Sopenharmony_ci		r5or6_last_column = r5or6_last_row_offset / strip_size;
25088c2ecf20Sopenharmony_ci#endif
25098c2ecf20Sopenharmony_ci		if (r5or6_first_column != r5or6_last_column)
25108c2ecf20Sopenharmony_ci			return PQI_RAID_BYPASS_INELIGIBLE;
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci		/* Request is eligible */
25138c2ecf20Sopenharmony_ci		map_row =
25148c2ecf20Sopenharmony_ci			((u32)(first_row >> raid_map->parity_rotation_shift)) %
25158c2ecf20Sopenharmony_ci			get_unaligned_le16(&raid_map->row_cnt);
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci		map_index = (first_group *
25188c2ecf20Sopenharmony_ci			(get_unaligned_le16(&raid_map->row_cnt) *
25198c2ecf20Sopenharmony_ci			total_disks_per_row)) +
25208c2ecf20Sopenharmony_ci			(map_row * total_disks_per_row) + first_column;
25218c2ecf20Sopenharmony_ci	}
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	aio_handle = raid_map->disk_data[map_index].aio_handle;
25248c2ecf20Sopenharmony_ci	disk_block = get_unaligned_le64(&raid_map->disk_starting_blk) +
25258c2ecf20Sopenharmony_ci		first_row * strip_size +
25268c2ecf20Sopenharmony_ci		(first_row_offset - first_column * strip_size);
25278c2ecf20Sopenharmony_ci	disk_block_cnt = block_cnt;
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	/* Handle differing logical/physical block sizes. */
25308c2ecf20Sopenharmony_ci	if (raid_map->phys_blk_shift) {
25318c2ecf20Sopenharmony_ci		disk_block <<= raid_map->phys_blk_shift;
25328c2ecf20Sopenharmony_ci		disk_block_cnt <<= raid_map->phys_blk_shift;
25338c2ecf20Sopenharmony_ci	}
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	if (unlikely(disk_block_cnt > 0xffff))
25368c2ecf20Sopenharmony_ci		return PQI_RAID_BYPASS_INELIGIBLE;
25378c2ecf20Sopenharmony_ci
25388c2ecf20Sopenharmony_ci	/* Build the new CDB for the physical disk I/O. */
25398c2ecf20Sopenharmony_ci	if (disk_block > 0xffffffff) {
25408c2ecf20Sopenharmony_ci		cdb[0] = is_write ? WRITE_16 : READ_16;
25418c2ecf20Sopenharmony_ci		cdb[1] = 0;
25428c2ecf20Sopenharmony_ci		put_unaligned_be64(disk_block, &cdb[2]);
25438c2ecf20Sopenharmony_ci		put_unaligned_be32(disk_block_cnt, &cdb[10]);
25448c2ecf20Sopenharmony_ci		cdb[14] = 0;
25458c2ecf20Sopenharmony_ci		cdb[15] = 0;
25468c2ecf20Sopenharmony_ci		cdb_length = 16;
25478c2ecf20Sopenharmony_ci	} else {
25488c2ecf20Sopenharmony_ci		cdb[0] = is_write ? WRITE_10 : READ_10;
25498c2ecf20Sopenharmony_ci		cdb[1] = 0;
25508c2ecf20Sopenharmony_ci		put_unaligned_be32((u32)disk_block, &cdb[2]);
25518c2ecf20Sopenharmony_ci		cdb[6] = 0;
25528c2ecf20Sopenharmony_ci		put_unaligned_be16((u16)disk_block_cnt, &cdb[7]);
25538c2ecf20Sopenharmony_ci		cdb[9] = 0;
25548c2ecf20Sopenharmony_ci		cdb_length = 10;
25558c2ecf20Sopenharmony_ci	}
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci	if (get_unaligned_le16(&raid_map->flags) &
25588c2ecf20Sopenharmony_ci		RAID_MAP_ENCRYPTION_ENABLED) {
25598c2ecf20Sopenharmony_ci		pqi_set_encryption_info(&encryption_info, raid_map,
25608c2ecf20Sopenharmony_ci			first_block);
25618c2ecf20Sopenharmony_ci		encryption_info_ptr = &encryption_info;
25628c2ecf20Sopenharmony_ci	} else {
25638c2ecf20Sopenharmony_ci		encryption_info_ptr = NULL;
25648c2ecf20Sopenharmony_ci	}
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	return pqi_aio_submit_io(ctrl_info, scmd, aio_handle,
25678c2ecf20Sopenharmony_ci		cdb, cdb_length, queue_group, encryption_info_ptr, true);
25688c2ecf20Sopenharmony_ci}
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci#define PQI_STATUS_IDLE		0x0
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci#define PQI_CREATE_ADMIN_QUEUE_PAIR	1
25738c2ecf20Sopenharmony_ci#define PQI_DELETE_ADMIN_QUEUE_PAIR	2
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci#define PQI_DEVICE_STATE_POWER_ON_AND_RESET		0x0
25768c2ecf20Sopenharmony_ci#define PQI_DEVICE_STATE_STATUS_AVAILABLE		0x1
25778c2ecf20Sopenharmony_ci#define PQI_DEVICE_STATE_ALL_REGISTERS_READY		0x2
25788c2ecf20Sopenharmony_ci#define PQI_DEVICE_STATE_ADMIN_QUEUE_PAIR_READY		0x3
25798c2ecf20Sopenharmony_ci#define PQI_DEVICE_STATE_ERROR				0x4
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci#define PQI_MODE_READY_TIMEOUT_SECS		30
25828c2ecf20Sopenharmony_ci#define PQI_MODE_READY_POLL_INTERVAL_MSECS	1
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_cistatic int pqi_wait_for_pqi_mode_ready(struct pqi_ctrl_info *ctrl_info)
25858c2ecf20Sopenharmony_ci{
25868c2ecf20Sopenharmony_ci	struct pqi_device_registers __iomem *pqi_registers;
25878c2ecf20Sopenharmony_ci	unsigned long timeout;
25888c2ecf20Sopenharmony_ci	u64 signature;
25898c2ecf20Sopenharmony_ci	u8 status;
25908c2ecf20Sopenharmony_ci
25918c2ecf20Sopenharmony_ci	pqi_registers = ctrl_info->pqi_registers;
25928c2ecf20Sopenharmony_ci	timeout = (PQI_MODE_READY_TIMEOUT_SECS * PQI_HZ) + jiffies;
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci	while (1) {
25958c2ecf20Sopenharmony_ci		signature = readq(&pqi_registers->signature);
25968c2ecf20Sopenharmony_ci		if (memcmp(&signature, PQI_DEVICE_SIGNATURE,
25978c2ecf20Sopenharmony_ci			sizeof(signature)) == 0)
25988c2ecf20Sopenharmony_ci			break;
25998c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
26008c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
26018c2ecf20Sopenharmony_ci				"timed out waiting for PQI signature\n");
26028c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
26038c2ecf20Sopenharmony_ci		}
26048c2ecf20Sopenharmony_ci		msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS);
26058c2ecf20Sopenharmony_ci	}
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci	while (1) {
26088c2ecf20Sopenharmony_ci		status = readb(&pqi_registers->function_and_status_code);
26098c2ecf20Sopenharmony_ci		if (status == PQI_STATUS_IDLE)
26108c2ecf20Sopenharmony_ci			break;
26118c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
26128c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
26138c2ecf20Sopenharmony_ci				"timed out waiting for PQI IDLE\n");
26148c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
26158c2ecf20Sopenharmony_ci		}
26168c2ecf20Sopenharmony_ci		msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS);
26178c2ecf20Sopenharmony_ci	}
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	while (1) {
26208c2ecf20Sopenharmony_ci		if (readl(&pqi_registers->device_status) ==
26218c2ecf20Sopenharmony_ci			PQI_DEVICE_STATE_ALL_REGISTERS_READY)
26228c2ecf20Sopenharmony_ci			break;
26238c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
26248c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
26258c2ecf20Sopenharmony_ci				"timed out waiting for PQI all registers ready\n");
26268c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
26278c2ecf20Sopenharmony_ci		}
26288c2ecf20Sopenharmony_ci		msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS);
26298c2ecf20Sopenharmony_ci	}
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	return 0;
26328c2ecf20Sopenharmony_ci}
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_cistatic inline void pqi_aio_path_disabled(struct pqi_io_request *io_request)
26358c2ecf20Sopenharmony_ci{
26368c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	device = io_request->scmd->device->hostdata;
26398c2ecf20Sopenharmony_ci	device->raid_bypass_enabled = false;
26408c2ecf20Sopenharmony_ci	device->aio_enabled = false;
26418c2ecf20Sopenharmony_ci}
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_cistatic inline void pqi_take_device_offline(struct scsi_device *sdev, char *path)
26448c2ecf20Sopenharmony_ci{
26458c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
26468c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	device = sdev->hostdata;
26498c2ecf20Sopenharmony_ci	if (device->device_offline)
26508c2ecf20Sopenharmony_ci		return;
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	device->device_offline = true;
26538c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
26548c2ecf20Sopenharmony_ci	pqi_schedule_rescan_worker(ctrl_info);
26558c2ecf20Sopenharmony_ci	dev_err(&ctrl_info->pci_dev->dev, "re-scanning %s scsi %d:%d:%d:%d\n",
26568c2ecf20Sopenharmony_ci		path, ctrl_info->scsi_host->host_no, device->bus,
26578c2ecf20Sopenharmony_ci		device->target, device->lun);
26588c2ecf20Sopenharmony_ci}
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_cistatic void pqi_process_raid_io_error(struct pqi_io_request *io_request)
26618c2ecf20Sopenharmony_ci{
26628c2ecf20Sopenharmony_ci	u8 scsi_status;
26638c2ecf20Sopenharmony_ci	u8 host_byte;
26648c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
26658c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info;
26668c2ecf20Sopenharmony_ci	size_t sense_data_length;
26678c2ecf20Sopenharmony_ci	int residual_count;
26688c2ecf20Sopenharmony_ci	int xfer_count;
26698c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
26728c2ecf20Sopenharmony_ci	if (!scmd)
26738c2ecf20Sopenharmony_ci		return;
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	error_info = io_request->error_info;
26768c2ecf20Sopenharmony_ci	scsi_status = error_info->status;
26778c2ecf20Sopenharmony_ci	host_byte = DID_OK;
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci	switch (error_info->data_out_result) {
26808c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_GOOD:
26818c2ecf20Sopenharmony_ci		break;
26828c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_UNDERFLOW:
26838c2ecf20Sopenharmony_ci		xfer_count =
26848c2ecf20Sopenharmony_ci			get_unaligned_le32(&error_info->data_out_transferred);
26858c2ecf20Sopenharmony_ci		residual_count = scsi_bufflen(scmd) - xfer_count;
26868c2ecf20Sopenharmony_ci		scsi_set_resid(scmd, residual_count);
26878c2ecf20Sopenharmony_ci		if (xfer_count < scmd->underflow)
26888c2ecf20Sopenharmony_ci			host_byte = DID_SOFT_ERROR;
26898c2ecf20Sopenharmony_ci		break;
26908c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_UNSOLICITED_ABORT:
26918c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_ABORTED:
26928c2ecf20Sopenharmony_ci		host_byte = DID_ABORT;
26938c2ecf20Sopenharmony_ci		break;
26948c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_TIMEOUT:
26958c2ecf20Sopenharmony_ci		host_byte = DID_TIME_OUT;
26968c2ecf20Sopenharmony_ci		break;
26978c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_OVERFLOW:
26988c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PROTOCOL_ERROR:
26998c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_ERROR:
27008c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA:
27018c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE:
27028c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_ERROR:
27038c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_HARDWARE_ERROR:
27048c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR:
27058c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT:
27068c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED:
27078c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED:
27088c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED:
27098c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST:
27108c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION:
27118c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED:
27128c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ:
27138c2ecf20Sopenharmony_ci	default:
27148c2ecf20Sopenharmony_ci		host_byte = DID_ERROR;
27158c2ecf20Sopenharmony_ci		break;
27168c2ecf20Sopenharmony_ci	}
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci	sense_data_length = get_unaligned_le16(&error_info->sense_data_length);
27198c2ecf20Sopenharmony_ci	if (sense_data_length == 0)
27208c2ecf20Sopenharmony_ci		sense_data_length =
27218c2ecf20Sopenharmony_ci			get_unaligned_le16(&error_info->response_data_length);
27228c2ecf20Sopenharmony_ci	if (sense_data_length) {
27238c2ecf20Sopenharmony_ci		if (sense_data_length > sizeof(error_info->data))
27248c2ecf20Sopenharmony_ci			sense_data_length = sizeof(error_info->data);
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci		if (scsi_status == SAM_STAT_CHECK_CONDITION &&
27278c2ecf20Sopenharmony_ci			scsi_normalize_sense(error_info->data,
27288c2ecf20Sopenharmony_ci				sense_data_length, &sshdr) &&
27298c2ecf20Sopenharmony_ci				sshdr.sense_key == HARDWARE_ERROR &&
27308c2ecf20Sopenharmony_ci				sshdr.asc == 0x3e) {
27318c2ecf20Sopenharmony_ci			struct pqi_ctrl_info *ctrl_info = shost_to_hba(scmd->device->host);
27328c2ecf20Sopenharmony_ci			struct pqi_scsi_dev *device = scmd->device->hostdata;
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci			switch (sshdr.ascq) {
27358c2ecf20Sopenharmony_ci			case 0x1: /* LOGICAL UNIT FAILURE */
27368c2ecf20Sopenharmony_ci				if (printk_ratelimit())
27378c2ecf20Sopenharmony_ci					scmd_printk(KERN_ERR, scmd, "received 'logical unit failure' from controller for scsi %d:%d:%d:%d\n",
27388c2ecf20Sopenharmony_ci						ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun);
27398c2ecf20Sopenharmony_ci				pqi_take_device_offline(scmd->device, "RAID");
27408c2ecf20Sopenharmony_ci				host_byte = DID_NO_CONNECT;
27418c2ecf20Sopenharmony_ci				break;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci			default: /* See http://www.t10.org/lists/asc-num.htm#ASC_3E */
27448c2ecf20Sopenharmony_ci				if (printk_ratelimit())
27458c2ecf20Sopenharmony_ci					scmd_printk(KERN_ERR, scmd, "received unhandled error %d from controller for scsi %d:%d:%d:%d\n",
27468c2ecf20Sopenharmony_ci						sshdr.ascq, ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun);
27478c2ecf20Sopenharmony_ci				break;
27488c2ecf20Sopenharmony_ci			}
27498c2ecf20Sopenharmony_ci		}
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci		if (sense_data_length > SCSI_SENSE_BUFFERSIZE)
27528c2ecf20Sopenharmony_ci			sense_data_length = SCSI_SENSE_BUFFERSIZE;
27538c2ecf20Sopenharmony_ci		memcpy(scmd->sense_buffer, error_info->data,
27548c2ecf20Sopenharmony_ci			sense_data_length);
27558c2ecf20Sopenharmony_ci	}
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	scmd->result = scsi_status;
27588c2ecf20Sopenharmony_ci	set_host_byte(scmd, host_byte);
27598c2ecf20Sopenharmony_ci}
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_cistatic void pqi_process_aio_io_error(struct pqi_io_request *io_request)
27628c2ecf20Sopenharmony_ci{
27638c2ecf20Sopenharmony_ci	u8 scsi_status;
27648c2ecf20Sopenharmony_ci	u8 host_byte;
27658c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
27668c2ecf20Sopenharmony_ci	struct pqi_aio_error_info *error_info;
27678c2ecf20Sopenharmony_ci	size_t sense_data_length;
27688c2ecf20Sopenharmony_ci	int residual_count;
27698c2ecf20Sopenharmony_ci	int xfer_count;
27708c2ecf20Sopenharmony_ci	bool device_offline;
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
27738c2ecf20Sopenharmony_ci	error_info = io_request->error_info;
27748c2ecf20Sopenharmony_ci	host_byte = DID_OK;
27758c2ecf20Sopenharmony_ci	sense_data_length = 0;
27768c2ecf20Sopenharmony_ci	device_offline = false;
27778c2ecf20Sopenharmony_ci
27788c2ecf20Sopenharmony_ci	switch (error_info->service_response) {
27798c2ecf20Sopenharmony_ci	case PQI_AIO_SERV_RESPONSE_COMPLETE:
27808c2ecf20Sopenharmony_ci		scsi_status = error_info->status;
27818c2ecf20Sopenharmony_ci		break;
27828c2ecf20Sopenharmony_ci	case PQI_AIO_SERV_RESPONSE_FAILURE:
27838c2ecf20Sopenharmony_ci		switch (error_info->status) {
27848c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_IO_ABORTED:
27858c2ecf20Sopenharmony_ci			scsi_status = SAM_STAT_TASK_ABORTED;
27868c2ecf20Sopenharmony_ci			break;
27878c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_UNDERRUN:
27888c2ecf20Sopenharmony_ci			scsi_status = SAM_STAT_GOOD;
27898c2ecf20Sopenharmony_ci			residual_count = get_unaligned_le32(
27908c2ecf20Sopenharmony_ci						&error_info->residual_count);
27918c2ecf20Sopenharmony_ci			scsi_set_resid(scmd, residual_count);
27928c2ecf20Sopenharmony_ci			xfer_count = scsi_bufflen(scmd) - residual_count;
27938c2ecf20Sopenharmony_ci			if (xfer_count < scmd->underflow)
27948c2ecf20Sopenharmony_ci				host_byte = DID_SOFT_ERROR;
27958c2ecf20Sopenharmony_ci			break;
27968c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_OVERRUN:
27978c2ecf20Sopenharmony_ci			scsi_status = SAM_STAT_GOOD;
27988c2ecf20Sopenharmony_ci			break;
27998c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_AIO_PATH_DISABLED:
28008c2ecf20Sopenharmony_ci			pqi_aio_path_disabled(io_request);
28018c2ecf20Sopenharmony_ci			scsi_status = SAM_STAT_GOOD;
28028c2ecf20Sopenharmony_ci			io_request->status = -EAGAIN;
28038c2ecf20Sopenharmony_ci			break;
28048c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_NO_PATH_TO_DEVICE:
28058c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_INVALID_DEVICE:
28068c2ecf20Sopenharmony_ci			if (!io_request->raid_bypass) {
28078c2ecf20Sopenharmony_ci				device_offline = true;
28088c2ecf20Sopenharmony_ci				pqi_take_device_offline(scmd->device, "AIO");
28098c2ecf20Sopenharmony_ci				host_byte = DID_NO_CONNECT;
28108c2ecf20Sopenharmony_ci			}
28118c2ecf20Sopenharmony_ci			scsi_status = SAM_STAT_CHECK_CONDITION;
28128c2ecf20Sopenharmony_ci			break;
28138c2ecf20Sopenharmony_ci		case PQI_AIO_STATUS_IO_ERROR:
28148c2ecf20Sopenharmony_ci		default:
28158c2ecf20Sopenharmony_ci			scsi_status = SAM_STAT_CHECK_CONDITION;
28168c2ecf20Sopenharmony_ci			break;
28178c2ecf20Sopenharmony_ci		}
28188c2ecf20Sopenharmony_ci		break;
28198c2ecf20Sopenharmony_ci	case PQI_AIO_SERV_RESPONSE_TMF_COMPLETE:
28208c2ecf20Sopenharmony_ci	case PQI_AIO_SERV_RESPONSE_TMF_SUCCEEDED:
28218c2ecf20Sopenharmony_ci		scsi_status = SAM_STAT_GOOD;
28228c2ecf20Sopenharmony_ci		break;
28238c2ecf20Sopenharmony_ci	case PQI_AIO_SERV_RESPONSE_TMF_REJECTED:
28248c2ecf20Sopenharmony_ci	case PQI_AIO_SERV_RESPONSE_TMF_INCORRECT_LUN:
28258c2ecf20Sopenharmony_ci	default:
28268c2ecf20Sopenharmony_ci		scsi_status = SAM_STAT_CHECK_CONDITION;
28278c2ecf20Sopenharmony_ci		break;
28288c2ecf20Sopenharmony_ci	}
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci	if (error_info->data_present) {
28318c2ecf20Sopenharmony_ci		sense_data_length =
28328c2ecf20Sopenharmony_ci			get_unaligned_le16(&error_info->data_length);
28338c2ecf20Sopenharmony_ci		if (sense_data_length) {
28348c2ecf20Sopenharmony_ci			if (sense_data_length > sizeof(error_info->data))
28358c2ecf20Sopenharmony_ci				sense_data_length = sizeof(error_info->data);
28368c2ecf20Sopenharmony_ci			if (sense_data_length > SCSI_SENSE_BUFFERSIZE)
28378c2ecf20Sopenharmony_ci				sense_data_length = SCSI_SENSE_BUFFERSIZE;
28388c2ecf20Sopenharmony_ci			memcpy(scmd->sense_buffer, error_info->data,
28398c2ecf20Sopenharmony_ci				sense_data_length);
28408c2ecf20Sopenharmony_ci		}
28418c2ecf20Sopenharmony_ci	}
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_ci	if (device_offline && sense_data_length == 0)
28448c2ecf20Sopenharmony_ci		scsi_build_sense_buffer(0, scmd->sense_buffer, HARDWARE_ERROR,
28458c2ecf20Sopenharmony_ci			0x3e, 0x1);
28468c2ecf20Sopenharmony_ci
28478c2ecf20Sopenharmony_ci	scmd->result = scsi_status;
28488c2ecf20Sopenharmony_ci	set_host_byte(scmd, host_byte);
28498c2ecf20Sopenharmony_ci}
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_cistatic void pqi_process_io_error(unsigned int iu_type,
28528c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request)
28538c2ecf20Sopenharmony_ci{
28548c2ecf20Sopenharmony_ci	switch (iu_type) {
28558c2ecf20Sopenharmony_ci	case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR:
28568c2ecf20Sopenharmony_ci		pqi_process_raid_io_error(io_request);
28578c2ecf20Sopenharmony_ci		break;
28588c2ecf20Sopenharmony_ci	case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR:
28598c2ecf20Sopenharmony_ci		pqi_process_aio_io_error(io_request);
28608c2ecf20Sopenharmony_ci		break;
28618c2ecf20Sopenharmony_ci	}
28628c2ecf20Sopenharmony_ci}
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_cistatic int pqi_interpret_task_management_response(
28658c2ecf20Sopenharmony_ci	struct pqi_task_management_response *response)
28668c2ecf20Sopenharmony_ci{
28678c2ecf20Sopenharmony_ci	int rc;
28688c2ecf20Sopenharmony_ci
28698c2ecf20Sopenharmony_ci	switch (response->response_code) {
28708c2ecf20Sopenharmony_ci	case SOP_TMF_COMPLETE:
28718c2ecf20Sopenharmony_ci	case SOP_TMF_FUNCTION_SUCCEEDED:
28728c2ecf20Sopenharmony_ci		rc = 0;
28738c2ecf20Sopenharmony_ci		break;
28748c2ecf20Sopenharmony_ci	case SOP_TMF_REJECTED:
28758c2ecf20Sopenharmony_ci		rc = -EAGAIN;
28768c2ecf20Sopenharmony_ci		break;
28778c2ecf20Sopenharmony_ci	default:
28788c2ecf20Sopenharmony_ci		rc = -EIO;
28798c2ecf20Sopenharmony_ci		break;
28808c2ecf20Sopenharmony_ci	}
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_ci	return rc;
28838c2ecf20Sopenharmony_ci}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_cistatic inline void pqi_invalid_response(struct pqi_ctrl_info *ctrl_info)
28868c2ecf20Sopenharmony_ci{
28878c2ecf20Sopenharmony_ci	pqi_take_ctrl_offline(ctrl_info);
28888c2ecf20Sopenharmony_ci}
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_cistatic int pqi_process_io_intr(struct pqi_ctrl_info *ctrl_info, struct pqi_queue_group *queue_group)
28918c2ecf20Sopenharmony_ci{
28928c2ecf20Sopenharmony_ci	int num_responses;
28938c2ecf20Sopenharmony_ci	pqi_index_t oq_pi;
28948c2ecf20Sopenharmony_ci	pqi_index_t oq_ci;
28958c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
28968c2ecf20Sopenharmony_ci	struct pqi_io_response *response;
28978c2ecf20Sopenharmony_ci	u16 request_id;
28988c2ecf20Sopenharmony_ci
28998c2ecf20Sopenharmony_ci	num_responses = 0;
29008c2ecf20Sopenharmony_ci	oq_ci = queue_group->oq_ci_copy;
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci	while (1) {
29038c2ecf20Sopenharmony_ci		oq_pi = readl(queue_group->oq_pi);
29048c2ecf20Sopenharmony_ci		if (oq_pi >= ctrl_info->num_elements_per_oq) {
29058c2ecf20Sopenharmony_ci			pqi_invalid_response(ctrl_info);
29068c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
29078c2ecf20Sopenharmony_ci				"I/O interrupt: producer index (%u) out of range (0-%u): consumer index: %u\n",
29088c2ecf20Sopenharmony_ci				oq_pi, ctrl_info->num_elements_per_oq - 1, oq_ci);
29098c2ecf20Sopenharmony_ci			return -1;
29108c2ecf20Sopenharmony_ci		}
29118c2ecf20Sopenharmony_ci		if (oq_pi == oq_ci)
29128c2ecf20Sopenharmony_ci			break;
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci		num_responses++;
29158c2ecf20Sopenharmony_ci		response = queue_group->oq_element_array +
29168c2ecf20Sopenharmony_ci			(oq_ci * PQI_OPERATIONAL_OQ_ELEMENT_LENGTH);
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci		request_id = get_unaligned_le16(&response->request_id);
29198c2ecf20Sopenharmony_ci		if (request_id >= ctrl_info->max_io_slots) {
29208c2ecf20Sopenharmony_ci			pqi_invalid_response(ctrl_info);
29218c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
29228c2ecf20Sopenharmony_ci				"request ID in response (%u) out of range (0-%u): producer index: %u  consumer index: %u\n",
29238c2ecf20Sopenharmony_ci				request_id, ctrl_info->max_io_slots - 1, oq_pi, oq_ci);
29248c2ecf20Sopenharmony_ci			return -1;
29258c2ecf20Sopenharmony_ci		}
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_ci		io_request = &ctrl_info->io_request_pool[request_id];
29288c2ecf20Sopenharmony_ci		if (atomic_read(&io_request->refcount) == 0) {
29298c2ecf20Sopenharmony_ci			pqi_invalid_response(ctrl_info);
29308c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
29318c2ecf20Sopenharmony_ci				"request ID in response (%u) does not match an outstanding I/O request: producer index: %u  consumer index: %u\n",
29328c2ecf20Sopenharmony_ci				request_id, oq_pi, oq_ci);
29338c2ecf20Sopenharmony_ci			return -1;
29348c2ecf20Sopenharmony_ci		}
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci		switch (response->header.iu_type) {
29378c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS:
29388c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS:
29398c2ecf20Sopenharmony_ci			if (io_request->scmd)
29408c2ecf20Sopenharmony_ci				io_request->scmd->result = 0;
29418c2ecf20Sopenharmony_ci			fallthrough;
29428c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_GENERAL_MANAGEMENT:
29438c2ecf20Sopenharmony_ci			break;
29448c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_VENDOR_GENERAL:
29458c2ecf20Sopenharmony_ci			io_request->status =
29468c2ecf20Sopenharmony_ci				get_unaligned_le16(
29478c2ecf20Sopenharmony_ci				&((struct pqi_vendor_general_response *)
29488c2ecf20Sopenharmony_ci					response)->status);
29498c2ecf20Sopenharmony_ci			break;
29508c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_TASK_MANAGEMENT:
29518c2ecf20Sopenharmony_ci			io_request->status =
29528c2ecf20Sopenharmony_ci				pqi_interpret_task_management_response(
29538c2ecf20Sopenharmony_ci					(void *)response);
29548c2ecf20Sopenharmony_ci			break;
29558c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_AIO_PATH_DISABLED:
29568c2ecf20Sopenharmony_ci			pqi_aio_path_disabled(io_request);
29578c2ecf20Sopenharmony_ci			io_request->status = -EAGAIN;
29588c2ecf20Sopenharmony_ci			break;
29598c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR:
29608c2ecf20Sopenharmony_ci		case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR:
29618c2ecf20Sopenharmony_ci			io_request->error_info = ctrl_info->error_buffer +
29628c2ecf20Sopenharmony_ci				(get_unaligned_le16(&response->error_index) *
29638c2ecf20Sopenharmony_ci				PQI_ERROR_BUFFER_ELEMENT_LENGTH);
29648c2ecf20Sopenharmony_ci			pqi_process_io_error(response->header.iu_type, io_request);
29658c2ecf20Sopenharmony_ci			break;
29668c2ecf20Sopenharmony_ci		default:
29678c2ecf20Sopenharmony_ci			pqi_invalid_response(ctrl_info);
29688c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
29698c2ecf20Sopenharmony_ci				"unexpected IU type: 0x%x: producer index: %u  consumer index: %u\n",
29708c2ecf20Sopenharmony_ci				response->header.iu_type, oq_pi, oq_ci);
29718c2ecf20Sopenharmony_ci			return -1;
29728c2ecf20Sopenharmony_ci		}
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci		io_request->io_complete_callback(io_request, io_request->context);
29758c2ecf20Sopenharmony_ci
29768c2ecf20Sopenharmony_ci		/*
29778c2ecf20Sopenharmony_ci		 * Note that the I/O request structure CANNOT BE TOUCHED after
29788c2ecf20Sopenharmony_ci		 * returning from the I/O completion callback!
29798c2ecf20Sopenharmony_ci		 */
29808c2ecf20Sopenharmony_ci		oq_ci = (oq_ci + 1) % ctrl_info->num_elements_per_oq;
29818c2ecf20Sopenharmony_ci	}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	if (num_responses) {
29848c2ecf20Sopenharmony_ci		queue_group->oq_ci_copy = oq_ci;
29858c2ecf20Sopenharmony_ci		writel(oq_ci, queue_group->oq_ci);
29868c2ecf20Sopenharmony_ci	}
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci	return num_responses;
29898c2ecf20Sopenharmony_ci}
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_cistatic inline unsigned int pqi_num_elements_free(unsigned int pi,
29928c2ecf20Sopenharmony_ci	unsigned int ci, unsigned int elements_in_queue)
29938c2ecf20Sopenharmony_ci{
29948c2ecf20Sopenharmony_ci	unsigned int num_elements_used;
29958c2ecf20Sopenharmony_ci
29968c2ecf20Sopenharmony_ci	if (pi >= ci)
29978c2ecf20Sopenharmony_ci		num_elements_used = pi - ci;
29988c2ecf20Sopenharmony_ci	else
29998c2ecf20Sopenharmony_ci		num_elements_used = elements_in_queue - ci + pi;
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	return elements_in_queue - num_elements_used - 1;
30028c2ecf20Sopenharmony_ci}
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_cistatic void pqi_send_event_ack(struct pqi_ctrl_info *ctrl_info,
30058c2ecf20Sopenharmony_ci	struct pqi_event_acknowledge_request *iu, size_t iu_length)
30068c2ecf20Sopenharmony_ci{
30078c2ecf20Sopenharmony_ci	pqi_index_t iq_pi;
30088c2ecf20Sopenharmony_ci	pqi_index_t iq_ci;
30098c2ecf20Sopenharmony_ci	unsigned long flags;
30108c2ecf20Sopenharmony_ci	void *next_element;
30118c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
30128c2ecf20Sopenharmony_ci
30138c2ecf20Sopenharmony_ci	queue_group = &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP];
30148c2ecf20Sopenharmony_ci	put_unaligned_le16(queue_group->oq_id, &iu->header.response_queue_id);
30158c2ecf20Sopenharmony_ci
30168c2ecf20Sopenharmony_ci	while (1) {
30178c2ecf20Sopenharmony_ci		spin_lock_irqsave(&queue_group->submit_lock[RAID_PATH], flags);
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_ci		iq_pi = queue_group->iq_pi_copy[RAID_PATH];
30208c2ecf20Sopenharmony_ci		iq_ci = readl(queue_group->iq_ci[RAID_PATH]);
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_ci		if (pqi_num_elements_free(iq_pi, iq_ci,
30238c2ecf20Sopenharmony_ci			ctrl_info->num_elements_per_iq))
30248c2ecf20Sopenharmony_ci			break;
30258c2ecf20Sopenharmony_ci
30268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(
30278c2ecf20Sopenharmony_ci			&queue_group->submit_lock[RAID_PATH], flags);
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info))
30308c2ecf20Sopenharmony_ci			return;
30318c2ecf20Sopenharmony_ci	}
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci	next_element = queue_group->iq_element_array[RAID_PATH] +
30348c2ecf20Sopenharmony_ci		(iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ci	memcpy(next_element, iu, iu_length);
30378c2ecf20Sopenharmony_ci
30388c2ecf20Sopenharmony_ci	iq_pi = (iq_pi + 1) % ctrl_info->num_elements_per_iq;
30398c2ecf20Sopenharmony_ci	queue_group->iq_pi_copy[RAID_PATH] = iq_pi;
30408c2ecf20Sopenharmony_ci
30418c2ecf20Sopenharmony_ci	/*
30428c2ecf20Sopenharmony_ci	 * This write notifies the controller that an IU is available to be
30438c2ecf20Sopenharmony_ci	 * processed.
30448c2ecf20Sopenharmony_ci	 */
30458c2ecf20Sopenharmony_ci	writel(iq_pi, queue_group->iq_pi[RAID_PATH]);
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue_group->submit_lock[RAID_PATH], flags);
30488c2ecf20Sopenharmony_ci}
30498c2ecf20Sopenharmony_ci
30508c2ecf20Sopenharmony_cistatic void pqi_acknowledge_event(struct pqi_ctrl_info *ctrl_info,
30518c2ecf20Sopenharmony_ci	struct pqi_event *event)
30528c2ecf20Sopenharmony_ci{
30538c2ecf20Sopenharmony_ci	struct pqi_event_acknowledge_request request;
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT;
30588c2ecf20Sopenharmony_ci	put_unaligned_le16(sizeof(request) - PQI_REQUEST_HEADER_LENGTH,
30598c2ecf20Sopenharmony_ci		&request.header.iu_length);
30608c2ecf20Sopenharmony_ci	request.event_type = event->event_type;
30618c2ecf20Sopenharmony_ci	request.event_id = event->event_id;
30628c2ecf20Sopenharmony_ci	request.additional_event_id = event->additional_event_id;
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	pqi_send_event_ack(ctrl_info, &request, sizeof(request));
30658c2ecf20Sopenharmony_ci}
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci#define PQI_SOFT_RESET_STATUS_TIMEOUT_SECS		30
30688c2ecf20Sopenharmony_ci#define PQI_SOFT_RESET_STATUS_POLL_INTERVAL_SECS	1
30698c2ecf20Sopenharmony_ci
30708c2ecf20Sopenharmony_cistatic enum pqi_soft_reset_status pqi_poll_for_soft_reset_status(
30718c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
30728c2ecf20Sopenharmony_ci{
30738c2ecf20Sopenharmony_ci	unsigned long timeout;
30748c2ecf20Sopenharmony_ci	u8 status;
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci	timeout = (PQI_SOFT_RESET_STATUS_TIMEOUT_SECS * PQI_HZ) + jiffies;
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	while (1) {
30798c2ecf20Sopenharmony_ci		status = pqi_read_soft_reset_status(ctrl_info);
30808c2ecf20Sopenharmony_ci		if (status & PQI_SOFT_RESET_INITIATE)
30818c2ecf20Sopenharmony_ci			return RESET_INITIATE_DRIVER;
30828c2ecf20Sopenharmony_ci
30838c2ecf20Sopenharmony_ci		if (status & PQI_SOFT_RESET_ABORT)
30848c2ecf20Sopenharmony_ci			return RESET_ABORT;
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
30878c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
30888c2ecf20Sopenharmony_ci				"timed out waiting for soft reset status\n");
30898c2ecf20Sopenharmony_ci			return RESET_TIMEDOUT;
30908c2ecf20Sopenharmony_ci		}
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_ci		if (!sis_is_firmware_running(ctrl_info))
30938c2ecf20Sopenharmony_ci			return RESET_NORESPONSE;
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_ci		ssleep(PQI_SOFT_RESET_STATUS_POLL_INTERVAL_SECS);
30968c2ecf20Sopenharmony_ci	}
30978c2ecf20Sopenharmony_ci}
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_cistatic void pqi_process_soft_reset(struct pqi_ctrl_info *ctrl_info,
31008c2ecf20Sopenharmony_ci	enum pqi_soft_reset_status reset_status)
31018c2ecf20Sopenharmony_ci{
31028c2ecf20Sopenharmony_ci	int rc;
31038c2ecf20Sopenharmony_ci
31048c2ecf20Sopenharmony_ci	switch (reset_status) {
31058c2ecf20Sopenharmony_ci	case RESET_INITIATE_DRIVER:
31068c2ecf20Sopenharmony_ci	case RESET_TIMEDOUT:
31078c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev,
31088c2ecf20Sopenharmony_ci			"resetting controller %u\n", ctrl_info->ctrl_id);
31098c2ecf20Sopenharmony_ci		sis_soft_reset(ctrl_info);
31108c2ecf20Sopenharmony_ci		fallthrough;
31118c2ecf20Sopenharmony_ci	case RESET_INITIATE_FIRMWARE:
31128c2ecf20Sopenharmony_ci		rc = pqi_ofa_ctrl_restart(ctrl_info);
31138c2ecf20Sopenharmony_ci		pqi_ofa_free_host_buffer(ctrl_info);
31148c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev,
31158c2ecf20Sopenharmony_ci			"Online Firmware Activation for controller %u: %s\n",
31168c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id, rc == 0 ? "SUCCESS" : "FAILED");
31178c2ecf20Sopenharmony_ci		break;
31188c2ecf20Sopenharmony_ci	case RESET_ABORT:
31198c2ecf20Sopenharmony_ci		pqi_ofa_ctrl_unquiesce(ctrl_info);
31208c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev,
31218c2ecf20Sopenharmony_ci			"Online Firmware Activation for controller %u: %s\n",
31228c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id, "ABORTED");
31238c2ecf20Sopenharmony_ci		break;
31248c2ecf20Sopenharmony_ci	case RESET_NORESPONSE:
31258c2ecf20Sopenharmony_ci		pqi_ofa_free_host_buffer(ctrl_info);
31268c2ecf20Sopenharmony_ci		pqi_take_ctrl_offline(ctrl_info);
31278c2ecf20Sopenharmony_ci		break;
31288c2ecf20Sopenharmony_ci	}
31298c2ecf20Sopenharmony_ci}
31308c2ecf20Sopenharmony_ci
31318c2ecf20Sopenharmony_cistatic void pqi_ofa_process_event(struct pqi_ctrl_info *ctrl_info,
31328c2ecf20Sopenharmony_ci	struct pqi_event *event)
31338c2ecf20Sopenharmony_ci{
31348c2ecf20Sopenharmony_ci	u16 event_id;
31358c2ecf20Sopenharmony_ci	enum pqi_soft_reset_status status;
31368c2ecf20Sopenharmony_ci
31378c2ecf20Sopenharmony_ci	event_id = get_unaligned_le16(&event->event_id);
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	mutex_lock(&ctrl_info->ofa_mutex);
31408c2ecf20Sopenharmony_ci
31418c2ecf20Sopenharmony_ci	if (event_id == PQI_EVENT_OFA_QUIESCE) {
31428c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev,
31438c2ecf20Sopenharmony_ci			"Received Online Firmware Activation quiesce event for controller %u\n",
31448c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id);
31458c2ecf20Sopenharmony_ci		pqi_ofa_ctrl_quiesce(ctrl_info);
31468c2ecf20Sopenharmony_ci		pqi_acknowledge_event(ctrl_info, event);
31478c2ecf20Sopenharmony_ci		if (ctrl_info->soft_reset_handshake_supported) {
31488c2ecf20Sopenharmony_ci			status = pqi_poll_for_soft_reset_status(ctrl_info);
31498c2ecf20Sopenharmony_ci			pqi_process_soft_reset(ctrl_info, status);
31508c2ecf20Sopenharmony_ci		} else {
31518c2ecf20Sopenharmony_ci			pqi_process_soft_reset(ctrl_info,
31528c2ecf20Sopenharmony_ci					RESET_INITIATE_FIRMWARE);
31538c2ecf20Sopenharmony_ci		}
31548c2ecf20Sopenharmony_ci
31558c2ecf20Sopenharmony_ci	} else if (event_id == PQI_EVENT_OFA_MEMORY_ALLOCATION) {
31568c2ecf20Sopenharmony_ci		pqi_acknowledge_event(ctrl_info, event);
31578c2ecf20Sopenharmony_ci		pqi_ofa_setup_host_buffer(ctrl_info,
31588c2ecf20Sopenharmony_ci			le32_to_cpu(event->ofa_bytes_requested));
31598c2ecf20Sopenharmony_ci		pqi_ofa_host_memory_update(ctrl_info);
31608c2ecf20Sopenharmony_ci	} else if (event_id == PQI_EVENT_OFA_CANCELLED) {
31618c2ecf20Sopenharmony_ci		pqi_ofa_free_host_buffer(ctrl_info);
31628c2ecf20Sopenharmony_ci		pqi_acknowledge_event(ctrl_info, event);
31638c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev,
31648c2ecf20Sopenharmony_ci			"Online Firmware Activation(%u) cancel reason : %u\n",
31658c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id, event->ofa_cancel_reason);
31668c2ecf20Sopenharmony_ci	}
31678c2ecf20Sopenharmony_ci
31688c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl_info->ofa_mutex);
31698c2ecf20Sopenharmony_ci}
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_cistatic void pqi_event_worker(struct work_struct *work)
31728c2ecf20Sopenharmony_ci{
31738c2ecf20Sopenharmony_ci	unsigned int i;
31748c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
31758c2ecf20Sopenharmony_ci	struct pqi_event *event;
31768c2ecf20Sopenharmony_ci
31778c2ecf20Sopenharmony_ci	ctrl_info = container_of(work, struct pqi_ctrl_info, event_work);
31788c2ecf20Sopenharmony_ci
31798c2ecf20Sopenharmony_ci	pqi_ctrl_busy(ctrl_info);
31808c2ecf20Sopenharmony_ci	pqi_wait_if_ctrl_blocked(ctrl_info, NO_TIMEOUT);
31818c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
31828c2ecf20Sopenharmony_ci		goto out;
31838c2ecf20Sopenharmony_ci
31848c2ecf20Sopenharmony_ci	pqi_schedule_rescan_worker_delayed(ctrl_info);
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci	event = ctrl_info->events;
31878c2ecf20Sopenharmony_ci	for (i = 0; i < PQI_NUM_SUPPORTED_EVENTS; i++) {
31888c2ecf20Sopenharmony_ci		if (event->pending) {
31898c2ecf20Sopenharmony_ci			event->pending = false;
31908c2ecf20Sopenharmony_ci			if (event->event_type == PQI_EVENT_TYPE_OFA) {
31918c2ecf20Sopenharmony_ci				pqi_ctrl_unbusy(ctrl_info);
31928c2ecf20Sopenharmony_ci				pqi_ofa_process_event(ctrl_info, event);
31938c2ecf20Sopenharmony_ci				return;
31948c2ecf20Sopenharmony_ci			}
31958c2ecf20Sopenharmony_ci			pqi_acknowledge_event(ctrl_info, event);
31968c2ecf20Sopenharmony_ci		}
31978c2ecf20Sopenharmony_ci		event++;
31988c2ecf20Sopenharmony_ci	}
31998c2ecf20Sopenharmony_ci
32008c2ecf20Sopenharmony_ciout:
32018c2ecf20Sopenharmony_ci	pqi_ctrl_unbusy(ctrl_info);
32028c2ecf20Sopenharmony_ci}
32038c2ecf20Sopenharmony_ci
32048c2ecf20Sopenharmony_ci#define PQI_HEARTBEAT_TIMER_INTERVAL	(10 * PQI_HZ)
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_cistatic void pqi_heartbeat_timer_handler(struct timer_list *t)
32078c2ecf20Sopenharmony_ci{
32088c2ecf20Sopenharmony_ci	int num_interrupts;
32098c2ecf20Sopenharmony_ci	u32 heartbeat_count;
32108c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info = from_timer(ctrl_info, t,
32118c2ecf20Sopenharmony_ci						     heartbeat_timer);
32128c2ecf20Sopenharmony_ci
32138c2ecf20Sopenharmony_ci	pqi_check_ctrl_health(ctrl_info);
32148c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
32158c2ecf20Sopenharmony_ci		return;
32168c2ecf20Sopenharmony_ci
32178c2ecf20Sopenharmony_ci	num_interrupts = atomic_read(&ctrl_info->num_interrupts);
32188c2ecf20Sopenharmony_ci	heartbeat_count = pqi_read_heartbeat_counter(ctrl_info);
32198c2ecf20Sopenharmony_ci
32208c2ecf20Sopenharmony_ci	if (num_interrupts == ctrl_info->previous_num_interrupts) {
32218c2ecf20Sopenharmony_ci		if (heartbeat_count == ctrl_info->previous_heartbeat_count) {
32228c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
32238c2ecf20Sopenharmony_ci				"no heartbeat detected - last heartbeat count: %u\n",
32248c2ecf20Sopenharmony_ci				heartbeat_count);
32258c2ecf20Sopenharmony_ci			pqi_take_ctrl_offline(ctrl_info);
32268c2ecf20Sopenharmony_ci			return;
32278c2ecf20Sopenharmony_ci		}
32288c2ecf20Sopenharmony_ci	} else {
32298c2ecf20Sopenharmony_ci		ctrl_info->previous_num_interrupts = num_interrupts;
32308c2ecf20Sopenharmony_ci	}
32318c2ecf20Sopenharmony_ci
32328c2ecf20Sopenharmony_ci	ctrl_info->previous_heartbeat_count = heartbeat_count;
32338c2ecf20Sopenharmony_ci	mod_timer(&ctrl_info->heartbeat_timer,
32348c2ecf20Sopenharmony_ci		jiffies + PQI_HEARTBEAT_TIMER_INTERVAL);
32358c2ecf20Sopenharmony_ci}
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_cistatic void pqi_start_heartbeat_timer(struct pqi_ctrl_info *ctrl_info)
32388c2ecf20Sopenharmony_ci{
32398c2ecf20Sopenharmony_ci	if (!ctrl_info->heartbeat_counter)
32408c2ecf20Sopenharmony_ci		return;
32418c2ecf20Sopenharmony_ci
32428c2ecf20Sopenharmony_ci	ctrl_info->previous_num_interrupts =
32438c2ecf20Sopenharmony_ci		atomic_read(&ctrl_info->num_interrupts);
32448c2ecf20Sopenharmony_ci	ctrl_info->previous_heartbeat_count =
32458c2ecf20Sopenharmony_ci		pqi_read_heartbeat_counter(ctrl_info);
32468c2ecf20Sopenharmony_ci
32478c2ecf20Sopenharmony_ci	ctrl_info->heartbeat_timer.expires =
32488c2ecf20Sopenharmony_ci		jiffies + PQI_HEARTBEAT_TIMER_INTERVAL;
32498c2ecf20Sopenharmony_ci	add_timer(&ctrl_info->heartbeat_timer);
32508c2ecf20Sopenharmony_ci}
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_cistatic inline void pqi_stop_heartbeat_timer(struct pqi_ctrl_info *ctrl_info)
32538c2ecf20Sopenharmony_ci{
32548c2ecf20Sopenharmony_ci	del_timer_sync(&ctrl_info->heartbeat_timer);
32558c2ecf20Sopenharmony_ci}
32568c2ecf20Sopenharmony_ci
32578c2ecf20Sopenharmony_cistatic inline int pqi_event_type_to_event_index(unsigned int event_type)
32588c2ecf20Sopenharmony_ci{
32598c2ecf20Sopenharmony_ci	int index;
32608c2ecf20Sopenharmony_ci
32618c2ecf20Sopenharmony_ci	for (index = 0; index < ARRAY_SIZE(pqi_supported_event_types); index++)
32628c2ecf20Sopenharmony_ci		if (event_type == pqi_supported_event_types[index])
32638c2ecf20Sopenharmony_ci			return index;
32648c2ecf20Sopenharmony_ci
32658c2ecf20Sopenharmony_ci	return -1;
32668c2ecf20Sopenharmony_ci}
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_cistatic inline bool pqi_is_supported_event(unsigned int event_type)
32698c2ecf20Sopenharmony_ci{
32708c2ecf20Sopenharmony_ci	return pqi_event_type_to_event_index(event_type) != -1;
32718c2ecf20Sopenharmony_ci}
32728c2ecf20Sopenharmony_ci
32738c2ecf20Sopenharmony_cistatic void pqi_ofa_capture_event_payload(struct pqi_event *event,
32748c2ecf20Sopenharmony_ci	struct pqi_event_response *response)
32758c2ecf20Sopenharmony_ci{
32768c2ecf20Sopenharmony_ci	u16 event_id;
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci	event_id = get_unaligned_le16(&event->event_id);
32798c2ecf20Sopenharmony_ci
32808c2ecf20Sopenharmony_ci	if (event->event_type == PQI_EVENT_TYPE_OFA) {
32818c2ecf20Sopenharmony_ci		if (event_id == PQI_EVENT_OFA_MEMORY_ALLOCATION) {
32828c2ecf20Sopenharmony_ci			event->ofa_bytes_requested =
32838c2ecf20Sopenharmony_ci			response->data.ofa_memory_allocation.bytes_requested;
32848c2ecf20Sopenharmony_ci		} else if (event_id == PQI_EVENT_OFA_CANCELLED) {
32858c2ecf20Sopenharmony_ci			event->ofa_cancel_reason =
32868c2ecf20Sopenharmony_ci			response->data.ofa_cancelled.reason;
32878c2ecf20Sopenharmony_ci		}
32888c2ecf20Sopenharmony_ci	}
32898c2ecf20Sopenharmony_ci}
32908c2ecf20Sopenharmony_ci
32918c2ecf20Sopenharmony_cistatic int pqi_process_event_intr(struct pqi_ctrl_info *ctrl_info)
32928c2ecf20Sopenharmony_ci{
32938c2ecf20Sopenharmony_ci	int num_events;
32948c2ecf20Sopenharmony_ci	pqi_index_t oq_pi;
32958c2ecf20Sopenharmony_ci	pqi_index_t oq_ci;
32968c2ecf20Sopenharmony_ci	struct pqi_event_queue *event_queue;
32978c2ecf20Sopenharmony_ci	struct pqi_event_response *response;
32988c2ecf20Sopenharmony_ci	struct pqi_event *event;
32998c2ecf20Sopenharmony_ci	int event_index;
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_ci	event_queue = &ctrl_info->event_queue;
33028c2ecf20Sopenharmony_ci	num_events = 0;
33038c2ecf20Sopenharmony_ci	oq_ci = event_queue->oq_ci_copy;
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_ci	while (1) {
33068c2ecf20Sopenharmony_ci		oq_pi = readl(event_queue->oq_pi);
33078c2ecf20Sopenharmony_ci		if (oq_pi >= PQI_NUM_EVENT_QUEUE_ELEMENTS) {
33088c2ecf20Sopenharmony_ci			pqi_invalid_response(ctrl_info);
33098c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
33108c2ecf20Sopenharmony_ci				"event interrupt: producer index (%u) out of range (0-%u): consumer index: %u\n",
33118c2ecf20Sopenharmony_ci				oq_pi, PQI_NUM_EVENT_QUEUE_ELEMENTS - 1, oq_ci);
33128c2ecf20Sopenharmony_ci			return -1;
33138c2ecf20Sopenharmony_ci		}
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ci		if (oq_pi == oq_ci)
33168c2ecf20Sopenharmony_ci			break;
33178c2ecf20Sopenharmony_ci
33188c2ecf20Sopenharmony_ci		num_events++;
33198c2ecf20Sopenharmony_ci		response = event_queue->oq_element_array + (oq_ci * PQI_EVENT_OQ_ELEMENT_LENGTH);
33208c2ecf20Sopenharmony_ci
33218c2ecf20Sopenharmony_ci		event_index =
33228c2ecf20Sopenharmony_ci			pqi_event_type_to_event_index(response->event_type);
33238c2ecf20Sopenharmony_ci
33248c2ecf20Sopenharmony_ci		if (event_index >= 0 && response->request_acknowledge) {
33258c2ecf20Sopenharmony_ci			event = &ctrl_info->events[event_index];
33268c2ecf20Sopenharmony_ci			event->pending = true;
33278c2ecf20Sopenharmony_ci			event->event_type = response->event_type;
33288c2ecf20Sopenharmony_ci			event->event_id = response->event_id;
33298c2ecf20Sopenharmony_ci			event->additional_event_id = response->additional_event_id;
33308c2ecf20Sopenharmony_ci			if (event->event_type == PQI_EVENT_TYPE_OFA)
33318c2ecf20Sopenharmony_ci				pqi_ofa_capture_event_payload(event, response);
33328c2ecf20Sopenharmony_ci		}
33338c2ecf20Sopenharmony_ci
33348c2ecf20Sopenharmony_ci		oq_ci = (oq_ci + 1) % PQI_NUM_EVENT_QUEUE_ELEMENTS;
33358c2ecf20Sopenharmony_ci	}
33368c2ecf20Sopenharmony_ci
33378c2ecf20Sopenharmony_ci	if (num_events) {
33388c2ecf20Sopenharmony_ci		event_queue->oq_ci_copy = oq_ci;
33398c2ecf20Sopenharmony_ci		writel(oq_ci, event_queue->oq_ci);
33408c2ecf20Sopenharmony_ci		schedule_work(&ctrl_info->event_work);
33418c2ecf20Sopenharmony_ci	}
33428c2ecf20Sopenharmony_ci
33438c2ecf20Sopenharmony_ci	return num_events;
33448c2ecf20Sopenharmony_ci}
33458c2ecf20Sopenharmony_ci
33468c2ecf20Sopenharmony_ci#define PQI_LEGACY_INTX_MASK	0x1
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_cistatic inline void pqi_configure_legacy_intx(struct pqi_ctrl_info *ctrl_info,
33498c2ecf20Sopenharmony_ci	bool enable_intx)
33508c2ecf20Sopenharmony_ci{
33518c2ecf20Sopenharmony_ci	u32 intx_mask;
33528c2ecf20Sopenharmony_ci	struct pqi_device_registers __iomem *pqi_registers;
33538c2ecf20Sopenharmony_ci	volatile void __iomem *register_addr;
33548c2ecf20Sopenharmony_ci
33558c2ecf20Sopenharmony_ci	pqi_registers = ctrl_info->pqi_registers;
33568c2ecf20Sopenharmony_ci
33578c2ecf20Sopenharmony_ci	if (enable_intx)
33588c2ecf20Sopenharmony_ci		register_addr = &pqi_registers->legacy_intx_mask_clear;
33598c2ecf20Sopenharmony_ci	else
33608c2ecf20Sopenharmony_ci		register_addr = &pqi_registers->legacy_intx_mask_set;
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_ci	intx_mask = readl(register_addr);
33638c2ecf20Sopenharmony_ci	intx_mask |= PQI_LEGACY_INTX_MASK;
33648c2ecf20Sopenharmony_ci	writel(intx_mask, register_addr);
33658c2ecf20Sopenharmony_ci}
33668c2ecf20Sopenharmony_ci
33678c2ecf20Sopenharmony_cistatic void pqi_change_irq_mode(struct pqi_ctrl_info *ctrl_info,
33688c2ecf20Sopenharmony_ci	enum pqi_irq_mode new_mode)
33698c2ecf20Sopenharmony_ci{
33708c2ecf20Sopenharmony_ci	switch (ctrl_info->irq_mode) {
33718c2ecf20Sopenharmony_ci	case IRQ_MODE_MSIX:
33728c2ecf20Sopenharmony_ci		switch (new_mode) {
33738c2ecf20Sopenharmony_ci		case IRQ_MODE_MSIX:
33748c2ecf20Sopenharmony_ci			break;
33758c2ecf20Sopenharmony_ci		case IRQ_MODE_INTX:
33768c2ecf20Sopenharmony_ci			pqi_configure_legacy_intx(ctrl_info, true);
33778c2ecf20Sopenharmony_ci			sis_enable_intx(ctrl_info);
33788c2ecf20Sopenharmony_ci			break;
33798c2ecf20Sopenharmony_ci		case IRQ_MODE_NONE:
33808c2ecf20Sopenharmony_ci			break;
33818c2ecf20Sopenharmony_ci		}
33828c2ecf20Sopenharmony_ci		break;
33838c2ecf20Sopenharmony_ci	case IRQ_MODE_INTX:
33848c2ecf20Sopenharmony_ci		switch (new_mode) {
33858c2ecf20Sopenharmony_ci		case IRQ_MODE_MSIX:
33868c2ecf20Sopenharmony_ci			pqi_configure_legacy_intx(ctrl_info, false);
33878c2ecf20Sopenharmony_ci			sis_enable_msix(ctrl_info);
33888c2ecf20Sopenharmony_ci			break;
33898c2ecf20Sopenharmony_ci		case IRQ_MODE_INTX:
33908c2ecf20Sopenharmony_ci			break;
33918c2ecf20Sopenharmony_ci		case IRQ_MODE_NONE:
33928c2ecf20Sopenharmony_ci			pqi_configure_legacy_intx(ctrl_info, false);
33938c2ecf20Sopenharmony_ci			break;
33948c2ecf20Sopenharmony_ci		}
33958c2ecf20Sopenharmony_ci		break;
33968c2ecf20Sopenharmony_ci	case IRQ_MODE_NONE:
33978c2ecf20Sopenharmony_ci		switch (new_mode) {
33988c2ecf20Sopenharmony_ci		case IRQ_MODE_MSIX:
33998c2ecf20Sopenharmony_ci			sis_enable_msix(ctrl_info);
34008c2ecf20Sopenharmony_ci			break;
34018c2ecf20Sopenharmony_ci		case IRQ_MODE_INTX:
34028c2ecf20Sopenharmony_ci			pqi_configure_legacy_intx(ctrl_info, true);
34038c2ecf20Sopenharmony_ci			sis_enable_intx(ctrl_info);
34048c2ecf20Sopenharmony_ci			break;
34058c2ecf20Sopenharmony_ci		case IRQ_MODE_NONE:
34068c2ecf20Sopenharmony_ci			break;
34078c2ecf20Sopenharmony_ci		}
34088c2ecf20Sopenharmony_ci		break;
34098c2ecf20Sopenharmony_ci	}
34108c2ecf20Sopenharmony_ci
34118c2ecf20Sopenharmony_ci	ctrl_info->irq_mode = new_mode;
34128c2ecf20Sopenharmony_ci}
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci#define PQI_LEGACY_INTX_PENDING		0x1
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_cistatic inline bool pqi_is_valid_irq(struct pqi_ctrl_info *ctrl_info)
34178c2ecf20Sopenharmony_ci{
34188c2ecf20Sopenharmony_ci	bool valid_irq;
34198c2ecf20Sopenharmony_ci	u32 intx_status;
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci	switch (ctrl_info->irq_mode) {
34228c2ecf20Sopenharmony_ci	case IRQ_MODE_MSIX:
34238c2ecf20Sopenharmony_ci		valid_irq = true;
34248c2ecf20Sopenharmony_ci		break;
34258c2ecf20Sopenharmony_ci	case IRQ_MODE_INTX:
34268c2ecf20Sopenharmony_ci		intx_status =
34278c2ecf20Sopenharmony_ci			readl(&ctrl_info->pqi_registers->legacy_intx_status);
34288c2ecf20Sopenharmony_ci		if (intx_status & PQI_LEGACY_INTX_PENDING)
34298c2ecf20Sopenharmony_ci			valid_irq = true;
34308c2ecf20Sopenharmony_ci		else
34318c2ecf20Sopenharmony_ci			valid_irq = false;
34328c2ecf20Sopenharmony_ci		break;
34338c2ecf20Sopenharmony_ci	case IRQ_MODE_NONE:
34348c2ecf20Sopenharmony_ci	default:
34358c2ecf20Sopenharmony_ci		valid_irq = false;
34368c2ecf20Sopenharmony_ci		break;
34378c2ecf20Sopenharmony_ci	}
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_ci	return valid_irq;
34408c2ecf20Sopenharmony_ci}
34418c2ecf20Sopenharmony_ci
34428c2ecf20Sopenharmony_cistatic irqreturn_t pqi_irq_handler(int irq, void *data)
34438c2ecf20Sopenharmony_ci{
34448c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
34458c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
34468c2ecf20Sopenharmony_ci	int num_io_responses_handled;
34478c2ecf20Sopenharmony_ci	int num_events_handled;
34488c2ecf20Sopenharmony_ci
34498c2ecf20Sopenharmony_ci	queue_group = data;
34508c2ecf20Sopenharmony_ci	ctrl_info = queue_group->ctrl_info;
34518c2ecf20Sopenharmony_ci
34528c2ecf20Sopenharmony_ci	if (!pqi_is_valid_irq(ctrl_info))
34538c2ecf20Sopenharmony_ci		return IRQ_NONE;
34548c2ecf20Sopenharmony_ci
34558c2ecf20Sopenharmony_ci	num_io_responses_handled = pqi_process_io_intr(ctrl_info, queue_group);
34568c2ecf20Sopenharmony_ci	if (num_io_responses_handled < 0)
34578c2ecf20Sopenharmony_ci		goto out;
34588c2ecf20Sopenharmony_ci
34598c2ecf20Sopenharmony_ci	if (irq == ctrl_info->event_irq) {
34608c2ecf20Sopenharmony_ci		num_events_handled = pqi_process_event_intr(ctrl_info);
34618c2ecf20Sopenharmony_ci		if (num_events_handled < 0)
34628c2ecf20Sopenharmony_ci			goto out;
34638c2ecf20Sopenharmony_ci	} else {
34648c2ecf20Sopenharmony_ci		num_events_handled = 0;
34658c2ecf20Sopenharmony_ci	}
34668c2ecf20Sopenharmony_ci
34678c2ecf20Sopenharmony_ci	if (num_io_responses_handled + num_events_handled > 0)
34688c2ecf20Sopenharmony_ci		atomic_inc(&ctrl_info->num_interrupts);
34698c2ecf20Sopenharmony_ci
34708c2ecf20Sopenharmony_ci	pqi_start_io(ctrl_info, queue_group, RAID_PATH, NULL);
34718c2ecf20Sopenharmony_ci	pqi_start_io(ctrl_info, queue_group, AIO_PATH, NULL);
34728c2ecf20Sopenharmony_ci
34738c2ecf20Sopenharmony_ciout:
34748c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
34758c2ecf20Sopenharmony_ci}
34768c2ecf20Sopenharmony_ci
34778c2ecf20Sopenharmony_cistatic int pqi_request_irqs(struct pqi_ctrl_info *ctrl_info)
34788c2ecf20Sopenharmony_ci{
34798c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = ctrl_info->pci_dev;
34808c2ecf20Sopenharmony_ci	int i;
34818c2ecf20Sopenharmony_ci	int rc;
34828c2ecf20Sopenharmony_ci
34838c2ecf20Sopenharmony_ci	ctrl_info->event_irq = pci_irq_vector(pci_dev, 0);
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_msix_vectors_enabled; i++) {
34868c2ecf20Sopenharmony_ci		rc = request_irq(pci_irq_vector(pci_dev, i), pqi_irq_handler, 0,
34878c2ecf20Sopenharmony_ci			DRIVER_NAME_SHORT, &ctrl_info->queue_groups[i]);
34888c2ecf20Sopenharmony_ci		if (rc) {
34898c2ecf20Sopenharmony_ci			dev_err(&pci_dev->dev,
34908c2ecf20Sopenharmony_ci				"irq %u init failed with error %d\n",
34918c2ecf20Sopenharmony_ci				pci_irq_vector(pci_dev, i), rc);
34928c2ecf20Sopenharmony_ci			return rc;
34938c2ecf20Sopenharmony_ci		}
34948c2ecf20Sopenharmony_ci		ctrl_info->num_msix_vectors_initialized++;
34958c2ecf20Sopenharmony_ci	}
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci	return 0;
34988c2ecf20Sopenharmony_ci}
34998c2ecf20Sopenharmony_ci
35008c2ecf20Sopenharmony_cistatic void pqi_free_irqs(struct pqi_ctrl_info *ctrl_info)
35018c2ecf20Sopenharmony_ci{
35028c2ecf20Sopenharmony_ci	int i;
35038c2ecf20Sopenharmony_ci
35048c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++)
35058c2ecf20Sopenharmony_ci		free_irq(pci_irq_vector(ctrl_info->pci_dev, i),
35068c2ecf20Sopenharmony_ci			&ctrl_info->queue_groups[i]);
35078c2ecf20Sopenharmony_ci
35088c2ecf20Sopenharmony_ci	ctrl_info->num_msix_vectors_initialized = 0;
35098c2ecf20Sopenharmony_ci}
35108c2ecf20Sopenharmony_ci
35118c2ecf20Sopenharmony_cistatic int pqi_enable_msix_interrupts(struct pqi_ctrl_info *ctrl_info)
35128c2ecf20Sopenharmony_ci{
35138c2ecf20Sopenharmony_ci	int num_vectors_enabled;
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	num_vectors_enabled = pci_alloc_irq_vectors(ctrl_info->pci_dev,
35168c2ecf20Sopenharmony_ci			PQI_MIN_MSIX_VECTORS, ctrl_info->num_queue_groups,
35178c2ecf20Sopenharmony_ci			PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
35188c2ecf20Sopenharmony_ci	if (num_vectors_enabled < 0) {
35198c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
35208c2ecf20Sopenharmony_ci			"MSI-X init failed with error %d\n",
35218c2ecf20Sopenharmony_ci			num_vectors_enabled);
35228c2ecf20Sopenharmony_ci		return num_vectors_enabled;
35238c2ecf20Sopenharmony_ci	}
35248c2ecf20Sopenharmony_ci
35258c2ecf20Sopenharmony_ci	ctrl_info->num_msix_vectors_enabled = num_vectors_enabled;
35268c2ecf20Sopenharmony_ci	ctrl_info->irq_mode = IRQ_MODE_MSIX;
35278c2ecf20Sopenharmony_ci	return 0;
35288c2ecf20Sopenharmony_ci}
35298c2ecf20Sopenharmony_ci
35308c2ecf20Sopenharmony_cistatic void pqi_disable_msix_interrupts(struct pqi_ctrl_info *ctrl_info)
35318c2ecf20Sopenharmony_ci{
35328c2ecf20Sopenharmony_ci	if (ctrl_info->num_msix_vectors_enabled) {
35338c2ecf20Sopenharmony_ci		pci_free_irq_vectors(ctrl_info->pci_dev);
35348c2ecf20Sopenharmony_ci		ctrl_info->num_msix_vectors_enabled = 0;
35358c2ecf20Sopenharmony_ci	}
35368c2ecf20Sopenharmony_ci}
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_cistatic int pqi_alloc_operational_queues(struct pqi_ctrl_info *ctrl_info)
35398c2ecf20Sopenharmony_ci{
35408c2ecf20Sopenharmony_ci	unsigned int i;
35418c2ecf20Sopenharmony_ci	size_t alloc_length;
35428c2ecf20Sopenharmony_ci	size_t element_array_length_per_iq;
35438c2ecf20Sopenharmony_ci	size_t element_array_length_per_oq;
35448c2ecf20Sopenharmony_ci	void *element_array;
35458c2ecf20Sopenharmony_ci	void __iomem *next_queue_index;
35468c2ecf20Sopenharmony_ci	void *aligned_pointer;
35478c2ecf20Sopenharmony_ci	unsigned int num_inbound_queues;
35488c2ecf20Sopenharmony_ci	unsigned int num_outbound_queues;
35498c2ecf20Sopenharmony_ci	unsigned int num_queue_indexes;
35508c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_ci	element_array_length_per_iq =
35538c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH *
35548c2ecf20Sopenharmony_ci		ctrl_info->num_elements_per_iq;
35558c2ecf20Sopenharmony_ci	element_array_length_per_oq =
35568c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_OQ_ELEMENT_LENGTH *
35578c2ecf20Sopenharmony_ci		ctrl_info->num_elements_per_oq;
35588c2ecf20Sopenharmony_ci	num_inbound_queues = ctrl_info->num_queue_groups * 2;
35598c2ecf20Sopenharmony_ci	num_outbound_queues = ctrl_info->num_queue_groups;
35608c2ecf20Sopenharmony_ci	num_queue_indexes = (ctrl_info->num_queue_groups * 3) + 1;
35618c2ecf20Sopenharmony_ci
35628c2ecf20Sopenharmony_ci	aligned_pointer = NULL;
35638c2ecf20Sopenharmony_ci
35648c2ecf20Sopenharmony_ci	for (i = 0; i < num_inbound_queues; i++) {
35658c2ecf20Sopenharmony_ci		aligned_pointer = PTR_ALIGN(aligned_pointer,
35668c2ecf20Sopenharmony_ci			PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
35678c2ecf20Sopenharmony_ci		aligned_pointer += element_array_length_per_iq;
35688c2ecf20Sopenharmony_ci	}
35698c2ecf20Sopenharmony_ci
35708c2ecf20Sopenharmony_ci	for (i = 0; i < num_outbound_queues; i++) {
35718c2ecf20Sopenharmony_ci		aligned_pointer = PTR_ALIGN(aligned_pointer,
35728c2ecf20Sopenharmony_ci			PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
35738c2ecf20Sopenharmony_ci		aligned_pointer += element_array_length_per_oq;
35748c2ecf20Sopenharmony_ci	}
35758c2ecf20Sopenharmony_ci
35768c2ecf20Sopenharmony_ci	aligned_pointer = PTR_ALIGN(aligned_pointer,
35778c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
35788c2ecf20Sopenharmony_ci	aligned_pointer += PQI_NUM_EVENT_QUEUE_ELEMENTS *
35798c2ecf20Sopenharmony_ci		PQI_EVENT_OQ_ELEMENT_LENGTH;
35808c2ecf20Sopenharmony_ci
35818c2ecf20Sopenharmony_ci	for (i = 0; i < num_queue_indexes; i++) {
35828c2ecf20Sopenharmony_ci		aligned_pointer = PTR_ALIGN(aligned_pointer,
35838c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_INDEX_ALIGNMENT);
35848c2ecf20Sopenharmony_ci		aligned_pointer += sizeof(pqi_index_t);
35858c2ecf20Sopenharmony_ci	}
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_ci	alloc_length = (size_t)aligned_pointer +
35888c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT;
35898c2ecf20Sopenharmony_ci
35908c2ecf20Sopenharmony_ci	alloc_length += PQI_EXTRA_SGL_MEMORY;
35918c2ecf20Sopenharmony_ci
35928c2ecf20Sopenharmony_ci	ctrl_info->queue_memory_base =
35938c2ecf20Sopenharmony_ci		dma_alloc_coherent(&ctrl_info->pci_dev->dev, alloc_length,
35948c2ecf20Sopenharmony_ci				   &ctrl_info->queue_memory_base_dma_handle,
35958c2ecf20Sopenharmony_ci				   GFP_KERNEL);
35968c2ecf20Sopenharmony_ci
35978c2ecf20Sopenharmony_ci	if (!ctrl_info->queue_memory_base)
35988c2ecf20Sopenharmony_ci		return -ENOMEM;
35998c2ecf20Sopenharmony_ci
36008c2ecf20Sopenharmony_ci	ctrl_info->queue_memory_length = alloc_length;
36018c2ecf20Sopenharmony_ci
36028c2ecf20Sopenharmony_ci	element_array = PTR_ALIGN(ctrl_info->queue_memory_base,
36038c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
36048c2ecf20Sopenharmony_ci
36058c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
36068c2ecf20Sopenharmony_ci		queue_group = &ctrl_info->queue_groups[i];
36078c2ecf20Sopenharmony_ci		queue_group->iq_element_array[RAID_PATH] = element_array;
36088c2ecf20Sopenharmony_ci		queue_group->iq_element_array_bus_addr[RAID_PATH] =
36098c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle +
36108c2ecf20Sopenharmony_ci				(element_array - ctrl_info->queue_memory_base);
36118c2ecf20Sopenharmony_ci		element_array += element_array_length_per_iq;
36128c2ecf20Sopenharmony_ci		element_array = PTR_ALIGN(element_array,
36138c2ecf20Sopenharmony_ci			PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
36148c2ecf20Sopenharmony_ci		queue_group->iq_element_array[AIO_PATH] = element_array;
36158c2ecf20Sopenharmony_ci		queue_group->iq_element_array_bus_addr[AIO_PATH] =
36168c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle +
36178c2ecf20Sopenharmony_ci			(element_array - ctrl_info->queue_memory_base);
36188c2ecf20Sopenharmony_ci		element_array += element_array_length_per_iq;
36198c2ecf20Sopenharmony_ci		element_array = PTR_ALIGN(element_array,
36208c2ecf20Sopenharmony_ci			PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
36218c2ecf20Sopenharmony_ci	}
36228c2ecf20Sopenharmony_ci
36238c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
36248c2ecf20Sopenharmony_ci		queue_group = &ctrl_info->queue_groups[i];
36258c2ecf20Sopenharmony_ci		queue_group->oq_element_array = element_array;
36268c2ecf20Sopenharmony_ci		queue_group->oq_element_array_bus_addr =
36278c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle +
36288c2ecf20Sopenharmony_ci			(element_array - ctrl_info->queue_memory_base);
36298c2ecf20Sopenharmony_ci		element_array += element_array_length_per_oq;
36308c2ecf20Sopenharmony_ci		element_array = PTR_ALIGN(element_array,
36318c2ecf20Sopenharmony_ci			PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
36328c2ecf20Sopenharmony_ci	}
36338c2ecf20Sopenharmony_ci
36348c2ecf20Sopenharmony_ci	ctrl_info->event_queue.oq_element_array = element_array;
36358c2ecf20Sopenharmony_ci	ctrl_info->event_queue.oq_element_array_bus_addr =
36368c2ecf20Sopenharmony_ci		ctrl_info->queue_memory_base_dma_handle +
36378c2ecf20Sopenharmony_ci		(element_array - ctrl_info->queue_memory_base);
36388c2ecf20Sopenharmony_ci	element_array += PQI_NUM_EVENT_QUEUE_ELEMENTS *
36398c2ecf20Sopenharmony_ci		PQI_EVENT_OQ_ELEMENT_LENGTH;
36408c2ecf20Sopenharmony_ci
36418c2ecf20Sopenharmony_ci	next_queue_index = (void __iomem *)PTR_ALIGN(element_array,
36428c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_INDEX_ALIGNMENT);
36438c2ecf20Sopenharmony_ci
36448c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
36458c2ecf20Sopenharmony_ci		queue_group = &ctrl_info->queue_groups[i];
36468c2ecf20Sopenharmony_ci		queue_group->iq_ci[RAID_PATH] = next_queue_index;
36478c2ecf20Sopenharmony_ci		queue_group->iq_ci_bus_addr[RAID_PATH] =
36488c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle +
36498c2ecf20Sopenharmony_ci			(next_queue_index -
36508c2ecf20Sopenharmony_ci			(void __iomem *)ctrl_info->queue_memory_base);
36518c2ecf20Sopenharmony_ci		next_queue_index += sizeof(pqi_index_t);
36528c2ecf20Sopenharmony_ci		next_queue_index = PTR_ALIGN(next_queue_index,
36538c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_INDEX_ALIGNMENT);
36548c2ecf20Sopenharmony_ci		queue_group->iq_ci[AIO_PATH] = next_queue_index;
36558c2ecf20Sopenharmony_ci		queue_group->iq_ci_bus_addr[AIO_PATH] =
36568c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle +
36578c2ecf20Sopenharmony_ci			(next_queue_index -
36588c2ecf20Sopenharmony_ci			(void __iomem *)ctrl_info->queue_memory_base);
36598c2ecf20Sopenharmony_ci		next_queue_index += sizeof(pqi_index_t);
36608c2ecf20Sopenharmony_ci		next_queue_index = PTR_ALIGN(next_queue_index,
36618c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_INDEX_ALIGNMENT);
36628c2ecf20Sopenharmony_ci		queue_group->oq_pi = next_queue_index;
36638c2ecf20Sopenharmony_ci		queue_group->oq_pi_bus_addr =
36648c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle +
36658c2ecf20Sopenharmony_ci			(next_queue_index -
36668c2ecf20Sopenharmony_ci			(void __iomem *)ctrl_info->queue_memory_base);
36678c2ecf20Sopenharmony_ci		next_queue_index += sizeof(pqi_index_t);
36688c2ecf20Sopenharmony_ci		next_queue_index = PTR_ALIGN(next_queue_index,
36698c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_INDEX_ALIGNMENT);
36708c2ecf20Sopenharmony_ci	}
36718c2ecf20Sopenharmony_ci
36728c2ecf20Sopenharmony_ci	ctrl_info->event_queue.oq_pi = next_queue_index;
36738c2ecf20Sopenharmony_ci	ctrl_info->event_queue.oq_pi_bus_addr =
36748c2ecf20Sopenharmony_ci		ctrl_info->queue_memory_base_dma_handle +
36758c2ecf20Sopenharmony_ci		(next_queue_index -
36768c2ecf20Sopenharmony_ci		(void __iomem *)ctrl_info->queue_memory_base);
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_ci	return 0;
36798c2ecf20Sopenharmony_ci}
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_cistatic void pqi_init_operational_queues(struct pqi_ctrl_info *ctrl_info)
36828c2ecf20Sopenharmony_ci{
36838c2ecf20Sopenharmony_ci	unsigned int i;
36848c2ecf20Sopenharmony_ci	u16 next_iq_id = PQI_MIN_OPERATIONAL_QUEUE_ID;
36858c2ecf20Sopenharmony_ci	u16 next_oq_id = PQI_MIN_OPERATIONAL_QUEUE_ID;
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	/*
36888c2ecf20Sopenharmony_ci	 * Initialize the backpointers to the controller structure in
36898c2ecf20Sopenharmony_ci	 * each operational queue group structure.
36908c2ecf20Sopenharmony_ci	 */
36918c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++)
36928c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].ctrl_info = ctrl_info;
36938c2ecf20Sopenharmony_ci
36948c2ecf20Sopenharmony_ci	/*
36958c2ecf20Sopenharmony_ci	 * Assign IDs to all operational queues.  Note that the IDs
36968c2ecf20Sopenharmony_ci	 * assigned to operational IQs are independent of the IDs
36978c2ecf20Sopenharmony_ci	 * assigned to operational OQs.
36988c2ecf20Sopenharmony_ci	 */
36998c2ecf20Sopenharmony_ci	ctrl_info->event_queue.oq_id = next_oq_id++;
37008c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
37018c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].iq_id[RAID_PATH] = next_iq_id++;
37028c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].iq_id[AIO_PATH] = next_iq_id++;
37038c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].oq_id = next_oq_id++;
37048c2ecf20Sopenharmony_ci	}
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci	/*
37078c2ecf20Sopenharmony_ci	 * Assign MSI-X table entry indexes to all queues.  Note that the
37088c2ecf20Sopenharmony_ci	 * interrupt for the event queue is shared with the first queue group.
37098c2ecf20Sopenharmony_ci	 */
37108c2ecf20Sopenharmony_ci	ctrl_info->event_queue.int_msg_num = 0;
37118c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++)
37128c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].int_msg_num = i;
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
37158c2ecf20Sopenharmony_ci		spin_lock_init(&ctrl_info->queue_groups[i].submit_lock[0]);
37168c2ecf20Sopenharmony_ci		spin_lock_init(&ctrl_info->queue_groups[i].submit_lock[1]);
37178c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ctrl_info->queue_groups[i].request_list[0]);
37188c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ctrl_info->queue_groups[i].request_list[1]);
37198c2ecf20Sopenharmony_ci	}
37208c2ecf20Sopenharmony_ci}
37218c2ecf20Sopenharmony_ci
37228c2ecf20Sopenharmony_cistatic int pqi_alloc_admin_queues(struct pqi_ctrl_info *ctrl_info)
37238c2ecf20Sopenharmony_ci{
37248c2ecf20Sopenharmony_ci	size_t alloc_length;
37258c2ecf20Sopenharmony_ci	struct pqi_admin_queues_aligned *admin_queues_aligned;
37268c2ecf20Sopenharmony_ci	struct pqi_admin_queues *admin_queues;
37278c2ecf20Sopenharmony_ci
37288c2ecf20Sopenharmony_ci	alloc_length = sizeof(struct pqi_admin_queues_aligned) +
37298c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT;
37308c2ecf20Sopenharmony_ci
37318c2ecf20Sopenharmony_ci	ctrl_info->admin_queue_memory_base =
37328c2ecf20Sopenharmony_ci		dma_alloc_coherent(&ctrl_info->pci_dev->dev, alloc_length,
37338c2ecf20Sopenharmony_ci				   &ctrl_info->admin_queue_memory_base_dma_handle,
37348c2ecf20Sopenharmony_ci				   GFP_KERNEL);
37358c2ecf20Sopenharmony_ci
37368c2ecf20Sopenharmony_ci	if (!ctrl_info->admin_queue_memory_base)
37378c2ecf20Sopenharmony_ci		return -ENOMEM;
37388c2ecf20Sopenharmony_ci
37398c2ecf20Sopenharmony_ci	ctrl_info->admin_queue_memory_length = alloc_length;
37408c2ecf20Sopenharmony_ci
37418c2ecf20Sopenharmony_ci	admin_queues = &ctrl_info->admin_queues;
37428c2ecf20Sopenharmony_ci	admin_queues_aligned = PTR_ALIGN(ctrl_info->admin_queue_memory_base,
37438c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT);
37448c2ecf20Sopenharmony_ci	admin_queues->iq_element_array =
37458c2ecf20Sopenharmony_ci		&admin_queues_aligned->iq_element_array;
37468c2ecf20Sopenharmony_ci	admin_queues->oq_element_array =
37478c2ecf20Sopenharmony_ci		&admin_queues_aligned->oq_element_array;
37488c2ecf20Sopenharmony_ci	admin_queues->iq_ci = &admin_queues_aligned->iq_ci;
37498c2ecf20Sopenharmony_ci	admin_queues->oq_pi =
37508c2ecf20Sopenharmony_ci		(pqi_index_t __iomem *)&admin_queues_aligned->oq_pi;
37518c2ecf20Sopenharmony_ci
37528c2ecf20Sopenharmony_ci	admin_queues->iq_element_array_bus_addr =
37538c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base_dma_handle +
37548c2ecf20Sopenharmony_ci		(admin_queues->iq_element_array -
37558c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base);
37568c2ecf20Sopenharmony_ci	admin_queues->oq_element_array_bus_addr =
37578c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base_dma_handle +
37588c2ecf20Sopenharmony_ci		(admin_queues->oq_element_array -
37598c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base);
37608c2ecf20Sopenharmony_ci	admin_queues->iq_ci_bus_addr =
37618c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base_dma_handle +
37628c2ecf20Sopenharmony_ci		((void *)admin_queues->iq_ci -
37638c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base);
37648c2ecf20Sopenharmony_ci	admin_queues->oq_pi_bus_addr =
37658c2ecf20Sopenharmony_ci		ctrl_info->admin_queue_memory_base_dma_handle +
37668c2ecf20Sopenharmony_ci		((void __iomem *)admin_queues->oq_pi -
37678c2ecf20Sopenharmony_ci		(void __iomem *)ctrl_info->admin_queue_memory_base);
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci	return 0;
37708c2ecf20Sopenharmony_ci}
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_ci#define PQI_ADMIN_QUEUE_CREATE_TIMEOUT_JIFFIES		PQI_HZ
37738c2ecf20Sopenharmony_ci#define PQI_ADMIN_QUEUE_CREATE_POLL_INTERVAL_MSECS	1
37748c2ecf20Sopenharmony_ci
37758c2ecf20Sopenharmony_cistatic int pqi_create_admin_queues(struct pqi_ctrl_info *ctrl_info)
37768c2ecf20Sopenharmony_ci{
37778c2ecf20Sopenharmony_ci	struct pqi_device_registers __iomem *pqi_registers;
37788c2ecf20Sopenharmony_ci	struct pqi_admin_queues *admin_queues;
37798c2ecf20Sopenharmony_ci	unsigned long timeout;
37808c2ecf20Sopenharmony_ci	u8 status;
37818c2ecf20Sopenharmony_ci	u32 reg;
37828c2ecf20Sopenharmony_ci
37838c2ecf20Sopenharmony_ci	pqi_registers = ctrl_info->pqi_registers;
37848c2ecf20Sopenharmony_ci	admin_queues = &ctrl_info->admin_queues;
37858c2ecf20Sopenharmony_ci
37868c2ecf20Sopenharmony_ci	writeq((u64)admin_queues->iq_element_array_bus_addr,
37878c2ecf20Sopenharmony_ci		&pqi_registers->admin_iq_element_array_addr);
37888c2ecf20Sopenharmony_ci	writeq((u64)admin_queues->oq_element_array_bus_addr,
37898c2ecf20Sopenharmony_ci		&pqi_registers->admin_oq_element_array_addr);
37908c2ecf20Sopenharmony_ci	writeq((u64)admin_queues->iq_ci_bus_addr,
37918c2ecf20Sopenharmony_ci		&pqi_registers->admin_iq_ci_addr);
37928c2ecf20Sopenharmony_ci	writeq((u64)admin_queues->oq_pi_bus_addr,
37938c2ecf20Sopenharmony_ci		&pqi_registers->admin_oq_pi_addr);
37948c2ecf20Sopenharmony_ci
37958c2ecf20Sopenharmony_ci	reg = PQI_ADMIN_IQ_NUM_ELEMENTS |
37968c2ecf20Sopenharmony_ci		(PQI_ADMIN_OQ_NUM_ELEMENTS << 8) |
37978c2ecf20Sopenharmony_ci		(admin_queues->int_msg_num << 16);
37988c2ecf20Sopenharmony_ci	writel(reg, &pqi_registers->admin_iq_num_elements);
37998c2ecf20Sopenharmony_ci	writel(PQI_CREATE_ADMIN_QUEUE_PAIR,
38008c2ecf20Sopenharmony_ci		&pqi_registers->function_and_status_code);
38018c2ecf20Sopenharmony_ci
38028c2ecf20Sopenharmony_ci	timeout = PQI_ADMIN_QUEUE_CREATE_TIMEOUT_JIFFIES + jiffies;
38038c2ecf20Sopenharmony_ci	while (1) {
38048c2ecf20Sopenharmony_ci		status = readb(&pqi_registers->function_and_status_code);
38058c2ecf20Sopenharmony_ci		if (status == PQI_STATUS_IDLE)
38068c2ecf20Sopenharmony_ci			break;
38078c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout))
38088c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
38098c2ecf20Sopenharmony_ci		msleep(PQI_ADMIN_QUEUE_CREATE_POLL_INTERVAL_MSECS);
38108c2ecf20Sopenharmony_ci	}
38118c2ecf20Sopenharmony_ci
38128c2ecf20Sopenharmony_ci	/*
38138c2ecf20Sopenharmony_ci	 * The offset registers are not initialized to the correct
38148c2ecf20Sopenharmony_ci	 * offsets until *after* the create admin queue pair command
38158c2ecf20Sopenharmony_ci	 * completes successfully.
38168c2ecf20Sopenharmony_ci	 */
38178c2ecf20Sopenharmony_ci	admin_queues->iq_pi = ctrl_info->iomem_base +
38188c2ecf20Sopenharmony_ci		PQI_DEVICE_REGISTERS_OFFSET +
38198c2ecf20Sopenharmony_ci		readq(&pqi_registers->admin_iq_pi_offset);
38208c2ecf20Sopenharmony_ci	admin_queues->oq_ci = ctrl_info->iomem_base +
38218c2ecf20Sopenharmony_ci		PQI_DEVICE_REGISTERS_OFFSET +
38228c2ecf20Sopenharmony_ci		readq(&pqi_registers->admin_oq_ci_offset);
38238c2ecf20Sopenharmony_ci
38248c2ecf20Sopenharmony_ci	return 0;
38258c2ecf20Sopenharmony_ci}
38268c2ecf20Sopenharmony_ci
38278c2ecf20Sopenharmony_cistatic void pqi_submit_admin_request(struct pqi_ctrl_info *ctrl_info,
38288c2ecf20Sopenharmony_ci	struct pqi_general_admin_request *request)
38298c2ecf20Sopenharmony_ci{
38308c2ecf20Sopenharmony_ci	struct pqi_admin_queues *admin_queues;
38318c2ecf20Sopenharmony_ci	void *next_element;
38328c2ecf20Sopenharmony_ci	pqi_index_t iq_pi;
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_ci	admin_queues = &ctrl_info->admin_queues;
38358c2ecf20Sopenharmony_ci	iq_pi = admin_queues->iq_pi_copy;
38368c2ecf20Sopenharmony_ci
38378c2ecf20Sopenharmony_ci	next_element = admin_queues->iq_element_array +
38388c2ecf20Sopenharmony_ci		(iq_pi * PQI_ADMIN_IQ_ELEMENT_LENGTH);
38398c2ecf20Sopenharmony_ci
38408c2ecf20Sopenharmony_ci	memcpy(next_element, request, sizeof(*request));
38418c2ecf20Sopenharmony_ci
38428c2ecf20Sopenharmony_ci	iq_pi = (iq_pi + 1) % PQI_ADMIN_IQ_NUM_ELEMENTS;
38438c2ecf20Sopenharmony_ci	admin_queues->iq_pi_copy = iq_pi;
38448c2ecf20Sopenharmony_ci
38458c2ecf20Sopenharmony_ci	/*
38468c2ecf20Sopenharmony_ci	 * This write notifies the controller that an IU is available to be
38478c2ecf20Sopenharmony_ci	 * processed.
38488c2ecf20Sopenharmony_ci	 */
38498c2ecf20Sopenharmony_ci	writel(iq_pi, admin_queues->iq_pi);
38508c2ecf20Sopenharmony_ci}
38518c2ecf20Sopenharmony_ci
38528c2ecf20Sopenharmony_ci#define PQI_ADMIN_REQUEST_TIMEOUT_SECS	60
38538c2ecf20Sopenharmony_ci
38548c2ecf20Sopenharmony_cistatic int pqi_poll_for_admin_response(struct pqi_ctrl_info *ctrl_info,
38558c2ecf20Sopenharmony_ci	struct pqi_general_admin_response *response)
38568c2ecf20Sopenharmony_ci{
38578c2ecf20Sopenharmony_ci	struct pqi_admin_queues *admin_queues;
38588c2ecf20Sopenharmony_ci	pqi_index_t oq_pi;
38598c2ecf20Sopenharmony_ci	pqi_index_t oq_ci;
38608c2ecf20Sopenharmony_ci	unsigned long timeout;
38618c2ecf20Sopenharmony_ci
38628c2ecf20Sopenharmony_ci	admin_queues = &ctrl_info->admin_queues;
38638c2ecf20Sopenharmony_ci	oq_ci = admin_queues->oq_ci_copy;
38648c2ecf20Sopenharmony_ci
38658c2ecf20Sopenharmony_ci	timeout = (PQI_ADMIN_REQUEST_TIMEOUT_SECS * PQI_HZ) + jiffies;
38668c2ecf20Sopenharmony_ci
38678c2ecf20Sopenharmony_ci	while (1) {
38688c2ecf20Sopenharmony_ci		oq_pi = readl(admin_queues->oq_pi);
38698c2ecf20Sopenharmony_ci		if (oq_pi != oq_ci)
38708c2ecf20Sopenharmony_ci			break;
38718c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
38728c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
38738c2ecf20Sopenharmony_ci				"timed out waiting for admin response\n");
38748c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
38758c2ecf20Sopenharmony_ci		}
38768c2ecf20Sopenharmony_ci		if (!sis_is_firmware_running(ctrl_info))
38778c2ecf20Sopenharmony_ci			return -ENXIO;
38788c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
38798c2ecf20Sopenharmony_ci	}
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_ci	memcpy(response, admin_queues->oq_element_array +
38828c2ecf20Sopenharmony_ci		(oq_ci * PQI_ADMIN_OQ_ELEMENT_LENGTH), sizeof(*response));
38838c2ecf20Sopenharmony_ci
38848c2ecf20Sopenharmony_ci	oq_ci = (oq_ci + 1) % PQI_ADMIN_OQ_NUM_ELEMENTS;
38858c2ecf20Sopenharmony_ci	admin_queues->oq_ci_copy = oq_ci;
38868c2ecf20Sopenharmony_ci	writel(oq_ci, admin_queues->oq_ci);
38878c2ecf20Sopenharmony_ci
38888c2ecf20Sopenharmony_ci	return 0;
38898c2ecf20Sopenharmony_ci}
38908c2ecf20Sopenharmony_ci
38918c2ecf20Sopenharmony_cistatic void pqi_start_io(struct pqi_ctrl_info *ctrl_info,
38928c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group, enum pqi_io_path path,
38938c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request)
38948c2ecf20Sopenharmony_ci{
38958c2ecf20Sopenharmony_ci	struct pqi_io_request *next;
38968c2ecf20Sopenharmony_ci	void *next_element;
38978c2ecf20Sopenharmony_ci	pqi_index_t iq_pi;
38988c2ecf20Sopenharmony_ci	pqi_index_t iq_ci;
38998c2ecf20Sopenharmony_ci	size_t iu_length;
39008c2ecf20Sopenharmony_ci	unsigned long flags;
39018c2ecf20Sopenharmony_ci	unsigned int num_elements_needed;
39028c2ecf20Sopenharmony_ci	unsigned int num_elements_to_end_of_queue;
39038c2ecf20Sopenharmony_ci	size_t copy_count;
39048c2ecf20Sopenharmony_ci	struct pqi_iu_header *request;
39058c2ecf20Sopenharmony_ci
39068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue_group->submit_lock[path], flags);
39078c2ecf20Sopenharmony_ci
39088c2ecf20Sopenharmony_ci	if (io_request) {
39098c2ecf20Sopenharmony_ci		io_request->queue_group = queue_group;
39108c2ecf20Sopenharmony_ci		list_add_tail(&io_request->request_list_entry,
39118c2ecf20Sopenharmony_ci			&queue_group->request_list[path]);
39128c2ecf20Sopenharmony_ci	}
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci	iq_pi = queue_group->iq_pi_copy[path];
39158c2ecf20Sopenharmony_ci
39168c2ecf20Sopenharmony_ci	list_for_each_entry_safe(io_request, next,
39178c2ecf20Sopenharmony_ci		&queue_group->request_list[path], request_list_entry) {
39188c2ecf20Sopenharmony_ci
39198c2ecf20Sopenharmony_ci		request = io_request->iu;
39208c2ecf20Sopenharmony_ci
39218c2ecf20Sopenharmony_ci		iu_length = get_unaligned_le16(&request->iu_length) +
39228c2ecf20Sopenharmony_ci			PQI_REQUEST_HEADER_LENGTH;
39238c2ecf20Sopenharmony_ci		num_elements_needed =
39248c2ecf20Sopenharmony_ci			DIV_ROUND_UP(iu_length,
39258c2ecf20Sopenharmony_ci				PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
39268c2ecf20Sopenharmony_ci
39278c2ecf20Sopenharmony_ci		iq_ci = readl(queue_group->iq_ci[path]);
39288c2ecf20Sopenharmony_ci
39298c2ecf20Sopenharmony_ci		if (num_elements_needed > pqi_num_elements_free(iq_pi, iq_ci,
39308c2ecf20Sopenharmony_ci			ctrl_info->num_elements_per_iq))
39318c2ecf20Sopenharmony_ci			break;
39328c2ecf20Sopenharmony_ci
39338c2ecf20Sopenharmony_ci		put_unaligned_le16(queue_group->oq_id,
39348c2ecf20Sopenharmony_ci			&request->response_queue_id);
39358c2ecf20Sopenharmony_ci
39368c2ecf20Sopenharmony_ci		next_element = queue_group->iq_element_array[path] +
39378c2ecf20Sopenharmony_ci			(iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
39388c2ecf20Sopenharmony_ci
39398c2ecf20Sopenharmony_ci		num_elements_to_end_of_queue =
39408c2ecf20Sopenharmony_ci			ctrl_info->num_elements_per_iq - iq_pi;
39418c2ecf20Sopenharmony_ci
39428c2ecf20Sopenharmony_ci		if (num_elements_needed <= num_elements_to_end_of_queue) {
39438c2ecf20Sopenharmony_ci			memcpy(next_element, request, iu_length);
39448c2ecf20Sopenharmony_ci		} else {
39458c2ecf20Sopenharmony_ci			copy_count = num_elements_to_end_of_queue *
39468c2ecf20Sopenharmony_ci				PQI_OPERATIONAL_IQ_ELEMENT_LENGTH;
39478c2ecf20Sopenharmony_ci			memcpy(next_element, request, copy_count);
39488c2ecf20Sopenharmony_ci			memcpy(queue_group->iq_element_array[path],
39498c2ecf20Sopenharmony_ci				(u8 *)request + copy_count,
39508c2ecf20Sopenharmony_ci				iu_length - copy_count);
39518c2ecf20Sopenharmony_ci		}
39528c2ecf20Sopenharmony_ci
39538c2ecf20Sopenharmony_ci		iq_pi = (iq_pi + num_elements_needed) %
39548c2ecf20Sopenharmony_ci			ctrl_info->num_elements_per_iq;
39558c2ecf20Sopenharmony_ci
39568c2ecf20Sopenharmony_ci		list_del(&io_request->request_list_entry);
39578c2ecf20Sopenharmony_ci	}
39588c2ecf20Sopenharmony_ci
39598c2ecf20Sopenharmony_ci	if (iq_pi != queue_group->iq_pi_copy[path]) {
39608c2ecf20Sopenharmony_ci		queue_group->iq_pi_copy[path] = iq_pi;
39618c2ecf20Sopenharmony_ci		/*
39628c2ecf20Sopenharmony_ci		 * This write notifies the controller that one or more IUs are
39638c2ecf20Sopenharmony_ci		 * available to be processed.
39648c2ecf20Sopenharmony_ci		 */
39658c2ecf20Sopenharmony_ci		writel(iq_pi, queue_group->iq_pi[path]);
39668c2ecf20Sopenharmony_ci	}
39678c2ecf20Sopenharmony_ci
39688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue_group->submit_lock[path], flags);
39698c2ecf20Sopenharmony_ci}
39708c2ecf20Sopenharmony_ci
39718c2ecf20Sopenharmony_ci#define PQI_WAIT_FOR_COMPLETION_IO_TIMEOUT_SECS		10
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_cistatic int pqi_wait_for_completion_io(struct pqi_ctrl_info *ctrl_info,
39748c2ecf20Sopenharmony_ci	struct completion *wait)
39758c2ecf20Sopenharmony_ci{
39768c2ecf20Sopenharmony_ci	int rc;
39778c2ecf20Sopenharmony_ci
39788c2ecf20Sopenharmony_ci	while (1) {
39798c2ecf20Sopenharmony_ci		if (wait_for_completion_io_timeout(wait,
39808c2ecf20Sopenharmony_ci			PQI_WAIT_FOR_COMPLETION_IO_TIMEOUT_SECS * PQI_HZ)) {
39818c2ecf20Sopenharmony_ci			rc = 0;
39828c2ecf20Sopenharmony_ci			break;
39838c2ecf20Sopenharmony_ci		}
39848c2ecf20Sopenharmony_ci
39858c2ecf20Sopenharmony_ci		pqi_check_ctrl_health(ctrl_info);
39868c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info)) {
39878c2ecf20Sopenharmony_ci			rc = -ENXIO;
39888c2ecf20Sopenharmony_ci			break;
39898c2ecf20Sopenharmony_ci		}
39908c2ecf20Sopenharmony_ci	}
39918c2ecf20Sopenharmony_ci
39928c2ecf20Sopenharmony_ci	return rc;
39938c2ecf20Sopenharmony_ci}
39948c2ecf20Sopenharmony_ci
39958c2ecf20Sopenharmony_cistatic void pqi_raid_synchronous_complete(struct pqi_io_request *io_request,
39968c2ecf20Sopenharmony_ci	void *context)
39978c2ecf20Sopenharmony_ci{
39988c2ecf20Sopenharmony_ci	struct completion *waiting = context;
39998c2ecf20Sopenharmony_ci
40008c2ecf20Sopenharmony_ci	complete(waiting);
40018c2ecf20Sopenharmony_ci}
40028c2ecf20Sopenharmony_ci
40038c2ecf20Sopenharmony_cistatic int pqi_process_raid_io_error_synchronous(
40048c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info)
40058c2ecf20Sopenharmony_ci{
40068c2ecf20Sopenharmony_ci	int rc = -EIO;
40078c2ecf20Sopenharmony_ci
40088c2ecf20Sopenharmony_ci	switch (error_info->data_out_result) {
40098c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_GOOD:
40108c2ecf20Sopenharmony_ci		if (error_info->status == SAM_STAT_GOOD)
40118c2ecf20Sopenharmony_ci			rc = 0;
40128c2ecf20Sopenharmony_ci		break;
40138c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_UNDERFLOW:
40148c2ecf20Sopenharmony_ci		if (error_info->status == SAM_STAT_GOOD ||
40158c2ecf20Sopenharmony_ci			error_info->status == SAM_STAT_CHECK_CONDITION)
40168c2ecf20Sopenharmony_ci			rc = 0;
40178c2ecf20Sopenharmony_ci		break;
40188c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_ABORTED:
40198c2ecf20Sopenharmony_ci		rc = PQI_CMD_STATUS_ABORTED;
40208c2ecf20Sopenharmony_ci		break;
40218c2ecf20Sopenharmony_ci	}
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_ci	return rc;
40248c2ecf20Sopenharmony_ci}
40258c2ecf20Sopenharmony_ci
40268c2ecf20Sopenharmony_cistatic int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info,
40278c2ecf20Sopenharmony_ci	struct pqi_iu_header *request, unsigned int flags,
40288c2ecf20Sopenharmony_ci	struct pqi_raid_error_info *error_info, unsigned long timeout_msecs)
40298c2ecf20Sopenharmony_ci{
40308c2ecf20Sopenharmony_ci	int rc = 0;
40318c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
40328c2ecf20Sopenharmony_ci	unsigned long start_jiffies;
40338c2ecf20Sopenharmony_ci	unsigned long msecs_blocked;
40348c2ecf20Sopenharmony_ci	size_t iu_length;
40358c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(wait);
40368c2ecf20Sopenharmony_ci
40378c2ecf20Sopenharmony_ci	/*
40388c2ecf20Sopenharmony_ci	 * Note that specifying PQI_SYNC_FLAGS_INTERRUPTABLE and a timeout value
40398c2ecf20Sopenharmony_ci	 * are mutually exclusive.
40408c2ecf20Sopenharmony_ci	 */
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_ci	if (flags & PQI_SYNC_FLAGS_INTERRUPTABLE) {
40438c2ecf20Sopenharmony_ci		if (down_interruptible(&ctrl_info->sync_request_sem))
40448c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
40458c2ecf20Sopenharmony_ci	} else {
40468c2ecf20Sopenharmony_ci		if (timeout_msecs == NO_TIMEOUT) {
40478c2ecf20Sopenharmony_ci			down(&ctrl_info->sync_request_sem);
40488c2ecf20Sopenharmony_ci		} else {
40498c2ecf20Sopenharmony_ci			start_jiffies = jiffies;
40508c2ecf20Sopenharmony_ci			if (down_timeout(&ctrl_info->sync_request_sem,
40518c2ecf20Sopenharmony_ci				msecs_to_jiffies(timeout_msecs)))
40528c2ecf20Sopenharmony_ci				return -ETIMEDOUT;
40538c2ecf20Sopenharmony_ci			msecs_blocked =
40548c2ecf20Sopenharmony_ci				jiffies_to_msecs(jiffies - start_jiffies);
40558c2ecf20Sopenharmony_ci			if (msecs_blocked >= timeout_msecs) {
40568c2ecf20Sopenharmony_ci				rc = -ETIMEDOUT;
40578c2ecf20Sopenharmony_ci				goto out;
40588c2ecf20Sopenharmony_ci			}
40598c2ecf20Sopenharmony_ci			timeout_msecs -= msecs_blocked;
40608c2ecf20Sopenharmony_ci		}
40618c2ecf20Sopenharmony_ci	}
40628c2ecf20Sopenharmony_ci
40638c2ecf20Sopenharmony_ci	pqi_ctrl_busy(ctrl_info);
40648c2ecf20Sopenharmony_ci	timeout_msecs = pqi_wait_if_ctrl_blocked(ctrl_info, timeout_msecs);
40658c2ecf20Sopenharmony_ci	if (timeout_msecs == 0) {
40668c2ecf20Sopenharmony_ci		pqi_ctrl_unbusy(ctrl_info);
40678c2ecf20Sopenharmony_ci		rc = -ETIMEDOUT;
40688c2ecf20Sopenharmony_ci		goto out;
40698c2ecf20Sopenharmony_ci	}
40708c2ecf20Sopenharmony_ci
40718c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info)) {
40728c2ecf20Sopenharmony_ci		pqi_ctrl_unbusy(ctrl_info);
40738c2ecf20Sopenharmony_ci		rc = -ENXIO;
40748c2ecf20Sopenharmony_ci		goto out;
40758c2ecf20Sopenharmony_ci	}
40768c2ecf20Sopenharmony_ci
40778c2ecf20Sopenharmony_ci	atomic_inc(&ctrl_info->sync_cmds_outstanding);
40788c2ecf20Sopenharmony_ci
40798c2ecf20Sopenharmony_ci	io_request = pqi_alloc_io_request(ctrl_info);
40808c2ecf20Sopenharmony_ci
40818c2ecf20Sopenharmony_ci	put_unaligned_le16(io_request->index,
40828c2ecf20Sopenharmony_ci		&(((struct pqi_raid_path_request *)request)->request_id));
40838c2ecf20Sopenharmony_ci
40848c2ecf20Sopenharmony_ci	if (request->iu_type == PQI_REQUEST_IU_RAID_PATH_IO)
40858c2ecf20Sopenharmony_ci		((struct pqi_raid_path_request *)request)->error_index =
40868c2ecf20Sopenharmony_ci			((struct pqi_raid_path_request *)request)->request_id;
40878c2ecf20Sopenharmony_ci
40888c2ecf20Sopenharmony_ci	iu_length = get_unaligned_le16(&request->iu_length) +
40898c2ecf20Sopenharmony_ci		PQI_REQUEST_HEADER_LENGTH;
40908c2ecf20Sopenharmony_ci	memcpy(io_request->iu, request, iu_length);
40918c2ecf20Sopenharmony_ci
40928c2ecf20Sopenharmony_ci	io_request->io_complete_callback = pqi_raid_synchronous_complete;
40938c2ecf20Sopenharmony_ci	io_request->context = &wait;
40948c2ecf20Sopenharmony_ci
40958c2ecf20Sopenharmony_ci	pqi_start_io(ctrl_info,
40968c2ecf20Sopenharmony_ci		&ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH,
40978c2ecf20Sopenharmony_ci		io_request);
40988c2ecf20Sopenharmony_ci
40998c2ecf20Sopenharmony_ci	pqi_ctrl_unbusy(ctrl_info);
41008c2ecf20Sopenharmony_ci
41018c2ecf20Sopenharmony_ci	if (timeout_msecs == NO_TIMEOUT) {
41028c2ecf20Sopenharmony_ci		pqi_wait_for_completion_io(ctrl_info, &wait);
41038c2ecf20Sopenharmony_ci	} else {
41048c2ecf20Sopenharmony_ci		if (!wait_for_completion_io_timeout(&wait,
41058c2ecf20Sopenharmony_ci			msecs_to_jiffies(timeout_msecs))) {
41068c2ecf20Sopenharmony_ci			dev_warn(&ctrl_info->pci_dev->dev,
41078c2ecf20Sopenharmony_ci				"command timed out\n");
41088c2ecf20Sopenharmony_ci			rc = -ETIMEDOUT;
41098c2ecf20Sopenharmony_ci		}
41108c2ecf20Sopenharmony_ci	}
41118c2ecf20Sopenharmony_ci
41128c2ecf20Sopenharmony_ci	if (error_info) {
41138c2ecf20Sopenharmony_ci		if (io_request->error_info)
41148c2ecf20Sopenharmony_ci			memcpy(error_info, io_request->error_info,
41158c2ecf20Sopenharmony_ci				sizeof(*error_info));
41168c2ecf20Sopenharmony_ci		else
41178c2ecf20Sopenharmony_ci			memset(error_info, 0, sizeof(*error_info));
41188c2ecf20Sopenharmony_ci	} else if (rc == 0 && io_request->error_info) {
41198c2ecf20Sopenharmony_ci		rc = pqi_process_raid_io_error_synchronous(
41208c2ecf20Sopenharmony_ci			io_request->error_info);
41218c2ecf20Sopenharmony_ci	}
41228c2ecf20Sopenharmony_ci
41238c2ecf20Sopenharmony_ci	pqi_free_io_request(io_request);
41248c2ecf20Sopenharmony_ci
41258c2ecf20Sopenharmony_ci	atomic_dec(&ctrl_info->sync_cmds_outstanding);
41268c2ecf20Sopenharmony_ciout:
41278c2ecf20Sopenharmony_ci	up(&ctrl_info->sync_request_sem);
41288c2ecf20Sopenharmony_ci
41298c2ecf20Sopenharmony_ci	return rc;
41308c2ecf20Sopenharmony_ci}
41318c2ecf20Sopenharmony_ci
41328c2ecf20Sopenharmony_cistatic int pqi_validate_admin_response(
41338c2ecf20Sopenharmony_ci	struct pqi_general_admin_response *response, u8 expected_function_code)
41348c2ecf20Sopenharmony_ci{
41358c2ecf20Sopenharmony_ci	if (response->header.iu_type != PQI_RESPONSE_IU_GENERAL_ADMIN)
41368c2ecf20Sopenharmony_ci		return -EINVAL;
41378c2ecf20Sopenharmony_ci
41388c2ecf20Sopenharmony_ci	if (get_unaligned_le16(&response->header.iu_length) !=
41398c2ecf20Sopenharmony_ci		PQI_GENERAL_ADMIN_IU_LENGTH)
41408c2ecf20Sopenharmony_ci		return -EINVAL;
41418c2ecf20Sopenharmony_ci
41428c2ecf20Sopenharmony_ci	if (response->function_code != expected_function_code)
41438c2ecf20Sopenharmony_ci		return -EINVAL;
41448c2ecf20Sopenharmony_ci
41458c2ecf20Sopenharmony_ci	if (response->status != PQI_GENERAL_ADMIN_STATUS_SUCCESS)
41468c2ecf20Sopenharmony_ci		return -EINVAL;
41478c2ecf20Sopenharmony_ci
41488c2ecf20Sopenharmony_ci	return 0;
41498c2ecf20Sopenharmony_ci}
41508c2ecf20Sopenharmony_ci
41518c2ecf20Sopenharmony_cistatic int pqi_submit_admin_request_synchronous(
41528c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info,
41538c2ecf20Sopenharmony_ci	struct pqi_general_admin_request *request,
41548c2ecf20Sopenharmony_ci	struct pqi_general_admin_response *response)
41558c2ecf20Sopenharmony_ci{
41568c2ecf20Sopenharmony_ci	int rc;
41578c2ecf20Sopenharmony_ci
41588c2ecf20Sopenharmony_ci	pqi_submit_admin_request(ctrl_info, request);
41598c2ecf20Sopenharmony_ci
41608c2ecf20Sopenharmony_ci	rc = pqi_poll_for_admin_response(ctrl_info, response);
41618c2ecf20Sopenharmony_ci
41628c2ecf20Sopenharmony_ci	if (rc == 0)
41638c2ecf20Sopenharmony_ci		rc = pqi_validate_admin_response(response,
41648c2ecf20Sopenharmony_ci			request->function_code);
41658c2ecf20Sopenharmony_ci
41668c2ecf20Sopenharmony_ci	return rc;
41678c2ecf20Sopenharmony_ci}
41688c2ecf20Sopenharmony_ci
41698c2ecf20Sopenharmony_cistatic int pqi_report_device_capability(struct pqi_ctrl_info *ctrl_info)
41708c2ecf20Sopenharmony_ci{
41718c2ecf20Sopenharmony_ci	int rc;
41728c2ecf20Sopenharmony_ci	struct pqi_general_admin_request request;
41738c2ecf20Sopenharmony_ci	struct pqi_general_admin_response response;
41748c2ecf20Sopenharmony_ci	struct pqi_device_capability *capability;
41758c2ecf20Sopenharmony_ci	struct pqi_iu_layer_descriptor *sop_iu_layer_descriptor;
41768c2ecf20Sopenharmony_ci
41778c2ecf20Sopenharmony_ci	capability = kmalloc(sizeof(*capability), GFP_KERNEL);
41788c2ecf20Sopenharmony_ci	if (!capability)
41798c2ecf20Sopenharmony_ci		return -ENOMEM;
41808c2ecf20Sopenharmony_ci
41818c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
41828c2ecf20Sopenharmony_ci
41838c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
41848c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
41858c2ecf20Sopenharmony_ci		&request.header.iu_length);
41868c2ecf20Sopenharmony_ci	request.function_code =
41878c2ecf20Sopenharmony_ci		PQI_GENERAL_ADMIN_FUNCTION_REPORT_DEVICE_CAPABILITY;
41888c2ecf20Sopenharmony_ci	put_unaligned_le32(sizeof(*capability),
41898c2ecf20Sopenharmony_ci		&request.data.report_device_capability.buffer_length);
41908c2ecf20Sopenharmony_ci
41918c2ecf20Sopenharmony_ci	rc = pqi_map_single(ctrl_info->pci_dev,
41928c2ecf20Sopenharmony_ci		&request.data.report_device_capability.sg_descriptor,
41938c2ecf20Sopenharmony_ci		capability, sizeof(*capability),
41948c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE);
41958c2ecf20Sopenharmony_ci	if (rc)
41968c2ecf20Sopenharmony_ci		goto out;
41978c2ecf20Sopenharmony_ci
41988c2ecf20Sopenharmony_ci	rc = pqi_submit_admin_request_synchronous(ctrl_info, &request,
41998c2ecf20Sopenharmony_ci		&response);
42008c2ecf20Sopenharmony_ci
42018c2ecf20Sopenharmony_ci	pqi_pci_unmap(ctrl_info->pci_dev,
42028c2ecf20Sopenharmony_ci		&request.data.report_device_capability.sg_descriptor, 1,
42038c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE);
42048c2ecf20Sopenharmony_ci
42058c2ecf20Sopenharmony_ci	if (rc)
42068c2ecf20Sopenharmony_ci		goto out;
42078c2ecf20Sopenharmony_ci
42088c2ecf20Sopenharmony_ci	if (response.status != PQI_GENERAL_ADMIN_STATUS_SUCCESS) {
42098c2ecf20Sopenharmony_ci		rc = -EIO;
42108c2ecf20Sopenharmony_ci		goto out;
42118c2ecf20Sopenharmony_ci	}
42128c2ecf20Sopenharmony_ci
42138c2ecf20Sopenharmony_ci	ctrl_info->max_inbound_queues =
42148c2ecf20Sopenharmony_ci		get_unaligned_le16(&capability->max_inbound_queues);
42158c2ecf20Sopenharmony_ci	ctrl_info->max_elements_per_iq =
42168c2ecf20Sopenharmony_ci		get_unaligned_le16(&capability->max_elements_per_iq);
42178c2ecf20Sopenharmony_ci	ctrl_info->max_iq_element_length =
42188c2ecf20Sopenharmony_ci		get_unaligned_le16(&capability->max_iq_element_length)
42198c2ecf20Sopenharmony_ci		* 16;
42208c2ecf20Sopenharmony_ci	ctrl_info->max_outbound_queues =
42218c2ecf20Sopenharmony_ci		get_unaligned_le16(&capability->max_outbound_queues);
42228c2ecf20Sopenharmony_ci	ctrl_info->max_elements_per_oq =
42238c2ecf20Sopenharmony_ci		get_unaligned_le16(&capability->max_elements_per_oq);
42248c2ecf20Sopenharmony_ci	ctrl_info->max_oq_element_length =
42258c2ecf20Sopenharmony_ci		get_unaligned_le16(&capability->max_oq_element_length)
42268c2ecf20Sopenharmony_ci		* 16;
42278c2ecf20Sopenharmony_ci
42288c2ecf20Sopenharmony_ci	sop_iu_layer_descriptor =
42298c2ecf20Sopenharmony_ci		&capability->iu_layer_descriptors[PQI_PROTOCOL_SOP];
42308c2ecf20Sopenharmony_ci
42318c2ecf20Sopenharmony_ci	ctrl_info->max_inbound_iu_length_per_firmware =
42328c2ecf20Sopenharmony_ci		get_unaligned_le16(
42338c2ecf20Sopenharmony_ci			&sop_iu_layer_descriptor->max_inbound_iu_length);
42348c2ecf20Sopenharmony_ci	ctrl_info->inbound_spanning_supported =
42358c2ecf20Sopenharmony_ci		sop_iu_layer_descriptor->inbound_spanning_supported;
42368c2ecf20Sopenharmony_ci	ctrl_info->outbound_spanning_supported =
42378c2ecf20Sopenharmony_ci		sop_iu_layer_descriptor->outbound_spanning_supported;
42388c2ecf20Sopenharmony_ci
42398c2ecf20Sopenharmony_ciout:
42408c2ecf20Sopenharmony_ci	kfree(capability);
42418c2ecf20Sopenharmony_ci
42428c2ecf20Sopenharmony_ci	return rc;
42438c2ecf20Sopenharmony_ci}
42448c2ecf20Sopenharmony_ci
42458c2ecf20Sopenharmony_cistatic int pqi_validate_device_capability(struct pqi_ctrl_info *ctrl_info)
42468c2ecf20Sopenharmony_ci{
42478c2ecf20Sopenharmony_ci	if (ctrl_info->max_iq_element_length <
42488c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) {
42498c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
42508c2ecf20Sopenharmony_ci			"max. inbound queue element length of %d is less than the required length of %d\n",
42518c2ecf20Sopenharmony_ci			ctrl_info->max_iq_element_length,
42528c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
42538c2ecf20Sopenharmony_ci		return -EINVAL;
42548c2ecf20Sopenharmony_ci	}
42558c2ecf20Sopenharmony_ci
42568c2ecf20Sopenharmony_ci	if (ctrl_info->max_oq_element_length <
42578c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_OQ_ELEMENT_LENGTH) {
42588c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
42598c2ecf20Sopenharmony_ci			"max. outbound queue element length of %d is less than the required length of %d\n",
42608c2ecf20Sopenharmony_ci			ctrl_info->max_oq_element_length,
42618c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_OQ_ELEMENT_LENGTH);
42628c2ecf20Sopenharmony_ci		return -EINVAL;
42638c2ecf20Sopenharmony_ci	}
42648c2ecf20Sopenharmony_ci
42658c2ecf20Sopenharmony_ci	if (ctrl_info->max_inbound_iu_length_per_firmware <
42668c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) {
42678c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
42688c2ecf20Sopenharmony_ci			"max. inbound IU length of %u is less than the min. required length of %d\n",
42698c2ecf20Sopenharmony_ci			ctrl_info->max_inbound_iu_length_per_firmware,
42708c2ecf20Sopenharmony_ci			PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
42718c2ecf20Sopenharmony_ci		return -EINVAL;
42728c2ecf20Sopenharmony_ci	}
42738c2ecf20Sopenharmony_ci
42748c2ecf20Sopenharmony_ci	if (!ctrl_info->inbound_spanning_supported) {
42758c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
42768c2ecf20Sopenharmony_ci			"the controller does not support inbound spanning\n");
42778c2ecf20Sopenharmony_ci		return -EINVAL;
42788c2ecf20Sopenharmony_ci	}
42798c2ecf20Sopenharmony_ci
42808c2ecf20Sopenharmony_ci	if (ctrl_info->outbound_spanning_supported) {
42818c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
42828c2ecf20Sopenharmony_ci			"the controller supports outbound spanning but this driver does not\n");
42838c2ecf20Sopenharmony_ci		return -EINVAL;
42848c2ecf20Sopenharmony_ci	}
42858c2ecf20Sopenharmony_ci
42868c2ecf20Sopenharmony_ci	return 0;
42878c2ecf20Sopenharmony_ci}
42888c2ecf20Sopenharmony_ci
42898c2ecf20Sopenharmony_cistatic int pqi_create_event_queue(struct pqi_ctrl_info *ctrl_info)
42908c2ecf20Sopenharmony_ci{
42918c2ecf20Sopenharmony_ci	int rc;
42928c2ecf20Sopenharmony_ci	struct pqi_event_queue *event_queue;
42938c2ecf20Sopenharmony_ci	struct pqi_general_admin_request request;
42948c2ecf20Sopenharmony_ci	struct pqi_general_admin_response response;
42958c2ecf20Sopenharmony_ci
42968c2ecf20Sopenharmony_ci	event_queue = &ctrl_info->event_queue;
42978c2ecf20Sopenharmony_ci
42988c2ecf20Sopenharmony_ci	/*
42998c2ecf20Sopenharmony_ci	 * Create OQ (Outbound Queue - device to host queue) to dedicate
43008c2ecf20Sopenharmony_ci	 * to events.
43018c2ecf20Sopenharmony_ci	 */
43028c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
43038c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
43048c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
43058c2ecf20Sopenharmony_ci		&request.header.iu_length);
43068c2ecf20Sopenharmony_ci	request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ;
43078c2ecf20Sopenharmony_ci	put_unaligned_le16(event_queue->oq_id,
43088c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.queue_id);
43098c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)event_queue->oq_element_array_bus_addr,
43108c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.element_array_addr);
43118c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)event_queue->oq_pi_bus_addr,
43128c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.pi_addr);
43138c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_NUM_EVENT_QUEUE_ELEMENTS,
43148c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.num_elements);
43158c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_EVENT_OQ_ELEMENT_LENGTH / 16,
43168c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.element_length);
43178c2ecf20Sopenharmony_ci	request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP;
43188c2ecf20Sopenharmony_ci	put_unaligned_le16(event_queue->int_msg_num,
43198c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.int_msg_num);
43208c2ecf20Sopenharmony_ci
43218c2ecf20Sopenharmony_ci	rc = pqi_submit_admin_request_synchronous(ctrl_info, &request,
43228c2ecf20Sopenharmony_ci		&response);
43238c2ecf20Sopenharmony_ci	if (rc)
43248c2ecf20Sopenharmony_ci		return rc;
43258c2ecf20Sopenharmony_ci
43268c2ecf20Sopenharmony_ci	event_queue->oq_ci = ctrl_info->iomem_base +
43278c2ecf20Sopenharmony_ci		PQI_DEVICE_REGISTERS_OFFSET +
43288c2ecf20Sopenharmony_ci		get_unaligned_le64(
43298c2ecf20Sopenharmony_ci			&response.data.create_operational_oq.oq_ci_offset);
43308c2ecf20Sopenharmony_ci
43318c2ecf20Sopenharmony_ci	return 0;
43328c2ecf20Sopenharmony_ci}
43338c2ecf20Sopenharmony_ci
43348c2ecf20Sopenharmony_cistatic int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info,
43358c2ecf20Sopenharmony_ci	unsigned int group_number)
43368c2ecf20Sopenharmony_ci{
43378c2ecf20Sopenharmony_ci	int rc;
43388c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
43398c2ecf20Sopenharmony_ci	struct pqi_general_admin_request request;
43408c2ecf20Sopenharmony_ci	struct pqi_general_admin_response response;
43418c2ecf20Sopenharmony_ci
43428c2ecf20Sopenharmony_ci	queue_group = &ctrl_info->queue_groups[group_number];
43438c2ecf20Sopenharmony_ci
43448c2ecf20Sopenharmony_ci	/*
43458c2ecf20Sopenharmony_ci	 * Create IQ (Inbound Queue - host to device queue) for
43468c2ecf20Sopenharmony_ci	 * RAID path.
43478c2ecf20Sopenharmony_ci	 */
43488c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
43498c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
43508c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
43518c2ecf20Sopenharmony_ci		&request.header.iu_length);
43528c2ecf20Sopenharmony_ci	request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ;
43538c2ecf20Sopenharmony_ci	put_unaligned_le16(queue_group->iq_id[RAID_PATH],
43548c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.queue_id);
43558c2ecf20Sopenharmony_ci	put_unaligned_le64(
43568c2ecf20Sopenharmony_ci		(u64)queue_group->iq_element_array_bus_addr[RAID_PATH],
43578c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.element_array_addr);
43588c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)queue_group->iq_ci_bus_addr[RAID_PATH],
43598c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.ci_addr);
43608c2ecf20Sopenharmony_ci	put_unaligned_le16(ctrl_info->num_elements_per_iq,
43618c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.num_elements);
43628c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16,
43638c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.element_length);
43648c2ecf20Sopenharmony_ci	request.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP;
43658c2ecf20Sopenharmony_ci
43668c2ecf20Sopenharmony_ci	rc = pqi_submit_admin_request_synchronous(ctrl_info, &request,
43678c2ecf20Sopenharmony_ci		&response);
43688c2ecf20Sopenharmony_ci	if (rc) {
43698c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
43708c2ecf20Sopenharmony_ci			"error creating inbound RAID queue\n");
43718c2ecf20Sopenharmony_ci		return rc;
43728c2ecf20Sopenharmony_ci	}
43738c2ecf20Sopenharmony_ci
43748c2ecf20Sopenharmony_ci	queue_group->iq_pi[RAID_PATH] = ctrl_info->iomem_base +
43758c2ecf20Sopenharmony_ci		PQI_DEVICE_REGISTERS_OFFSET +
43768c2ecf20Sopenharmony_ci		get_unaligned_le64(
43778c2ecf20Sopenharmony_ci			&response.data.create_operational_iq.iq_pi_offset);
43788c2ecf20Sopenharmony_ci
43798c2ecf20Sopenharmony_ci	/*
43808c2ecf20Sopenharmony_ci	 * Create IQ (Inbound Queue - host to device queue) for
43818c2ecf20Sopenharmony_ci	 * Advanced I/O (AIO) path.
43828c2ecf20Sopenharmony_ci	 */
43838c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
43848c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
43858c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
43868c2ecf20Sopenharmony_ci		&request.header.iu_length);
43878c2ecf20Sopenharmony_ci	request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ;
43888c2ecf20Sopenharmony_ci	put_unaligned_le16(queue_group->iq_id[AIO_PATH],
43898c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.queue_id);
43908c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)queue_group->
43918c2ecf20Sopenharmony_ci		iq_element_array_bus_addr[AIO_PATH],
43928c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.element_array_addr);
43938c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)queue_group->iq_ci_bus_addr[AIO_PATH],
43948c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.ci_addr);
43958c2ecf20Sopenharmony_ci	put_unaligned_le16(ctrl_info->num_elements_per_iq,
43968c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.num_elements);
43978c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16,
43988c2ecf20Sopenharmony_ci		&request.data.create_operational_iq.element_length);
43998c2ecf20Sopenharmony_ci	request.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP;
44008c2ecf20Sopenharmony_ci
44018c2ecf20Sopenharmony_ci	rc = pqi_submit_admin_request_synchronous(ctrl_info, &request,
44028c2ecf20Sopenharmony_ci		&response);
44038c2ecf20Sopenharmony_ci	if (rc) {
44048c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
44058c2ecf20Sopenharmony_ci			"error creating inbound AIO queue\n");
44068c2ecf20Sopenharmony_ci		return rc;
44078c2ecf20Sopenharmony_ci	}
44088c2ecf20Sopenharmony_ci
44098c2ecf20Sopenharmony_ci	queue_group->iq_pi[AIO_PATH] = ctrl_info->iomem_base +
44108c2ecf20Sopenharmony_ci		PQI_DEVICE_REGISTERS_OFFSET +
44118c2ecf20Sopenharmony_ci		get_unaligned_le64(
44128c2ecf20Sopenharmony_ci			&response.data.create_operational_iq.iq_pi_offset);
44138c2ecf20Sopenharmony_ci
44148c2ecf20Sopenharmony_ci	/*
44158c2ecf20Sopenharmony_ci	 * Designate the 2nd IQ as the AIO path.  By default, all IQs are
44168c2ecf20Sopenharmony_ci	 * assumed to be for RAID path I/O unless we change the queue's
44178c2ecf20Sopenharmony_ci	 * property.
44188c2ecf20Sopenharmony_ci	 */
44198c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
44208c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
44218c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
44228c2ecf20Sopenharmony_ci		&request.header.iu_length);
44238c2ecf20Sopenharmony_ci	request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CHANGE_IQ_PROPERTY;
44248c2ecf20Sopenharmony_ci	put_unaligned_le16(queue_group->iq_id[AIO_PATH],
44258c2ecf20Sopenharmony_ci		&request.data.change_operational_iq_properties.queue_id);
44268c2ecf20Sopenharmony_ci	put_unaligned_le32(PQI_IQ_PROPERTY_IS_AIO_QUEUE,
44278c2ecf20Sopenharmony_ci		&request.data.change_operational_iq_properties.vendor_specific);
44288c2ecf20Sopenharmony_ci
44298c2ecf20Sopenharmony_ci	rc = pqi_submit_admin_request_synchronous(ctrl_info, &request,
44308c2ecf20Sopenharmony_ci		&response);
44318c2ecf20Sopenharmony_ci	if (rc) {
44328c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
44338c2ecf20Sopenharmony_ci			"error changing queue property\n");
44348c2ecf20Sopenharmony_ci		return rc;
44358c2ecf20Sopenharmony_ci	}
44368c2ecf20Sopenharmony_ci
44378c2ecf20Sopenharmony_ci	/*
44388c2ecf20Sopenharmony_ci	 * Create OQ (Outbound Queue - device to host queue).
44398c2ecf20Sopenharmony_ci	 */
44408c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
44418c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
44428c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
44438c2ecf20Sopenharmony_ci		&request.header.iu_length);
44448c2ecf20Sopenharmony_ci	request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ;
44458c2ecf20Sopenharmony_ci	put_unaligned_le16(queue_group->oq_id,
44468c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.queue_id);
44478c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)queue_group->oq_element_array_bus_addr,
44488c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.element_array_addr);
44498c2ecf20Sopenharmony_ci	put_unaligned_le64((u64)queue_group->oq_pi_bus_addr,
44508c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.pi_addr);
44518c2ecf20Sopenharmony_ci	put_unaligned_le16(ctrl_info->num_elements_per_oq,
44528c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.num_elements);
44538c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH / 16,
44548c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.element_length);
44558c2ecf20Sopenharmony_ci	request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP;
44568c2ecf20Sopenharmony_ci	put_unaligned_le16(queue_group->int_msg_num,
44578c2ecf20Sopenharmony_ci		&request.data.create_operational_oq.int_msg_num);
44588c2ecf20Sopenharmony_ci
44598c2ecf20Sopenharmony_ci	rc = pqi_submit_admin_request_synchronous(ctrl_info, &request,
44608c2ecf20Sopenharmony_ci		&response);
44618c2ecf20Sopenharmony_ci	if (rc) {
44628c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
44638c2ecf20Sopenharmony_ci			"error creating outbound queue\n");
44648c2ecf20Sopenharmony_ci		return rc;
44658c2ecf20Sopenharmony_ci	}
44668c2ecf20Sopenharmony_ci
44678c2ecf20Sopenharmony_ci	queue_group->oq_ci = ctrl_info->iomem_base +
44688c2ecf20Sopenharmony_ci		PQI_DEVICE_REGISTERS_OFFSET +
44698c2ecf20Sopenharmony_ci		get_unaligned_le64(
44708c2ecf20Sopenharmony_ci			&response.data.create_operational_oq.oq_ci_offset);
44718c2ecf20Sopenharmony_ci
44728c2ecf20Sopenharmony_ci	return 0;
44738c2ecf20Sopenharmony_ci}
44748c2ecf20Sopenharmony_ci
44758c2ecf20Sopenharmony_cistatic int pqi_create_queues(struct pqi_ctrl_info *ctrl_info)
44768c2ecf20Sopenharmony_ci{
44778c2ecf20Sopenharmony_ci	int rc;
44788c2ecf20Sopenharmony_ci	unsigned int i;
44798c2ecf20Sopenharmony_ci
44808c2ecf20Sopenharmony_ci	rc = pqi_create_event_queue(ctrl_info);
44818c2ecf20Sopenharmony_ci	if (rc) {
44828c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
44838c2ecf20Sopenharmony_ci			"error creating event queue\n");
44848c2ecf20Sopenharmony_ci		return rc;
44858c2ecf20Sopenharmony_ci	}
44868c2ecf20Sopenharmony_ci
44878c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
44888c2ecf20Sopenharmony_ci		rc = pqi_create_queue_group(ctrl_info, i);
44898c2ecf20Sopenharmony_ci		if (rc) {
44908c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
44918c2ecf20Sopenharmony_ci				"error creating queue group number %u/%u\n",
44928c2ecf20Sopenharmony_ci				i, ctrl_info->num_queue_groups);
44938c2ecf20Sopenharmony_ci			return rc;
44948c2ecf20Sopenharmony_ci		}
44958c2ecf20Sopenharmony_ci	}
44968c2ecf20Sopenharmony_ci
44978c2ecf20Sopenharmony_ci	return 0;
44988c2ecf20Sopenharmony_ci}
44998c2ecf20Sopenharmony_ci
45008c2ecf20Sopenharmony_ci#define PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH	\
45018c2ecf20Sopenharmony_ci	(offsetof(struct pqi_event_config, descriptors) + \
45028c2ecf20Sopenharmony_ci	(PQI_MAX_EVENT_DESCRIPTORS * sizeof(struct pqi_event_descriptor)))
45038c2ecf20Sopenharmony_ci
45048c2ecf20Sopenharmony_cistatic int pqi_configure_events(struct pqi_ctrl_info *ctrl_info,
45058c2ecf20Sopenharmony_ci	bool enable_events)
45068c2ecf20Sopenharmony_ci{
45078c2ecf20Sopenharmony_ci	int rc;
45088c2ecf20Sopenharmony_ci	unsigned int i;
45098c2ecf20Sopenharmony_ci	struct pqi_event_config *event_config;
45108c2ecf20Sopenharmony_ci	struct pqi_event_descriptor *event_descriptor;
45118c2ecf20Sopenharmony_ci	struct pqi_general_management_request request;
45128c2ecf20Sopenharmony_ci
45138c2ecf20Sopenharmony_ci	event_config = kmalloc(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH,
45148c2ecf20Sopenharmony_ci		GFP_KERNEL);
45158c2ecf20Sopenharmony_ci	if (!event_config)
45168c2ecf20Sopenharmony_ci		return -ENOMEM;
45178c2ecf20Sopenharmony_ci
45188c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
45198c2ecf20Sopenharmony_ci
45208c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_REPORT_VENDOR_EVENT_CONFIG;
45218c2ecf20Sopenharmony_ci	put_unaligned_le16(offsetof(struct pqi_general_management_request,
45228c2ecf20Sopenharmony_ci		data.report_event_configuration.sg_descriptors[1]) -
45238c2ecf20Sopenharmony_ci		PQI_REQUEST_HEADER_LENGTH, &request.header.iu_length);
45248c2ecf20Sopenharmony_ci	put_unaligned_le32(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH,
45258c2ecf20Sopenharmony_ci		&request.data.report_event_configuration.buffer_length);
45268c2ecf20Sopenharmony_ci
45278c2ecf20Sopenharmony_ci	rc = pqi_map_single(ctrl_info->pci_dev,
45288c2ecf20Sopenharmony_ci		request.data.report_event_configuration.sg_descriptors,
45298c2ecf20Sopenharmony_ci		event_config, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH,
45308c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE);
45318c2ecf20Sopenharmony_ci	if (rc)
45328c2ecf20Sopenharmony_ci		goto out;
45338c2ecf20Sopenharmony_ci
45348c2ecf20Sopenharmony_ci	rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
45358c2ecf20Sopenharmony_ci		0, NULL, NO_TIMEOUT);
45368c2ecf20Sopenharmony_ci
45378c2ecf20Sopenharmony_ci	pqi_pci_unmap(ctrl_info->pci_dev,
45388c2ecf20Sopenharmony_ci		request.data.report_event_configuration.sg_descriptors, 1,
45398c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE);
45408c2ecf20Sopenharmony_ci
45418c2ecf20Sopenharmony_ci	if (rc)
45428c2ecf20Sopenharmony_ci		goto out;
45438c2ecf20Sopenharmony_ci
45448c2ecf20Sopenharmony_ci	for (i = 0; i < event_config->num_event_descriptors; i++) {
45458c2ecf20Sopenharmony_ci		event_descriptor = &event_config->descriptors[i];
45468c2ecf20Sopenharmony_ci		if (enable_events &&
45478c2ecf20Sopenharmony_ci			pqi_is_supported_event(event_descriptor->event_type))
45488c2ecf20Sopenharmony_ci			put_unaligned_le16(ctrl_info->event_queue.oq_id,
45498c2ecf20Sopenharmony_ci					&event_descriptor->oq_id);
45508c2ecf20Sopenharmony_ci		else
45518c2ecf20Sopenharmony_ci			put_unaligned_le16(0, &event_descriptor->oq_id);
45528c2ecf20Sopenharmony_ci	}
45538c2ecf20Sopenharmony_ci
45548c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
45558c2ecf20Sopenharmony_ci
45568c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_SET_VENDOR_EVENT_CONFIG;
45578c2ecf20Sopenharmony_ci	put_unaligned_le16(offsetof(struct pqi_general_management_request,
45588c2ecf20Sopenharmony_ci		data.report_event_configuration.sg_descriptors[1]) -
45598c2ecf20Sopenharmony_ci		PQI_REQUEST_HEADER_LENGTH, &request.header.iu_length);
45608c2ecf20Sopenharmony_ci	put_unaligned_le32(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH,
45618c2ecf20Sopenharmony_ci		&request.data.report_event_configuration.buffer_length);
45628c2ecf20Sopenharmony_ci
45638c2ecf20Sopenharmony_ci	rc = pqi_map_single(ctrl_info->pci_dev,
45648c2ecf20Sopenharmony_ci		request.data.report_event_configuration.sg_descriptors,
45658c2ecf20Sopenharmony_ci		event_config, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH,
45668c2ecf20Sopenharmony_ci		DMA_TO_DEVICE);
45678c2ecf20Sopenharmony_ci	if (rc)
45688c2ecf20Sopenharmony_ci		goto out;
45698c2ecf20Sopenharmony_ci
45708c2ecf20Sopenharmony_ci	rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0,
45718c2ecf20Sopenharmony_ci		NULL, NO_TIMEOUT);
45728c2ecf20Sopenharmony_ci
45738c2ecf20Sopenharmony_ci	pqi_pci_unmap(ctrl_info->pci_dev,
45748c2ecf20Sopenharmony_ci		request.data.report_event_configuration.sg_descriptors, 1,
45758c2ecf20Sopenharmony_ci		DMA_TO_DEVICE);
45768c2ecf20Sopenharmony_ci
45778c2ecf20Sopenharmony_ciout:
45788c2ecf20Sopenharmony_ci	kfree(event_config);
45798c2ecf20Sopenharmony_ci
45808c2ecf20Sopenharmony_ci	return rc;
45818c2ecf20Sopenharmony_ci}
45828c2ecf20Sopenharmony_ci
45838c2ecf20Sopenharmony_cistatic inline int pqi_enable_events(struct pqi_ctrl_info *ctrl_info)
45848c2ecf20Sopenharmony_ci{
45858c2ecf20Sopenharmony_ci	return pqi_configure_events(ctrl_info, true);
45868c2ecf20Sopenharmony_ci}
45878c2ecf20Sopenharmony_ci
45888c2ecf20Sopenharmony_cistatic inline int pqi_disable_events(struct pqi_ctrl_info *ctrl_info)
45898c2ecf20Sopenharmony_ci{
45908c2ecf20Sopenharmony_ci	return pqi_configure_events(ctrl_info, false);
45918c2ecf20Sopenharmony_ci}
45928c2ecf20Sopenharmony_ci
45938c2ecf20Sopenharmony_cistatic void pqi_free_all_io_requests(struct pqi_ctrl_info *ctrl_info)
45948c2ecf20Sopenharmony_ci{
45958c2ecf20Sopenharmony_ci	unsigned int i;
45968c2ecf20Sopenharmony_ci	struct device *dev;
45978c2ecf20Sopenharmony_ci	size_t sg_chain_buffer_length;
45988c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
45998c2ecf20Sopenharmony_ci
46008c2ecf20Sopenharmony_ci	if (!ctrl_info->io_request_pool)
46018c2ecf20Sopenharmony_ci		return;
46028c2ecf20Sopenharmony_ci
46038c2ecf20Sopenharmony_ci	dev = &ctrl_info->pci_dev->dev;
46048c2ecf20Sopenharmony_ci	sg_chain_buffer_length = ctrl_info->sg_chain_buffer_length;
46058c2ecf20Sopenharmony_ci	io_request = ctrl_info->io_request_pool;
46068c2ecf20Sopenharmony_ci
46078c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->max_io_slots; i++) {
46088c2ecf20Sopenharmony_ci		kfree(io_request->iu);
46098c2ecf20Sopenharmony_ci		if (!io_request->sg_chain_buffer)
46108c2ecf20Sopenharmony_ci			break;
46118c2ecf20Sopenharmony_ci		dma_free_coherent(dev, sg_chain_buffer_length,
46128c2ecf20Sopenharmony_ci			io_request->sg_chain_buffer,
46138c2ecf20Sopenharmony_ci			io_request->sg_chain_buffer_dma_handle);
46148c2ecf20Sopenharmony_ci		io_request++;
46158c2ecf20Sopenharmony_ci	}
46168c2ecf20Sopenharmony_ci
46178c2ecf20Sopenharmony_ci	kfree(ctrl_info->io_request_pool);
46188c2ecf20Sopenharmony_ci	ctrl_info->io_request_pool = NULL;
46198c2ecf20Sopenharmony_ci}
46208c2ecf20Sopenharmony_ci
46218c2ecf20Sopenharmony_cistatic inline int pqi_alloc_error_buffer(struct pqi_ctrl_info *ctrl_info)
46228c2ecf20Sopenharmony_ci{
46238c2ecf20Sopenharmony_ci
46248c2ecf20Sopenharmony_ci	ctrl_info->error_buffer = dma_alloc_coherent(&ctrl_info->pci_dev->dev,
46258c2ecf20Sopenharmony_ci				     ctrl_info->error_buffer_length,
46268c2ecf20Sopenharmony_ci				     &ctrl_info->error_buffer_dma_handle,
46278c2ecf20Sopenharmony_ci				     GFP_KERNEL);
46288c2ecf20Sopenharmony_ci	if (!ctrl_info->error_buffer)
46298c2ecf20Sopenharmony_ci		return -ENOMEM;
46308c2ecf20Sopenharmony_ci
46318c2ecf20Sopenharmony_ci	return 0;
46328c2ecf20Sopenharmony_ci}
46338c2ecf20Sopenharmony_ci
46348c2ecf20Sopenharmony_cistatic int pqi_alloc_io_resources(struct pqi_ctrl_info *ctrl_info)
46358c2ecf20Sopenharmony_ci{
46368c2ecf20Sopenharmony_ci	unsigned int i;
46378c2ecf20Sopenharmony_ci	void *sg_chain_buffer;
46388c2ecf20Sopenharmony_ci	size_t sg_chain_buffer_length;
46398c2ecf20Sopenharmony_ci	dma_addr_t sg_chain_buffer_dma_handle;
46408c2ecf20Sopenharmony_ci	struct device *dev;
46418c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
46428c2ecf20Sopenharmony_ci
46438c2ecf20Sopenharmony_ci	ctrl_info->io_request_pool =
46448c2ecf20Sopenharmony_ci		kcalloc(ctrl_info->max_io_slots,
46458c2ecf20Sopenharmony_ci			sizeof(ctrl_info->io_request_pool[0]), GFP_KERNEL);
46468c2ecf20Sopenharmony_ci
46478c2ecf20Sopenharmony_ci	if (!ctrl_info->io_request_pool) {
46488c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
46498c2ecf20Sopenharmony_ci			"failed to allocate I/O request pool\n");
46508c2ecf20Sopenharmony_ci		goto error;
46518c2ecf20Sopenharmony_ci	}
46528c2ecf20Sopenharmony_ci
46538c2ecf20Sopenharmony_ci	dev = &ctrl_info->pci_dev->dev;
46548c2ecf20Sopenharmony_ci	sg_chain_buffer_length = ctrl_info->sg_chain_buffer_length;
46558c2ecf20Sopenharmony_ci	io_request = ctrl_info->io_request_pool;
46568c2ecf20Sopenharmony_ci
46578c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->max_io_slots; i++) {
46588c2ecf20Sopenharmony_ci		io_request->iu =
46598c2ecf20Sopenharmony_ci			kmalloc(ctrl_info->max_inbound_iu_length, GFP_KERNEL);
46608c2ecf20Sopenharmony_ci
46618c2ecf20Sopenharmony_ci		if (!io_request->iu) {
46628c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
46638c2ecf20Sopenharmony_ci				"failed to allocate IU buffers\n");
46648c2ecf20Sopenharmony_ci			goto error;
46658c2ecf20Sopenharmony_ci		}
46668c2ecf20Sopenharmony_ci
46678c2ecf20Sopenharmony_ci		sg_chain_buffer = dma_alloc_coherent(dev,
46688c2ecf20Sopenharmony_ci			sg_chain_buffer_length, &sg_chain_buffer_dma_handle,
46698c2ecf20Sopenharmony_ci			GFP_KERNEL);
46708c2ecf20Sopenharmony_ci
46718c2ecf20Sopenharmony_ci		if (!sg_chain_buffer) {
46728c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
46738c2ecf20Sopenharmony_ci				"failed to allocate PQI scatter-gather chain buffers\n");
46748c2ecf20Sopenharmony_ci			goto error;
46758c2ecf20Sopenharmony_ci		}
46768c2ecf20Sopenharmony_ci
46778c2ecf20Sopenharmony_ci		io_request->index = i;
46788c2ecf20Sopenharmony_ci		io_request->sg_chain_buffer = sg_chain_buffer;
46798c2ecf20Sopenharmony_ci		io_request->sg_chain_buffer_dma_handle =
46808c2ecf20Sopenharmony_ci			sg_chain_buffer_dma_handle;
46818c2ecf20Sopenharmony_ci		io_request++;
46828c2ecf20Sopenharmony_ci	}
46838c2ecf20Sopenharmony_ci
46848c2ecf20Sopenharmony_ci	return 0;
46858c2ecf20Sopenharmony_ci
46868c2ecf20Sopenharmony_cierror:
46878c2ecf20Sopenharmony_ci	pqi_free_all_io_requests(ctrl_info);
46888c2ecf20Sopenharmony_ci
46898c2ecf20Sopenharmony_ci	return -ENOMEM;
46908c2ecf20Sopenharmony_ci}
46918c2ecf20Sopenharmony_ci
46928c2ecf20Sopenharmony_ci/*
46938c2ecf20Sopenharmony_ci * Calculate required resources that are sized based on max. outstanding
46948c2ecf20Sopenharmony_ci * requests and max. transfer size.
46958c2ecf20Sopenharmony_ci */
46968c2ecf20Sopenharmony_ci
46978c2ecf20Sopenharmony_cistatic void pqi_calculate_io_resources(struct pqi_ctrl_info *ctrl_info)
46988c2ecf20Sopenharmony_ci{
46998c2ecf20Sopenharmony_ci	u32 max_transfer_size;
47008c2ecf20Sopenharmony_ci	u32 max_sg_entries;
47018c2ecf20Sopenharmony_ci
47028c2ecf20Sopenharmony_ci	ctrl_info->scsi_ml_can_queue =
47038c2ecf20Sopenharmony_ci		ctrl_info->max_outstanding_requests - PQI_RESERVED_IO_SLOTS;
47048c2ecf20Sopenharmony_ci	ctrl_info->max_io_slots = ctrl_info->max_outstanding_requests;
47058c2ecf20Sopenharmony_ci
47068c2ecf20Sopenharmony_ci	ctrl_info->error_buffer_length =
47078c2ecf20Sopenharmony_ci		ctrl_info->max_io_slots * PQI_ERROR_BUFFER_ELEMENT_LENGTH;
47088c2ecf20Sopenharmony_ci
47098c2ecf20Sopenharmony_ci	if (reset_devices)
47108c2ecf20Sopenharmony_ci		max_transfer_size = min(ctrl_info->max_transfer_size,
47118c2ecf20Sopenharmony_ci			PQI_MAX_TRANSFER_SIZE_KDUMP);
47128c2ecf20Sopenharmony_ci	else
47138c2ecf20Sopenharmony_ci		max_transfer_size = min(ctrl_info->max_transfer_size,
47148c2ecf20Sopenharmony_ci			PQI_MAX_TRANSFER_SIZE);
47158c2ecf20Sopenharmony_ci
47168c2ecf20Sopenharmony_ci	max_sg_entries = max_transfer_size / PAGE_SIZE;
47178c2ecf20Sopenharmony_ci
47188c2ecf20Sopenharmony_ci	/* +1 to cover when the buffer is not page-aligned. */
47198c2ecf20Sopenharmony_ci	max_sg_entries++;
47208c2ecf20Sopenharmony_ci
47218c2ecf20Sopenharmony_ci	max_sg_entries = min(ctrl_info->max_sg_entries, max_sg_entries);
47228c2ecf20Sopenharmony_ci
47238c2ecf20Sopenharmony_ci	max_transfer_size = (max_sg_entries - 1) * PAGE_SIZE;
47248c2ecf20Sopenharmony_ci
47258c2ecf20Sopenharmony_ci	ctrl_info->sg_chain_buffer_length =
47268c2ecf20Sopenharmony_ci		(max_sg_entries * sizeof(struct pqi_sg_descriptor)) +
47278c2ecf20Sopenharmony_ci		PQI_EXTRA_SGL_MEMORY;
47288c2ecf20Sopenharmony_ci	ctrl_info->sg_tablesize = max_sg_entries;
47298c2ecf20Sopenharmony_ci	ctrl_info->max_sectors = max_transfer_size / 512;
47308c2ecf20Sopenharmony_ci}
47318c2ecf20Sopenharmony_ci
47328c2ecf20Sopenharmony_cistatic void pqi_calculate_queue_resources(struct pqi_ctrl_info *ctrl_info)
47338c2ecf20Sopenharmony_ci{
47348c2ecf20Sopenharmony_ci	int num_queue_groups;
47358c2ecf20Sopenharmony_ci	u16 num_elements_per_iq;
47368c2ecf20Sopenharmony_ci	u16 num_elements_per_oq;
47378c2ecf20Sopenharmony_ci
47388c2ecf20Sopenharmony_ci	if (reset_devices) {
47398c2ecf20Sopenharmony_ci		num_queue_groups = 1;
47408c2ecf20Sopenharmony_ci	} else {
47418c2ecf20Sopenharmony_ci		int num_cpus;
47428c2ecf20Sopenharmony_ci		int max_queue_groups;
47438c2ecf20Sopenharmony_ci
47448c2ecf20Sopenharmony_ci		max_queue_groups = min(ctrl_info->max_inbound_queues / 2,
47458c2ecf20Sopenharmony_ci			ctrl_info->max_outbound_queues - 1);
47468c2ecf20Sopenharmony_ci		max_queue_groups = min(max_queue_groups, PQI_MAX_QUEUE_GROUPS);
47478c2ecf20Sopenharmony_ci
47488c2ecf20Sopenharmony_ci		num_cpus = num_online_cpus();
47498c2ecf20Sopenharmony_ci		num_queue_groups = min(num_cpus, ctrl_info->max_msix_vectors);
47508c2ecf20Sopenharmony_ci		num_queue_groups = min(num_queue_groups, max_queue_groups);
47518c2ecf20Sopenharmony_ci	}
47528c2ecf20Sopenharmony_ci
47538c2ecf20Sopenharmony_ci	ctrl_info->num_queue_groups = num_queue_groups;
47548c2ecf20Sopenharmony_ci	ctrl_info->max_hw_queue_index = num_queue_groups - 1;
47558c2ecf20Sopenharmony_ci
47568c2ecf20Sopenharmony_ci	/*
47578c2ecf20Sopenharmony_ci	 * Make sure that the max. inbound IU length is an even multiple
47588c2ecf20Sopenharmony_ci	 * of our inbound element length.
47598c2ecf20Sopenharmony_ci	 */
47608c2ecf20Sopenharmony_ci	ctrl_info->max_inbound_iu_length =
47618c2ecf20Sopenharmony_ci		(ctrl_info->max_inbound_iu_length_per_firmware /
47628c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) *
47638c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH;
47648c2ecf20Sopenharmony_ci
47658c2ecf20Sopenharmony_ci	num_elements_per_iq =
47668c2ecf20Sopenharmony_ci		(ctrl_info->max_inbound_iu_length /
47678c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
47688c2ecf20Sopenharmony_ci
47698c2ecf20Sopenharmony_ci	/* Add one because one element in each queue is unusable. */
47708c2ecf20Sopenharmony_ci	num_elements_per_iq++;
47718c2ecf20Sopenharmony_ci
47728c2ecf20Sopenharmony_ci	num_elements_per_iq = min(num_elements_per_iq,
47738c2ecf20Sopenharmony_ci		ctrl_info->max_elements_per_iq);
47748c2ecf20Sopenharmony_ci
47758c2ecf20Sopenharmony_ci	num_elements_per_oq = ((num_elements_per_iq - 1) * 2) + 1;
47768c2ecf20Sopenharmony_ci	num_elements_per_oq = min(num_elements_per_oq,
47778c2ecf20Sopenharmony_ci		ctrl_info->max_elements_per_oq);
47788c2ecf20Sopenharmony_ci
47798c2ecf20Sopenharmony_ci	ctrl_info->num_elements_per_iq = num_elements_per_iq;
47808c2ecf20Sopenharmony_ci	ctrl_info->num_elements_per_oq = num_elements_per_oq;
47818c2ecf20Sopenharmony_ci
47828c2ecf20Sopenharmony_ci	ctrl_info->max_sg_per_iu =
47838c2ecf20Sopenharmony_ci		((ctrl_info->max_inbound_iu_length -
47848c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) /
47858c2ecf20Sopenharmony_ci		sizeof(struct pqi_sg_descriptor)) +
47868c2ecf20Sopenharmony_ci		PQI_MAX_EMBEDDED_SG_DESCRIPTORS;
47878c2ecf20Sopenharmony_ci}
47888c2ecf20Sopenharmony_ci
47898c2ecf20Sopenharmony_cistatic inline void pqi_set_sg_descriptor(
47908c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *sg_descriptor, struct scatterlist *sg)
47918c2ecf20Sopenharmony_ci{
47928c2ecf20Sopenharmony_ci	u64 address = (u64)sg_dma_address(sg);
47938c2ecf20Sopenharmony_ci	unsigned int length = sg_dma_len(sg);
47948c2ecf20Sopenharmony_ci
47958c2ecf20Sopenharmony_ci	put_unaligned_le64(address, &sg_descriptor->address);
47968c2ecf20Sopenharmony_ci	put_unaligned_le32(length, &sg_descriptor->length);
47978c2ecf20Sopenharmony_ci	put_unaligned_le32(0, &sg_descriptor->flags);
47988c2ecf20Sopenharmony_ci}
47998c2ecf20Sopenharmony_ci
48008c2ecf20Sopenharmony_cistatic int pqi_build_raid_sg_list(struct pqi_ctrl_info *ctrl_info,
48018c2ecf20Sopenharmony_ci	struct pqi_raid_path_request *request, struct scsi_cmnd *scmd,
48028c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request)
48038c2ecf20Sopenharmony_ci{
48048c2ecf20Sopenharmony_ci	int i;
48058c2ecf20Sopenharmony_ci	u16 iu_length;
48068c2ecf20Sopenharmony_ci	int sg_count;
48078c2ecf20Sopenharmony_ci	bool chained;
48088c2ecf20Sopenharmony_ci	unsigned int num_sg_in_iu;
48098c2ecf20Sopenharmony_ci	unsigned int max_sg_per_iu;
48108c2ecf20Sopenharmony_ci	struct scatterlist *sg;
48118c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *sg_descriptor;
48128c2ecf20Sopenharmony_ci
48138c2ecf20Sopenharmony_ci	sg_count = scsi_dma_map(scmd);
48148c2ecf20Sopenharmony_ci	if (sg_count < 0)
48158c2ecf20Sopenharmony_ci		return sg_count;
48168c2ecf20Sopenharmony_ci
48178c2ecf20Sopenharmony_ci	iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) -
48188c2ecf20Sopenharmony_ci		PQI_REQUEST_HEADER_LENGTH;
48198c2ecf20Sopenharmony_ci
48208c2ecf20Sopenharmony_ci	if (sg_count == 0)
48218c2ecf20Sopenharmony_ci		goto out;
48228c2ecf20Sopenharmony_ci
48238c2ecf20Sopenharmony_ci	sg = scsi_sglist(scmd);
48248c2ecf20Sopenharmony_ci	sg_descriptor = request->sg_descriptors;
48258c2ecf20Sopenharmony_ci	max_sg_per_iu = ctrl_info->max_sg_per_iu - 1;
48268c2ecf20Sopenharmony_ci	chained = false;
48278c2ecf20Sopenharmony_ci	num_sg_in_iu = 0;
48288c2ecf20Sopenharmony_ci	i = 0;
48298c2ecf20Sopenharmony_ci
48308c2ecf20Sopenharmony_ci	while (1) {
48318c2ecf20Sopenharmony_ci		pqi_set_sg_descriptor(sg_descriptor, sg);
48328c2ecf20Sopenharmony_ci		if (!chained)
48338c2ecf20Sopenharmony_ci			num_sg_in_iu++;
48348c2ecf20Sopenharmony_ci		i++;
48358c2ecf20Sopenharmony_ci		if (i == sg_count)
48368c2ecf20Sopenharmony_ci			break;
48378c2ecf20Sopenharmony_ci		sg_descriptor++;
48388c2ecf20Sopenharmony_ci		if (i == max_sg_per_iu) {
48398c2ecf20Sopenharmony_ci			put_unaligned_le64(
48408c2ecf20Sopenharmony_ci				(u64)io_request->sg_chain_buffer_dma_handle,
48418c2ecf20Sopenharmony_ci				&sg_descriptor->address);
48428c2ecf20Sopenharmony_ci			put_unaligned_le32((sg_count - num_sg_in_iu)
48438c2ecf20Sopenharmony_ci				* sizeof(*sg_descriptor),
48448c2ecf20Sopenharmony_ci				&sg_descriptor->length);
48458c2ecf20Sopenharmony_ci			put_unaligned_le32(CISS_SG_CHAIN,
48468c2ecf20Sopenharmony_ci				&sg_descriptor->flags);
48478c2ecf20Sopenharmony_ci			chained = true;
48488c2ecf20Sopenharmony_ci			num_sg_in_iu++;
48498c2ecf20Sopenharmony_ci			sg_descriptor = io_request->sg_chain_buffer;
48508c2ecf20Sopenharmony_ci		}
48518c2ecf20Sopenharmony_ci		sg = sg_next(sg);
48528c2ecf20Sopenharmony_ci	}
48538c2ecf20Sopenharmony_ci
48548c2ecf20Sopenharmony_ci	put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags);
48558c2ecf20Sopenharmony_ci	request->partial = chained;
48568c2ecf20Sopenharmony_ci	iu_length += num_sg_in_iu * sizeof(*sg_descriptor);
48578c2ecf20Sopenharmony_ci
48588c2ecf20Sopenharmony_ciout:
48598c2ecf20Sopenharmony_ci	put_unaligned_le16(iu_length, &request->header.iu_length);
48608c2ecf20Sopenharmony_ci
48618c2ecf20Sopenharmony_ci	return 0;
48628c2ecf20Sopenharmony_ci}
48638c2ecf20Sopenharmony_ci
48648c2ecf20Sopenharmony_cistatic int pqi_build_aio_sg_list(struct pqi_ctrl_info *ctrl_info,
48658c2ecf20Sopenharmony_ci	struct pqi_aio_path_request *request, struct scsi_cmnd *scmd,
48668c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request)
48678c2ecf20Sopenharmony_ci{
48688c2ecf20Sopenharmony_ci	int i;
48698c2ecf20Sopenharmony_ci	u16 iu_length;
48708c2ecf20Sopenharmony_ci	int sg_count;
48718c2ecf20Sopenharmony_ci	bool chained;
48728c2ecf20Sopenharmony_ci	unsigned int num_sg_in_iu;
48738c2ecf20Sopenharmony_ci	unsigned int max_sg_per_iu;
48748c2ecf20Sopenharmony_ci	struct scatterlist *sg;
48758c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *sg_descriptor;
48768c2ecf20Sopenharmony_ci
48778c2ecf20Sopenharmony_ci	sg_count = scsi_dma_map(scmd);
48788c2ecf20Sopenharmony_ci	if (sg_count < 0)
48798c2ecf20Sopenharmony_ci		return sg_count;
48808c2ecf20Sopenharmony_ci
48818c2ecf20Sopenharmony_ci	iu_length = offsetof(struct pqi_aio_path_request, sg_descriptors) -
48828c2ecf20Sopenharmony_ci		PQI_REQUEST_HEADER_LENGTH;
48838c2ecf20Sopenharmony_ci	num_sg_in_iu = 0;
48848c2ecf20Sopenharmony_ci
48858c2ecf20Sopenharmony_ci	if (sg_count == 0)
48868c2ecf20Sopenharmony_ci		goto out;
48878c2ecf20Sopenharmony_ci
48888c2ecf20Sopenharmony_ci	sg = scsi_sglist(scmd);
48898c2ecf20Sopenharmony_ci	sg_descriptor = request->sg_descriptors;
48908c2ecf20Sopenharmony_ci	max_sg_per_iu = ctrl_info->max_sg_per_iu - 1;
48918c2ecf20Sopenharmony_ci	chained = false;
48928c2ecf20Sopenharmony_ci	i = 0;
48938c2ecf20Sopenharmony_ci
48948c2ecf20Sopenharmony_ci	while (1) {
48958c2ecf20Sopenharmony_ci		pqi_set_sg_descriptor(sg_descriptor, sg);
48968c2ecf20Sopenharmony_ci		if (!chained)
48978c2ecf20Sopenharmony_ci			num_sg_in_iu++;
48988c2ecf20Sopenharmony_ci		i++;
48998c2ecf20Sopenharmony_ci		if (i == sg_count)
49008c2ecf20Sopenharmony_ci			break;
49018c2ecf20Sopenharmony_ci		sg_descriptor++;
49028c2ecf20Sopenharmony_ci		if (i == max_sg_per_iu) {
49038c2ecf20Sopenharmony_ci			put_unaligned_le64(
49048c2ecf20Sopenharmony_ci				(u64)io_request->sg_chain_buffer_dma_handle,
49058c2ecf20Sopenharmony_ci				&sg_descriptor->address);
49068c2ecf20Sopenharmony_ci			put_unaligned_le32((sg_count - num_sg_in_iu)
49078c2ecf20Sopenharmony_ci				* sizeof(*sg_descriptor),
49088c2ecf20Sopenharmony_ci				&sg_descriptor->length);
49098c2ecf20Sopenharmony_ci			put_unaligned_le32(CISS_SG_CHAIN,
49108c2ecf20Sopenharmony_ci				&sg_descriptor->flags);
49118c2ecf20Sopenharmony_ci			chained = true;
49128c2ecf20Sopenharmony_ci			num_sg_in_iu++;
49138c2ecf20Sopenharmony_ci			sg_descriptor = io_request->sg_chain_buffer;
49148c2ecf20Sopenharmony_ci		}
49158c2ecf20Sopenharmony_ci		sg = sg_next(sg);
49168c2ecf20Sopenharmony_ci	}
49178c2ecf20Sopenharmony_ci
49188c2ecf20Sopenharmony_ci	put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags);
49198c2ecf20Sopenharmony_ci	request->partial = chained;
49208c2ecf20Sopenharmony_ci	iu_length += num_sg_in_iu * sizeof(*sg_descriptor);
49218c2ecf20Sopenharmony_ci
49228c2ecf20Sopenharmony_ciout:
49238c2ecf20Sopenharmony_ci	put_unaligned_le16(iu_length, &request->header.iu_length);
49248c2ecf20Sopenharmony_ci	request->num_sg_descriptors = num_sg_in_iu;
49258c2ecf20Sopenharmony_ci
49268c2ecf20Sopenharmony_ci	return 0;
49278c2ecf20Sopenharmony_ci}
49288c2ecf20Sopenharmony_ci
49298c2ecf20Sopenharmony_cistatic void pqi_raid_io_complete(struct pqi_io_request *io_request,
49308c2ecf20Sopenharmony_ci	void *context)
49318c2ecf20Sopenharmony_ci{
49328c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
49338c2ecf20Sopenharmony_ci
49348c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
49358c2ecf20Sopenharmony_ci	pqi_free_io_request(io_request);
49368c2ecf20Sopenharmony_ci	scsi_dma_unmap(scmd);
49378c2ecf20Sopenharmony_ci	pqi_scsi_done(scmd);
49388c2ecf20Sopenharmony_ci}
49398c2ecf20Sopenharmony_ci
49408c2ecf20Sopenharmony_cistatic int pqi_raid_submit_scsi_cmd_with_io_request(
49418c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info, struct pqi_io_request *io_request,
49428c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, struct scsi_cmnd *scmd,
49438c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group)
49448c2ecf20Sopenharmony_ci{
49458c2ecf20Sopenharmony_ci	int rc;
49468c2ecf20Sopenharmony_ci	size_t cdb_length;
49478c2ecf20Sopenharmony_ci	struct pqi_raid_path_request *request;
49488c2ecf20Sopenharmony_ci
49498c2ecf20Sopenharmony_ci	io_request->io_complete_callback = pqi_raid_io_complete;
49508c2ecf20Sopenharmony_ci	io_request->scmd = scmd;
49518c2ecf20Sopenharmony_ci
49528c2ecf20Sopenharmony_ci	request = io_request->iu;
49538c2ecf20Sopenharmony_ci	memset(request, 0,
49548c2ecf20Sopenharmony_ci		offsetof(struct pqi_raid_path_request, sg_descriptors));
49558c2ecf20Sopenharmony_ci
49568c2ecf20Sopenharmony_ci	request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
49578c2ecf20Sopenharmony_ci	put_unaligned_le32(scsi_bufflen(scmd), &request->buffer_length);
49588c2ecf20Sopenharmony_ci	request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
49598c2ecf20Sopenharmony_ci	put_unaligned_le16(io_request->index, &request->request_id);
49608c2ecf20Sopenharmony_ci	request->error_index = request->request_id;
49618c2ecf20Sopenharmony_ci	memcpy(request->lun_number, device->scsi3addr,
49628c2ecf20Sopenharmony_ci		sizeof(request->lun_number));
49638c2ecf20Sopenharmony_ci
49648c2ecf20Sopenharmony_ci	cdb_length = min_t(size_t, scmd->cmd_len, sizeof(request->cdb));
49658c2ecf20Sopenharmony_ci	memcpy(request->cdb, scmd->cmnd, cdb_length);
49668c2ecf20Sopenharmony_ci
49678c2ecf20Sopenharmony_ci	switch (cdb_length) {
49688c2ecf20Sopenharmony_ci	case 6:
49698c2ecf20Sopenharmony_ci	case 10:
49708c2ecf20Sopenharmony_ci	case 12:
49718c2ecf20Sopenharmony_ci	case 16:
49728c2ecf20Sopenharmony_ci		/* No bytes in the Additional CDB bytes field */
49738c2ecf20Sopenharmony_ci		request->additional_cdb_bytes_usage =
49748c2ecf20Sopenharmony_ci			SOP_ADDITIONAL_CDB_BYTES_0;
49758c2ecf20Sopenharmony_ci		break;
49768c2ecf20Sopenharmony_ci	case 20:
49778c2ecf20Sopenharmony_ci		/* 4 bytes in the Additional cdb field */
49788c2ecf20Sopenharmony_ci		request->additional_cdb_bytes_usage =
49798c2ecf20Sopenharmony_ci			SOP_ADDITIONAL_CDB_BYTES_4;
49808c2ecf20Sopenharmony_ci		break;
49818c2ecf20Sopenharmony_ci	case 24:
49828c2ecf20Sopenharmony_ci		/* 8 bytes in the Additional cdb field */
49838c2ecf20Sopenharmony_ci		request->additional_cdb_bytes_usage =
49848c2ecf20Sopenharmony_ci			SOP_ADDITIONAL_CDB_BYTES_8;
49858c2ecf20Sopenharmony_ci		break;
49868c2ecf20Sopenharmony_ci	case 28:
49878c2ecf20Sopenharmony_ci		/* 12 bytes in the Additional cdb field */
49888c2ecf20Sopenharmony_ci		request->additional_cdb_bytes_usage =
49898c2ecf20Sopenharmony_ci			SOP_ADDITIONAL_CDB_BYTES_12;
49908c2ecf20Sopenharmony_ci		break;
49918c2ecf20Sopenharmony_ci	case 32:
49928c2ecf20Sopenharmony_ci	default:
49938c2ecf20Sopenharmony_ci		/* 16 bytes in the Additional cdb field */
49948c2ecf20Sopenharmony_ci		request->additional_cdb_bytes_usage =
49958c2ecf20Sopenharmony_ci			SOP_ADDITIONAL_CDB_BYTES_16;
49968c2ecf20Sopenharmony_ci		break;
49978c2ecf20Sopenharmony_ci	}
49988c2ecf20Sopenharmony_ci
49998c2ecf20Sopenharmony_ci	switch (scmd->sc_data_direction) {
50008c2ecf20Sopenharmony_ci	case DMA_FROM_DEVICE:
50018c2ecf20Sopenharmony_ci		request->data_direction = SOP_READ_FLAG;
50028c2ecf20Sopenharmony_ci		break;
50038c2ecf20Sopenharmony_ci	case DMA_TO_DEVICE:
50048c2ecf20Sopenharmony_ci		request->data_direction = SOP_WRITE_FLAG;
50058c2ecf20Sopenharmony_ci		break;
50068c2ecf20Sopenharmony_ci	case DMA_NONE:
50078c2ecf20Sopenharmony_ci		request->data_direction = SOP_NO_DIRECTION_FLAG;
50088c2ecf20Sopenharmony_ci		break;
50098c2ecf20Sopenharmony_ci	case DMA_BIDIRECTIONAL:
50108c2ecf20Sopenharmony_ci		request->data_direction = SOP_BIDIRECTIONAL;
50118c2ecf20Sopenharmony_ci		break;
50128c2ecf20Sopenharmony_ci	default:
50138c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
50148c2ecf20Sopenharmony_ci			"unknown data direction: %d\n",
50158c2ecf20Sopenharmony_ci			scmd->sc_data_direction);
50168c2ecf20Sopenharmony_ci		break;
50178c2ecf20Sopenharmony_ci	}
50188c2ecf20Sopenharmony_ci
50198c2ecf20Sopenharmony_ci	rc = pqi_build_raid_sg_list(ctrl_info, request, scmd, io_request);
50208c2ecf20Sopenharmony_ci	if (rc) {
50218c2ecf20Sopenharmony_ci		pqi_free_io_request(io_request);
50228c2ecf20Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
50238c2ecf20Sopenharmony_ci	}
50248c2ecf20Sopenharmony_ci
50258c2ecf20Sopenharmony_ci	pqi_start_io(ctrl_info, queue_group, RAID_PATH, io_request);
50268c2ecf20Sopenharmony_ci
50278c2ecf20Sopenharmony_ci	return 0;
50288c2ecf20Sopenharmony_ci}
50298c2ecf20Sopenharmony_ci
50308c2ecf20Sopenharmony_cistatic inline int pqi_raid_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info,
50318c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, struct scsi_cmnd *scmd,
50328c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group)
50338c2ecf20Sopenharmony_ci{
50348c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
50358c2ecf20Sopenharmony_ci
50368c2ecf20Sopenharmony_ci	io_request = pqi_alloc_io_request(ctrl_info);
50378c2ecf20Sopenharmony_ci
50388c2ecf20Sopenharmony_ci	return pqi_raid_submit_scsi_cmd_with_io_request(ctrl_info, io_request,
50398c2ecf20Sopenharmony_ci		device, scmd, queue_group);
50408c2ecf20Sopenharmony_ci}
50418c2ecf20Sopenharmony_ci
50428c2ecf20Sopenharmony_cistatic inline void pqi_schedule_bypass_retry(struct pqi_ctrl_info *ctrl_info)
50438c2ecf20Sopenharmony_ci{
50448c2ecf20Sopenharmony_ci	if (!pqi_ctrl_blocked(ctrl_info))
50458c2ecf20Sopenharmony_ci		schedule_work(&ctrl_info->raid_bypass_retry_work);
50468c2ecf20Sopenharmony_ci}
50478c2ecf20Sopenharmony_ci
50488c2ecf20Sopenharmony_cistatic bool pqi_raid_bypass_retry_needed(struct pqi_io_request *io_request)
50498c2ecf20Sopenharmony_ci{
50508c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
50518c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
50528c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
50538c2ecf20Sopenharmony_ci
50548c2ecf20Sopenharmony_ci	if (!io_request->raid_bypass)
50558c2ecf20Sopenharmony_ci		return false;
50568c2ecf20Sopenharmony_ci
50578c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
50588c2ecf20Sopenharmony_ci	if ((scmd->result & 0xff) == SAM_STAT_GOOD)
50598c2ecf20Sopenharmony_ci		return false;
50608c2ecf20Sopenharmony_ci	if (host_byte(scmd->result) == DID_NO_CONNECT)
50618c2ecf20Sopenharmony_ci		return false;
50628c2ecf20Sopenharmony_ci
50638c2ecf20Sopenharmony_ci	device = scmd->device->hostdata;
50648c2ecf20Sopenharmony_ci	if (pqi_device_offline(device))
50658c2ecf20Sopenharmony_ci		return false;
50668c2ecf20Sopenharmony_ci
50678c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(scmd->device->host);
50688c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
50698c2ecf20Sopenharmony_ci		return false;
50708c2ecf20Sopenharmony_ci
50718c2ecf20Sopenharmony_ci	return true;
50728c2ecf20Sopenharmony_ci}
50738c2ecf20Sopenharmony_ci
50748c2ecf20Sopenharmony_cistatic inline void pqi_add_to_raid_bypass_retry_list(
50758c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info,
50768c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request, bool at_head)
50778c2ecf20Sopenharmony_ci{
50788c2ecf20Sopenharmony_ci	unsigned long flags;
50798c2ecf20Sopenharmony_ci
50808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->raid_bypass_retry_list_lock, flags);
50818c2ecf20Sopenharmony_ci	if (at_head)
50828c2ecf20Sopenharmony_ci		list_add(&io_request->request_list_entry,
50838c2ecf20Sopenharmony_ci			&ctrl_info->raid_bypass_retry_list);
50848c2ecf20Sopenharmony_ci	else
50858c2ecf20Sopenharmony_ci		list_add_tail(&io_request->request_list_entry,
50868c2ecf20Sopenharmony_ci			&ctrl_info->raid_bypass_retry_list);
50878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->raid_bypass_retry_list_lock, flags);
50888c2ecf20Sopenharmony_ci}
50898c2ecf20Sopenharmony_ci
50908c2ecf20Sopenharmony_cistatic void pqi_queued_raid_bypass_complete(struct pqi_io_request *io_request,
50918c2ecf20Sopenharmony_ci	void *context)
50928c2ecf20Sopenharmony_ci{
50938c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
50948c2ecf20Sopenharmony_ci
50958c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
50968c2ecf20Sopenharmony_ci	pqi_free_io_request(io_request);
50978c2ecf20Sopenharmony_ci	pqi_scsi_done(scmd);
50988c2ecf20Sopenharmony_ci}
50998c2ecf20Sopenharmony_ci
51008c2ecf20Sopenharmony_cistatic void pqi_queue_raid_bypass_retry(struct pqi_io_request *io_request)
51018c2ecf20Sopenharmony_ci{
51028c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
51038c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
51048c2ecf20Sopenharmony_ci
51058c2ecf20Sopenharmony_ci	io_request->io_complete_callback = pqi_queued_raid_bypass_complete;
51068c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
51078c2ecf20Sopenharmony_ci	scmd->result = 0;
51088c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(scmd->device->host);
51098c2ecf20Sopenharmony_ci
51108c2ecf20Sopenharmony_ci	pqi_add_to_raid_bypass_retry_list(ctrl_info, io_request, false);
51118c2ecf20Sopenharmony_ci	pqi_schedule_bypass_retry(ctrl_info);
51128c2ecf20Sopenharmony_ci}
51138c2ecf20Sopenharmony_ci
51148c2ecf20Sopenharmony_cistatic int pqi_retry_raid_bypass(struct pqi_io_request *io_request)
51158c2ecf20Sopenharmony_ci{
51168c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
51178c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
51188c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
51198c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
51208c2ecf20Sopenharmony_ci
51218c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
51228c2ecf20Sopenharmony_ci	device = scmd->device->hostdata;
51238c2ecf20Sopenharmony_ci	if (pqi_device_in_reset(device)) {
51248c2ecf20Sopenharmony_ci		pqi_free_io_request(io_request);
51258c2ecf20Sopenharmony_ci		set_host_byte(scmd, DID_RESET);
51268c2ecf20Sopenharmony_ci		pqi_scsi_done(scmd);
51278c2ecf20Sopenharmony_ci		return 0;
51288c2ecf20Sopenharmony_ci	}
51298c2ecf20Sopenharmony_ci
51308c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(scmd->device->host);
51318c2ecf20Sopenharmony_ci	queue_group = io_request->queue_group;
51328c2ecf20Sopenharmony_ci
51338c2ecf20Sopenharmony_ci	pqi_reinit_io_request(io_request);
51348c2ecf20Sopenharmony_ci
51358c2ecf20Sopenharmony_ci	return pqi_raid_submit_scsi_cmd_with_io_request(ctrl_info, io_request,
51368c2ecf20Sopenharmony_ci		device, scmd, queue_group);
51378c2ecf20Sopenharmony_ci}
51388c2ecf20Sopenharmony_ci
51398c2ecf20Sopenharmony_cistatic inline struct pqi_io_request *pqi_next_queued_raid_bypass_request(
51408c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
51418c2ecf20Sopenharmony_ci{
51428c2ecf20Sopenharmony_ci	unsigned long flags;
51438c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
51448c2ecf20Sopenharmony_ci
51458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->raid_bypass_retry_list_lock, flags);
51468c2ecf20Sopenharmony_ci	io_request = list_first_entry_or_null(
51478c2ecf20Sopenharmony_ci		&ctrl_info->raid_bypass_retry_list,
51488c2ecf20Sopenharmony_ci		struct pqi_io_request, request_list_entry);
51498c2ecf20Sopenharmony_ci	if (io_request)
51508c2ecf20Sopenharmony_ci		list_del(&io_request->request_list_entry);
51518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->raid_bypass_retry_list_lock, flags);
51528c2ecf20Sopenharmony_ci
51538c2ecf20Sopenharmony_ci	return io_request;
51548c2ecf20Sopenharmony_ci}
51558c2ecf20Sopenharmony_ci
51568c2ecf20Sopenharmony_cistatic void pqi_retry_raid_bypass_requests(struct pqi_ctrl_info *ctrl_info)
51578c2ecf20Sopenharmony_ci{
51588c2ecf20Sopenharmony_ci	int rc;
51598c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
51608c2ecf20Sopenharmony_ci
51618c2ecf20Sopenharmony_ci	pqi_ctrl_busy(ctrl_info);
51628c2ecf20Sopenharmony_ci
51638c2ecf20Sopenharmony_ci	while (1) {
51648c2ecf20Sopenharmony_ci		if (pqi_ctrl_blocked(ctrl_info))
51658c2ecf20Sopenharmony_ci			break;
51668c2ecf20Sopenharmony_ci		io_request = pqi_next_queued_raid_bypass_request(ctrl_info);
51678c2ecf20Sopenharmony_ci		if (!io_request)
51688c2ecf20Sopenharmony_ci			break;
51698c2ecf20Sopenharmony_ci		rc = pqi_retry_raid_bypass(io_request);
51708c2ecf20Sopenharmony_ci		if (rc) {
51718c2ecf20Sopenharmony_ci			pqi_add_to_raid_bypass_retry_list(ctrl_info, io_request,
51728c2ecf20Sopenharmony_ci				true);
51738c2ecf20Sopenharmony_ci			pqi_schedule_bypass_retry(ctrl_info);
51748c2ecf20Sopenharmony_ci			break;
51758c2ecf20Sopenharmony_ci		}
51768c2ecf20Sopenharmony_ci	}
51778c2ecf20Sopenharmony_ci
51788c2ecf20Sopenharmony_ci	pqi_ctrl_unbusy(ctrl_info);
51798c2ecf20Sopenharmony_ci}
51808c2ecf20Sopenharmony_ci
51818c2ecf20Sopenharmony_cistatic void pqi_raid_bypass_retry_worker(struct work_struct *work)
51828c2ecf20Sopenharmony_ci{
51838c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
51848c2ecf20Sopenharmony_ci
51858c2ecf20Sopenharmony_ci	ctrl_info = container_of(work, struct pqi_ctrl_info,
51868c2ecf20Sopenharmony_ci		raid_bypass_retry_work);
51878c2ecf20Sopenharmony_ci	pqi_retry_raid_bypass_requests(ctrl_info);
51888c2ecf20Sopenharmony_ci}
51898c2ecf20Sopenharmony_ci
51908c2ecf20Sopenharmony_cistatic void pqi_clear_all_queued_raid_bypass_retries(
51918c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info)
51928c2ecf20Sopenharmony_ci{
51938c2ecf20Sopenharmony_ci	unsigned long flags;
51948c2ecf20Sopenharmony_ci
51958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->raid_bypass_retry_list_lock, flags);
51968c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctrl_info->raid_bypass_retry_list);
51978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->raid_bypass_retry_list_lock, flags);
51988c2ecf20Sopenharmony_ci}
51998c2ecf20Sopenharmony_ci
52008c2ecf20Sopenharmony_cistatic void pqi_aio_io_complete(struct pqi_io_request *io_request,
52018c2ecf20Sopenharmony_ci	void *context)
52028c2ecf20Sopenharmony_ci{
52038c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
52048c2ecf20Sopenharmony_ci
52058c2ecf20Sopenharmony_ci	scmd = io_request->scmd;
52068c2ecf20Sopenharmony_ci	scsi_dma_unmap(scmd);
52078c2ecf20Sopenharmony_ci	if (io_request->status == -EAGAIN)
52088c2ecf20Sopenharmony_ci		set_host_byte(scmd, DID_IMM_RETRY);
52098c2ecf20Sopenharmony_ci	else if (pqi_raid_bypass_retry_needed(io_request)) {
52108c2ecf20Sopenharmony_ci		pqi_queue_raid_bypass_retry(io_request);
52118c2ecf20Sopenharmony_ci		return;
52128c2ecf20Sopenharmony_ci	}
52138c2ecf20Sopenharmony_ci	pqi_free_io_request(io_request);
52148c2ecf20Sopenharmony_ci	pqi_scsi_done(scmd);
52158c2ecf20Sopenharmony_ci}
52168c2ecf20Sopenharmony_ci
52178c2ecf20Sopenharmony_cistatic inline int pqi_aio_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info,
52188c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, struct scsi_cmnd *scmd,
52198c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group)
52208c2ecf20Sopenharmony_ci{
52218c2ecf20Sopenharmony_ci	return pqi_aio_submit_io(ctrl_info, scmd, device->aio_handle,
52228c2ecf20Sopenharmony_ci		scmd->cmnd, scmd->cmd_len, queue_group, NULL, false);
52238c2ecf20Sopenharmony_ci}
52248c2ecf20Sopenharmony_ci
52258c2ecf20Sopenharmony_cistatic int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info,
52268c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb,
52278c2ecf20Sopenharmony_ci	unsigned int cdb_length, struct pqi_queue_group *queue_group,
52288c2ecf20Sopenharmony_ci	struct pqi_encryption_info *encryption_info, bool raid_bypass)
52298c2ecf20Sopenharmony_ci{
52308c2ecf20Sopenharmony_ci	int rc;
52318c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
52328c2ecf20Sopenharmony_ci	struct pqi_aio_path_request *request;
52338c2ecf20Sopenharmony_ci
52348c2ecf20Sopenharmony_ci	io_request = pqi_alloc_io_request(ctrl_info);
52358c2ecf20Sopenharmony_ci	io_request->io_complete_callback = pqi_aio_io_complete;
52368c2ecf20Sopenharmony_ci	io_request->scmd = scmd;
52378c2ecf20Sopenharmony_ci	io_request->raid_bypass = raid_bypass;
52388c2ecf20Sopenharmony_ci
52398c2ecf20Sopenharmony_ci	request = io_request->iu;
52408c2ecf20Sopenharmony_ci	memset(request, 0,
52418c2ecf20Sopenharmony_ci		offsetof(struct pqi_raid_path_request, sg_descriptors));
52428c2ecf20Sopenharmony_ci
52438c2ecf20Sopenharmony_ci	request->header.iu_type = PQI_REQUEST_IU_AIO_PATH_IO;
52448c2ecf20Sopenharmony_ci	put_unaligned_le32(aio_handle, &request->nexus_id);
52458c2ecf20Sopenharmony_ci	put_unaligned_le32(scsi_bufflen(scmd), &request->buffer_length);
52468c2ecf20Sopenharmony_ci	request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
52478c2ecf20Sopenharmony_ci	put_unaligned_le16(io_request->index, &request->request_id);
52488c2ecf20Sopenharmony_ci	request->error_index = request->request_id;
52498c2ecf20Sopenharmony_ci	if (cdb_length > sizeof(request->cdb))
52508c2ecf20Sopenharmony_ci		cdb_length = sizeof(request->cdb);
52518c2ecf20Sopenharmony_ci	request->cdb_length = cdb_length;
52528c2ecf20Sopenharmony_ci	memcpy(request->cdb, cdb, cdb_length);
52538c2ecf20Sopenharmony_ci
52548c2ecf20Sopenharmony_ci	switch (scmd->sc_data_direction) {
52558c2ecf20Sopenharmony_ci	case DMA_TO_DEVICE:
52568c2ecf20Sopenharmony_ci		request->data_direction = SOP_READ_FLAG;
52578c2ecf20Sopenharmony_ci		break;
52588c2ecf20Sopenharmony_ci	case DMA_FROM_DEVICE:
52598c2ecf20Sopenharmony_ci		request->data_direction = SOP_WRITE_FLAG;
52608c2ecf20Sopenharmony_ci		break;
52618c2ecf20Sopenharmony_ci	case DMA_NONE:
52628c2ecf20Sopenharmony_ci		request->data_direction = SOP_NO_DIRECTION_FLAG;
52638c2ecf20Sopenharmony_ci		break;
52648c2ecf20Sopenharmony_ci	case DMA_BIDIRECTIONAL:
52658c2ecf20Sopenharmony_ci		request->data_direction = SOP_BIDIRECTIONAL;
52668c2ecf20Sopenharmony_ci		break;
52678c2ecf20Sopenharmony_ci	default:
52688c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
52698c2ecf20Sopenharmony_ci			"unknown data direction: %d\n",
52708c2ecf20Sopenharmony_ci			scmd->sc_data_direction);
52718c2ecf20Sopenharmony_ci		break;
52728c2ecf20Sopenharmony_ci	}
52738c2ecf20Sopenharmony_ci
52748c2ecf20Sopenharmony_ci	if (encryption_info) {
52758c2ecf20Sopenharmony_ci		request->encryption_enable = true;
52768c2ecf20Sopenharmony_ci		put_unaligned_le16(encryption_info->data_encryption_key_index,
52778c2ecf20Sopenharmony_ci			&request->data_encryption_key_index);
52788c2ecf20Sopenharmony_ci		put_unaligned_le32(encryption_info->encrypt_tweak_lower,
52798c2ecf20Sopenharmony_ci			&request->encrypt_tweak_lower);
52808c2ecf20Sopenharmony_ci		put_unaligned_le32(encryption_info->encrypt_tweak_upper,
52818c2ecf20Sopenharmony_ci			&request->encrypt_tweak_upper);
52828c2ecf20Sopenharmony_ci	}
52838c2ecf20Sopenharmony_ci
52848c2ecf20Sopenharmony_ci	rc = pqi_build_aio_sg_list(ctrl_info, request, scmd, io_request);
52858c2ecf20Sopenharmony_ci	if (rc) {
52868c2ecf20Sopenharmony_ci		pqi_free_io_request(io_request);
52878c2ecf20Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
52888c2ecf20Sopenharmony_ci	}
52898c2ecf20Sopenharmony_ci
52908c2ecf20Sopenharmony_ci	pqi_start_io(ctrl_info, queue_group, AIO_PATH, io_request);
52918c2ecf20Sopenharmony_ci
52928c2ecf20Sopenharmony_ci	return 0;
52938c2ecf20Sopenharmony_ci}
52948c2ecf20Sopenharmony_ci
52958c2ecf20Sopenharmony_cistatic inline u16 pqi_get_hw_queue(struct pqi_ctrl_info *ctrl_info,
52968c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd)
52978c2ecf20Sopenharmony_ci{
52988c2ecf20Sopenharmony_ci	u16 hw_queue;
52998c2ecf20Sopenharmony_ci
53008c2ecf20Sopenharmony_ci	hw_queue = blk_mq_unique_tag_to_hwq(blk_mq_unique_tag(scmd->request));
53018c2ecf20Sopenharmony_ci	if (hw_queue > ctrl_info->max_hw_queue_index)
53028c2ecf20Sopenharmony_ci		hw_queue = 0;
53038c2ecf20Sopenharmony_ci
53048c2ecf20Sopenharmony_ci	return hw_queue;
53058c2ecf20Sopenharmony_ci}
53068c2ecf20Sopenharmony_ci
53078c2ecf20Sopenharmony_ci/*
53088c2ecf20Sopenharmony_ci * This function gets called just before we hand the completed SCSI request
53098c2ecf20Sopenharmony_ci * back to the SML.
53108c2ecf20Sopenharmony_ci */
53118c2ecf20Sopenharmony_ci
53128c2ecf20Sopenharmony_civoid pqi_prep_for_scsi_done(struct scsi_cmnd *scmd)
53138c2ecf20Sopenharmony_ci{
53148c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
53158c2ecf20Sopenharmony_ci
53168c2ecf20Sopenharmony_ci	if (!scmd->device) {
53178c2ecf20Sopenharmony_ci		set_host_byte(scmd, DID_NO_CONNECT);
53188c2ecf20Sopenharmony_ci		return;
53198c2ecf20Sopenharmony_ci	}
53208c2ecf20Sopenharmony_ci
53218c2ecf20Sopenharmony_ci	device = scmd->device->hostdata;
53228c2ecf20Sopenharmony_ci	if (!device) {
53238c2ecf20Sopenharmony_ci		set_host_byte(scmd, DID_NO_CONNECT);
53248c2ecf20Sopenharmony_ci		return;
53258c2ecf20Sopenharmony_ci	}
53268c2ecf20Sopenharmony_ci
53278c2ecf20Sopenharmony_ci	atomic_dec(&device->scsi_cmds_outstanding);
53288c2ecf20Sopenharmony_ci}
53298c2ecf20Sopenharmony_ci
53308c2ecf20Sopenharmony_cistatic int pqi_scsi_queue_command(struct Scsi_Host *shost,
53318c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd)
53328c2ecf20Sopenharmony_ci{
53338c2ecf20Sopenharmony_ci	int rc;
53348c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
53358c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
53368c2ecf20Sopenharmony_ci	u16 hw_queue;
53378c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
53388c2ecf20Sopenharmony_ci	bool raid_bypassed;
53398c2ecf20Sopenharmony_ci
53408c2ecf20Sopenharmony_ci	device = scmd->device->hostdata;
53418c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
53428c2ecf20Sopenharmony_ci
53438c2ecf20Sopenharmony_ci	if (!device) {
53448c2ecf20Sopenharmony_ci		set_host_byte(scmd, DID_NO_CONNECT);
53458c2ecf20Sopenharmony_ci		pqi_scsi_done(scmd);
53468c2ecf20Sopenharmony_ci		return 0;
53478c2ecf20Sopenharmony_ci	}
53488c2ecf20Sopenharmony_ci
53498c2ecf20Sopenharmony_ci	atomic_inc(&device->scsi_cmds_outstanding);
53508c2ecf20Sopenharmony_ci
53518c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info) || pqi_device_in_remove(ctrl_info,
53528c2ecf20Sopenharmony_ci								device)) {
53538c2ecf20Sopenharmony_ci		set_host_byte(scmd, DID_NO_CONNECT);
53548c2ecf20Sopenharmony_ci		pqi_scsi_done(scmd);
53558c2ecf20Sopenharmony_ci		return 0;
53568c2ecf20Sopenharmony_ci	}
53578c2ecf20Sopenharmony_ci
53588c2ecf20Sopenharmony_ci	pqi_ctrl_busy(ctrl_info);
53598c2ecf20Sopenharmony_ci	if (pqi_ctrl_blocked(ctrl_info) || pqi_device_in_reset(device) ||
53608c2ecf20Sopenharmony_ci	    pqi_ctrl_in_ofa(ctrl_info) || pqi_ctrl_in_shutdown(ctrl_info)) {
53618c2ecf20Sopenharmony_ci		rc = SCSI_MLQUEUE_HOST_BUSY;
53628c2ecf20Sopenharmony_ci		goto out;
53638c2ecf20Sopenharmony_ci	}
53648c2ecf20Sopenharmony_ci
53658c2ecf20Sopenharmony_ci	/*
53668c2ecf20Sopenharmony_ci	 * This is necessary because the SML doesn't zero out this field during
53678c2ecf20Sopenharmony_ci	 * error recovery.
53688c2ecf20Sopenharmony_ci	 */
53698c2ecf20Sopenharmony_ci	scmd->result = 0;
53708c2ecf20Sopenharmony_ci
53718c2ecf20Sopenharmony_ci	hw_queue = pqi_get_hw_queue(ctrl_info, scmd);
53728c2ecf20Sopenharmony_ci	queue_group = &ctrl_info->queue_groups[hw_queue];
53738c2ecf20Sopenharmony_ci
53748c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device)) {
53758c2ecf20Sopenharmony_ci		raid_bypassed = false;
53768c2ecf20Sopenharmony_ci		if (device->raid_bypass_enabled &&
53778c2ecf20Sopenharmony_ci			!blk_rq_is_passthrough(scmd->request)) {
53788c2ecf20Sopenharmony_ci			rc = pqi_raid_bypass_submit_scsi_cmd(ctrl_info, device,
53798c2ecf20Sopenharmony_ci				scmd, queue_group);
53808c2ecf20Sopenharmony_ci			if (rc == 0 || rc == SCSI_MLQUEUE_HOST_BUSY) {
53818c2ecf20Sopenharmony_ci				raid_bypassed = true;
53828c2ecf20Sopenharmony_ci				atomic_inc(&device->raid_bypass_cnt);
53838c2ecf20Sopenharmony_ci			}
53848c2ecf20Sopenharmony_ci		}
53858c2ecf20Sopenharmony_ci		if (!raid_bypassed)
53868c2ecf20Sopenharmony_ci			rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, queue_group);
53878c2ecf20Sopenharmony_ci	} else {
53888c2ecf20Sopenharmony_ci		if (device->aio_enabled)
53898c2ecf20Sopenharmony_ci			rc = pqi_aio_submit_scsi_cmd(ctrl_info, device, scmd, queue_group);
53908c2ecf20Sopenharmony_ci		else
53918c2ecf20Sopenharmony_ci			rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, queue_group);
53928c2ecf20Sopenharmony_ci	}
53938c2ecf20Sopenharmony_ci
53948c2ecf20Sopenharmony_ciout:
53958c2ecf20Sopenharmony_ci	pqi_ctrl_unbusy(ctrl_info);
53968c2ecf20Sopenharmony_ci	if (rc)
53978c2ecf20Sopenharmony_ci		atomic_dec(&device->scsi_cmds_outstanding);
53988c2ecf20Sopenharmony_ci
53998c2ecf20Sopenharmony_ci	return rc;
54008c2ecf20Sopenharmony_ci}
54018c2ecf20Sopenharmony_ci
54028c2ecf20Sopenharmony_cistatic int pqi_wait_until_queued_io_drained(struct pqi_ctrl_info *ctrl_info,
54038c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group)
54048c2ecf20Sopenharmony_ci{
54058c2ecf20Sopenharmony_ci	unsigned int path;
54068c2ecf20Sopenharmony_ci	unsigned long flags;
54078c2ecf20Sopenharmony_ci	bool list_is_empty;
54088c2ecf20Sopenharmony_ci
54098c2ecf20Sopenharmony_ci	for (path = 0; path < 2; path++) {
54108c2ecf20Sopenharmony_ci		while (1) {
54118c2ecf20Sopenharmony_ci			spin_lock_irqsave(
54128c2ecf20Sopenharmony_ci				&queue_group->submit_lock[path], flags);
54138c2ecf20Sopenharmony_ci			list_is_empty =
54148c2ecf20Sopenharmony_ci				list_empty(&queue_group->request_list[path]);
54158c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(
54168c2ecf20Sopenharmony_ci				&queue_group->submit_lock[path], flags);
54178c2ecf20Sopenharmony_ci			if (list_is_empty)
54188c2ecf20Sopenharmony_ci				break;
54198c2ecf20Sopenharmony_ci			pqi_check_ctrl_health(ctrl_info);
54208c2ecf20Sopenharmony_ci			if (pqi_ctrl_offline(ctrl_info))
54218c2ecf20Sopenharmony_ci				return -ENXIO;
54228c2ecf20Sopenharmony_ci			usleep_range(1000, 2000);
54238c2ecf20Sopenharmony_ci		}
54248c2ecf20Sopenharmony_ci	}
54258c2ecf20Sopenharmony_ci
54268c2ecf20Sopenharmony_ci	return 0;
54278c2ecf20Sopenharmony_ci}
54288c2ecf20Sopenharmony_ci
54298c2ecf20Sopenharmony_cistatic int pqi_wait_until_inbound_queues_empty(struct pqi_ctrl_info *ctrl_info)
54308c2ecf20Sopenharmony_ci{
54318c2ecf20Sopenharmony_ci	int rc;
54328c2ecf20Sopenharmony_ci	unsigned int i;
54338c2ecf20Sopenharmony_ci	unsigned int path;
54348c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
54358c2ecf20Sopenharmony_ci	pqi_index_t iq_pi;
54368c2ecf20Sopenharmony_ci	pqi_index_t iq_ci;
54378c2ecf20Sopenharmony_ci
54388c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
54398c2ecf20Sopenharmony_ci		queue_group = &ctrl_info->queue_groups[i];
54408c2ecf20Sopenharmony_ci
54418c2ecf20Sopenharmony_ci		rc = pqi_wait_until_queued_io_drained(ctrl_info, queue_group);
54428c2ecf20Sopenharmony_ci		if (rc)
54438c2ecf20Sopenharmony_ci			return rc;
54448c2ecf20Sopenharmony_ci
54458c2ecf20Sopenharmony_ci		for (path = 0; path < 2; path++) {
54468c2ecf20Sopenharmony_ci			iq_pi = queue_group->iq_pi_copy[path];
54478c2ecf20Sopenharmony_ci
54488c2ecf20Sopenharmony_ci			while (1) {
54498c2ecf20Sopenharmony_ci				iq_ci = readl(queue_group->iq_ci[path]);
54508c2ecf20Sopenharmony_ci				if (iq_ci == iq_pi)
54518c2ecf20Sopenharmony_ci					break;
54528c2ecf20Sopenharmony_ci				pqi_check_ctrl_health(ctrl_info);
54538c2ecf20Sopenharmony_ci				if (pqi_ctrl_offline(ctrl_info))
54548c2ecf20Sopenharmony_ci					return -ENXIO;
54558c2ecf20Sopenharmony_ci				usleep_range(1000, 2000);
54568c2ecf20Sopenharmony_ci			}
54578c2ecf20Sopenharmony_ci		}
54588c2ecf20Sopenharmony_ci	}
54598c2ecf20Sopenharmony_ci
54608c2ecf20Sopenharmony_ci	return 0;
54618c2ecf20Sopenharmony_ci}
54628c2ecf20Sopenharmony_ci
54638c2ecf20Sopenharmony_cistatic void pqi_fail_io_queued_for_device(struct pqi_ctrl_info *ctrl_info,
54648c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
54658c2ecf20Sopenharmony_ci{
54668c2ecf20Sopenharmony_ci	unsigned int i;
54678c2ecf20Sopenharmony_ci	unsigned int path;
54688c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
54698c2ecf20Sopenharmony_ci	unsigned long flags;
54708c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
54718c2ecf20Sopenharmony_ci	struct pqi_io_request *next;
54728c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
54738c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *scsi_device;
54748c2ecf20Sopenharmony_ci
54758c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
54768c2ecf20Sopenharmony_ci		queue_group = &ctrl_info->queue_groups[i];
54778c2ecf20Sopenharmony_ci
54788c2ecf20Sopenharmony_ci		for (path = 0; path < 2; path++) {
54798c2ecf20Sopenharmony_ci			spin_lock_irqsave(
54808c2ecf20Sopenharmony_ci				&queue_group->submit_lock[path], flags);
54818c2ecf20Sopenharmony_ci
54828c2ecf20Sopenharmony_ci			list_for_each_entry_safe(io_request, next,
54838c2ecf20Sopenharmony_ci				&queue_group->request_list[path],
54848c2ecf20Sopenharmony_ci				request_list_entry) {
54858c2ecf20Sopenharmony_ci				scmd = io_request->scmd;
54868c2ecf20Sopenharmony_ci				if (!scmd)
54878c2ecf20Sopenharmony_ci					continue;
54888c2ecf20Sopenharmony_ci
54898c2ecf20Sopenharmony_ci				scsi_device = scmd->device->hostdata;
54908c2ecf20Sopenharmony_ci				if (scsi_device != device)
54918c2ecf20Sopenharmony_ci					continue;
54928c2ecf20Sopenharmony_ci
54938c2ecf20Sopenharmony_ci				list_del(&io_request->request_list_entry);
54948c2ecf20Sopenharmony_ci				set_host_byte(scmd, DID_RESET);
54958c2ecf20Sopenharmony_ci				pqi_free_io_request(io_request);
54968c2ecf20Sopenharmony_ci				scsi_dma_unmap(scmd);
54978c2ecf20Sopenharmony_ci				pqi_scsi_done(scmd);
54988c2ecf20Sopenharmony_ci			}
54998c2ecf20Sopenharmony_ci
55008c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(
55018c2ecf20Sopenharmony_ci				&queue_group->submit_lock[path], flags);
55028c2ecf20Sopenharmony_ci		}
55038c2ecf20Sopenharmony_ci	}
55048c2ecf20Sopenharmony_ci}
55058c2ecf20Sopenharmony_ci
55068c2ecf20Sopenharmony_cistatic void pqi_fail_io_queued_for_all_devices(struct pqi_ctrl_info *ctrl_info)
55078c2ecf20Sopenharmony_ci{
55088c2ecf20Sopenharmony_ci	unsigned int i;
55098c2ecf20Sopenharmony_ci	unsigned int path;
55108c2ecf20Sopenharmony_ci	struct pqi_queue_group *queue_group;
55118c2ecf20Sopenharmony_ci	unsigned long flags;
55128c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
55138c2ecf20Sopenharmony_ci	struct pqi_io_request *next;
55148c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
55158c2ecf20Sopenharmony_ci
55168c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
55178c2ecf20Sopenharmony_ci		queue_group = &ctrl_info->queue_groups[i];
55188c2ecf20Sopenharmony_ci
55198c2ecf20Sopenharmony_ci		for (path = 0; path < 2; path++) {
55208c2ecf20Sopenharmony_ci			spin_lock_irqsave(&queue_group->submit_lock[path],
55218c2ecf20Sopenharmony_ci						flags);
55228c2ecf20Sopenharmony_ci
55238c2ecf20Sopenharmony_ci			list_for_each_entry_safe(io_request, next,
55248c2ecf20Sopenharmony_ci				&queue_group->request_list[path],
55258c2ecf20Sopenharmony_ci				request_list_entry) {
55268c2ecf20Sopenharmony_ci
55278c2ecf20Sopenharmony_ci				scmd = io_request->scmd;
55288c2ecf20Sopenharmony_ci				if (!scmd)
55298c2ecf20Sopenharmony_ci					continue;
55308c2ecf20Sopenharmony_ci
55318c2ecf20Sopenharmony_ci				list_del(&io_request->request_list_entry);
55328c2ecf20Sopenharmony_ci				set_host_byte(scmd, DID_RESET);
55338c2ecf20Sopenharmony_ci				pqi_free_io_request(io_request);
55348c2ecf20Sopenharmony_ci				scsi_dma_unmap(scmd);
55358c2ecf20Sopenharmony_ci				pqi_scsi_done(scmd);
55368c2ecf20Sopenharmony_ci			}
55378c2ecf20Sopenharmony_ci
55388c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(
55398c2ecf20Sopenharmony_ci				&queue_group->submit_lock[path], flags);
55408c2ecf20Sopenharmony_ci		}
55418c2ecf20Sopenharmony_ci	}
55428c2ecf20Sopenharmony_ci}
55438c2ecf20Sopenharmony_ci
55448c2ecf20Sopenharmony_cistatic int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info,
55458c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, unsigned long timeout_secs)
55468c2ecf20Sopenharmony_ci{
55478c2ecf20Sopenharmony_ci	unsigned long timeout;
55488c2ecf20Sopenharmony_ci
55498c2ecf20Sopenharmony_ci	timeout = (timeout_secs * PQI_HZ) + jiffies;
55508c2ecf20Sopenharmony_ci
55518c2ecf20Sopenharmony_ci	while (atomic_read(&device->scsi_cmds_outstanding)) {
55528c2ecf20Sopenharmony_ci		pqi_check_ctrl_health(ctrl_info);
55538c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info))
55548c2ecf20Sopenharmony_ci			return -ENXIO;
55558c2ecf20Sopenharmony_ci		if (timeout_secs != NO_TIMEOUT) {
55568c2ecf20Sopenharmony_ci			if (time_after(jiffies, timeout)) {
55578c2ecf20Sopenharmony_ci				dev_err(&ctrl_info->pci_dev->dev,
55588c2ecf20Sopenharmony_ci					"timed out waiting for pending IO\n");
55598c2ecf20Sopenharmony_ci				return -ETIMEDOUT;
55608c2ecf20Sopenharmony_ci			}
55618c2ecf20Sopenharmony_ci		}
55628c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
55638c2ecf20Sopenharmony_ci	}
55648c2ecf20Sopenharmony_ci
55658c2ecf20Sopenharmony_ci	return 0;
55668c2ecf20Sopenharmony_ci}
55678c2ecf20Sopenharmony_ci
55688c2ecf20Sopenharmony_cistatic int pqi_ctrl_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info,
55698c2ecf20Sopenharmony_ci	unsigned long timeout_secs)
55708c2ecf20Sopenharmony_ci{
55718c2ecf20Sopenharmony_ci	bool io_pending;
55728c2ecf20Sopenharmony_ci	unsigned long flags;
55738c2ecf20Sopenharmony_ci	unsigned long timeout;
55748c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
55758c2ecf20Sopenharmony_ci
55768c2ecf20Sopenharmony_ci	timeout = (timeout_secs * PQI_HZ) + jiffies;
55778c2ecf20Sopenharmony_ci	while (1) {
55788c2ecf20Sopenharmony_ci		io_pending = false;
55798c2ecf20Sopenharmony_ci
55808c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
55818c2ecf20Sopenharmony_ci		list_for_each_entry(device, &ctrl_info->scsi_device_list,
55828c2ecf20Sopenharmony_ci			scsi_device_list_entry) {
55838c2ecf20Sopenharmony_ci			if (atomic_read(&device->scsi_cmds_outstanding)) {
55848c2ecf20Sopenharmony_ci				io_pending = true;
55858c2ecf20Sopenharmony_ci				break;
55868c2ecf20Sopenharmony_ci			}
55878c2ecf20Sopenharmony_ci		}
55888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock,
55898c2ecf20Sopenharmony_ci					flags);
55908c2ecf20Sopenharmony_ci
55918c2ecf20Sopenharmony_ci		if (!io_pending)
55928c2ecf20Sopenharmony_ci			break;
55938c2ecf20Sopenharmony_ci
55948c2ecf20Sopenharmony_ci		pqi_check_ctrl_health(ctrl_info);
55958c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info))
55968c2ecf20Sopenharmony_ci			return -ENXIO;
55978c2ecf20Sopenharmony_ci
55988c2ecf20Sopenharmony_ci		if (timeout_secs != NO_TIMEOUT) {
55998c2ecf20Sopenharmony_ci			if (time_after(jiffies, timeout)) {
56008c2ecf20Sopenharmony_ci				dev_err(&ctrl_info->pci_dev->dev,
56018c2ecf20Sopenharmony_ci					"timed out waiting for pending IO\n");
56028c2ecf20Sopenharmony_ci				return -ETIMEDOUT;
56038c2ecf20Sopenharmony_ci			}
56048c2ecf20Sopenharmony_ci		}
56058c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
56068c2ecf20Sopenharmony_ci	}
56078c2ecf20Sopenharmony_ci
56088c2ecf20Sopenharmony_ci	return 0;
56098c2ecf20Sopenharmony_ci}
56108c2ecf20Sopenharmony_ci
56118c2ecf20Sopenharmony_cistatic int pqi_ctrl_wait_for_pending_sync_cmds(struct pqi_ctrl_info *ctrl_info)
56128c2ecf20Sopenharmony_ci{
56138c2ecf20Sopenharmony_ci	while (atomic_read(&ctrl_info->sync_cmds_outstanding)) {
56148c2ecf20Sopenharmony_ci		pqi_check_ctrl_health(ctrl_info);
56158c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info))
56168c2ecf20Sopenharmony_ci			return -ENXIO;
56178c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
56188c2ecf20Sopenharmony_ci	}
56198c2ecf20Sopenharmony_ci
56208c2ecf20Sopenharmony_ci	return 0;
56218c2ecf20Sopenharmony_ci}
56228c2ecf20Sopenharmony_ci
56238c2ecf20Sopenharmony_cistatic void pqi_lun_reset_complete(struct pqi_io_request *io_request,
56248c2ecf20Sopenharmony_ci	void *context)
56258c2ecf20Sopenharmony_ci{
56268c2ecf20Sopenharmony_ci	struct completion *waiting = context;
56278c2ecf20Sopenharmony_ci
56288c2ecf20Sopenharmony_ci	complete(waiting);
56298c2ecf20Sopenharmony_ci}
56308c2ecf20Sopenharmony_ci
56318c2ecf20Sopenharmony_ci#define PQI_LUN_RESET_TIMEOUT_SECS		30
56328c2ecf20Sopenharmony_ci#define PQI_LUN_RESET_POLL_COMPLETION_SECS	10
56338c2ecf20Sopenharmony_ci
56348c2ecf20Sopenharmony_cistatic int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info,
56358c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device, struct completion *wait)
56368c2ecf20Sopenharmony_ci{
56378c2ecf20Sopenharmony_ci	int rc;
56388c2ecf20Sopenharmony_ci
56398c2ecf20Sopenharmony_ci	while (1) {
56408c2ecf20Sopenharmony_ci		if (wait_for_completion_io_timeout(wait,
56418c2ecf20Sopenharmony_ci			PQI_LUN_RESET_POLL_COMPLETION_SECS * PQI_HZ)) {
56428c2ecf20Sopenharmony_ci			rc = 0;
56438c2ecf20Sopenharmony_ci			break;
56448c2ecf20Sopenharmony_ci		}
56458c2ecf20Sopenharmony_ci
56468c2ecf20Sopenharmony_ci		pqi_check_ctrl_health(ctrl_info);
56478c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info)) {
56488c2ecf20Sopenharmony_ci			rc = -ENXIO;
56498c2ecf20Sopenharmony_ci			break;
56508c2ecf20Sopenharmony_ci		}
56518c2ecf20Sopenharmony_ci	}
56528c2ecf20Sopenharmony_ci
56538c2ecf20Sopenharmony_ci	return rc;
56548c2ecf20Sopenharmony_ci}
56558c2ecf20Sopenharmony_ci
56568c2ecf20Sopenharmony_cistatic int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info,
56578c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
56588c2ecf20Sopenharmony_ci{
56598c2ecf20Sopenharmony_ci	int rc;
56608c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
56618c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(wait);
56628c2ecf20Sopenharmony_ci	struct pqi_task_management_request *request;
56638c2ecf20Sopenharmony_ci
56648c2ecf20Sopenharmony_ci	io_request = pqi_alloc_io_request(ctrl_info);
56658c2ecf20Sopenharmony_ci	io_request->io_complete_callback = pqi_lun_reset_complete;
56668c2ecf20Sopenharmony_ci	io_request->context = &wait;
56678c2ecf20Sopenharmony_ci
56688c2ecf20Sopenharmony_ci	request = io_request->iu;
56698c2ecf20Sopenharmony_ci	memset(request, 0, sizeof(*request));
56708c2ecf20Sopenharmony_ci
56718c2ecf20Sopenharmony_ci	request->header.iu_type = PQI_REQUEST_IU_TASK_MANAGEMENT;
56728c2ecf20Sopenharmony_ci	put_unaligned_le16(sizeof(*request) - PQI_REQUEST_HEADER_LENGTH,
56738c2ecf20Sopenharmony_ci		&request->header.iu_length);
56748c2ecf20Sopenharmony_ci	put_unaligned_le16(io_request->index, &request->request_id);
56758c2ecf20Sopenharmony_ci	memcpy(request->lun_number, device->scsi3addr,
56768c2ecf20Sopenharmony_ci		sizeof(request->lun_number));
56778c2ecf20Sopenharmony_ci	request->task_management_function = SOP_TASK_MANAGEMENT_LUN_RESET;
56788c2ecf20Sopenharmony_ci	if (ctrl_info->tmf_iu_timeout_supported)
56798c2ecf20Sopenharmony_ci		put_unaligned_le16(PQI_LUN_RESET_TIMEOUT_SECS,
56808c2ecf20Sopenharmony_ci					&request->timeout);
56818c2ecf20Sopenharmony_ci
56828c2ecf20Sopenharmony_ci	pqi_start_io(ctrl_info,
56838c2ecf20Sopenharmony_ci		&ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH,
56848c2ecf20Sopenharmony_ci		io_request);
56858c2ecf20Sopenharmony_ci
56868c2ecf20Sopenharmony_ci	rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, &wait);
56878c2ecf20Sopenharmony_ci	if (rc == 0)
56888c2ecf20Sopenharmony_ci		rc = io_request->status;
56898c2ecf20Sopenharmony_ci
56908c2ecf20Sopenharmony_ci	pqi_free_io_request(io_request);
56918c2ecf20Sopenharmony_ci
56928c2ecf20Sopenharmony_ci	return rc;
56938c2ecf20Sopenharmony_ci}
56948c2ecf20Sopenharmony_ci
56958c2ecf20Sopenharmony_ci/* Performs a reset at the LUN level. */
56968c2ecf20Sopenharmony_ci
56978c2ecf20Sopenharmony_ci#define PQI_LUN_RESET_RETRIES			3
56988c2ecf20Sopenharmony_ci#define PQI_LUN_RESET_RETRY_INTERVAL_MSECS	10000
56998c2ecf20Sopenharmony_ci#define PQI_LUN_RESET_PENDING_IO_TIMEOUT_SECS	120
57008c2ecf20Sopenharmony_ci
57018c2ecf20Sopenharmony_cistatic int _pqi_device_reset(struct pqi_ctrl_info *ctrl_info,
57028c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
57038c2ecf20Sopenharmony_ci{
57048c2ecf20Sopenharmony_ci	int rc;
57058c2ecf20Sopenharmony_ci	unsigned int retries;
57068c2ecf20Sopenharmony_ci	unsigned long timeout_secs;
57078c2ecf20Sopenharmony_ci
57088c2ecf20Sopenharmony_ci	for (retries = 0;;) {
57098c2ecf20Sopenharmony_ci		rc = pqi_lun_reset(ctrl_info, device);
57108c2ecf20Sopenharmony_ci		if (rc == 0 || ++retries > PQI_LUN_RESET_RETRIES)
57118c2ecf20Sopenharmony_ci			break;
57128c2ecf20Sopenharmony_ci		msleep(PQI_LUN_RESET_RETRY_INTERVAL_MSECS);
57138c2ecf20Sopenharmony_ci	}
57148c2ecf20Sopenharmony_ci
57158c2ecf20Sopenharmony_ci	timeout_secs = rc ? PQI_LUN_RESET_PENDING_IO_TIMEOUT_SECS : NO_TIMEOUT;
57168c2ecf20Sopenharmony_ci
57178c2ecf20Sopenharmony_ci	rc |= pqi_device_wait_for_pending_io(ctrl_info, device, timeout_secs);
57188c2ecf20Sopenharmony_ci
57198c2ecf20Sopenharmony_ci	return rc == 0 ? SUCCESS : FAILED;
57208c2ecf20Sopenharmony_ci}
57218c2ecf20Sopenharmony_ci
57228c2ecf20Sopenharmony_cistatic int pqi_device_reset(struct pqi_ctrl_info *ctrl_info,
57238c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device)
57248c2ecf20Sopenharmony_ci{
57258c2ecf20Sopenharmony_ci	int rc;
57268c2ecf20Sopenharmony_ci
57278c2ecf20Sopenharmony_ci	mutex_lock(&ctrl_info->lun_reset_mutex);
57288c2ecf20Sopenharmony_ci
57298c2ecf20Sopenharmony_ci	pqi_ctrl_block_requests(ctrl_info);
57308c2ecf20Sopenharmony_ci	pqi_ctrl_wait_until_quiesced(ctrl_info);
57318c2ecf20Sopenharmony_ci	pqi_fail_io_queued_for_device(ctrl_info, device);
57328c2ecf20Sopenharmony_ci	rc = pqi_wait_until_inbound_queues_empty(ctrl_info);
57338c2ecf20Sopenharmony_ci	pqi_device_reset_start(device);
57348c2ecf20Sopenharmony_ci	pqi_ctrl_unblock_requests(ctrl_info);
57358c2ecf20Sopenharmony_ci
57368c2ecf20Sopenharmony_ci	if (rc)
57378c2ecf20Sopenharmony_ci		rc = FAILED;
57388c2ecf20Sopenharmony_ci	else
57398c2ecf20Sopenharmony_ci		rc = _pqi_device_reset(ctrl_info, device);
57408c2ecf20Sopenharmony_ci
57418c2ecf20Sopenharmony_ci	pqi_device_reset_done(device);
57428c2ecf20Sopenharmony_ci
57438c2ecf20Sopenharmony_ci	mutex_unlock(&ctrl_info->lun_reset_mutex);
57448c2ecf20Sopenharmony_ci
57458c2ecf20Sopenharmony_ci	return rc;
57468c2ecf20Sopenharmony_ci}
57478c2ecf20Sopenharmony_ci
57488c2ecf20Sopenharmony_cistatic int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd)
57498c2ecf20Sopenharmony_ci{
57508c2ecf20Sopenharmony_ci	int rc;
57518c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
57528c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
57538c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
57548c2ecf20Sopenharmony_ci
57558c2ecf20Sopenharmony_ci	shost = scmd->device->host;
57568c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
57578c2ecf20Sopenharmony_ci	device = scmd->device->hostdata;
57588c2ecf20Sopenharmony_ci
57598c2ecf20Sopenharmony_ci	dev_err(&ctrl_info->pci_dev->dev,
57608c2ecf20Sopenharmony_ci		"resetting scsi %d:%d:%d:%d\n",
57618c2ecf20Sopenharmony_ci		shost->host_no, device->bus, device->target, device->lun);
57628c2ecf20Sopenharmony_ci
57638c2ecf20Sopenharmony_ci	pqi_check_ctrl_health(ctrl_info);
57648c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info) ||
57658c2ecf20Sopenharmony_ci		pqi_device_reset_blocked(ctrl_info)) {
57668c2ecf20Sopenharmony_ci		rc = FAILED;
57678c2ecf20Sopenharmony_ci		goto out;
57688c2ecf20Sopenharmony_ci	}
57698c2ecf20Sopenharmony_ci
57708c2ecf20Sopenharmony_ci	pqi_wait_until_ofa_finished(ctrl_info);
57718c2ecf20Sopenharmony_ci
57728c2ecf20Sopenharmony_ci	atomic_inc(&ctrl_info->sync_cmds_outstanding);
57738c2ecf20Sopenharmony_ci	rc = pqi_device_reset(ctrl_info, device);
57748c2ecf20Sopenharmony_ci	atomic_dec(&ctrl_info->sync_cmds_outstanding);
57758c2ecf20Sopenharmony_ci
57768c2ecf20Sopenharmony_ciout:
57778c2ecf20Sopenharmony_ci	dev_err(&ctrl_info->pci_dev->dev,
57788c2ecf20Sopenharmony_ci		"reset of scsi %d:%d:%d:%d: %s\n",
57798c2ecf20Sopenharmony_ci		shost->host_no, device->bus, device->target, device->lun,
57808c2ecf20Sopenharmony_ci		rc == SUCCESS ? "SUCCESS" : "FAILED");
57818c2ecf20Sopenharmony_ci
57828c2ecf20Sopenharmony_ci	return rc;
57838c2ecf20Sopenharmony_ci}
57848c2ecf20Sopenharmony_ci
57858c2ecf20Sopenharmony_cistatic int pqi_slave_alloc(struct scsi_device *sdev)
57868c2ecf20Sopenharmony_ci{
57878c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
57888c2ecf20Sopenharmony_ci	unsigned long flags;
57898c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
57908c2ecf20Sopenharmony_ci	struct scsi_target *starget;
57918c2ecf20Sopenharmony_ci	struct sas_rphy *rphy;
57928c2ecf20Sopenharmony_ci
57938c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
57948c2ecf20Sopenharmony_ci
57958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
57968c2ecf20Sopenharmony_ci
57978c2ecf20Sopenharmony_ci	if (sdev_channel(sdev) == PQI_PHYSICAL_DEVICE_BUS) {
57988c2ecf20Sopenharmony_ci		starget = scsi_target(sdev);
57998c2ecf20Sopenharmony_ci		rphy = target_to_rphy(starget);
58008c2ecf20Sopenharmony_ci		device = pqi_find_device_by_sas_rphy(ctrl_info, rphy);
58018c2ecf20Sopenharmony_ci		if (device) {
58028c2ecf20Sopenharmony_ci			device->target = sdev_id(sdev);
58038c2ecf20Sopenharmony_ci			device->lun = sdev->lun;
58048c2ecf20Sopenharmony_ci			device->target_lun_valid = true;
58058c2ecf20Sopenharmony_ci		}
58068c2ecf20Sopenharmony_ci	} else {
58078c2ecf20Sopenharmony_ci		device = pqi_find_scsi_dev(ctrl_info, sdev_channel(sdev),
58088c2ecf20Sopenharmony_ci			sdev_id(sdev), sdev->lun);
58098c2ecf20Sopenharmony_ci	}
58108c2ecf20Sopenharmony_ci
58118c2ecf20Sopenharmony_ci	if (device) {
58128c2ecf20Sopenharmony_ci		sdev->hostdata = device;
58138c2ecf20Sopenharmony_ci		device->sdev = sdev;
58148c2ecf20Sopenharmony_ci		if (device->queue_depth) {
58158c2ecf20Sopenharmony_ci			device->advertised_queue_depth = device->queue_depth;
58168c2ecf20Sopenharmony_ci			scsi_change_queue_depth(sdev,
58178c2ecf20Sopenharmony_ci				device->advertised_queue_depth);
58188c2ecf20Sopenharmony_ci		}
58198c2ecf20Sopenharmony_ci		if (pqi_is_logical_device(device))
58208c2ecf20Sopenharmony_ci			pqi_disable_write_same(sdev);
58218c2ecf20Sopenharmony_ci		else
58228c2ecf20Sopenharmony_ci			sdev->allow_restart = 1;
58238c2ecf20Sopenharmony_ci	}
58248c2ecf20Sopenharmony_ci
58258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
58268c2ecf20Sopenharmony_ci
58278c2ecf20Sopenharmony_ci	return 0;
58288c2ecf20Sopenharmony_ci}
58298c2ecf20Sopenharmony_ci
58308c2ecf20Sopenharmony_cistatic int pqi_map_queues(struct Scsi_Host *shost)
58318c2ecf20Sopenharmony_ci{
58328c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info = shost_to_hba(shost);
58338c2ecf20Sopenharmony_ci
58348c2ecf20Sopenharmony_ci	return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
58358c2ecf20Sopenharmony_ci					ctrl_info->pci_dev, 0);
58368c2ecf20Sopenharmony_ci}
58378c2ecf20Sopenharmony_ci
58388c2ecf20Sopenharmony_cistatic int pqi_slave_configure(struct scsi_device *sdev)
58398c2ecf20Sopenharmony_ci{
58408c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
58418c2ecf20Sopenharmony_ci
58428c2ecf20Sopenharmony_ci	device = sdev->hostdata;
58438c2ecf20Sopenharmony_ci	device->devtype = sdev->type;
58448c2ecf20Sopenharmony_ci
58458c2ecf20Sopenharmony_ci	return 0;
58468c2ecf20Sopenharmony_ci}
58478c2ecf20Sopenharmony_ci
58488c2ecf20Sopenharmony_cistatic void pqi_slave_destroy(struct scsi_device *sdev)
58498c2ecf20Sopenharmony_ci{
58508c2ecf20Sopenharmony_ci	unsigned long flags;
58518c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
58528c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
58538c2ecf20Sopenharmony_ci
58548c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
58558c2ecf20Sopenharmony_ci
58568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
58578c2ecf20Sopenharmony_ci
58588c2ecf20Sopenharmony_ci	device = sdev->hostdata;
58598c2ecf20Sopenharmony_ci	if (device) {
58608c2ecf20Sopenharmony_ci		sdev->hostdata = NULL;
58618c2ecf20Sopenharmony_ci		if (!list_empty(&device->scsi_device_list_entry))
58628c2ecf20Sopenharmony_ci			list_del(&device->scsi_device_list_entry);
58638c2ecf20Sopenharmony_ci	}
58648c2ecf20Sopenharmony_ci
58658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
58668c2ecf20Sopenharmony_ci
58678c2ecf20Sopenharmony_ci	if (device) {
58688c2ecf20Sopenharmony_ci		pqi_dev_info(ctrl_info, "removed", device);
58698c2ecf20Sopenharmony_ci		pqi_free_device(device);
58708c2ecf20Sopenharmony_ci	}
58718c2ecf20Sopenharmony_ci}
58728c2ecf20Sopenharmony_ci
58738c2ecf20Sopenharmony_cistatic int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg)
58748c2ecf20Sopenharmony_ci{
58758c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev;
58768c2ecf20Sopenharmony_ci	u32 subsystem_vendor;
58778c2ecf20Sopenharmony_ci	u32 subsystem_device;
58788c2ecf20Sopenharmony_ci	cciss_pci_info_struct pciinfo;
58798c2ecf20Sopenharmony_ci
58808c2ecf20Sopenharmony_ci	if (!arg)
58818c2ecf20Sopenharmony_ci		return -EINVAL;
58828c2ecf20Sopenharmony_ci
58838c2ecf20Sopenharmony_ci	pci_dev = ctrl_info->pci_dev;
58848c2ecf20Sopenharmony_ci
58858c2ecf20Sopenharmony_ci	pciinfo.domain = pci_domain_nr(pci_dev->bus);
58868c2ecf20Sopenharmony_ci	pciinfo.bus = pci_dev->bus->number;
58878c2ecf20Sopenharmony_ci	pciinfo.dev_fn = pci_dev->devfn;
58888c2ecf20Sopenharmony_ci	subsystem_vendor = pci_dev->subsystem_vendor;
58898c2ecf20Sopenharmony_ci	subsystem_device = pci_dev->subsystem_device;
58908c2ecf20Sopenharmony_ci	pciinfo.board_id = ((subsystem_device << 16) & 0xffff0000) | subsystem_vendor;
58918c2ecf20Sopenharmony_ci
58928c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &pciinfo, sizeof(pciinfo)))
58938c2ecf20Sopenharmony_ci		return -EFAULT;
58948c2ecf20Sopenharmony_ci
58958c2ecf20Sopenharmony_ci	return 0;
58968c2ecf20Sopenharmony_ci}
58978c2ecf20Sopenharmony_ci
58988c2ecf20Sopenharmony_cistatic int pqi_getdrivver_ioctl(void __user *arg)
58998c2ecf20Sopenharmony_ci{
59008c2ecf20Sopenharmony_ci	u32 version;
59018c2ecf20Sopenharmony_ci
59028c2ecf20Sopenharmony_ci	if (!arg)
59038c2ecf20Sopenharmony_ci		return -EINVAL;
59048c2ecf20Sopenharmony_ci
59058c2ecf20Sopenharmony_ci	version = (DRIVER_MAJOR << 28) | (DRIVER_MINOR << 24) |
59068c2ecf20Sopenharmony_ci		(DRIVER_RELEASE << 16) | DRIVER_REVISION;
59078c2ecf20Sopenharmony_ci
59088c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &version, sizeof(version)))
59098c2ecf20Sopenharmony_ci		return -EFAULT;
59108c2ecf20Sopenharmony_ci
59118c2ecf20Sopenharmony_ci	return 0;
59128c2ecf20Sopenharmony_ci}
59138c2ecf20Sopenharmony_ci
59148c2ecf20Sopenharmony_cistruct ciss_error_info {
59158c2ecf20Sopenharmony_ci	u8	scsi_status;
59168c2ecf20Sopenharmony_ci	int	command_status;
59178c2ecf20Sopenharmony_ci	size_t	sense_data_length;
59188c2ecf20Sopenharmony_ci};
59198c2ecf20Sopenharmony_ci
59208c2ecf20Sopenharmony_cistatic void pqi_error_info_to_ciss(struct pqi_raid_error_info *pqi_error_info,
59218c2ecf20Sopenharmony_ci	struct ciss_error_info *ciss_error_info)
59228c2ecf20Sopenharmony_ci{
59238c2ecf20Sopenharmony_ci	int ciss_cmd_status;
59248c2ecf20Sopenharmony_ci	size_t sense_data_length;
59258c2ecf20Sopenharmony_ci
59268c2ecf20Sopenharmony_ci	switch (pqi_error_info->data_out_result) {
59278c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_GOOD:
59288c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_SUCCESS;
59298c2ecf20Sopenharmony_ci		break;
59308c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_UNDERFLOW:
59318c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_DATA_UNDERRUN;
59328c2ecf20Sopenharmony_ci		break;
59338c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_OVERFLOW:
59348c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_DATA_OVERRUN;
59358c2ecf20Sopenharmony_ci		break;
59368c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PROTOCOL_ERROR:
59378c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_ERROR:
59388c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA:
59398c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE:
59408c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_ERROR:
59418c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_PROTOCOL_ERROR;
59428c2ecf20Sopenharmony_ci		break;
59438c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_HARDWARE_ERROR:
59448c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR:
59458c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT:
59468c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED:
59478c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED:
59488c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED:
59498c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST:
59508c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION:
59518c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED:
59528c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ:
59538c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_HARDWARE_ERROR;
59548c2ecf20Sopenharmony_ci		break;
59558c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_UNSOLICITED_ABORT:
59568c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_UNSOLICITED_ABORT;
59578c2ecf20Sopenharmony_ci		break;
59588c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_ABORTED:
59598c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_ABORTED;
59608c2ecf20Sopenharmony_ci		break;
59618c2ecf20Sopenharmony_ci	case PQI_DATA_IN_OUT_TIMEOUT:
59628c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_TIMEOUT;
59638c2ecf20Sopenharmony_ci		break;
59648c2ecf20Sopenharmony_ci	default:
59658c2ecf20Sopenharmony_ci		ciss_cmd_status = CISS_CMD_STATUS_TARGET_STATUS;
59668c2ecf20Sopenharmony_ci		break;
59678c2ecf20Sopenharmony_ci	}
59688c2ecf20Sopenharmony_ci
59698c2ecf20Sopenharmony_ci	sense_data_length =
59708c2ecf20Sopenharmony_ci		get_unaligned_le16(&pqi_error_info->sense_data_length);
59718c2ecf20Sopenharmony_ci	if (sense_data_length == 0)
59728c2ecf20Sopenharmony_ci		sense_data_length =
59738c2ecf20Sopenharmony_ci		get_unaligned_le16(&pqi_error_info->response_data_length);
59748c2ecf20Sopenharmony_ci	if (sense_data_length)
59758c2ecf20Sopenharmony_ci		if (sense_data_length > sizeof(pqi_error_info->data))
59768c2ecf20Sopenharmony_ci			sense_data_length = sizeof(pqi_error_info->data);
59778c2ecf20Sopenharmony_ci
59788c2ecf20Sopenharmony_ci	ciss_error_info->scsi_status = pqi_error_info->status;
59798c2ecf20Sopenharmony_ci	ciss_error_info->command_status = ciss_cmd_status;
59808c2ecf20Sopenharmony_ci	ciss_error_info->sense_data_length = sense_data_length;
59818c2ecf20Sopenharmony_ci}
59828c2ecf20Sopenharmony_ci
59838c2ecf20Sopenharmony_cistatic int pqi_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg)
59848c2ecf20Sopenharmony_ci{
59858c2ecf20Sopenharmony_ci	int rc;
59868c2ecf20Sopenharmony_ci	char *kernel_buffer = NULL;
59878c2ecf20Sopenharmony_ci	u16 iu_length;
59888c2ecf20Sopenharmony_ci	size_t sense_data_length;
59898c2ecf20Sopenharmony_ci	IOCTL_Command_struct iocommand;
59908c2ecf20Sopenharmony_ci	struct pqi_raid_path_request request;
59918c2ecf20Sopenharmony_ci	struct pqi_raid_error_info pqi_error_info;
59928c2ecf20Sopenharmony_ci	struct ciss_error_info ciss_error_info;
59938c2ecf20Sopenharmony_ci
59948c2ecf20Sopenharmony_ci	if (pqi_ctrl_offline(ctrl_info))
59958c2ecf20Sopenharmony_ci		return -ENXIO;
59968c2ecf20Sopenharmony_ci	if (!arg)
59978c2ecf20Sopenharmony_ci		return -EINVAL;
59988c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_RAWIO))
59998c2ecf20Sopenharmony_ci		return -EPERM;
60008c2ecf20Sopenharmony_ci	if (copy_from_user(&iocommand, arg, sizeof(iocommand)))
60018c2ecf20Sopenharmony_ci		return -EFAULT;
60028c2ecf20Sopenharmony_ci	if (iocommand.buf_size < 1 &&
60038c2ecf20Sopenharmony_ci		iocommand.Request.Type.Direction != XFER_NONE)
60048c2ecf20Sopenharmony_ci		return -EINVAL;
60058c2ecf20Sopenharmony_ci	if (iocommand.Request.CDBLen > sizeof(request.cdb))
60068c2ecf20Sopenharmony_ci		return -EINVAL;
60078c2ecf20Sopenharmony_ci	if (iocommand.Request.Type.Type != TYPE_CMD)
60088c2ecf20Sopenharmony_ci		return -EINVAL;
60098c2ecf20Sopenharmony_ci
60108c2ecf20Sopenharmony_ci	switch (iocommand.Request.Type.Direction) {
60118c2ecf20Sopenharmony_ci	case XFER_NONE:
60128c2ecf20Sopenharmony_ci	case XFER_WRITE:
60138c2ecf20Sopenharmony_ci	case XFER_READ:
60148c2ecf20Sopenharmony_ci	case XFER_READ | XFER_WRITE:
60158c2ecf20Sopenharmony_ci		break;
60168c2ecf20Sopenharmony_ci	default:
60178c2ecf20Sopenharmony_ci		return -EINVAL;
60188c2ecf20Sopenharmony_ci	}
60198c2ecf20Sopenharmony_ci
60208c2ecf20Sopenharmony_ci	if (iocommand.buf_size > 0) {
60218c2ecf20Sopenharmony_ci		kernel_buffer = kmalloc(iocommand.buf_size, GFP_KERNEL);
60228c2ecf20Sopenharmony_ci		if (!kernel_buffer)
60238c2ecf20Sopenharmony_ci			return -ENOMEM;
60248c2ecf20Sopenharmony_ci		if (iocommand.Request.Type.Direction & XFER_WRITE) {
60258c2ecf20Sopenharmony_ci			if (copy_from_user(kernel_buffer, iocommand.buf,
60268c2ecf20Sopenharmony_ci				iocommand.buf_size)) {
60278c2ecf20Sopenharmony_ci				rc = -EFAULT;
60288c2ecf20Sopenharmony_ci				goto out;
60298c2ecf20Sopenharmony_ci			}
60308c2ecf20Sopenharmony_ci		} else {
60318c2ecf20Sopenharmony_ci			memset(kernel_buffer, 0, iocommand.buf_size);
60328c2ecf20Sopenharmony_ci		}
60338c2ecf20Sopenharmony_ci	}
60348c2ecf20Sopenharmony_ci
60358c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
60368c2ecf20Sopenharmony_ci
60378c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
60388c2ecf20Sopenharmony_ci	iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) -
60398c2ecf20Sopenharmony_ci		PQI_REQUEST_HEADER_LENGTH;
60408c2ecf20Sopenharmony_ci	memcpy(request.lun_number, iocommand.LUN_info.LunAddrBytes,
60418c2ecf20Sopenharmony_ci		sizeof(request.lun_number));
60428c2ecf20Sopenharmony_ci	memcpy(request.cdb, iocommand.Request.CDB, iocommand.Request.CDBLen);
60438c2ecf20Sopenharmony_ci	request.additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0;
60448c2ecf20Sopenharmony_ci
60458c2ecf20Sopenharmony_ci	switch (iocommand.Request.Type.Direction) {
60468c2ecf20Sopenharmony_ci	case XFER_NONE:
60478c2ecf20Sopenharmony_ci		request.data_direction = SOP_NO_DIRECTION_FLAG;
60488c2ecf20Sopenharmony_ci		break;
60498c2ecf20Sopenharmony_ci	case XFER_WRITE:
60508c2ecf20Sopenharmony_ci		request.data_direction = SOP_WRITE_FLAG;
60518c2ecf20Sopenharmony_ci		break;
60528c2ecf20Sopenharmony_ci	case XFER_READ:
60538c2ecf20Sopenharmony_ci		request.data_direction = SOP_READ_FLAG;
60548c2ecf20Sopenharmony_ci		break;
60558c2ecf20Sopenharmony_ci	case XFER_READ | XFER_WRITE:
60568c2ecf20Sopenharmony_ci		request.data_direction = SOP_BIDIRECTIONAL;
60578c2ecf20Sopenharmony_ci		break;
60588c2ecf20Sopenharmony_ci	}
60598c2ecf20Sopenharmony_ci
60608c2ecf20Sopenharmony_ci	request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
60618c2ecf20Sopenharmony_ci
60628c2ecf20Sopenharmony_ci	if (iocommand.buf_size > 0) {
60638c2ecf20Sopenharmony_ci		put_unaligned_le32(iocommand.buf_size, &request.buffer_length);
60648c2ecf20Sopenharmony_ci
60658c2ecf20Sopenharmony_ci		rc = pqi_map_single(ctrl_info->pci_dev,
60668c2ecf20Sopenharmony_ci			&request.sg_descriptors[0], kernel_buffer,
60678c2ecf20Sopenharmony_ci			iocommand.buf_size, DMA_BIDIRECTIONAL);
60688c2ecf20Sopenharmony_ci		if (rc)
60698c2ecf20Sopenharmony_ci			goto out;
60708c2ecf20Sopenharmony_ci
60718c2ecf20Sopenharmony_ci		iu_length += sizeof(request.sg_descriptors[0]);
60728c2ecf20Sopenharmony_ci	}
60738c2ecf20Sopenharmony_ci
60748c2ecf20Sopenharmony_ci	put_unaligned_le16(iu_length, &request.header.iu_length);
60758c2ecf20Sopenharmony_ci
60768c2ecf20Sopenharmony_ci	if (ctrl_info->raid_iu_timeout_supported)
60778c2ecf20Sopenharmony_ci		put_unaligned_le32(iocommand.Request.Timeout, &request.timeout);
60788c2ecf20Sopenharmony_ci
60798c2ecf20Sopenharmony_ci	rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
60808c2ecf20Sopenharmony_ci		PQI_SYNC_FLAGS_INTERRUPTABLE, &pqi_error_info, NO_TIMEOUT);
60818c2ecf20Sopenharmony_ci
60828c2ecf20Sopenharmony_ci	if (iocommand.buf_size > 0)
60838c2ecf20Sopenharmony_ci		pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1,
60848c2ecf20Sopenharmony_ci			DMA_BIDIRECTIONAL);
60858c2ecf20Sopenharmony_ci
60868c2ecf20Sopenharmony_ci	memset(&iocommand.error_info, 0, sizeof(iocommand.error_info));
60878c2ecf20Sopenharmony_ci
60888c2ecf20Sopenharmony_ci	if (rc == 0) {
60898c2ecf20Sopenharmony_ci		pqi_error_info_to_ciss(&pqi_error_info, &ciss_error_info);
60908c2ecf20Sopenharmony_ci		iocommand.error_info.ScsiStatus = ciss_error_info.scsi_status;
60918c2ecf20Sopenharmony_ci		iocommand.error_info.CommandStatus =
60928c2ecf20Sopenharmony_ci			ciss_error_info.command_status;
60938c2ecf20Sopenharmony_ci		sense_data_length = ciss_error_info.sense_data_length;
60948c2ecf20Sopenharmony_ci		if (sense_data_length) {
60958c2ecf20Sopenharmony_ci			if (sense_data_length >
60968c2ecf20Sopenharmony_ci				sizeof(iocommand.error_info.SenseInfo))
60978c2ecf20Sopenharmony_ci				sense_data_length =
60988c2ecf20Sopenharmony_ci					sizeof(iocommand.error_info.SenseInfo);
60998c2ecf20Sopenharmony_ci			memcpy(iocommand.error_info.SenseInfo,
61008c2ecf20Sopenharmony_ci				pqi_error_info.data, sense_data_length);
61018c2ecf20Sopenharmony_ci			iocommand.error_info.SenseLen = sense_data_length;
61028c2ecf20Sopenharmony_ci		}
61038c2ecf20Sopenharmony_ci	}
61048c2ecf20Sopenharmony_ci
61058c2ecf20Sopenharmony_ci	if (copy_to_user(arg, &iocommand, sizeof(iocommand))) {
61068c2ecf20Sopenharmony_ci		rc = -EFAULT;
61078c2ecf20Sopenharmony_ci		goto out;
61088c2ecf20Sopenharmony_ci	}
61098c2ecf20Sopenharmony_ci
61108c2ecf20Sopenharmony_ci	if (rc == 0 && iocommand.buf_size > 0 &&
61118c2ecf20Sopenharmony_ci		(iocommand.Request.Type.Direction & XFER_READ)) {
61128c2ecf20Sopenharmony_ci		if (copy_to_user(iocommand.buf, kernel_buffer,
61138c2ecf20Sopenharmony_ci			iocommand.buf_size)) {
61148c2ecf20Sopenharmony_ci			rc = -EFAULT;
61158c2ecf20Sopenharmony_ci		}
61168c2ecf20Sopenharmony_ci	}
61178c2ecf20Sopenharmony_ci
61188c2ecf20Sopenharmony_ciout:
61198c2ecf20Sopenharmony_ci	kfree(kernel_buffer);
61208c2ecf20Sopenharmony_ci
61218c2ecf20Sopenharmony_ci	return rc;
61228c2ecf20Sopenharmony_ci}
61238c2ecf20Sopenharmony_ci
61248c2ecf20Sopenharmony_cistatic int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd,
61258c2ecf20Sopenharmony_ci		     void __user *arg)
61268c2ecf20Sopenharmony_ci{
61278c2ecf20Sopenharmony_ci	int rc;
61288c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
61298c2ecf20Sopenharmony_ci
61308c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
61318c2ecf20Sopenharmony_ci
61328c2ecf20Sopenharmony_ci	if (pqi_ctrl_in_ofa(ctrl_info) || pqi_ctrl_in_shutdown(ctrl_info))
61338c2ecf20Sopenharmony_ci		return -EBUSY;
61348c2ecf20Sopenharmony_ci
61358c2ecf20Sopenharmony_ci	switch (cmd) {
61368c2ecf20Sopenharmony_ci	case CCISS_DEREGDISK:
61378c2ecf20Sopenharmony_ci	case CCISS_REGNEWDISK:
61388c2ecf20Sopenharmony_ci	case CCISS_REGNEWD:
61398c2ecf20Sopenharmony_ci		rc = pqi_scan_scsi_devices(ctrl_info);
61408c2ecf20Sopenharmony_ci		break;
61418c2ecf20Sopenharmony_ci	case CCISS_GETPCIINFO:
61428c2ecf20Sopenharmony_ci		rc = pqi_getpciinfo_ioctl(ctrl_info, arg);
61438c2ecf20Sopenharmony_ci		break;
61448c2ecf20Sopenharmony_ci	case CCISS_GETDRIVVER:
61458c2ecf20Sopenharmony_ci		rc = pqi_getdrivver_ioctl(arg);
61468c2ecf20Sopenharmony_ci		break;
61478c2ecf20Sopenharmony_ci	case CCISS_PASSTHRU:
61488c2ecf20Sopenharmony_ci		rc = pqi_passthru_ioctl(ctrl_info, arg);
61498c2ecf20Sopenharmony_ci		break;
61508c2ecf20Sopenharmony_ci	default:
61518c2ecf20Sopenharmony_ci		rc = -EINVAL;
61528c2ecf20Sopenharmony_ci		break;
61538c2ecf20Sopenharmony_ci	}
61548c2ecf20Sopenharmony_ci
61558c2ecf20Sopenharmony_ci	return rc;
61568c2ecf20Sopenharmony_ci}
61578c2ecf20Sopenharmony_ci
61588c2ecf20Sopenharmony_cistatic ssize_t pqi_firmware_version_show(struct device *dev,
61598c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
61608c2ecf20Sopenharmony_ci{
61618c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
61628c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
61638c2ecf20Sopenharmony_ci
61648c2ecf20Sopenharmony_ci	shost = class_to_shost(dev);
61658c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
61668c2ecf20Sopenharmony_ci
61678c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%s\n", ctrl_info->firmware_version);
61688c2ecf20Sopenharmony_ci}
61698c2ecf20Sopenharmony_ci
61708c2ecf20Sopenharmony_cistatic ssize_t pqi_driver_version_show(struct device *dev,
61718c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
61728c2ecf20Sopenharmony_ci{
61738c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%s\n",
61748c2ecf20Sopenharmony_ci			DRIVER_VERSION BUILD_TIMESTAMP);
61758c2ecf20Sopenharmony_ci}
61768c2ecf20Sopenharmony_ci
61778c2ecf20Sopenharmony_cistatic ssize_t pqi_serial_number_show(struct device *dev,
61788c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
61798c2ecf20Sopenharmony_ci{
61808c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
61818c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
61828c2ecf20Sopenharmony_ci
61838c2ecf20Sopenharmony_ci	shost = class_to_shost(dev);
61848c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
61858c2ecf20Sopenharmony_ci
61868c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%s\n", ctrl_info->serial_number);
61878c2ecf20Sopenharmony_ci}
61888c2ecf20Sopenharmony_ci
61898c2ecf20Sopenharmony_cistatic ssize_t pqi_model_show(struct device *dev,
61908c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
61918c2ecf20Sopenharmony_ci{
61928c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
61938c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
61948c2ecf20Sopenharmony_ci
61958c2ecf20Sopenharmony_ci	shost = class_to_shost(dev);
61968c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
61978c2ecf20Sopenharmony_ci
61988c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%s\n", ctrl_info->model);
61998c2ecf20Sopenharmony_ci}
62008c2ecf20Sopenharmony_ci
62018c2ecf20Sopenharmony_cistatic ssize_t pqi_vendor_show(struct device *dev,
62028c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
62038c2ecf20Sopenharmony_ci{
62048c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
62058c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
62068c2ecf20Sopenharmony_ci
62078c2ecf20Sopenharmony_ci	shost = class_to_shost(dev);
62088c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(shost);
62098c2ecf20Sopenharmony_ci
62108c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%s\n", ctrl_info->vendor);
62118c2ecf20Sopenharmony_ci}
62128c2ecf20Sopenharmony_ci
62138c2ecf20Sopenharmony_cistatic ssize_t pqi_host_rescan_store(struct device *dev,
62148c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buffer, size_t count)
62158c2ecf20Sopenharmony_ci{
62168c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
62178c2ecf20Sopenharmony_ci
62188c2ecf20Sopenharmony_ci	pqi_scan_start(shost);
62198c2ecf20Sopenharmony_ci
62208c2ecf20Sopenharmony_ci	return count;
62218c2ecf20Sopenharmony_ci}
62228c2ecf20Sopenharmony_ci
62238c2ecf20Sopenharmony_cistatic ssize_t pqi_lockup_action_show(struct device *dev,
62248c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
62258c2ecf20Sopenharmony_ci{
62268c2ecf20Sopenharmony_ci	int count = 0;
62278c2ecf20Sopenharmony_ci	unsigned int i;
62288c2ecf20Sopenharmony_ci
62298c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pqi_lockup_actions); i++) {
62308c2ecf20Sopenharmony_ci		if (pqi_lockup_actions[i].action == pqi_lockup_action)
62318c2ecf20Sopenharmony_ci			count += scnprintf(buffer + count, PAGE_SIZE - count,
62328c2ecf20Sopenharmony_ci				"[%s] ", pqi_lockup_actions[i].name);
62338c2ecf20Sopenharmony_ci		else
62348c2ecf20Sopenharmony_ci			count += scnprintf(buffer + count, PAGE_SIZE - count,
62358c2ecf20Sopenharmony_ci				"%s ", pqi_lockup_actions[i].name);
62368c2ecf20Sopenharmony_ci	}
62378c2ecf20Sopenharmony_ci
62388c2ecf20Sopenharmony_ci	count += scnprintf(buffer + count, PAGE_SIZE - count, "\n");
62398c2ecf20Sopenharmony_ci
62408c2ecf20Sopenharmony_ci	return count;
62418c2ecf20Sopenharmony_ci}
62428c2ecf20Sopenharmony_ci
62438c2ecf20Sopenharmony_cistatic ssize_t pqi_lockup_action_store(struct device *dev,
62448c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buffer, size_t count)
62458c2ecf20Sopenharmony_ci{
62468c2ecf20Sopenharmony_ci	unsigned int i;
62478c2ecf20Sopenharmony_ci	char *action_name;
62488c2ecf20Sopenharmony_ci	char action_name_buffer[32];
62498c2ecf20Sopenharmony_ci
62508c2ecf20Sopenharmony_ci	strlcpy(action_name_buffer, buffer, sizeof(action_name_buffer));
62518c2ecf20Sopenharmony_ci	action_name = strstrip(action_name_buffer);
62528c2ecf20Sopenharmony_ci
62538c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pqi_lockup_actions); i++) {
62548c2ecf20Sopenharmony_ci		if (strcmp(action_name, pqi_lockup_actions[i].name) == 0) {
62558c2ecf20Sopenharmony_ci			pqi_lockup_action = pqi_lockup_actions[i].action;
62568c2ecf20Sopenharmony_ci			return count;
62578c2ecf20Sopenharmony_ci		}
62588c2ecf20Sopenharmony_ci	}
62598c2ecf20Sopenharmony_ci
62608c2ecf20Sopenharmony_ci	return -EINVAL;
62618c2ecf20Sopenharmony_ci}
62628c2ecf20Sopenharmony_ci
62638c2ecf20Sopenharmony_cistatic DEVICE_ATTR(driver_version, 0444, pqi_driver_version_show, NULL);
62648c2ecf20Sopenharmony_cistatic DEVICE_ATTR(firmware_version, 0444, pqi_firmware_version_show, NULL);
62658c2ecf20Sopenharmony_cistatic DEVICE_ATTR(model, 0444, pqi_model_show, NULL);
62668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(serial_number, 0444, pqi_serial_number_show, NULL);
62678c2ecf20Sopenharmony_cistatic DEVICE_ATTR(vendor, 0444, pqi_vendor_show, NULL);
62688c2ecf20Sopenharmony_cistatic DEVICE_ATTR(rescan, 0200, NULL, pqi_host_rescan_store);
62698c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lockup_action, 0644,
62708c2ecf20Sopenharmony_ci	pqi_lockup_action_show, pqi_lockup_action_store);
62718c2ecf20Sopenharmony_ci
62728c2ecf20Sopenharmony_cistatic struct device_attribute *pqi_shost_attrs[] = {
62738c2ecf20Sopenharmony_ci	&dev_attr_driver_version,
62748c2ecf20Sopenharmony_ci	&dev_attr_firmware_version,
62758c2ecf20Sopenharmony_ci	&dev_attr_model,
62768c2ecf20Sopenharmony_ci	&dev_attr_serial_number,
62778c2ecf20Sopenharmony_ci	&dev_attr_vendor,
62788c2ecf20Sopenharmony_ci	&dev_attr_rescan,
62798c2ecf20Sopenharmony_ci	&dev_attr_lockup_action,
62808c2ecf20Sopenharmony_ci	NULL
62818c2ecf20Sopenharmony_ci};
62828c2ecf20Sopenharmony_ci
62838c2ecf20Sopenharmony_cistatic ssize_t pqi_unique_id_show(struct device *dev,
62848c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
62858c2ecf20Sopenharmony_ci{
62868c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
62878c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
62888c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
62898c2ecf20Sopenharmony_ci	unsigned long flags;
62908c2ecf20Sopenharmony_ci	u8 unique_id[16];
62918c2ecf20Sopenharmony_ci
62928c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
62938c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
62948c2ecf20Sopenharmony_ci
62958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
62968c2ecf20Sopenharmony_ci
62978c2ecf20Sopenharmony_ci	device = sdev->hostdata;
62988c2ecf20Sopenharmony_ci	if (!device) {
62998c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
63008c2ecf20Sopenharmony_ci		return -ENODEV;
63018c2ecf20Sopenharmony_ci	}
63028c2ecf20Sopenharmony_ci
63038c2ecf20Sopenharmony_ci	if (device->is_physical_device) {
63048c2ecf20Sopenharmony_ci		memset(unique_id, 0, 8);
63058c2ecf20Sopenharmony_ci		memcpy(unique_id + 8, &device->wwid, sizeof(device->wwid));
63068c2ecf20Sopenharmony_ci	} else {
63078c2ecf20Sopenharmony_ci		memcpy(unique_id, device->volume_id, sizeof(device->volume_id));
63088c2ecf20Sopenharmony_ci	}
63098c2ecf20Sopenharmony_ci
63108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
63118c2ecf20Sopenharmony_ci
63128c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE,
63138c2ecf20Sopenharmony_ci		"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
63148c2ecf20Sopenharmony_ci		unique_id[0], unique_id[1], unique_id[2], unique_id[3],
63158c2ecf20Sopenharmony_ci		unique_id[4], unique_id[5], unique_id[6], unique_id[7],
63168c2ecf20Sopenharmony_ci		unique_id[8], unique_id[9], unique_id[10], unique_id[11],
63178c2ecf20Sopenharmony_ci		unique_id[12], unique_id[13], unique_id[14], unique_id[15]);
63188c2ecf20Sopenharmony_ci}
63198c2ecf20Sopenharmony_ci
63208c2ecf20Sopenharmony_cistatic ssize_t pqi_lunid_show(struct device *dev,
63218c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
63228c2ecf20Sopenharmony_ci{
63238c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
63248c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
63258c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
63268c2ecf20Sopenharmony_ci	unsigned long flags;
63278c2ecf20Sopenharmony_ci	u8 lunid[8];
63288c2ecf20Sopenharmony_ci
63298c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
63308c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
63318c2ecf20Sopenharmony_ci
63328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
63338c2ecf20Sopenharmony_ci
63348c2ecf20Sopenharmony_ci	device = sdev->hostdata;
63358c2ecf20Sopenharmony_ci	if (!device) {
63368c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
63378c2ecf20Sopenharmony_ci		return -ENODEV;
63388c2ecf20Sopenharmony_ci	}
63398c2ecf20Sopenharmony_ci
63408c2ecf20Sopenharmony_ci	memcpy(lunid, device->scsi3addr, sizeof(lunid));
63418c2ecf20Sopenharmony_ci
63428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
63438c2ecf20Sopenharmony_ci
63448c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "0x%8phN\n", lunid);
63458c2ecf20Sopenharmony_ci}
63468c2ecf20Sopenharmony_ci
63478c2ecf20Sopenharmony_ci#define MAX_PATHS	8
63488c2ecf20Sopenharmony_ci
63498c2ecf20Sopenharmony_cistatic ssize_t pqi_path_info_show(struct device *dev,
63508c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf)
63518c2ecf20Sopenharmony_ci{
63528c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
63538c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
63548c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
63558c2ecf20Sopenharmony_ci	unsigned long flags;
63568c2ecf20Sopenharmony_ci	int i;
63578c2ecf20Sopenharmony_ci	int output_len = 0;
63588c2ecf20Sopenharmony_ci	u8 box;
63598c2ecf20Sopenharmony_ci	u8 bay;
63608c2ecf20Sopenharmony_ci	u8 path_map_index;
63618c2ecf20Sopenharmony_ci	char *active;
63628c2ecf20Sopenharmony_ci	u8 phys_connector[2];
63638c2ecf20Sopenharmony_ci
63648c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
63658c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
63668c2ecf20Sopenharmony_ci
63678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
63688c2ecf20Sopenharmony_ci
63698c2ecf20Sopenharmony_ci	device = sdev->hostdata;
63708c2ecf20Sopenharmony_ci	if (!device) {
63718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
63728c2ecf20Sopenharmony_ci		return -ENODEV;
63738c2ecf20Sopenharmony_ci	}
63748c2ecf20Sopenharmony_ci
63758c2ecf20Sopenharmony_ci	bay = device->bay;
63768c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_PATHS; i++) {
63778c2ecf20Sopenharmony_ci		path_map_index = 1 << i;
63788c2ecf20Sopenharmony_ci		if (i == device->active_path_index)
63798c2ecf20Sopenharmony_ci			active = "Active";
63808c2ecf20Sopenharmony_ci		else if (device->path_map & path_map_index)
63818c2ecf20Sopenharmony_ci			active = "Inactive";
63828c2ecf20Sopenharmony_ci		else
63838c2ecf20Sopenharmony_ci			continue;
63848c2ecf20Sopenharmony_ci
63858c2ecf20Sopenharmony_ci		output_len += scnprintf(buf + output_len,
63868c2ecf20Sopenharmony_ci					PAGE_SIZE - output_len,
63878c2ecf20Sopenharmony_ci					"[%d:%d:%d:%d] %20.20s ",
63888c2ecf20Sopenharmony_ci					ctrl_info->scsi_host->host_no,
63898c2ecf20Sopenharmony_ci					device->bus, device->target,
63908c2ecf20Sopenharmony_ci					device->lun,
63918c2ecf20Sopenharmony_ci					scsi_device_type(device->devtype));
63928c2ecf20Sopenharmony_ci
63938c2ecf20Sopenharmony_ci		if (device->devtype == TYPE_RAID ||
63948c2ecf20Sopenharmony_ci			pqi_is_logical_device(device))
63958c2ecf20Sopenharmony_ci			goto end_buffer;
63968c2ecf20Sopenharmony_ci
63978c2ecf20Sopenharmony_ci		memcpy(&phys_connector, &device->phys_connector[i],
63988c2ecf20Sopenharmony_ci			sizeof(phys_connector));
63998c2ecf20Sopenharmony_ci		if (phys_connector[0] < '0')
64008c2ecf20Sopenharmony_ci			phys_connector[0] = '0';
64018c2ecf20Sopenharmony_ci		if (phys_connector[1] < '0')
64028c2ecf20Sopenharmony_ci			phys_connector[1] = '0';
64038c2ecf20Sopenharmony_ci
64048c2ecf20Sopenharmony_ci		output_len += scnprintf(buf + output_len,
64058c2ecf20Sopenharmony_ci					PAGE_SIZE - output_len,
64068c2ecf20Sopenharmony_ci					"PORT: %.2s ", phys_connector);
64078c2ecf20Sopenharmony_ci
64088c2ecf20Sopenharmony_ci		box = device->box[i];
64098c2ecf20Sopenharmony_ci		if (box != 0 && box != 0xFF)
64108c2ecf20Sopenharmony_ci			output_len += scnprintf(buf + output_len,
64118c2ecf20Sopenharmony_ci						PAGE_SIZE - output_len,
64128c2ecf20Sopenharmony_ci						"BOX: %hhu ", box);
64138c2ecf20Sopenharmony_ci
64148c2ecf20Sopenharmony_ci		if ((device->devtype == TYPE_DISK ||
64158c2ecf20Sopenharmony_ci			device->devtype == TYPE_ZBC) &&
64168c2ecf20Sopenharmony_ci			pqi_expose_device(device))
64178c2ecf20Sopenharmony_ci			output_len += scnprintf(buf + output_len,
64188c2ecf20Sopenharmony_ci						PAGE_SIZE - output_len,
64198c2ecf20Sopenharmony_ci						"BAY: %hhu ", bay);
64208c2ecf20Sopenharmony_ci
64218c2ecf20Sopenharmony_ciend_buffer:
64228c2ecf20Sopenharmony_ci		output_len += scnprintf(buf + output_len,
64238c2ecf20Sopenharmony_ci					PAGE_SIZE - output_len,
64248c2ecf20Sopenharmony_ci					"%s\n", active);
64258c2ecf20Sopenharmony_ci	}
64268c2ecf20Sopenharmony_ci
64278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
64288c2ecf20Sopenharmony_ci
64298c2ecf20Sopenharmony_ci	return output_len;
64308c2ecf20Sopenharmony_ci}
64318c2ecf20Sopenharmony_ci
64328c2ecf20Sopenharmony_cistatic ssize_t pqi_sas_address_show(struct device *dev,
64338c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
64348c2ecf20Sopenharmony_ci{
64358c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
64368c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
64378c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
64388c2ecf20Sopenharmony_ci	unsigned long flags;
64398c2ecf20Sopenharmony_ci	u64 sas_address;
64408c2ecf20Sopenharmony_ci
64418c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
64428c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
64438c2ecf20Sopenharmony_ci
64448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
64458c2ecf20Sopenharmony_ci
64468c2ecf20Sopenharmony_ci	device = sdev->hostdata;
64478c2ecf20Sopenharmony_ci	if (!device || !pqi_is_device_with_sas_address(device)) {
64488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
64498c2ecf20Sopenharmony_ci		return -ENODEV;
64508c2ecf20Sopenharmony_ci	}
64518c2ecf20Sopenharmony_ci
64528c2ecf20Sopenharmony_ci	sas_address = device->sas_address;
64538c2ecf20Sopenharmony_ci
64548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
64558c2ecf20Sopenharmony_ci
64568c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "0x%016llx\n", sas_address);
64578c2ecf20Sopenharmony_ci}
64588c2ecf20Sopenharmony_ci
64598c2ecf20Sopenharmony_cistatic ssize_t pqi_ssd_smart_path_enabled_show(struct device *dev,
64608c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
64618c2ecf20Sopenharmony_ci{
64628c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
64638c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
64648c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
64658c2ecf20Sopenharmony_ci	unsigned long flags;
64668c2ecf20Sopenharmony_ci
64678c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
64688c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
64698c2ecf20Sopenharmony_ci
64708c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
64718c2ecf20Sopenharmony_ci
64728c2ecf20Sopenharmony_ci	device = sdev->hostdata;
64738c2ecf20Sopenharmony_ci	if (!device) {
64748c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
64758c2ecf20Sopenharmony_ci		return -ENODEV;
64768c2ecf20Sopenharmony_ci	}
64778c2ecf20Sopenharmony_ci
64788c2ecf20Sopenharmony_ci	buffer[0] = device->raid_bypass_enabled ? '1' : '0';
64798c2ecf20Sopenharmony_ci	buffer[1] = '\n';
64808c2ecf20Sopenharmony_ci	buffer[2] = '\0';
64818c2ecf20Sopenharmony_ci
64828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
64838c2ecf20Sopenharmony_ci
64848c2ecf20Sopenharmony_ci	return 2;
64858c2ecf20Sopenharmony_ci}
64868c2ecf20Sopenharmony_ci
64878c2ecf20Sopenharmony_cistatic ssize_t pqi_raid_level_show(struct device *dev,
64888c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
64898c2ecf20Sopenharmony_ci{
64908c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
64918c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
64928c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
64938c2ecf20Sopenharmony_ci	unsigned long flags;
64948c2ecf20Sopenharmony_ci	char *raid_level;
64958c2ecf20Sopenharmony_ci
64968c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
64978c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
64988c2ecf20Sopenharmony_ci
64998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
65008c2ecf20Sopenharmony_ci
65018c2ecf20Sopenharmony_ci	device = sdev->hostdata;
65028c2ecf20Sopenharmony_ci	if (!device) {
65038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
65048c2ecf20Sopenharmony_ci		return -ENODEV;
65058c2ecf20Sopenharmony_ci	}
65068c2ecf20Sopenharmony_ci
65078c2ecf20Sopenharmony_ci	if (pqi_is_logical_device(device))
65088c2ecf20Sopenharmony_ci		raid_level = pqi_raid_level_to_string(device->raid_level);
65098c2ecf20Sopenharmony_ci	else
65108c2ecf20Sopenharmony_ci		raid_level = "N/A";
65118c2ecf20Sopenharmony_ci
65128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
65138c2ecf20Sopenharmony_ci
65148c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%s\n", raid_level);
65158c2ecf20Sopenharmony_ci}
65168c2ecf20Sopenharmony_ci
65178c2ecf20Sopenharmony_cistatic ssize_t pqi_raid_bypass_cnt_show(struct device *dev,
65188c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buffer)
65198c2ecf20Sopenharmony_ci{
65208c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
65218c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
65228c2ecf20Sopenharmony_ci	struct pqi_scsi_dev *device;
65238c2ecf20Sopenharmony_ci	unsigned long flags;
65248c2ecf20Sopenharmony_ci	int raid_bypass_cnt;
65258c2ecf20Sopenharmony_ci
65268c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
65278c2ecf20Sopenharmony_ci	ctrl_info = shost_to_hba(sdev->host);
65288c2ecf20Sopenharmony_ci
65298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
65308c2ecf20Sopenharmony_ci
65318c2ecf20Sopenharmony_ci	device = sdev->hostdata;
65328c2ecf20Sopenharmony_ci	if (!device) {
65338c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
65348c2ecf20Sopenharmony_ci		return -ENODEV;
65358c2ecf20Sopenharmony_ci	}
65368c2ecf20Sopenharmony_ci
65378c2ecf20Sopenharmony_ci	raid_bypass_cnt = atomic_read(&device->raid_bypass_cnt);
65388c2ecf20Sopenharmony_ci
65398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
65408c2ecf20Sopenharmony_ci
65418c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "0x%x\n", raid_bypass_cnt);
65428c2ecf20Sopenharmony_ci}
65438c2ecf20Sopenharmony_ci
65448c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lunid, 0444, pqi_lunid_show, NULL);
65458c2ecf20Sopenharmony_cistatic DEVICE_ATTR(unique_id, 0444, pqi_unique_id_show, NULL);
65468c2ecf20Sopenharmony_cistatic DEVICE_ATTR(path_info, 0444, pqi_path_info_show, NULL);
65478c2ecf20Sopenharmony_cistatic DEVICE_ATTR(sas_address, 0444, pqi_sas_address_show, NULL);
65488c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ssd_smart_path_enabled, 0444, pqi_ssd_smart_path_enabled_show, NULL);
65498c2ecf20Sopenharmony_cistatic DEVICE_ATTR(raid_level, 0444, pqi_raid_level_show, NULL);
65508c2ecf20Sopenharmony_cistatic DEVICE_ATTR(raid_bypass_cnt, 0444, pqi_raid_bypass_cnt_show, NULL);
65518c2ecf20Sopenharmony_ci
65528c2ecf20Sopenharmony_cistatic struct device_attribute *pqi_sdev_attrs[] = {
65538c2ecf20Sopenharmony_ci	&dev_attr_lunid,
65548c2ecf20Sopenharmony_ci	&dev_attr_unique_id,
65558c2ecf20Sopenharmony_ci	&dev_attr_path_info,
65568c2ecf20Sopenharmony_ci	&dev_attr_sas_address,
65578c2ecf20Sopenharmony_ci	&dev_attr_ssd_smart_path_enabled,
65588c2ecf20Sopenharmony_ci	&dev_attr_raid_level,
65598c2ecf20Sopenharmony_ci	&dev_attr_raid_bypass_cnt,
65608c2ecf20Sopenharmony_ci	NULL
65618c2ecf20Sopenharmony_ci};
65628c2ecf20Sopenharmony_ci
65638c2ecf20Sopenharmony_cistatic struct scsi_host_template pqi_driver_template = {
65648c2ecf20Sopenharmony_ci	.module = THIS_MODULE,
65658c2ecf20Sopenharmony_ci	.name = DRIVER_NAME_SHORT,
65668c2ecf20Sopenharmony_ci	.proc_name = DRIVER_NAME_SHORT,
65678c2ecf20Sopenharmony_ci	.queuecommand = pqi_scsi_queue_command,
65688c2ecf20Sopenharmony_ci	.scan_start = pqi_scan_start,
65698c2ecf20Sopenharmony_ci	.scan_finished = pqi_scan_finished,
65708c2ecf20Sopenharmony_ci	.this_id = -1,
65718c2ecf20Sopenharmony_ci	.eh_device_reset_handler = pqi_eh_device_reset_handler,
65728c2ecf20Sopenharmony_ci	.ioctl = pqi_ioctl,
65738c2ecf20Sopenharmony_ci	.slave_alloc = pqi_slave_alloc,
65748c2ecf20Sopenharmony_ci	.slave_configure = pqi_slave_configure,
65758c2ecf20Sopenharmony_ci	.slave_destroy = pqi_slave_destroy,
65768c2ecf20Sopenharmony_ci	.map_queues = pqi_map_queues,
65778c2ecf20Sopenharmony_ci	.sdev_attrs = pqi_sdev_attrs,
65788c2ecf20Sopenharmony_ci	.shost_attrs = pqi_shost_attrs,
65798c2ecf20Sopenharmony_ci};
65808c2ecf20Sopenharmony_ci
65818c2ecf20Sopenharmony_cistatic int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info)
65828c2ecf20Sopenharmony_ci{
65838c2ecf20Sopenharmony_ci	int rc;
65848c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
65858c2ecf20Sopenharmony_ci
65868c2ecf20Sopenharmony_ci	shost = scsi_host_alloc(&pqi_driver_template, sizeof(ctrl_info));
65878c2ecf20Sopenharmony_ci	if (!shost) {
65888c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
65898c2ecf20Sopenharmony_ci			"scsi_host_alloc failed for controller %u\n",
65908c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id);
65918c2ecf20Sopenharmony_ci		return -ENOMEM;
65928c2ecf20Sopenharmony_ci	}
65938c2ecf20Sopenharmony_ci
65948c2ecf20Sopenharmony_ci	shost->io_port = 0;
65958c2ecf20Sopenharmony_ci	shost->n_io_port = 0;
65968c2ecf20Sopenharmony_ci	shost->this_id = -1;
65978c2ecf20Sopenharmony_ci	shost->max_channel = PQI_MAX_BUS;
65988c2ecf20Sopenharmony_ci	shost->max_cmd_len = MAX_COMMAND_SIZE;
65998c2ecf20Sopenharmony_ci	shost->max_lun = ~0;
66008c2ecf20Sopenharmony_ci	shost->max_id = ~0;
66018c2ecf20Sopenharmony_ci	shost->max_sectors = ctrl_info->max_sectors;
66028c2ecf20Sopenharmony_ci	shost->can_queue = ctrl_info->scsi_ml_can_queue;
66038c2ecf20Sopenharmony_ci	shost->cmd_per_lun = shost->can_queue;
66048c2ecf20Sopenharmony_ci	shost->sg_tablesize = ctrl_info->sg_tablesize;
66058c2ecf20Sopenharmony_ci	shost->transportt = pqi_sas_transport_template;
66068c2ecf20Sopenharmony_ci	shost->irq = pci_irq_vector(ctrl_info->pci_dev, 0);
66078c2ecf20Sopenharmony_ci	shost->unique_id = shost->irq;
66088c2ecf20Sopenharmony_ci	shost->nr_hw_queues = ctrl_info->num_queue_groups;
66098c2ecf20Sopenharmony_ci	shost->host_tagset = 1;
66108c2ecf20Sopenharmony_ci	shost->hostdata[0] = (unsigned long)ctrl_info;
66118c2ecf20Sopenharmony_ci
66128c2ecf20Sopenharmony_ci	rc = scsi_add_host(shost, &ctrl_info->pci_dev->dev);
66138c2ecf20Sopenharmony_ci	if (rc) {
66148c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
66158c2ecf20Sopenharmony_ci			"scsi_add_host failed for controller %u\n",
66168c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id);
66178c2ecf20Sopenharmony_ci		goto free_host;
66188c2ecf20Sopenharmony_ci	}
66198c2ecf20Sopenharmony_ci
66208c2ecf20Sopenharmony_ci	rc = pqi_add_sas_host(shost, ctrl_info);
66218c2ecf20Sopenharmony_ci	if (rc) {
66228c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
66238c2ecf20Sopenharmony_ci			"add SAS host failed for controller %u\n",
66248c2ecf20Sopenharmony_ci			ctrl_info->ctrl_id);
66258c2ecf20Sopenharmony_ci		goto remove_host;
66268c2ecf20Sopenharmony_ci	}
66278c2ecf20Sopenharmony_ci
66288c2ecf20Sopenharmony_ci	ctrl_info->scsi_host = shost;
66298c2ecf20Sopenharmony_ci
66308c2ecf20Sopenharmony_ci	return 0;
66318c2ecf20Sopenharmony_ci
66328c2ecf20Sopenharmony_ciremove_host:
66338c2ecf20Sopenharmony_ci	scsi_remove_host(shost);
66348c2ecf20Sopenharmony_cifree_host:
66358c2ecf20Sopenharmony_ci	scsi_host_put(shost);
66368c2ecf20Sopenharmony_ci
66378c2ecf20Sopenharmony_ci	return rc;
66388c2ecf20Sopenharmony_ci}
66398c2ecf20Sopenharmony_ci
66408c2ecf20Sopenharmony_cistatic void pqi_unregister_scsi(struct pqi_ctrl_info *ctrl_info)
66418c2ecf20Sopenharmony_ci{
66428c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
66438c2ecf20Sopenharmony_ci
66448c2ecf20Sopenharmony_ci	pqi_delete_sas_host(ctrl_info);
66458c2ecf20Sopenharmony_ci
66468c2ecf20Sopenharmony_ci	shost = ctrl_info->scsi_host;
66478c2ecf20Sopenharmony_ci	if (!shost)
66488c2ecf20Sopenharmony_ci		return;
66498c2ecf20Sopenharmony_ci
66508c2ecf20Sopenharmony_ci	scsi_remove_host(shost);
66518c2ecf20Sopenharmony_ci	scsi_host_put(shost);
66528c2ecf20Sopenharmony_ci}
66538c2ecf20Sopenharmony_ci
66548c2ecf20Sopenharmony_cistatic int pqi_wait_for_pqi_reset_completion(struct pqi_ctrl_info *ctrl_info)
66558c2ecf20Sopenharmony_ci{
66568c2ecf20Sopenharmony_ci	int rc = 0;
66578c2ecf20Sopenharmony_ci	struct pqi_device_registers __iomem *pqi_registers;
66588c2ecf20Sopenharmony_ci	unsigned long timeout;
66598c2ecf20Sopenharmony_ci	unsigned int timeout_msecs;
66608c2ecf20Sopenharmony_ci	union pqi_reset_register reset_reg;
66618c2ecf20Sopenharmony_ci
66628c2ecf20Sopenharmony_ci	pqi_registers = ctrl_info->pqi_registers;
66638c2ecf20Sopenharmony_ci	timeout_msecs = readw(&pqi_registers->max_reset_timeout) * 100;
66648c2ecf20Sopenharmony_ci	timeout = msecs_to_jiffies(timeout_msecs) + jiffies;
66658c2ecf20Sopenharmony_ci
66668c2ecf20Sopenharmony_ci	while (1) {
66678c2ecf20Sopenharmony_ci		msleep(PQI_RESET_POLL_INTERVAL_MSECS);
66688c2ecf20Sopenharmony_ci		reset_reg.all_bits = readl(&pqi_registers->device_reset);
66698c2ecf20Sopenharmony_ci		if (reset_reg.bits.reset_action == PQI_RESET_ACTION_COMPLETED)
66708c2ecf20Sopenharmony_ci			break;
66718c2ecf20Sopenharmony_ci		pqi_check_ctrl_health(ctrl_info);
66728c2ecf20Sopenharmony_ci		if (pqi_ctrl_offline(ctrl_info)) {
66738c2ecf20Sopenharmony_ci			rc = -ENXIO;
66748c2ecf20Sopenharmony_ci			break;
66758c2ecf20Sopenharmony_ci		}
66768c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
66778c2ecf20Sopenharmony_ci			rc = -ETIMEDOUT;
66788c2ecf20Sopenharmony_ci			break;
66798c2ecf20Sopenharmony_ci		}
66808c2ecf20Sopenharmony_ci	}
66818c2ecf20Sopenharmony_ci
66828c2ecf20Sopenharmony_ci	return rc;
66838c2ecf20Sopenharmony_ci}
66848c2ecf20Sopenharmony_ci
66858c2ecf20Sopenharmony_cistatic int pqi_reset(struct pqi_ctrl_info *ctrl_info)
66868c2ecf20Sopenharmony_ci{
66878c2ecf20Sopenharmony_ci	int rc;
66888c2ecf20Sopenharmony_ci	union pqi_reset_register reset_reg;
66898c2ecf20Sopenharmony_ci
66908c2ecf20Sopenharmony_ci	if (ctrl_info->pqi_reset_quiesce_supported) {
66918c2ecf20Sopenharmony_ci		rc = sis_pqi_reset_quiesce(ctrl_info);
66928c2ecf20Sopenharmony_ci		if (rc) {
66938c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
66948c2ecf20Sopenharmony_ci				"PQI reset failed during quiesce with error %d\n",
66958c2ecf20Sopenharmony_ci				rc);
66968c2ecf20Sopenharmony_ci			return rc;
66978c2ecf20Sopenharmony_ci		}
66988c2ecf20Sopenharmony_ci	}
66998c2ecf20Sopenharmony_ci
67008c2ecf20Sopenharmony_ci	reset_reg.all_bits = 0;
67018c2ecf20Sopenharmony_ci	reset_reg.bits.reset_type = PQI_RESET_TYPE_HARD_RESET;
67028c2ecf20Sopenharmony_ci	reset_reg.bits.reset_action = PQI_RESET_ACTION_RESET;
67038c2ecf20Sopenharmony_ci
67048c2ecf20Sopenharmony_ci	writel(reset_reg.all_bits, &ctrl_info->pqi_registers->device_reset);
67058c2ecf20Sopenharmony_ci
67068c2ecf20Sopenharmony_ci	rc = pqi_wait_for_pqi_reset_completion(ctrl_info);
67078c2ecf20Sopenharmony_ci	if (rc)
67088c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
67098c2ecf20Sopenharmony_ci			"PQI reset failed with error %d\n", rc);
67108c2ecf20Sopenharmony_ci
67118c2ecf20Sopenharmony_ci	return rc;
67128c2ecf20Sopenharmony_ci}
67138c2ecf20Sopenharmony_ci
67148c2ecf20Sopenharmony_cistatic int pqi_get_ctrl_serial_number(struct pqi_ctrl_info *ctrl_info)
67158c2ecf20Sopenharmony_ci{
67168c2ecf20Sopenharmony_ci	int rc;
67178c2ecf20Sopenharmony_ci	struct bmic_sense_subsystem_info *sense_info;
67188c2ecf20Sopenharmony_ci
67198c2ecf20Sopenharmony_ci	sense_info = kzalloc(sizeof(*sense_info), GFP_KERNEL);
67208c2ecf20Sopenharmony_ci	if (!sense_info)
67218c2ecf20Sopenharmony_ci		return -ENOMEM;
67228c2ecf20Sopenharmony_ci
67238c2ecf20Sopenharmony_ci	rc = pqi_sense_subsystem_info(ctrl_info, sense_info);
67248c2ecf20Sopenharmony_ci	if (rc)
67258c2ecf20Sopenharmony_ci		goto out;
67268c2ecf20Sopenharmony_ci
67278c2ecf20Sopenharmony_ci	memcpy(ctrl_info->serial_number, sense_info->ctrl_serial_number,
67288c2ecf20Sopenharmony_ci		sizeof(sense_info->ctrl_serial_number));
67298c2ecf20Sopenharmony_ci	ctrl_info->serial_number[sizeof(sense_info->ctrl_serial_number)] = '\0';
67308c2ecf20Sopenharmony_ci
67318c2ecf20Sopenharmony_ciout:
67328c2ecf20Sopenharmony_ci	kfree(sense_info);
67338c2ecf20Sopenharmony_ci
67348c2ecf20Sopenharmony_ci	return rc;
67358c2ecf20Sopenharmony_ci}
67368c2ecf20Sopenharmony_ci
67378c2ecf20Sopenharmony_cistatic int pqi_get_ctrl_product_details(struct pqi_ctrl_info *ctrl_info)
67388c2ecf20Sopenharmony_ci{
67398c2ecf20Sopenharmony_ci	int rc;
67408c2ecf20Sopenharmony_ci	struct bmic_identify_controller *identify;
67418c2ecf20Sopenharmony_ci
67428c2ecf20Sopenharmony_ci	identify = kmalloc(sizeof(*identify), GFP_KERNEL);
67438c2ecf20Sopenharmony_ci	if (!identify)
67448c2ecf20Sopenharmony_ci		return -ENOMEM;
67458c2ecf20Sopenharmony_ci
67468c2ecf20Sopenharmony_ci	rc = pqi_identify_controller(ctrl_info, identify);
67478c2ecf20Sopenharmony_ci	if (rc)
67488c2ecf20Sopenharmony_ci		goto out;
67498c2ecf20Sopenharmony_ci
67508c2ecf20Sopenharmony_ci	memcpy(ctrl_info->firmware_version, identify->firmware_version,
67518c2ecf20Sopenharmony_ci		sizeof(identify->firmware_version));
67528c2ecf20Sopenharmony_ci	ctrl_info->firmware_version[sizeof(identify->firmware_version)] = '\0';
67538c2ecf20Sopenharmony_ci	snprintf(ctrl_info->firmware_version +
67548c2ecf20Sopenharmony_ci		strlen(ctrl_info->firmware_version),
67558c2ecf20Sopenharmony_ci		sizeof(ctrl_info->firmware_version),
67568c2ecf20Sopenharmony_ci		"-%u", get_unaligned_le16(&identify->firmware_build_number));
67578c2ecf20Sopenharmony_ci
67588c2ecf20Sopenharmony_ci	memcpy(ctrl_info->model, identify->product_id,
67598c2ecf20Sopenharmony_ci		sizeof(identify->product_id));
67608c2ecf20Sopenharmony_ci	ctrl_info->model[sizeof(identify->product_id)] = '\0';
67618c2ecf20Sopenharmony_ci
67628c2ecf20Sopenharmony_ci	memcpy(ctrl_info->vendor, identify->vendor_id,
67638c2ecf20Sopenharmony_ci		sizeof(identify->vendor_id));
67648c2ecf20Sopenharmony_ci	ctrl_info->vendor[sizeof(identify->vendor_id)] = '\0';
67658c2ecf20Sopenharmony_ci
67668c2ecf20Sopenharmony_ciout:
67678c2ecf20Sopenharmony_ci	kfree(identify);
67688c2ecf20Sopenharmony_ci
67698c2ecf20Sopenharmony_ci	return rc;
67708c2ecf20Sopenharmony_ci}
67718c2ecf20Sopenharmony_ci
67728c2ecf20Sopenharmony_cistruct pqi_config_table_section_info {
67738c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
67748c2ecf20Sopenharmony_ci	void		*section;
67758c2ecf20Sopenharmony_ci	u32		section_offset;
67768c2ecf20Sopenharmony_ci	void __iomem	*section_iomem_addr;
67778c2ecf20Sopenharmony_ci};
67788c2ecf20Sopenharmony_ci
67798c2ecf20Sopenharmony_cistatic inline bool pqi_is_firmware_feature_supported(
67808c2ecf20Sopenharmony_ci	struct pqi_config_table_firmware_features *firmware_features,
67818c2ecf20Sopenharmony_ci	unsigned int bit_position)
67828c2ecf20Sopenharmony_ci{
67838c2ecf20Sopenharmony_ci	unsigned int byte_index;
67848c2ecf20Sopenharmony_ci
67858c2ecf20Sopenharmony_ci	byte_index = bit_position / BITS_PER_BYTE;
67868c2ecf20Sopenharmony_ci
67878c2ecf20Sopenharmony_ci	if (byte_index >= le16_to_cpu(firmware_features->num_elements))
67888c2ecf20Sopenharmony_ci		return false;
67898c2ecf20Sopenharmony_ci
67908c2ecf20Sopenharmony_ci	return firmware_features->features_supported[byte_index] &
67918c2ecf20Sopenharmony_ci		(1 << (bit_position % BITS_PER_BYTE)) ? true : false;
67928c2ecf20Sopenharmony_ci}
67938c2ecf20Sopenharmony_ci
67948c2ecf20Sopenharmony_cistatic inline bool pqi_is_firmware_feature_enabled(
67958c2ecf20Sopenharmony_ci	struct pqi_config_table_firmware_features *firmware_features,
67968c2ecf20Sopenharmony_ci	void __iomem *firmware_features_iomem_addr,
67978c2ecf20Sopenharmony_ci	unsigned int bit_position)
67988c2ecf20Sopenharmony_ci{
67998c2ecf20Sopenharmony_ci	unsigned int byte_index;
68008c2ecf20Sopenharmony_ci	u8 __iomem *features_enabled_iomem_addr;
68018c2ecf20Sopenharmony_ci
68028c2ecf20Sopenharmony_ci	byte_index = (bit_position / BITS_PER_BYTE) +
68038c2ecf20Sopenharmony_ci		(le16_to_cpu(firmware_features->num_elements) * 2);
68048c2ecf20Sopenharmony_ci
68058c2ecf20Sopenharmony_ci	features_enabled_iomem_addr = firmware_features_iomem_addr +
68068c2ecf20Sopenharmony_ci		offsetof(struct pqi_config_table_firmware_features,
68078c2ecf20Sopenharmony_ci			features_supported) + byte_index;
68088c2ecf20Sopenharmony_ci
68098c2ecf20Sopenharmony_ci	return *((__force u8 *)features_enabled_iomem_addr) &
68108c2ecf20Sopenharmony_ci		(1 << (bit_position % BITS_PER_BYTE)) ? true : false;
68118c2ecf20Sopenharmony_ci}
68128c2ecf20Sopenharmony_ci
68138c2ecf20Sopenharmony_cistatic inline void pqi_request_firmware_feature(
68148c2ecf20Sopenharmony_ci	struct pqi_config_table_firmware_features *firmware_features,
68158c2ecf20Sopenharmony_ci	unsigned int bit_position)
68168c2ecf20Sopenharmony_ci{
68178c2ecf20Sopenharmony_ci	unsigned int byte_index;
68188c2ecf20Sopenharmony_ci
68198c2ecf20Sopenharmony_ci	byte_index = (bit_position / BITS_PER_BYTE) +
68208c2ecf20Sopenharmony_ci		le16_to_cpu(firmware_features->num_elements);
68218c2ecf20Sopenharmony_ci
68228c2ecf20Sopenharmony_ci	firmware_features->features_supported[byte_index] |=
68238c2ecf20Sopenharmony_ci		(1 << (bit_position % BITS_PER_BYTE));
68248c2ecf20Sopenharmony_ci}
68258c2ecf20Sopenharmony_ci
68268c2ecf20Sopenharmony_cistatic int pqi_config_table_update(struct pqi_ctrl_info *ctrl_info,
68278c2ecf20Sopenharmony_ci	u16 first_section, u16 last_section)
68288c2ecf20Sopenharmony_ci{
68298c2ecf20Sopenharmony_ci	struct pqi_vendor_general_request request;
68308c2ecf20Sopenharmony_ci
68318c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
68328c2ecf20Sopenharmony_ci
68338c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_VENDOR_GENERAL;
68348c2ecf20Sopenharmony_ci	put_unaligned_le16(sizeof(request) - PQI_REQUEST_HEADER_LENGTH,
68358c2ecf20Sopenharmony_ci		&request.header.iu_length);
68368c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_VENDOR_GENERAL_CONFIG_TABLE_UPDATE,
68378c2ecf20Sopenharmony_ci		&request.function_code);
68388c2ecf20Sopenharmony_ci	put_unaligned_le16(first_section,
68398c2ecf20Sopenharmony_ci		&request.data.config_table_update.first_section);
68408c2ecf20Sopenharmony_ci	put_unaligned_le16(last_section,
68418c2ecf20Sopenharmony_ci		&request.data.config_table_update.last_section);
68428c2ecf20Sopenharmony_ci
68438c2ecf20Sopenharmony_ci	return pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
68448c2ecf20Sopenharmony_ci		0, NULL, NO_TIMEOUT);
68458c2ecf20Sopenharmony_ci}
68468c2ecf20Sopenharmony_ci
68478c2ecf20Sopenharmony_cistatic int pqi_enable_firmware_features(struct pqi_ctrl_info *ctrl_info,
68488c2ecf20Sopenharmony_ci	struct pqi_config_table_firmware_features *firmware_features,
68498c2ecf20Sopenharmony_ci	void __iomem *firmware_features_iomem_addr)
68508c2ecf20Sopenharmony_ci{
68518c2ecf20Sopenharmony_ci	void *features_requested;
68528c2ecf20Sopenharmony_ci	void __iomem *features_requested_iomem_addr;
68538c2ecf20Sopenharmony_ci
68548c2ecf20Sopenharmony_ci	features_requested = firmware_features->features_supported +
68558c2ecf20Sopenharmony_ci		le16_to_cpu(firmware_features->num_elements);
68568c2ecf20Sopenharmony_ci
68578c2ecf20Sopenharmony_ci	features_requested_iomem_addr = firmware_features_iomem_addr +
68588c2ecf20Sopenharmony_ci		(features_requested - (void *)firmware_features);
68598c2ecf20Sopenharmony_ci
68608c2ecf20Sopenharmony_ci	memcpy_toio(features_requested_iomem_addr, features_requested,
68618c2ecf20Sopenharmony_ci		le16_to_cpu(firmware_features->num_elements));
68628c2ecf20Sopenharmony_ci
68638c2ecf20Sopenharmony_ci	return pqi_config_table_update(ctrl_info,
68648c2ecf20Sopenharmony_ci		PQI_CONFIG_TABLE_SECTION_FIRMWARE_FEATURES,
68658c2ecf20Sopenharmony_ci		PQI_CONFIG_TABLE_SECTION_FIRMWARE_FEATURES);
68668c2ecf20Sopenharmony_ci}
68678c2ecf20Sopenharmony_ci
68688c2ecf20Sopenharmony_cistruct pqi_firmware_feature {
68698c2ecf20Sopenharmony_ci	char		*feature_name;
68708c2ecf20Sopenharmony_ci	unsigned int	feature_bit;
68718c2ecf20Sopenharmony_ci	bool		supported;
68728c2ecf20Sopenharmony_ci	bool		enabled;
68738c2ecf20Sopenharmony_ci	void (*feature_status)(struct pqi_ctrl_info *ctrl_info,
68748c2ecf20Sopenharmony_ci		struct pqi_firmware_feature *firmware_feature);
68758c2ecf20Sopenharmony_ci};
68768c2ecf20Sopenharmony_ci
68778c2ecf20Sopenharmony_cistatic void pqi_firmware_feature_status(struct pqi_ctrl_info *ctrl_info,
68788c2ecf20Sopenharmony_ci	struct pqi_firmware_feature *firmware_feature)
68798c2ecf20Sopenharmony_ci{
68808c2ecf20Sopenharmony_ci	if (!firmware_feature->supported) {
68818c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev, "%s not supported by controller\n",
68828c2ecf20Sopenharmony_ci			firmware_feature->feature_name);
68838c2ecf20Sopenharmony_ci		return;
68848c2ecf20Sopenharmony_ci	}
68858c2ecf20Sopenharmony_ci
68868c2ecf20Sopenharmony_ci	if (firmware_feature->enabled) {
68878c2ecf20Sopenharmony_ci		dev_info(&ctrl_info->pci_dev->dev,
68888c2ecf20Sopenharmony_ci			"%s enabled\n", firmware_feature->feature_name);
68898c2ecf20Sopenharmony_ci		return;
68908c2ecf20Sopenharmony_ci	}
68918c2ecf20Sopenharmony_ci
68928c2ecf20Sopenharmony_ci	dev_err(&ctrl_info->pci_dev->dev, "failed to enable %s\n",
68938c2ecf20Sopenharmony_ci		firmware_feature->feature_name);
68948c2ecf20Sopenharmony_ci}
68958c2ecf20Sopenharmony_ci
68968c2ecf20Sopenharmony_cistatic void pqi_ctrl_update_feature_flags(struct pqi_ctrl_info *ctrl_info,
68978c2ecf20Sopenharmony_ci	struct pqi_firmware_feature *firmware_feature)
68988c2ecf20Sopenharmony_ci{
68998c2ecf20Sopenharmony_ci	switch (firmware_feature->feature_bit) {
69008c2ecf20Sopenharmony_ci	case PQI_FIRMWARE_FEATURE_SOFT_RESET_HANDSHAKE:
69018c2ecf20Sopenharmony_ci		ctrl_info->soft_reset_handshake_supported =
69028c2ecf20Sopenharmony_ci			firmware_feature->enabled;
69038c2ecf20Sopenharmony_ci		break;
69048c2ecf20Sopenharmony_ci	case PQI_FIRMWARE_FEATURE_RAID_IU_TIMEOUT:
69058c2ecf20Sopenharmony_ci		ctrl_info->raid_iu_timeout_supported =
69068c2ecf20Sopenharmony_ci			firmware_feature->enabled;
69078c2ecf20Sopenharmony_ci		break;
69088c2ecf20Sopenharmony_ci	case PQI_FIRMWARE_FEATURE_TMF_IU_TIMEOUT:
69098c2ecf20Sopenharmony_ci		ctrl_info->tmf_iu_timeout_supported =
69108c2ecf20Sopenharmony_ci			firmware_feature->enabled;
69118c2ecf20Sopenharmony_ci		break;
69128c2ecf20Sopenharmony_ci	}
69138c2ecf20Sopenharmony_ci
69148c2ecf20Sopenharmony_ci	pqi_firmware_feature_status(ctrl_info, firmware_feature);
69158c2ecf20Sopenharmony_ci}
69168c2ecf20Sopenharmony_ci
69178c2ecf20Sopenharmony_cistatic inline void pqi_firmware_feature_update(struct pqi_ctrl_info *ctrl_info,
69188c2ecf20Sopenharmony_ci	struct pqi_firmware_feature *firmware_feature)
69198c2ecf20Sopenharmony_ci{
69208c2ecf20Sopenharmony_ci	if (firmware_feature->feature_status)
69218c2ecf20Sopenharmony_ci		firmware_feature->feature_status(ctrl_info, firmware_feature);
69228c2ecf20Sopenharmony_ci}
69238c2ecf20Sopenharmony_ci
69248c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pqi_firmware_features_mutex);
69258c2ecf20Sopenharmony_ci
69268c2ecf20Sopenharmony_cistatic struct pqi_firmware_feature pqi_firmware_features[] = {
69278c2ecf20Sopenharmony_ci	{
69288c2ecf20Sopenharmony_ci		.feature_name = "Online Firmware Activation",
69298c2ecf20Sopenharmony_ci		.feature_bit = PQI_FIRMWARE_FEATURE_OFA,
69308c2ecf20Sopenharmony_ci		.feature_status = pqi_firmware_feature_status,
69318c2ecf20Sopenharmony_ci	},
69328c2ecf20Sopenharmony_ci	{
69338c2ecf20Sopenharmony_ci		.feature_name = "Serial Management Protocol",
69348c2ecf20Sopenharmony_ci		.feature_bit = PQI_FIRMWARE_FEATURE_SMP,
69358c2ecf20Sopenharmony_ci		.feature_status = pqi_firmware_feature_status,
69368c2ecf20Sopenharmony_ci	},
69378c2ecf20Sopenharmony_ci	{
69388c2ecf20Sopenharmony_ci		.feature_name = "New Soft Reset Handshake",
69398c2ecf20Sopenharmony_ci		.feature_bit = PQI_FIRMWARE_FEATURE_SOFT_RESET_HANDSHAKE,
69408c2ecf20Sopenharmony_ci		.feature_status = pqi_ctrl_update_feature_flags,
69418c2ecf20Sopenharmony_ci	},
69428c2ecf20Sopenharmony_ci	{
69438c2ecf20Sopenharmony_ci		.feature_name = "RAID IU Timeout",
69448c2ecf20Sopenharmony_ci		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_IU_TIMEOUT,
69458c2ecf20Sopenharmony_ci		.feature_status = pqi_ctrl_update_feature_flags,
69468c2ecf20Sopenharmony_ci	},
69478c2ecf20Sopenharmony_ci	{
69488c2ecf20Sopenharmony_ci		.feature_name = "TMF IU Timeout",
69498c2ecf20Sopenharmony_ci		.feature_bit = PQI_FIRMWARE_FEATURE_TMF_IU_TIMEOUT,
69508c2ecf20Sopenharmony_ci		.feature_status = pqi_ctrl_update_feature_flags,
69518c2ecf20Sopenharmony_ci	},
69528c2ecf20Sopenharmony_ci};
69538c2ecf20Sopenharmony_ci
69548c2ecf20Sopenharmony_cistatic void pqi_process_firmware_features(
69558c2ecf20Sopenharmony_ci	struct pqi_config_table_section_info *section_info)
69568c2ecf20Sopenharmony_ci{
69578c2ecf20Sopenharmony_ci	int rc;
69588c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
69598c2ecf20Sopenharmony_ci	struct pqi_config_table_firmware_features *firmware_features;
69608c2ecf20Sopenharmony_ci	void __iomem *firmware_features_iomem_addr;
69618c2ecf20Sopenharmony_ci	unsigned int i;
69628c2ecf20Sopenharmony_ci	unsigned int num_features_supported;
69638c2ecf20Sopenharmony_ci
69648c2ecf20Sopenharmony_ci	ctrl_info = section_info->ctrl_info;
69658c2ecf20Sopenharmony_ci	firmware_features = section_info->section;
69668c2ecf20Sopenharmony_ci	firmware_features_iomem_addr = section_info->section_iomem_addr;
69678c2ecf20Sopenharmony_ci
69688c2ecf20Sopenharmony_ci	for (i = 0, num_features_supported = 0;
69698c2ecf20Sopenharmony_ci		i < ARRAY_SIZE(pqi_firmware_features); i++) {
69708c2ecf20Sopenharmony_ci		if (pqi_is_firmware_feature_supported(firmware_features,
69718c2ecf20Sopenharmony_ci			pqi_firmware_features[i].feature_bit)) {
69728c2ecf20Sopenharmony_ci			pqi_firmware_features[i].supported = true;
69738c2ecf20Sopenharmony_ci			num_features_supported++;
69748c2ecf20Sopenharmony_ci		} else {
69758c2ecf20Sopenharmony_ci			pqi_firmware_feature_update(ctrl_info,
69768c2ecf20Sopenharmony_ci				&pqi_firmware_features[i]);
69778c2ecf20Sopenharmony_ci		}
69788c2ecf20Sopenharmony_ci	}
69798c2ecf20Sopenharmony_ci
69808c2ecf20Sopenharmony_ci	if (num_features_supported == 0)
69818c2ecf20Sopenharmony_ci		return;
69828c2ecf20Sopenharmony_ci
69838c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
69848c2ecf20Sopenharmony_ci		if (!pqi_firmware_features[i].supported)
69858c2ecf20Sopenharmony_ci			continue;
69868c2ecf20Sopenharmony_ci		pqi_request_firmware_feature(firmware_features,
69878c2ecf20Sopenharmony_ci			pqi_firmware_features[i].feature_bit);
69888c2ecf20Sopenharmony_ci	}
69898c2ecf20Sopenharmony_ci
69908c2ecf20Sopenharmony_ci	rc = pqi_enable_firmware_features(ctrl_info, firmware_features,
69918c2ecf20Sopenharmony_ci		firmware_features_iomem_addr);
69928c2ecf20Sopenharmony_ci	if (rc) {
69938c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
69948c2ecf20Sopenharmony_ci			"failed to enable firmware features in PQI configuration table\n");
69958c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
69968c2ecf20Sopenharmony_ci			if (!pqi_firmware_features[i].supported)
69978c2ecf20Sopenharmony_ci				continue;
69988c2ecf20Sopenharmony_ci			pqi_firmware_feature_update(ctrl_info,
69998c2ecf20Sopenharmony_ci				&pqi_firmware_features[i]);
70008c2ecf20Sopenharmony_ci		}
70018c2ecf20Sopenharmony_ci		return;
70028c2ecf20Sopenharmony_ci	}
70038c2ecf20Sopenharmony_ci
70048c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
70058c2ecf20Sopenharmony_ci		if (!pqi_firmware_features[i].supported)
70068c2ecf20Sopenharmony_ci			continue;
70078c2ecf20Sopenharmony_ci		if (pqi_is_firmware_feature_enabled(firmware_features,
70088c2ecf20Sopenharmony_ci			firmware_features_iomem_addr,
70098c2ecf20Sopenharmony_ci			pqi_firmware_features[i].feature_bit)) {
70108c2ecf20Sopenharmony_ci			pqi_firmware_features[i].enabled = true;
70118c2ecf20Sopenharmony_ci		}
70128c2ecf20Sopenharmony_ci		pqi_firmware_feature_update(ctrl_info,
70138c2ecf20Sopenharmony_ci			&pqi_firmware_features[i]);
70148c2ecf20Sopenharmony_ci	}
70158c2ecf20Sopenharmony_ci}
70168c2ecf20Sopenharmony_ci
70178c2ecf20Sopenharmony_cistatic void pqi_init_firmware_features(void)
70188c2ecf20Sopenharmony_ci{
70198c2ecf20Sopenharmony_ci	unsigned int i;
70208c2ecf20Sopenharmony_ci
70218c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
70228c2ecf20Sopenharmony_ci		pqi_firmware_features[i].supported = false;
70238c2ecf20Sopenharmony_ci		pqi_firmware_features[i].enabled = false;
70248c2ecf20Sopenharmony_ci	}
70258c2ecf20Sopenharmony_ci}
70268c2ecf20Sopenharmony_ci
70278c2ecf20Sopenharmony_cistatic void pqi_process_firmware_features_section(
70288c2ecf20Sopenharmony_ci	struct pqi_config_table_section_info *section_info)
70298c2ecf20Sopenharmony_ci{
70308c2ecf20Sopenharmony_ci	mutex_lock(&pqi_firmware_features_mutex);
70318c2ecf20Sopenharmony_ci	pqi_init_firmware_features();
70328c2ecf20Sopenharmony_ci	pqi_process_firmware_features(section_info);
70338c2ecf20Sopenharmony_ci	mutex_unlock(&pqi_firmware_features_mutex);
70348c2ecf20Sopenharmony_ci}
70358c2ecf20Sopenharmony_ci
70368c2ecf20Sopenharmony_cistatic int pqi_process_config_table(struct pqi_ctrl_info *ctrl_info)
70378c2ecf20Sopenharmony_ci{
70388c2ecf20Sopenharmony_ci	u32 table_length;
70398c2ecf20Sopenharmony_ci	u32 section_offset;
70408c2ecf20Sopenharmony_ci	void __iomem *table_iomem_addr;
70418c2ecf20Sopenharmony_ci	struct pqi_config_table *config_table;
70428c2ecf20Sopenharmony_ci	struct pqi_config_table_section_header *section;
70438c2ecf20Sopenharmony_ci	struct pqi_config_table_section_info section_info;
70448c2ecf20Sopenharmony_ci
70458c2ecf20Sopenharmony_ci	table_length = ctrl_info->config_table_length;
70468c2ecf20Sopenharmony_ci	if (table_length == 0)
70478c2ecf20Sopenharmony_ci		return 0;
70488c2ecf20Sopenharmony_ci
70498c2ecf20Sopenharmony_ci	config_table = kmalloc(table_length, GFP_KERNEL);
70508c2ecf20Sopenharmony_ci	if (!config_table) {
70518c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
70528c2ecf20Sopenharmony_ci			"failed to allocate memory for PQI configuration table\n");
70538c2ecf20Sopenharmony_ci		return -ENOMEM;
70548c2ecf20Sopenharmony_ci	}
70558c2ecf20Sopenharmony_ci
70568c2ecf20Sopenharmony_ci	/*
70578c2ecf20Sopenharmony_ci	 * Copy the config table contents from I/O memory space into the
70588c2ecf20Sopenharmony_ci	 * temporary buffer.
70598c2ecf20Sopenharmony_ci	 */
70608c2ecf20Sopenharmony_ci	table_iomem_addr = ctrl_info->iomem_base +
70618c2ecf20Sopenharmony_ci		ctrl_info->config_table_offset;
70628c2ecf20Sopenharmony_ci	memcpy_fromio(config_table, table_iomem_addr, table_length);
70638c2ecf20Sopenharmony_ci
70648c2ecf20Sopenharmony_ci	section_info.ctrl_info = ctrl_info;
70658c2ecf20Sopenharmony_ci	section_offset =
70668c2ecf20Sopenharmony_ci		get_unaligned_le32(&config_table->first_section_offset);
70678c2ecf20Sopenharmony_ci
70688c2ecf20Sopenharmony_ci	while (section_offset) {
70698c2ecf20Sopenharmony_ci		section = (void *)config_table + section_offset;
70708c2ecf20Sopenharmony_ci
70718c2ecf20Sopenharmony_ci		section_info.section = section;
70728c2ecf20Sopenharmony_ci		section_info.section_offset = section_offset;
70738c2ecf20Sopenharmony_ci		section_info.section_iomem_addr =
70748c2ecf20Sopenharmony_ci			table_iomem_addr + section_offset;
70758c2ecf20Sopenharmony_ci
70768c2ecf20Sopenharmony_ci		switch (get_unaligned_le16(&section->section_id)) {
70778c2ecf20Sopenharmony_ci		case PQI_CONFIG_TABLE_SECTION_FIRMWARE_FEATURES:
70788c2ecf20Sopenharmony_ci			pqi_process_firmware_features_section(&section_info);
70798c2ecf20Sopenharmony_ci			break;
70808c2ecf20Sopenharmony_ci		case PQI_CONFIG_TABLE_SECTION_HEARTBEAT:
70818c2ecf20Sopenharmony_ci			if (pqi_disable_heartbeat)
70828c2ecf20Sopenharmony_ci				dev_warn(&ctrl_info->pci_dev->dev,
70838c2ecf20Sopenharmony_ci				"heartbeat disabled by module parameter\n");
70848c2ecf20Sopenharmony_ci			else
70858c2ecf20Sopenharmony_ci				ctrl_info->heartbeat_counter =
70868c2ecf20Sopenharmony_ci					table_iomem_addr +
70878c2ecf20Sopenharmony_ci					section_offset +
70888c2ecf20Sopenharmony_ci					offsetof(
70898c2ecf20Sopenharmony_ci					struct pqi_config_table_heartbeat,
70908c2ecf20Sopenharmony_ci						heartbeat_counter);
70918c2ecf20Sopenharmony_ci			break;
70928c2ecf20Sopenharmony_ci		case PQI_CONFIG_TABLE_SECTION_SOFT_RESET:
70938c2ecf20Sopenharmony_ci			ctrl_info->soft_reset_status =
70948c2ecf20Sopenharmony_ci				table_iomem_addr +
70958c2ecf20Sopenharmony_ci				section_offset +
70968c2ecf20Sopenharmony_ci				offsetof(struct pqi_config_table_soft_reset,
70978c2ecf20Sopenharmony_ci						soft_reset_status);
70988c2ecf20Sopenharmony_ci			break;
70998c2ecf20Sopenharmony_ci		}
71008c2ecf20Sopenharmony_ci
71018c2ecf20Sopenharmony_ci		section_offset =
71028c2ecf20Sopenharmony_ci			get_unaligned_le16(&section->next_section_offset);
71038c2ecf20Sopenharmony_ci	}
71048c2ecf20Sopenharmony_ci
71058c2ecf20Sopenharmony_ci	kfree(config_table);
71068c2ecf20Sopenharmony_ci
71078c2ecf20Sopenharmony_ci	return 0;
71088c2ecf20Sopenharmony_ci}
71098c2ecf20Sopenharmony_ci
71108c2ecf20Sopenharmony_ci/* Switches the controller from PQI mode back into SIS mode. */
71118c2ecf20Sopenharmony_ci
71128c2ecf20Sopenharmony_cistatic int pqi_revert_to_sis_mode(struct pqi_ctrl_info *ctrl_info)
71138c2ecf20Sopenharmony_ci{
71148c2ecf20Sopenharmony_ci	int rc;
71158c2ecf20Sopenharmony_ci
71168c2ecf20Sopenharmony_ci	pqi_change_irq_mode(ctrl_info, IRQ_MODE_NONE);
71178c2ecf20Sopenharmony_ci	rc = pqi_reset(ctrl_info);
71188c2ecf20Sopenharmony_ci	if (rc)
71198c2ecf20Sopenharmony_ci		return rc;
71208c2ecf20Sopenharmony_ci	rc = sis_reenable_sis_mode(ctrl_info);
71218c2ecf20Sopenharmony_ci	if (rc) {
71228c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
71238c2ecf20Sopenharmony_ci			"re-enabling SIS mode failed with error %d\n", rc);
71248c2ecf20Sopenharmony_ci		return rc;
71258c2ecf20Sopenharmony_ci	}
71268c2ecf20Sopenharmony_ci	pqi_save_ctrl_mode(ctrl_info, SIS_MODE);
71278c2ecf20Sopenharmony_ci
71288c2ecf20Sopenharmony_ci	return 0;
71298c2ecf20Sopenharmony_ci}
71308c2ecf20Sopenharmony_ci
71318c2ecf20Sopenharmony_ci/*
71328c2ecf20Sopenharmony_ci * If the controller isn't already in SIS mode, this function forces it into
71338c2ecf20Sopenharmony_ci * SIS mode.
71348c2ecf20Sopenharmony_ci */
71358c2ecf20Sopenharmony_ci
71368c2ecf20Sopenharmony_cistatic int pqi_force_sis_mode(struct pqi_ctrl_info *ctrl_info)
71378c2ecf20Sopenharmony_ci{
71388c2ecf20Sopenharmony_ci	if (!sis_is_firmware_running(ctrl_info))
71398c2ecf20Sopenharmony_ci		return -ENXIO;
71408c2ecf20Sopenharmony_ci
71418c2ecf20Sopenharmony_ci	if (pqi_get_ctrl_mode(ctrl_info) == SIS_MODE)
71428c2ecf20Sopenharmony_ci		return 0;
71438c2ecf20Sopenharmony_ci
71448c2ecf20Sopenharmony_ci	if (sis_is_kernel_up(ctrl_info)) {
71458c2ecf20Sopenharmony_ci		pqi_save_ctrl_mode(ctrl_info, SIS_MODE);
71468c2ecf20Sopenharmony_ci		return 0;
71478c2ecf20Sopenharmony_ci	}
71488c2ecf20Sopenharmony_ci
71498c2ecf20Sopenharmony_ci	return pqi_revert_to_sis_mode(ctrl_info);
71508c2ecf20Sopenharmony_ci}
71518c2ecf20Sopenharmony_ci
71528c2ecf20Sopenharmony_ci#define PQI_POST_RESET_DELAY_B4_MSGU_READY	5000
71538c2ecf20Sopenharmony_ci
71548c2ecf20Sopenharmony_cistatic int pqi_ctrl_init(struct pqi_ctrl_info *ctrl_info)
71558c2ecf20Sopenharmony_ci{
71568c2ecf20Sopenharmony_ci	int rc;
71578c2ecf20Sopenharmony_ci
71588c2ecf20Sopenharmony_ci	if (reset_devices) {
71598c2ecf20Sopenharmony_ci		sis_soft_reset(ctrl_info);
71608c2ecf20Sopenharmony_ci		msleep(PQI_POST_RESET_DELAY_B4_MSGU_READY);
71618c2ecf20Sopenharmony_ci	} else {
71628c2ecf20Sopenharmony_ci		rc = pqi_force_sis_mode(ctrl_info);
71638c2ecf20Sopenharmony_ci		if (rc)
71648c2ecf20Sopenharmony_ci			return rc;
71658c2ecf20Sopenharmony_ci	}
71668c2ecf20Sopenharmony_ci
71678c2ecf20Sopenharmony_ci	/*
71688c2ecf20Sopenharmony_ci	 * Wait until the controller is ready to start accepting SIS
71698c2ecf20Sopenharmony_ci	 * commands.
71708c2ecf20Sopenharmony_ci	 */
71718c2ecf20Sopenharmony_ci	rc = sis_wait_for_ctrl_ready(ctrl_info);
71728c2ecf20Sopenharmony_ci	if (rc)
71738c2ecf20Sopenharmony_ci		return rc;
71748c2ecf20Sopenharmony_ci
71758c2ecf20Sopenharmony_ci	/*
71768c2ecf20Sopenharmony_ci	 * Get the controller properties.  This allows us to determine
71778c2ecf20Sopenharmony_ci	 * whether or not it supports PQI mode.
71788c2ecf20Sopenharmony_ci	 */
71798c2ecf20Sopenharmony_ci	rc = sis_get_ctrl_properties(ctrl_info);
71808c2ecf20Sopenharmony_ci	if (rc) {
71818c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
71828c2ecf20Sopenharmony_ci			"error obtaining controller properties\n");
71838c2ecf20Sopenharmony_ci		return rc;
71848c2ecf20Sopenharmony_ci	}
71858c2ecf20Sopenharmony_ci
71868c2ecf20Sopenharmony_ci	rc = sis_get_pqi_capabilities(ctrl_info);
71878c2ecf20Sopenharmony_ci	if (rc) {
71888c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
71898c2ecf20Sopenharmony_ci			"error obtaining controller capabilities\n");
71908c2ecf20Sopenharmony_ci		return rc;
71918c2ecf20Sopenharmony_ci	}
71928c2ecf20Sopenharmony_ci
71938c2ecf20Sopenharmony_ci	if (reset_devices) {
71948c2ecf20Sopenharmony_ci		if (ctrl_info->max_outstanding_requests >
71958c2ecf20Sopenharmony_ci			PQI_MAX_OUTSTANDING_REQUESTS_KDUMP)
71968c2ecf20Sopenharmony_ci			ctrl_info->max_outstanding_requests =
71978c2ecf20Sopenharmony_ci					PQI_MAX_OUTSTANDING_REQUESTS_KDUMP;
71988c2ecf20Sopenharmony_ci	} else {
71998c2ecf20Sopenharmony_ci		if (ctrl_info->max_outstanding_requests >
72008c2ecf20Sopenharmony_ci			PQI_MAX_OUTSTANDING_REQUESTS)
72018c2ecf20Sopenharmony_ci			ctrl_info->max_outstanding_requests =
72028c2ecf20Sopenharmony_ci					PQI_MAX_OUTSTANDING_REQUESTS;
72038c2ecf20Sopenharmony_ci	}
72048c2ecf20Sopenharmony_ci
72058c2ecf20Sopenharmony_ci	pqi_calculate_io_resources(ctrl_info);
72068c2ecf20Sopenharmony_ci
72078c2ecf20Sopenharmony_ci	rc = pqi_alloc_error_buffer(ctrl_info);
72088c2ecf20Sopenharmony_ci	if (rc) {
72098c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72108c2ecf20Sopenharmony_ci			"failed to allocate PQI error buffer\n");
72118c2ecf20Sopenharmony_ci		return rc;
72128c2ecf20Sopenharmony_ci	}
72138c2ecf20Sopenharmony_ci
72148c2ecf20Sopenharmony_ci	/*
72158c2ecf20Sopenharmony_ci	 * If the function we are about to call succeeds, the
72168c2ecf20Sopenharmony_ci	 * controller will transition from legacy SIS mode
72178c2ecf20Sopenharmony_ci	 * into PQI mode.
72188c2ecf20Sopenharmony_ci	 */
72198c2ecf20Sopenharmony_ci	rc = sis_init_base_struct_addr(ctrl_info);
72208c2ecf20Sopenharmony_ci	if (rc) {
72218c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72228c2ecf20Sopenharmony_ci			"error initializing PQI mode\n");
72238c2ecf20Sopenharmony_ci		return rc;
72248c2ecf20Sopenharmony_ci	}
72258c2ecf20Sopenharmony_ci
72268c2ecf20Sopenharmony_ci	/* Wait for the controller to complete the SIS -> PQI transition. */
72278c2ecf20Sopenharmony_ci	rc = pqi_wait_for_pqi_mode_ready(ctrl_info);
72288c2ecf20Sopenharmony_ci	if (rc) {
72298c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72308c2ecf20Sopenharmony_ci			"transition to PQI mode failed\n");
72318c2ecf20Sopenharmony_ci		return rc;
72328c2ecf20Sopenharmony_ci	}
72338c2ecf20Sopenharmony_ci
72348c2ecf20Sopenharmony_ci	/* From here on, we are running in PQI mode. */
72358c2ecf20Sopenharmony_ci	ctrl_info->pqi_mode_enabled = true;
72368c2ecf20Sopenharmony_ci	pqi_save_ctrl_mode(ctrl_info, PQI_MODE);
72378c2ecf20Sopenharmony_ci
72388c2ecf20Sopenharmony_ci	rc = pqi_alloc_admin_queues(ctrl_info);
72398c2ecf20Sopenharmony_ci	if (rc) {
72408c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72418c2ecf20Sopenharmony_ci			"failed to allocate admin queues\n");
72428c2ecf20Sopenharmony_ci		return rc;
72438c2ecf20Sopenharmony_ci	}
72448c2ecf20Sopenharmony_ci
72458c2ecf20Sopenharmony_ci	rc = pqi_create_admin_queues(ctrl_info);
72468c2ecf20Sopenharmony_ci	if (rc) {
72478c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72488c2ecf20Sopenharmony_ci			"error creating admin queues\n");
72498c2ecf20Sopenharmony_ci		return rc;
72508c2ecf20Sopenharmony_ci	}
72518c2ecf20Sopenharmony_ci
72528c2ecf20Sopenharmony_ci	rc = pqi_report_device_capability(ctrl_info);
72538c2ecf20Sopenharmony_ci	if (rc) {
72548c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72558c2ecf20Sopenharmony_ci			"obtaining device capability failed\n");
72568c2ecf20Sopenharmony_ci		return rc;
72578c2ecf20Sopenharmony_ci	}
72588c2ecf20Sopenharmony_ci
72598c2ecf20Sopenharmony_ci	rc = pqi_validate_device_capability(ctrl_info);
72608c2ecf20Sopenharmony_ci	if (rc)
72618c2ecf20Sopenharmony_ci		return rc;
72628c2ecf20Sopenharmony_ci
72638c2ecf20Sopenharmony_ci	pqi_calculate_queue_resources(ctrl_info);
72648c2ecf20Sopenharmony_ci
72658c2ecf20Sopenharmony_ci	rc = pqi_enable_msix_interrupts(ctrl_info);
72668c2ecf20Sopenharmony_ci	if (rc)
72678c2ecf20Sopenharmony_ci		return rc;
72688c2ecf20Sopenharmony_ci
72698c2ecf20Sopenharmony_ci	if (ctrl_info->num_msix_vectors_enabled < ctrl_info->num_queue_groups) {
72708c2ecf20Sopenharmony_ci		ctrl_info->max_msix_vectors =
72718c2ecf20Sopenharmony_ci			ctrl_info->num_msix_vectors_enabled;
72728c2ecf20Sopenharmony_ci		pqi_calculate_queue_resources(ctrl_info);
72738c2ecf20Sopenharmony_ci	}
72748c2ecf20Sopenharmony_ci
72758c2ecf20Sopenharmony_ci	rc = pqi_alloc_io_resources(ctrl_info);
72768c2ecf20Sopenharmony_ci	if (rc)
72778c2ecf20Sopenharmony_ci		return rc;
72788c2ecf20Sopenharmony_ci
72798c2ecf20Sopenharmony_ci	rc = pqi_alloc_operational_queues(ctrl_info);
72808c2ecf20Sopenharmony_ci	if (rc) {
72818c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
72828c2ecf20Sopenharmony_ci			"failed to allocate operational queues\n");
72838c2ecf20Sopenharmony_ci		return rc;
72848c2ecf20Sopenharmony_ci	}
72858c2ecf20Sopenharmony_ci
72868c2ecf20Sopenharmony_ci	pqi_init_operational_queues(ctrl_info);
72878c2ecf20Sopenharmony_ci
72888c2ecf20Sopenharmony_ci	rc = pqi_request_irqs(ctrl_info);
72898c2ecf20Sopenharmony_ci	if (rc)
72908c2ecf20Sopenharmony_ci		return rc;
72918c2ecf20Sopenharmony_ci
72928c2ecf20Sopenharmony_ci	rc = pqi_create_queues(ctrl_info);
72938c2ecf20Sopenharmony_ci	if (rc)
72948c2ecf20Sopenharmony_ci		return rc;
72958c2ecf20Sopenharmony_ci
72968c2ecf20Sopenharmony_ci	pqi_change_irq_mode(ctrl_info, IRQ_MODE_MSIX);
72978c2ecf20Sopenharmony_ci
72988c2ecf20Sopenharmony_ci	ctrl_info->controller_online = true;
72998c2ecf20Sopenharmony_ci
73008c2ecf20Sopenharmony_ci	rc = pqi_process_config_table(ctrl_info);
73018c2ecf20Sopenharmony_ci	if (rc)
73028c2ecf20Sopenharmony_ci		return rc;
73038c2ecf20Sopenharmony_ci
73048c2ecf20Sopenharmony_ci	pqi_start_heartbeat_timer(ctrl_info);
73058c2ecf20Sopenharmony_ci
73068c2ecf20Sopenharmony_ci	rc = pqi_enable_events(ctrl_info);
73078c2ecf20Sopenharmony_ci	if (rc) {
73088c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
73098c2ecf20Sopenharmony_ci			"error enabling events\n");
73108c2ecf20Sopenharmony_ci		return rc;
73118c2ecf20Sopenharmony_ci	}
73128c2ecf20Sopenharmony_ci
73138c2ecf20Sopenharmony_ci	/* Register with the SCSI subsystem. */
73148c2ecf20Sopenharmony_ci	rc = pqi_register_scsi(ctrl_info);
73158c2ecf20Sopenharmony_ci	if (rc)
73168c2ecf20Sopenharmony_ci		return rc;
73178c2ecf20Sopenharmony_ci
73188c2ecf20Sopenharmony_ci	rc = pqi_get_ctrl_product_details(ctrl_info);
73198c2ecf20Sopenharmony_ci	if (rc) {
73208c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
73218c2ecf20Sopenharmony_ci			"error obtaining product details\n");
73228c2ecf20Sopenharmony_ci		return rc;
73238c2ecf20Sopenharmony_ci	}
73248c2ecf20Sopenharmony_ci
73258c2ecf20Sopenharmony_ci	rc = pqi_get_ctrl_serial_number(ctrl_info);
73268c2ecf20Sopenharmony_ci	if (rc) {
73278c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
73288c2ecf20Sopenharmony_ci			"error obtaining ctrl serial number\n");
73298c2ecf20Sopenharmony_ci		return rc;
73308c2ecf20Sopenharmony_ci	}
73318c2ecf20Sopenharmony_ci
73328c2ecf20Sopenharmony_ci	rc = pqi_set_diag_rescan(ctrl_info);
73338c2ecf20Sopenharmony_ci	if (rc) {
73348c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
73358c2ecf20Sopenharmony_ci			"error enabling multi-lun rescan\n");
73368c2ecf20Sopenharmony_ci		return rc;
73378c2ecf20Sopenharmony_ci	}
73388c2ecf20Sopenharmony_ci
73398c2ecf20Sopenharmony_ci	rc = pqi_write_driver_version_to_host_wellness(ctrl_info);
73408c2ecf20Sopenharmony_ci	if (rc) {
73418c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
73428c2ecf20Sopenharmony_ci			"error updating host wellness\n");
73438c2ecf20Sopenharmony_ci		return rc;
73448c2ecf20Sopenharmony_ci	}
73458c2ecf20Sopenharmony_ci
73468c2ecf20Sopenharmony_ci	pqi_schedule_update_time_worker(ctrl_info);
73478c2ecf20Sopenharmony_ci
73488c2ecf20Sopenharmony_ci	pqi_scan_scsi_devices(ctrl_info);
73498c2ecf20Sopenharmony_ci
73508c2ecf20Sopenharmony_ci	return 0;
73518c2ecf20Sopenharmony_ci}
73528c2ecf20Sopenharmony_ci
73538c2ecf20Sopenharmony_cistatic void pqi_reinit_queues(struct pqi_ctrl_info *ctrl_info)
73548c2ecf20Sopenharmony_ci{
73558c2ecf20Sopenharmony_ci	unsigned int i;
73568c2ecf20Sopenharmony_ci	struct pqi_admin_queues *admin_queues;
73578c2ecf20Sopenharmony_ci	struct pqi_event_queue *event_queue;
73588c2ecf20Sopenharmony_ci
73598c2ecf20Sopenharmony_ci	admin_queues = &ctrl_info->admin_queues;
73608c2ecf20Sopenharmony_ci	admin_queues->iq_pi_copy = 0;
73618c2ecf20Sopenharmony_ci	admin_queues->oq_ci_copy = 0;
73628c2ecf20Sopenharmony_ci	writel(0, admin_queues->oq_pi);
73638c2ecf20Sopenharmony_ci
73648c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->num_queue_groups; i++) {
73658c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].iq_pi_copy[RAID_PATH] = 0;
73668c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].iq_pi_copy[AIO_PATH] = 0;
73678c2ecf20Sopenharmony_ci		ctrl_info->queue_groups[i].oq_ci_copy = 0;
73688c2ecf20Sopenharmony_ci
73698c2ecf20Sopenharmony_ci		writel(0, ctrl_info->queue_groups[i].iq_ci[RAID_PATH]);
73708c2ecf20Sopenharmony_ci		writel(0, ctrl_info->queue_groups[i].iq_ci[AIO_PATH]);
73718c2ecf20Sopenharmony_ci		writel(0, ctrl_info->queue_groups[i].oq_pi);
73728c2ecf20Sopenharmony_ci	}
73738c2ecf20Sopenharmony_ci
73748c2ecf20Sopenharmony_ci	event_queue = &ctrl_info->event_queue;
73758c2ecf20Sopenharmony_ci	writel(0, event_queue->oq_pi);
73768c2ecf20Sopenharmony_ci	event_queue->oq_ci_copy = 0;
73778c2ecf20Sopenharmony_ci}
73788c2ecf20Sopenharmony_ci
73798c2ecf20Sopenharmony_cistatic int pqi_ctrl_init_resume(struct pqi_ctrl_info *ctrl_info)
73808c2ecf20Sopenharmony_ci{
73818c2ecf20Sopenharmony_ci	int rc;
73828c2ecf20Sopenharmony_ci
73838c2ecf20Sopenharmony_ci	rc = pqi_force_sis_mode(ctrl_info);
73848c2ecf20Sopenharmony_ci	if (rc)
73858c2ecf20Sopenharmony_ci		return rc;
73868c2ecf20Sopenharmony_ci
73878c2ecf20Sopenharmony_ci	/*
73888c2ecf20Sopenharmony_ci	 * Wait until the controller is ready to start accepting SIS
73898c2ecf20Sopenharmony_ci	 * commands.
73908c2ecf20Sopenharmony_ci	 */
73918c2ecf20Sopenharmony_ci	rc = sis_wait_for_ctrl_ready_resume(ctrl_info);
73928c2ecf20Sopenharmony_ci	if (rc)
73938c2ecf20Sopenharmony_ci		return rc;
73948c2ecf20Sopenharmony_ci
73958c2ecf20Sopenharmony_ci	/*
73968c2ecf20Sopenharmony_ci	 * Get the controller properties.  This allows us to determine
73978c2ecf20Sopenharmony_ci	 * whether or not it supports PQI mode.
73988c2ecf20Sopenharmony_ci	 */
73998c2ecf20Sopenharmony_ci	rc = sis_get_ctrl_properties(ctrl_info);
74008c2ecf20Sopenharmony_ci	if (rc) {
74018c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74028c2ecf20Sopenharmony_ci			"error obtaining controller properties\n");
74038c2ecf20Sopenharmony_ci		return rc;
74048c2ecf20Sopenharmony_ci	}
74058c2ecf20Sopenharmony_ci
74068c2ecf20Sopenharmony_ci	rc = sis_get_pqi_capabilities(ctrl_info);
74078c2ecf20Sopenharmony_ci	if (rc) {
74088c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74098c2ecf20Sopenharmony_ci			"error obtaining controller capabilities\n");
74108c2ecf20Sopenharmony_ci		return rc;
74118c2ecf20Sopenharmony_ci	}
74128c2ecf20Sopenharmony_ci
74138c2ecf20Sopenharmony_ci	/*
74148c2ecf20Sopenharmony_ci	 * If the function we are about to call succeeds, the
74158c2ecf20Sopenharmony_ci	 * controller will transition from legacy SIS mode
74168c2ecf20Sopenharmony_ci	 * into PQI mode.
74178c2ecf20Sopenharmony_ci	 */
74188c2ecf20Sopenharmony_ci	rc = sis_init_base_struct_addr(ctrl_info);
74198c2ecf20Sopenharmony_ci	if (rc) {
74208c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74218c2ecf20Sopenharmony_ci			"error initializing PQI mode\n");
74228c2ecf20Sopenharmony_ci		return rc;
74238c2ecf20Sopenharmony_ci	}
74248c2ecf20Sopenharmony_ci
74258c2ecf20Sopenharmony_ci	/* Wait for the controller to complete the SIS -> PQI transition. */
74268c2ecf20Sopenharmony_ci	rc = pqi_wait_for_pqi_mode_ready(ctrl_info);
74278c2ecf20Sopenharmony_ci	if (rc) {
74288c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74298c2ecf20Sopenharmony_ci			"transition to PQI mode failed\n");
74308c2ecf20Sopenharmony_ci		return rc;
74318c2ecf20Sopenharmony_ci	}
74328c2ecf20Sopenharmony_ci
74338c2ecf20Sopenharmony_ci	/* From here on, we are running in PQI mode. */
74348c2ecf20Sopenharmony_ci	ctrl_info->pqi_mode_enabled = true;
74358c2ecf20Sopenharmony_ci	pqi_save_ctrl_mode(ctrl_info, PQI_MODE);
74368c2ecf20Sopenharmony_ci
74378c2ecf20Sopenharmony_ci	pqi_reinit_queues(ctrl_info);
74388c2ecf20Sopenharmony_ci
74398c2ecf20Sopenharmony_ci	rc = pqi_create_admin_queues(ctrl_info);
74408c2ecf20Sopenharmony_ci	if (rc) {
74418c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74428c2ecf20Sopenharmony_ci			"error creating admin queues\n");
74438c2ecf20Sopenharmony_ci		return rc;
74448c2ecf20Sopenharmony_ci	}
74458c2ecf20Sopenharmony_ci
74468c2ecf20Sopenharmony_ci	rc = pqi_create_queues(ctrl_info);
74478c2ecf20Sopenharmony_ci	if (rc)
74488c2ecf20Sopenharmony_ci		return rc;
74498c2ecf20Sopenharmony_ci
74508c2ecf20Sopenharmony_ci	pqi_change_irq_mode(ctrl_info, IRQ_MODE_MSIX);
74518c2ecf20Sopenharmony_ci
74528c2ecf20Sopenharmony_ci	ctrl_info->controller_online = true;
74538c2ecf20Sopenharmony_ci	pqi_ctrl_unblock_requests(ctrl_info);
74548c2ecf20Sopenharmony_ci
74558c2ecf20Sopenharmony_ci	rc = pqi_process_config_table(ctrl_info);
74568c2ecf20Sopenharmony_ci	if (rc)
74578c2ecf20Sopenharmony_ci		return rc;
74588c2ecf20Sopenharmony_ci
74598c2ecf20Sopenharmony_ci	pqi_start_heartbeat_timer(ctrl_info);
74608c2ecf20Sopenharmony_ci
74618c2ecf20Sopenharmony_ci	rc = pqi_enable_events(ctrl_info);
74628c2ecf20Sopenharmony_ci	if (rc) {
74638c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74648c2ecf20Sopenharmony_ci			"error enabling events\n");
74658c2ecf20Sopenharmony_ci		return rc;
74668c2ecf20Sopenharmony_ci	}
74678c2ecf20Sopenharmony_ci
74688c2ecf20Sopenharmony_ci	rc = pqi_get_ctrl_product_details(ctrl_info);
74698c2ecf20Sopenharmony_ci	if (rc) {
74708c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74718c2ecf20Sopenharmony_ci			"error obtaining product details\n");
74728c2ecf20Sopenharmony_ci		return rc;
74738c2ecf20Sopenharmony_ci	}
74748c2ecf20Sopenharmony_ci
74758c2ecf20Sopenharmony_ci	rc = pqi_set_diag_rescan(ctrl_info);
74768c2ecf20Sopenharmony_ci	if (rc) {
74778c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74788c2ecf20Sopenharmony_ci			"error enabling multi-lun rescan\n");
74798c2ecf20Sopenharmony_ci		return rc;
74808c2ecf20Sopenharmony_ci	}
74818c2ecf20Sopenharmony_ci
74828c2ecf20Sopenharmony_ci	rc = pqi_write_driver_version_to_host_wellness(ctrl_info);
74838c2ecf20Sopenharmony_ci	if (rc) {
74848c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
74858c2ecf20Sopenharmony_ci			"error updating host wellness\n");
74868c2ecf20Sopenharmony_ci		return rc;
74878c2ecf20Sopenharmony_ci	}
74888c2ecf20Sopenharmony_ci
74898c2ecf20Sopenharmony_ci	pqi_schedule_update_time_worker(ctrl_info);
74908c2ecf20Sopenharmony_ci
74918c2ecf20Sopenharmony_ci	pqi_scan_scsi_devices(ctrl_info);
74928c2ecf20Sopenharmony_ci
74938c2ecf20Sopenharmony_ci	return 0;
74948c2ecf20Sopenharmony_ci}
74958c2ecf20Sopenharmony_ci
74968c2ecf20Sopenharmony_cistatic inline int pqi_set_pcie_completion_timeout(struct pci_dev *pci_dev,
74978c2ecf20Sopenharmony_ci	u16 timeout)
74988c2ecf20Sopenharmony_ci{
74998c2ecf20Sopenharmony_ci	int rc;
75008c2ecf20Sopenharmony_ci
75018c2ecf20Sopenharmony_ci	rc = pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL2,
75028c2ecf20Sopenharmony_ci		PCI_EXP_DEVCTL2_COMP_TIMEOUT, timeout);
75038c2ecf20Sopenharmony_ci
75048c2ecf20Sopenharmony_ci	return pcibios_err_to_errno(rc);
75058c2ecf20Sopenharmony_ci}
75068c2ecf20Sopenharmony_ci
75078c2ecf20Sopenharmony_cistatic int pqi_pci_init(struct pqi_ctrl_info *ctrl_info)
75088c2ecf20Sopenharmony_ci{
75098c2ecf20Sopenharmony_ci	int rc;
75108c2ecf20Sopenharmony_ci	u64 mask;
75118c2ecf20Sopenharmony_ci
75128c2ecf20Sopenharmony_ci	rc = pci_enable_device(ctrl_info->pci_dev);
75138c2ecf20Sopenharmony_ci	if (rc) {
75148c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
75158c2ecf20Sopenharmony_ci			"failed to enable PCI device\n");
75168c2ecf20Sopenharmony_ci		return rc;
75178c2ecf20Sopenharmony_ci	}
75188c2ecf20Sopenharmony_ci
75198c2ecf20Sopenharmony_ci	if (sizeof(dma_addr_t) > 4)
75208c2ecf20Sopenharmony_ci		mask = DMA_BIT_MASK(64);
75218c2ecf20Sopenharmony_ci	else
75228c2ecf20Sopenharmony_ci		mask = DMA_BIT_MASK(32);
75238c2ecf20Sopenharmony_ci
75248c2ecf20Sopenharmony_ci	rc = dma_set_mask_and_coherent(&ctrl_info->pci_dev->dev, mask);
75258c2ecf20Sopenharmony_ci	if (rc) {
75268c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev, "failed to set DMA mask\n");
75278c2ecf20Sopenharmony_ci		goto disable_device;
75288c2ecf20Sopenharmony_ci	}
75298c2ecf20Sopenharmony_ci
75308c2ecf20Sopenharmony_ci	rc = pci_request_regions(ctrl_info->pci_dev, DRIVER_NAME_SHORT);
75318c2ecf20Sopenharmony_ci	if (rc) {
75328c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
75338c2ecf20Sopenharmony_ci			"failed to obtain PCI resources\n");
75348c2ecf20Sopenharmony_ci		goto disable_device;
75358c2ecf20Sopenharmony_ci	}
75368c2ecf20Sopenharmony_ci
75378c2ecf20Sopenharmony_ci	ctrl_info->iomem_base = ioremap(pci_resource_start(
75388c2ecf20Sopenharmony_ci		ctrl_info->pci_dev, 0),
75398c2ecf20Sopenharmony_ci		sizeof(struct pqi_ctrl_registers));
75408c2ecf20Sopenharmony_ci	if (!ctrl_info->iomem_base) {
75418c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
75428c2ecf20Sopenharmony_ci			"failed to map memory for controller registers\n");
75438c2ecf20Sopenharmony_ci		rc = -ENOMEM;
75448c2ecf20Sopenharmony_ci		goto release_regions;
75458c2ecf20Sopenharmony_ci	}
75468c2ecf20Sopenharmony_ci
75478c2ecf20Sopenharmony_ci#define PCI_EXP_COMP_TIMEOUT_65_TO_210_MS		0x6
75488c2ecf20Sopenharmony_ci
75498c2ecf20Sopenharmony_ci	/* Increase the PCIe completion timeout. */
75508c2ecf20Sopenharmony_ci	rc = pqi_set_pcie_completion_timeout(ctrl_info->pci_dev,
75518c2ecf20Sopenharmony_ci		PCI_EXP_COMP_TIMEOUT_65_TO_210_MS);
75528c2ecf20Sopenharmony_ci	if (rc) {
75538c2ecf20Sopenharmony_ci		dev_err(&ctrl_info->pci_dev->dev,
75548c2ecf20Sopenharmony_ci			"failed to set PCIe completion timeout\n");
75558c2ecf20Sopenharmony_ci		goto release_regions;
75568c2ecf20Sopenharmony_ci	}
75578c2ecf20Sopenharmony_ci
75588c2ecf20Sopenharmony_ci	/* Enable bus mastering. */
75598c2ecf20Sopenharmony_ci	pci_set_master(ctrl_info->pci_dev);
75608c2ecf20Sopenharmony_ci
75618c2ecf20Sopenharmony_ci	ctrl_info->registers = ctrl_info->iomem_base;
75628c2ecf20Sopenharmony_ci	ctrl_info->pqi_registers = &ctrl_info->registers->pqi_registers;
75638c2ecf20Sopenharmony_ci
75648c2ecf20Sopenharmony_ci	pci_set_drvdata(ctrl_info->pci_dev, ctrl_info);
75658c2ecf20Sopenharmony_ci
75668c2ecf20Sopenharmony_ci	return 0;
75678c2ecf20Sopenharmony_ci
75688c2ecf20Sopenharmony_cirelease_regions:
75698c2ecf20Sopenharmony_ci	pci_release_regions(ctrl_info->pci_dev);
75708c2ecf20Sopenharmony_cidisable_device:
75718c2ecf20Sopenharmony_ci	pci_disable_device(ctrl_info->pci_dev);
75728c2ecf20Sopenharmony_ci
75738c2ecf20Sopenharmony_ci	return rc;
75748c2ecf20Sopenharmony_ci}
75758c2ecf20Sopenharmony_ci
75768c2ecf20Sopenharmony_cistatic void pqi_cleanup_pci_init(struct pqi_ctrl_info *ctrl_info)
75778c2ecf20Sopenharmony_ci{
75788c2ecf20Sopenharmony_ci	iounmap(ctrl_info->iomem_base);
75798c2ecf20Sopenharmony_ci	pci_release_regions(ctrl_info->pci_dev);
75808c2ecf20Sopenharmony_ci	if (pci_is_enabled(ctrl_info->pci_dev))
75818c2ecf20Sopenharmony_ci		pci_disable_device(ctrl_info->pci_dev);
75828c2ecf20Sopenharmony_ci	pci_set_drvdata(ctrl_info->pci_dev, NULL);
75838c2ecf20Sopenharmony_ci}
75848c2ecf20Sopenharmony_ci
75858c2ecf20Sopenharmony_cistatic struct pqi_ctrl_info *pqi_alloc_ctrl_info(int numa_node)
75868c2ecf20Sopenharmony_ci{
75878c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
75888c2ecf20Sopenharmony_ci
75898c2ecf20Sopenharmony_ci	ctrl_info = kzalloc_node(sizeof(struct pqi_ctrl_info),
75908c2ecf20Sopenharmony_ci			GFP_KERNEL, numa_node);
75918c2ecf20Sopenharmony_ci	if (!ctrl_info)
75928c2ecf20Sopenharmony_ci		return NULL;
75938c2ecf20Sopenharmony_ci
75948c2ecf20Sopenharmony_ci	mutex_init(&ctrl_info->scan_mutex);
75958c2ecf20Sopenharmony_ci	mutex_init(&ctrl_info->lun_reset_mutex);
75968c2ecf20Sopenharmony_ci	mutex_init(&ctrl_info->ofa_mutex);
75978c2ecf20Sopenharmony_ci
75988c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctrl_info->scsi_device_list);
75998c2ecf20Sopenharmony_ci	spin_lock_init(&ctrl_info->scsi_device_list_lock);
76008c2ecf20Sopenharmony_ci
76018c2ecf20Sopenharmony_ci	INIT_WORK(&ctrl_info->event_work, pqi_event_worker);
76028c2ecf20Sopenharmony_ci	atomic_set(&ctrl_info->num_interrupts, 0);
76038c2ecf20Sopenharmony_ci	atomic_set(&ctrl_info->sync_cmds_outstanding, 0);
76048c2ecf20Sopenharmony_ci
76058c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&ctrl_info->rescan_work, pqi_rescan_worker);
76068c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&ctrl_info->update_time_work, pqi_update_time_worker);
76078c2ecf20Sopenharmony_ci
76088c2ecf20Sopenharmony_ci	timer_setup(&ctrl_info->heartbeat_timer, pqi_heartbeat_timer_handler, 0);
76098c2ecf20Sopenharmony_ci	INIT_WORK(&ctrl_info->ctrl_offline_work, pqi_ctrl_offline_worker);
76108c2ecf20Sopenharmony_ci
76118c2ecf20Sopenharmony_ci	sema_init(&ctrl_info->sync_request_sem,
76128c2ecf20Sopenharmony_ci		PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS);
76138c2ecf20Sopenharmony_ci	init_waitqueue_head(&ctrl_info->block_requests_wait);
76148c2ecf20Sopenharmony_ci
76158c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctrl_info->raid_bypass_retry_list);
76168c2ecf20Sopenharmony_ci	spin_lock_init(&ctrl_info->raid_bypass_retry_list_lock);
76178c2ecf20Sopenharmony_ci	INIT_WORK(&ctrl_info->raid_bypass_retry_work,
76188c2ecf20Sopenharmony_ci		pqi_raid_bypass_retry_worker);
76198c2ecf20Sopenharmony_ci
76208c2ecf20Sopenharmony_ci	ctrl_info->ctrl_id = atomic_inc_return(&pqi_controller_count) - 1;
76218c2ecf20Sopenharmony_ci	ctrl_info->irq_mode = IRQ_MODE_NONE;
76228c2ecf20Sopenharmony_ci	ctrl_info->max_msix_vectors = PQI_MAX_MSIX_VECTORS;
76238c2ecf20Sopenharmony_ci
76248c2ecf20Sopenharmony_ci	return ctrl_info;
76258c2ecf20Sopenharmony_ci}
76268c2ecf20Sopenharmony_ci
76278c2ecf20Sopenharmony_cistatic inline void pqi_free_ctrl_info(struct pqi_ctrl_info *ctrl_info)
76288c2ecf20Sopenharmony_ci{
76298c2ecf20Sopenharmony_ci	kfree(ctrl_info);
76308c2ecf20Sopenharmony_ci}
76318c2ecf20Sopenharmony_ci
76328c2ecf20Sopenharmony_cistatic void pqi_free_interrupts(struct pqi_ctrl_info *ctrl_info)
76338c2ecf20Sopenharmony_ci{
76348c2ecf20Sopenharmony_ci	pqi_free_irqs(ctrl_info);
76358c2ecf20Sopenharmony_ci	pqi_disable_msix_interrupts(ctrl_info);
76368c2ecf20Sopenharmony_ci}
76378c2ecf20Sopenharmony_ci
76388c2ecf20Sopenharmony_cistatic void pqi_free_ctrl_resources(struct pqi_ctrl_info *ctrl_info)
76398c2ecf20Sopenharmony_ci{
76408c2ecf20Sopenharmony_ci	pqi_stop_heartbeat_timer(ctrl_info);
76418c2ecf20Sopenharmony_ci	pqi_free_interrupts(ctrl_info);
76428c2ecf20Sopenharmony_ci	if (ctrl_info->queue_memory_base)
76438c2ecf20Sopenharmony_ci		dma_free_coherent(&ctrl_info->pci_dev->dev,
76448c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_length,
76458c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base,
76468c2ecf20Sopenharmony_ci			ctrl_info->queue_memory_base_dma_handle);
76478c2ecf20Sopenharmony_ci	if (ctrl_info->admin_queue_memory_base)
76488c2ecf20Sopenharmony_ci		dma_free_coherent(&ctrl_info->pci_dev->dev,
76498c2ecf20Sopenharmony_ci			ctrl_info->admin_queue_memory_length,
76508c2ecf20Sopenharmony_ci			ctrl_info->admin_queue_memory_base,
76518c2ecf20Sopenharmony_ci			ctrl_info->admin_queue_memory_base_dma_handle);
76528c2ecf20Sopenharmony_ci	pqi_free_all_io_requests(ctrl_info);
76538c2ecf20Sopenharmony_ci	if (ctrl_info->error_buffer)
76548c2ecf20Sopenharmony_ci		dma_free_coherent(&ctrl_info->pci_dev->dev,
76558c2ecf20Sopenharmony_ci			ctrl_info->error_buffer_length,
76568c2ecf20Sopenharmony_ci			ctrl_info->error_buffer,
76578c2ecf20Sopenharmony_ci			ctrl_info->error_buffer_dma_handle);
76588c2ecf20Sopenharmony_ci	if (ctrl_info->iomem_base)
76598c2ecf20Sopenharmony_ci		pqi_cleanup_pci_init(ctrl_info);
76608c2ecf20Sopenharmony_ci	pqi_free_ctrl_info(ctrl_info);
76618c2ecf20Sopenharmony_ci}
76628c2ecf20Sopenharmony_ci
76638c2ecf20Sopenharmony_cistatic void pqi_remove_ctrl(struct pqi_ctrl_info *ctrl_info)
76648c2ecf20Sopenharmony_ci{
76658c2ecf20Sopenharmony_ci	pqi_cancel_rescan_worker(ctrl_info);
76668c2ecf20Sopenharmony_ci	pqi_cancel_update_time_worker(ctrl_info);
76678c2ecf20Sopenharmony_ci	pqi_unregister_scsi(ctrl_info);
76688c2ecf20Sopenharmony_ci	if (ctrl_info->pqi_mode_enabled)
76698c2ecf20Sopenharmony_ci		pqi_revert_to_sis_mode(ctrl_info);
76708c2ecf20Sopenharmony_ci	pqi_free_ctrl_resources(ctrl_info);
76718c2ecf20Sopenharmony_ci}
76728c2ecf20Sopenharmony_ci
76738c2ecf20Sopenharmony_cistatic void pqi_ofa_ctrl_quiesce(struct pqi_ctrl_info *ctrl_info)
76748c2ecf20Sopenharmony_ci{
76758c2ecf20Sopenharmony_ci	pqi_cancel_update_time_worker(ctrl_info);
76768c2ecf20Sopenharmony_ci	pqi_cancel_rescan_worker(ctrl_info);
76778c2ecf20Sopenharmony_ci	pqi_wait_until_lun_reset_finished(ctrl_info);
76788c2ecf20Sopenharmony_ci	pqi_wait_until_scan_finished(ctrl_info);
76798c2ecf20Sopenharmony_ci	pqi_ctrl_ofa_start(ctrl_info);
76808c2ecf20Sopenharmony_ci	pqi_ctrl_block_requests(ctrl_info);
76818c2ecf20Sopenharmony_ci	pqi_ctrl_wait_until_quiesced(ctrl_info);
76828c2ecf20Sopenharmony_ci	pqi_ctrl_wait_for_pending_io(ctrl_info, PQI_PENDING_IO_TIMEOUT_SECS);
76838c2ecf20Sopenharmony_ci	pqi_fail_io_queued_for_all_devices(ctrl_info);
76848c2ecf20Sopenharmony_ci	pqi_wait_until_inbound_queues_empty(ctrl_info);
76858c2ecf20Sopenharmony_ci	pqi_stop_heartbeat_timer(ctrl_info);
76868c2ecf20Sopenharmony_ci	ctrl_info->pqi_mode_enabled = false;
76878c2ecf20Sopenharmony_ci	pqi_save_ctrl_mode(ctrl_info, SIS_MODE);
76888c2ecf20Sopenharmony_ci}
76898c2ecf20Sopenharmony_ci
76908c2ecf20Sopenharmony_cistatic void pqi_ofa_ctrl_unquiesce(struct pqi_ctrl_info *ctrl_info)
76918c2ecf20Sopenharmony_ci{
76928c2ecf20Sopenharmony_ci	pqi_ofa_free_host_buffer(ctrl_info);
76938c2ecf20Sopenharmony_ci	ctrl_info->pqi_mode_enabled = true;
76948c2ecf20Sopenharmony_ci	pqi_save_ctrl_mode(ctrl_info, PQI_MODE);
76958c2ecf20Sopenharmony_ci	ctrl_info->controller_online = true;
76968c2ecf20Sopenharmony_ci	pqi_ctrl_unblock_requests(ctrl_info);
76978c2ecf20Sopenharmony_ci	pqi_start_heartbeat_timer(ctrl_info);
76988c2ecf20Sopenharmony_ci	pqi_schedule_update_time_worker(ctrl_info);
76998c2ecf20Sopenharmony_ci	pqi_clear_soft_reset_status(ctrl_info,
77008c2ecf20Sopenharmony_ci		PQI_SOFT_RESET_ABORT);
77018c2ecf20Sopenharmony_ci	pqi_scan_scsi_devices(ctrl_info);
77028c2ecf20Sopenharmony_ci}
77038c2ecf20Sopenharmony_ci
77048c2ecf20Sopenharmony_cistatic int pqi_ofa_alloc_mem(struct pqi_ctrl_info *ctrl_info,
77058c2ecf20Sopenharmony_ci	u32 total_size, u32 chunk_size)
77068c2ecf20Sopenharmony_ci{
77078c2ecf20Sopenharmony_ci	u32 sg_count;
77088c2ecf20Sopenharmony_ci	u32 size;
77098c2ecf20Sopenharmony_ci	int i;
77108c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *mem_descriptor = NULL;
77118c2ecf20Sopenharmony_ci	struct device *dev;
77128c2ecf20Sopenharmony_ci	struct pqi_ofa_memory *ofap;
77138c2ecf20Sopenharmony_ci
77148c2ecf20Sopenharmony_ci	dev = &ctrl_info->pci_dev->dev;
77158c2ecf20Sopenharmony_ci
77168c2ecf20Sopenharmony_ci	sg_count = (total_size + chunk_size - 1);
77178c2ecf20Sopenharmony_ci	sg_count /= chunk_size;
77188c2ecf20Sopenharmony_ci
77198c2ecf20Sopenharmony_ci	ofap = ctrl_info->pqi_ofa_mem_virt_addr;
77208c2ecf20Sopenharmony_ci
77218c2ecf20Sopenharmony_ci	if (sg_count*chunk_size < total_size)
77228c2ecf20Sopenharmony_ci		goto out;
77238c2ecf20Sopenharmony_ci
77248c2ecf20Sopenharmony_ci	ctrl_info->pqi_ofa_chunk_virt_addr =
77258c2ecf20Sopenharmony_ci				kcalloc(sg_count, sizeof(void *), GFP_KERNEL);
77268c2ecf20Sopenharmony_ci	if (!ctrl_info->pqi_ofa_chunk_virt_addr)
77278c2ecf20Sopenharmony_ci		goto out;
77288c2ecf20Sopenharmony_ci
77298c2ecf20Sopenharmony_ci	for (size = 0, i = 0; size < total_size; size += chunk_size, i++) {
77308c2ecf20Sopenharmony_ci		dma_addr_t dma_handle;
77318c2ecf20Sopenharmony_ci
77328c2ecf20Sopenharmony_ci		ctrl_info->pqi_ofa_chunk_virt_addr[i] =
77338c2ecf20Sopenharmony_ci			dma_alloc_coherent(dev, chunk_size, &dma_handle,
77348c2ecf20Sopenharmony_ci					   GFP_KERNEL);
77358c2ecf20Sopenharmony_ci
77368c2ecf20Sopenharmony_ci		if (!ctrl_info->pqi_ofa_chunk_virt_addr[i])
77378c2ecf20Sopenharmony_ci			break;
77388c2ecf20Sopenharmony_ci
77398c2ecf20Sopenharmony_ci		mem_descriptor = &ofap->sg_descriptor[i];
77408c2ecf20Sopenharmony_ci		put_unaligned_le64 ((u64) dma_handle, &mem_descriptor->address);
77418c2ecf20Sopenharmony_ci		put_unaligned_le32 (chunk_size, &mem_descriptor->length);
77428c2ecf20Sopenharmony_ci	}
77438c2ecf20Sopenharmony_ci
77448c2ecf20Sopenharmony_ci	if (!size || size < total_size)
77458c2ecf20Sopenharmony_ci		goto out_free_chunks;
77468c2ecf20Sopenharmony_ci
77478c2ecf20Sopenharmony_ci	put_unaligned_le32(CISS_SG_LAST, &mem_descriptor->flags);
77488c2ecf20Sopenharmony_ci	put_unaligned_le16(sg_count, &ofap->num_memory_descriptors);
77498c2ecf20Sopenharmony_ci	put_unaligned_le32(size, &ofap->bytes_allocated);
77508c2ecf20Sopenharmony_ci
77518c2ecf20Sopenharmony_ci	return 0;
77528c2ecf20Sopenharmony_ci
77538c2ecf20Sopenharmony_ciout_free_chunks:
77548c2ecf20Sopenharmony_ci	while (--i >= 0) {
77558c2ecf20Sopenharmony_ci		mem_descriptor = &ofap->sg_descriptor[i];
77568c2ecf20Sopenharmony_ci		dma_free_coherent(dev, chunk_size,
77578c2ecf20Sopenharmony_ci				ctrl_info->pqi_ofa_chunk_virt_addr[i],
77588c2ecf20Sopenharmony_ci				get_unaligned_le64(&mem_descriptor->address));
77598c2ecf20Sopenharmony_ci	}
77608c2ecf20Sopenharmony_ci	kfree(ctrl_info->pqi_ofa_chunk_virt_addr);
77618c2ecf20Sopenharmony_ci
77628c2ecf20Sopenharmony_ciout:
77638c2ecf20Sopenharmony_ci	put_unaligned_le32 (0, &ofap->bytes_allocated);
77648c2ecf20Sopenharmony_ci	return -ENOMEM;
77658c2ecf20Sopenharmony_ci}
77668c2ecf20Sopenharmony_ci
77678c2ecf20Sopenharmony_cistatic int pqi_ofa_alloc_host_buffer(struct pqi_ctrl_info *ctrl_info)
77688c2ecf20Sopenharmony_ci{
77698c2ecf20Sopenharmony_ci	u32 total_size;
77708c2ecf20Sopenharmony_ci	u32 min_chunk_size;
77718c2ecf20Sopenharmony_ci	u32 chunk_sz;
77728c2ecf20Sopenharmony_ci
77738c2ecf20Sopenharmony_ci	total_size = le32_to_cpu(
77748c2ecf20Sopenharmony_ci			ctrl_info->pqi_ofa_mem_virt_addr->bytes_allocated);
77758c2ecf20Sopenharmony_ci	min_chunk_size = total_size / PQI_OFA_MAX_SG_DESCRIPTORS;
77768c2ecf20Sopenharmony_ci
77778c2ecf20Sopenharmony_ci	for (chunk_sz = total_size; chunk_sz >= min_chunk_size; chunk_sz /= 2)
77788c2ecf20Sopenharmony_ci		if (!pqi_ofa_alloc_mem(ctrl_info, total_size, chunk_sz))
77798c2ecf20Sopenharmony_ci			return 0;
77808c2ecf20Sopenharmony_ci
77818c2ecf20Sopenharmony_ci	return -ENOMEM;
77828c2ecf20Sopenharmony_ci}
77838c2ecf20Sopenharmony_ci
77848c2ecf20Sopenharmony_cistatic void pqi_ofa_setup_host_buffer(struct pqi_ctrl_info *ctrl_info,
77858c2ecf20Sopenharmony_ci	u32 bytes_requested)
77868c2ecf20Sopenharmony_ci{
77878c2ecf20Sopenharmony_ci	struct pqi_ofa_memory *pqi_ofa_memory;
77888c2ecf20Sopenharmony_ci	struct device *dev;
77898c2ecf20Sopenharmony_ci
77908c2ecf20Sopenharmony_ci	dev = &ctrl_info->pci_dev->dev;
77918c2ecf20Sopenharmony_ci	pqi_ofa_memory = dma_alloc_coherent(dev,
77928c2ecf20Sopenharmony_ci					    PQI_OFA_MEMORY_DESCRIPTOR_LENGTH,
77938c2ecf20Sopenharmony_ci					    &ctrl_info->pqi_ofa_mem_dma_handle,
77948c2ecf20Sopenharmony_ci					    GFP_KERNEL);
77958c2ecf20Sopenharmony_ci
77968c2ecf20Sopenharmony_ci	if (!pqi_ofa_memory)
77978c2ecf20Sopenharmony_ci		return;
77988c2ecf20Sopenharmony_ci
77998c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_OFA_VERSION, &pqi_ofa_memory->version);
78008c2ecf20Sopenharmony_ci	memcpy(&pqi_ofa_memory->signature, PQI_OFA_SIGNATURE,
78018c2ecf20Sopenharmony_ci					sizeof(pqi_ofa_memory->signature));
78028c2ecf20Sopenharmony_ci	pqi_ofa_memory->bytes_allocated = cpu_to_le32(bytes_requested);
78038c2ecf20Sopenharmony_ci
78048c2ecf20Sopenharmony_ci	ctrl_info->pqi_ofa_mem_virt_addr = pqi_ofa_memory;
78058c2ecf20Sopenharmony_ci
78068c2ecf20Sopenharmony_ci	if (pqi_ofa_alloc_host_buffer(ctrl_info) < 0) {
78078c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to allocate host buffer of size = %u",
78088c2ecf20Sopenharmony_ci			bytes_requested);
78098c2ecf20Sopenharmony_ci	}
78108c2ecf20Sopenharmony_ci
78118c2ecf20Sopenharmony_ci	return;
78128c2ecf20Sopenharmony_ci}
78138c2ecf20Sopenharmony_ci
78148c2ecf20Sopenharmony_cistatic void pqi_ofa_free_host_buffer(struct pqi_ctrl_info *ctrl_info)
78158c2ecf20Sopenharmony_ci{
78168c2ecf20Sopenharmony_ci	int i;
78178c2ecf20Sopenharmony_ci	struct pqi_sg_descriptor *mem_descriptor;
78188c2ecf20Sopenharmony_ci	struct pqi_ofa_memory *ofap;
78198c2ecf20Sopenharmony_ci
78208c2ecf20Sopenharmony_ci	ofap = ctrl_info->pqi_ofa_mem_virt_addr;
78218c2ecf20Sopenharmony_ci
78228c2ecf20Sopenharmony_ci	if (!ofap)
78238c2ecf20Sopenharmony_ci		return;
78248c2ecf20Sopenharmony_ci
78258c2ecf20Sopenharmony_ci	if (!ofap->bytes_allocated)
78268c2ecf20Sopenharmony_ci		goto out;
78278c2ecf20Sopenharmony_ci
78288c2ecf20Sopenharmony_ci	mem_descriptor = ofap->sg_descriptor;
78298c2ecf20Sopenharmony_ci
78308c2ecf20Sopenharmony_ci	for (i = 0; i < get_unaligned_le16(&ofap->num_memory_descriptors);
78318c2ecf20Sopenharmony_ci		i++) {
78328c2ecf20Sopenharmony_ci		dma_free_coherent(&ctrl_info->pci_dev->dev,
78338c2ecf20Sopenharmony_ci			get_unaligned_le32(&mem_descriptor[i].length),
78348c2ecf20Sopenharmony_ci			ctrl_info->pqi_ofa_chunk_virt_addr[i],
78358c2ecf20Sopenharmony_ci			get_unaligned_le64(&mem_descriptor[i].address));
78368c2ecf20Sopenharmony_ci	}
78378c2ecf20Sopenharmony_ci	kfree(ctrl_info->pqi_ofa_chunk_virt_addr);
78388c2ecf20Sopenharmony_ci
78398c2ecf20Sopenharmony_ciout:
78408c2ecf20Sopenharmony_ci	dma_free_coherent(&ctrl_info->pci_dev->dev,
78418c2ecf20Sopenharmony_ci			PQI_OFA_MEMORY_DESCRIPTOR_LENGTH, ofap,
78428c2ecf20Sopenharmony_ci			ctrl_info->pqi_ofa_mem_dma_handle);
78438c2ecf20Sopenharmony_ci	ctrl_info->pqi_ofa_mem_virt_addr = NULL;
78448c2ecf20Sopenharmony_ci}
78458c2ecf20Sopenharmony_ci
78468c2ecf20Sopenharmony_cistatic int pqi_ofa_host_memory_update(struct pqi_ctrl_info *ctrl_info)
78478c2ecf20Sopenharmony_ci{
78488c2ecf20Sopenharmony_ci	struct pqi_vendor_general_request request;
78498c2ecf20Sopenharmony_ci	size_t size;
78508c2ecf20Sopenharmony_ci	struct pqi_ofa_memory *ofap;
78518c2ecf20Sopenharmony_ci
78528c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
78538c2ecf20Sopenharmony_ci
78548c2ecf20Sopenharmony_ci	ofap = ctrl_info->pqi_ofa_mem_virt_addr;
78558c2ecf20Sopenharmony_ci
78568c2ecf20Sopenharmony_ci	request.header.iu_type = PQI_REQUEST_IU_VENDOR_GENERAL;
78578c2ecf20Sopenharmony_ci	put_unaligned_le16(sizeof(request) - PQI_REQUEST_HEADER_LENGTH,
78588c2ecf20Sopenharmony_ci		&request.header.iu_length);
78598c2ecf20Sopenharmony_ci	put_unaligned_le16(PQI_VENDOR_GENERAL_HOST_MEMORY_UPDATE,
78608c2ecf20Sopenharmony_ci		&request.function_code);
78618c2ecf20Sopenharmony_ci
78628c2ecf20Sopenharmony_ci	if (ofap) {
78638c2ecf20Sopenharmony_ci		size = offsetof(struct pqi_ofa_memory, sg_descriptor) +
78648c2ecf20Sopenharmony_ci			get_unaligned_le16(&ofap->num_memory_descriptors) *
78658c2ecf20Sopenharmony_ci			sizeof(struct pqi_sg_descriptor);
78668c2ecf20Sopenharmony_ci
78678c2ecf20Sopenharmony_ci		put_unaligned_le64((u64)ctrl_info->pqi_ofa_mem_dma_handle,
78688c2ecf20Sopenharmony_ci			&request.data.ofa_memory_allocation.buffer_address);
78698c2ecf20Sopenharmony_ci		put_unaligned_le32(size,
78708c2ecf20Sopenharmony_ci			&request.data.ofa_memory_allocation.buffer_length);
78718c2ecf20Sopenharmony_ci
78728c2ecf20Sopenharmony_ci	}
78738c2ecf20Sopenharmony_ci
78748c2ecf20Sopenharmony_ci	return pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
78758c2ecf20Sopenharmony_ci		0, NULL, NO_TIMEOUT);
78768c2ecf20Sopenharmony_ci}
78778c2ecf20Sopenharmony_ci
78788c2ecf20Sopenharmony_cistatic int pqi_ofa_ctrl_restart(struct pqi_ctrl_info *ctrl_info)
78798c2ecf20Sopenharmony_ci{
78808c2ecf20Sopenharmony_ci	msleep(PQI_POST_RESET_DELAY_B4_MSGU_READY);
78818c2ecf20Sopenharmony_ci	return pqi_ctrl_init_resume(ctrl_info);
78828c2ecf20Sopenharmony_ci}
78838c2ecf20Sopenharmony_ci
78848c2ecf20Sopenharmony_cistatic void pqi_perform_lockup_action(void)
78858c2ecf20Sopenharmony_ci{
78868c2ecf20Sopenharmony_ci	switch (pqi_lockup_action) {
78878c2ecf20Sopenharmony_ci	case PANIC:
78888c2ecf20Sopenharmony_ci		panic("FATAL: Smart Family Controller lockup detected");
78898c2ecf20Sopenharmony_ci		break;
78908c2ecf20Sopenharmony_ci	case REBOOT:
78918c2ecf20Sopenharmony_ci		emergency_restart();
78928c2ecf20Sopenharmony_ci		break;
78938c2ecf20Sopenharmony_ci	case NONE:
78948c2ecf20Sopenharmony_ci	default:
78958c2ecf20Sopenharmony_ci		break;
78968c2ecf20Sopenharmony_ci	}
78978c2ecf20Sopenharmony_ci}
78988c2ecf20Sopenharmony_ci
78998c2ecf20Sopenharmony_cistatic struct pqi_raid_error_info pqi_ctrl_offline_raid_error_info = {
79008c2ecf20Sopenharmony_ci	.data_out_result = PQI_DATA_IN_OUT_HARDWARE_ERROR,
79018c2ecf20Sopenharmony_ci	.status = SAM_STAT_CHECK_CONDITION,
79028c2ecf20Sopenharmony_ci};
79038c2ecf20Sopenharmony_ci
79048c2ecf20Sopenharmony_cistatic void pqi_fail_all_outstanding_requests(struct pqi_ctrl_info *ctrl_info)
79058c2ecf20Sopenharmony_ci{
79068c2ecf20Sopenharmony_ci	unsigned int i;
79078c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
79088c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
79098c2ecf20Sopenharmony_ci
79108c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->max_io_slots; i++) {
79118c2ecf20Sopenharmony_ci		io_request = &ctrl_info->io_request_pool[i];
79128c2ecf20Sopenharmony_ci		if (atomic_read(&io_request->refcount) == 0)
79138c2ecf20Sopenharmony_ci			continue;
79148c2ecf20Sopenharmony_ci
79158c2ecf20Sopenharmony_ci		scmd = io_request->scmd;
79168c2ecf20Sopenharmony_ci		if (scmd) {
79178c2ecf20Sopenharmony_ci			set_host_byte(scmd, DID_NO_CONNECT);
79188c2ecf20Sopenharmony_ci		} else {
79198c2ecf20Sopenharmony_ci			io_request->status = -ENXIO;
79208c2ecf20Sopenharmony_ci			io_request->error_info =
79218c2ecf20Sopenharmony_ci				&pqi_ctrl_offline_raid_error_info;
79228c2ecf20Sopenharmony_ci		}
79238c2ecf20Sopenharmony_ci
79248c2ecf20Sopenharmony_ci		io_request->io_complete_callback(io_request,
79258c2ecf20Sopenharmony_ci			io_request->context);
79268c2ecf20Sopenharmony_ci	}
79278c2ecf20Sopenharmony_ci}
79288c2ecf20Sopenharmony_ci
79298c2ecf20Sopenharmony_cistatic void pqi_take_ctrl_offline_deferred(struct pqi_ctrl_info *ctrl_info)
79308c2ecf20Sopenharmony_ci{
79318c2ecf20Sopenharmony_ci	pqi_perform_lockup_action();
79328c2ecf20Sopenharmony_ci	pqi_stop_heartbeat_timer(ctrl_info);
79338c2ecf20Sopenharmony_ci	pqi_free_interrupts(ctrl_info);
79348c2ecf20Sopenharmony_ci	pqi_cancel_rescan_worker(ctrl_info);
79358c2ecf20Sopenharmony_ci	pqi_cancel_update_time_worker(ctrl_info);
79368c2ecf20Sopenharmony_ci	pqi_ctrl_wait_until_quiesced(ctrl_info);
79378c2ecf20Sopenharmony_ci	pqi_fail_all_outstanding_requests(ctrl_info);
79388c2ecf20Sopenharmony_ci	pqi_clear_all_queued_raid_bypass_retries(ctrl_info);
79398c2ecf20Sopenharmony_ci	pqi_ctrl_unblock_requests(ctrl_info);
79408c2ecf20Sopenharmony_ci}
79418c2ecf20Sopenharmony_ci
79428c2ecf20Sopenharmony_cistatic void pqi_ctrl_offline_worker(struct work_struct *work)
79438c2ecf20Sopenharmony_ci{
79448c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
79458c2ecf20Sopenharmony_ci
79468c2ecf20Sopenharmony_ci	ctrl_info = container_of(work, struct pqi_ctrl_info, ctrl_offline_work);
79478c2ecf20Sopenharmony_ci	pqi_take_ctrl_offline_deferred(ctrl_info);
79488c2ecf20Sopenharmony_ci}
79498c2ecf20Sopenharmony_ci
79508c2ecf20Sopenharmony_cistatic void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info)
79518c2ecf20Sopenharmony_ci{
79528c2ecf20Sopenharmony_ci	if (!ctrl_info->controller_online)
79538c2ecf20Sopenharmony_ci		return;
79548c2ecf20Sopenharmony_ci
79558c2ecf20Sopenharmony_ci	ctrl_info->controller_online = false;
79568c2ecf20Sopenharmony_ci	ctrl_info->pqi_mode_enabled = false;
79578c2ecf20Sopenharmony_ci	pqi_ctrl_block_requests(ctrl_info);
79588c2ecf20Sopenharmony_ci	if (!pqi_disable_ctrl_shutdown)
79598c2ecf20Sopenharmony_ci		sis_shutdown_ctrl(ctrl_info);
79608c2ecf20Sopenharmony_ci	pci_disable_device(ctrl_info->pci_dev);
79618c2ecf20Sopenharmony_ci	dev_err(&ctrl_info->pci_dev->dev, "controller offline\n");
79628c2ecf20Sopenharmony_ci	schedule_work(&ctrl_info->ctrl_offline_work);
79638c2ecf20Sopenharmony_ci}
79648c2ecf20Sopenharmony_ci
79658c2ecf20Sopenharmony_cistatic void pqi_print_ctrl_info(struct pci_dev *pci_dev,
79668c2ecf20Sopenharmony_ci	const struct pci_device_id *id)
79678c2ecf20Sopenharmony_ci{
79688c2ecf20Sopenharmony_ci	char *ctrl_description;
79698c2ecf20Sopenharmony_ci
79708c2ecf20Sopenharmony_ci	if (id->driver_data)
79718c2ecf20Sopenharmony_ci		ctrl_description = (char *)id->driver_data;
79728c2ecf20Sopenharmony_ci	else
79738c2ecf20Sopenharmony_ci		ctrl_description = "Microsemi Smart Family Controller";
79748c2ecf20Sopenharmony_ci
79758c2ecf20Sopenharmony_ci	dev_info(&pci_dev->dev, "%s found\n", ctrl_description);
79768c2ecf20Sopenharmony_ci}
79778c2ecf20Sopenharmony_ci
79788c2ecf20Sopenharmony_cistatic int pqi_pci_probe(struct pci_dev *pci_dev,
79798c2ecf20Sopenharmony_ci	const struct pci_device_id *id)
79808c2ecf20Sopenharmony_ci{
79818c2ecf20Sopenharmony_ci	int rc;
79828c2ecf20Sopenharmony_ci	int node, cp_node;
79838c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
79848c2ecf20Sopenharmony_ci
79858c2ecf20Sopenharmony_ci	pqi_print_ctrl_info(pci_dev, id);
79868c2ecf20Sopenharmony_ci
79878c2ecf20Sopenharmony_ci	if (pqi_disable_device_id_wildcards &&
79888c2ecf20Sopenharmony_ci		id->subvendor == PCI_ANY_ID &&
79898c2ecf20Sopenharmony_ci		id->subdevice == PCI_ANY_ID) {
79908c2ecf20Sopenharmony_ci		dev_warn(&pci_dev->dev,
79918c2ecf20Sopenharmony_ci			"controller not probed because device ID wildcards are disabled\n");
79928c2ecf20Sopenharmony_ci		return -ENODEV;
79938c2ecf20Sopenharmony_ci	}
79948c2ecf20Sopenharmony_ci
79958c2ecf20Sopenharmony_ci	if (id->subvendor == PCI_ANY_ID || id->subdevice == PCI_ANY_ID)
79968c2ecf20Sopenharmony_ci		dev_warn(&pci_dev->dev,
79978c2ecf20Sopenharmony_ci			"controller device ID matched using wildcards\n");
79988c2ecf20Sopenharmony_ci
79998c2ecf20Sopenharmony_ci	node = dev_to_node(&pci_dev->dev);
80008c2ecf20Sopenharmony_ci	if (node == NUMA_NO_NODE) {
80018c2ecf20Sopenharmony_ci		cp_node = cpu_to_node(0);
80028c2ecf20Sopenharmony_ci		if (cp_node == NUMA_NO_NODE)
80038c2ecf20Sopenharmony_ci			cp_node = 0;
80048c2ecf20Sopenharmony_ci		set_dev_node(&pci_dev->dev, cp_node);
80058c2ecf20Sopenharmony_ci	}
80068c2ecf20Sopenharmony_ci
80078c2ecf20Sopenharmony_ci	ctrl_info = pqi_alloc_ctrl_info(node);
80088c2ecf20Sopenharmony_ci	if (!ctrl_info) {
80098c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev,
80108c2ecf20Sopenharmony_ci			"failed to allocate controller info block\n");
80118c2ecf20Sopenharmony_ci		return -ENOMEM;
80128c2ecf20Sopenharmony_ci	}
80138c2ecf20Sopenharmony_ci
80148c2ecf20Sopenharmony_ci	ctrl_info->pci_dev = pci_dev;
80158c2ecf20Sopenharmony_ci
80168c2ecf20Sopenharmony_ci	rc = pqi_pci_init(ctrl_info);
80178c2ecf20Sopenharmony_ci	if (rc)
80188c2ecf20Sopenharmony_ci		goto error;
80198c2ecf20Sopenharmony_ci
80208c2ecf20Sopenharmony_ci	rc = pqi_ctrl_init(ctrl_info);
80218c2ecf20Sopenharmony_ci	if (rc)
80228c2ecf20Sopenharmony_ci		goto error;
80238c2ecf20Sopenharmony_ci
80248c2ecf20Sopenharmony_ci	return 0;
80258c2ecf20Sopenharmony_ci
80268c2ecf20Sopenharmony_cierror:
80278c2ecf20Sopenharmony_ci	pqi_remove_ctrl(ctrl_info);
80288c2ecf20Sopenharmony_ci
80298c2ecf20Sopenharmony_ci	return rc;
80308c2ecf20Sopenharmony_ci}
80318c2ecf20Sopenharmony_ci
80328c2ecf20Sopenharmony_cistatic void pqi_pci_remove(struct pci_dev *pci_dev)
80338c2ecf20Sopenharmony_ci{
80348c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
80358c2ecf20Sopenharmony_ci
80368c2ecf20Sopenharmony_ci	ctrl_info = pci_get_drvdata(pci_dev);
80378c2ecf20Sopenharmony_ci	if (!ctrl_info)
80388c2ecf20Sopenharmony_ci		return;
80398c2ecf20Sopenharmony_ci
80408c2ecf20Sopenharmony_ci	ctrl_info->in_shutdown = true;
80418c2ecf20Sopenharmony_ci
80428c2ecf20Sopenharmony_ci	pqi_remove_ctrl(ctrl_info);
80438c2ecf20Sopenharmony_ci}
80448c2ecf20Sopenharmony_ci
80458c2ecf20Sopenharmony_cistatic void pqi_crash_if_pending_command(struct pqi_ctrl_info *ctrl_info)
80468c2ecf20Sopenharmony_ci{
80478c2ecf20Sopenharmony_ci	unsigned int i;
80488c2ecf20Sopenharmony_ci	struct pqi_io_request *io_request;
80498c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmd;
80508c2ecf20Sopenharmony_ci
80518c2ecf20Sopenharmony_ci	for (i = 0; i < ctrl_info->max_io_slots; i++) {
80528c2ecf20Sopenharmony_ci		io_request = &ctrl_info->io_request_pool[i];
80538c2ecf20Sopenharmony_ci		if (atomic_read(&io_request->refcount) == 0)
80548c2ecf20Sopenharmony_ci			continue;
80558c2ecf20Sopenharmony_ci		scmd = io_request->scmd;
80568c2ecf20Sopenharmony_ci		WARN_ON(scmd != NULL); /* IO command from SML */
80578c2ecf20Sopenharmony_ci		WARN_ON(scmd == NULL); /* Non-IO cmd or driver initiated*/
80588c2ecf20Sopenharmony_ci	}
80598c2ecf20Sopenharmony_ci}
80608c2ecf20Sopenharmony_ci
80618c2ecf20Sopenharmony_cistatic void pqi_shutdown(struct pci_dev *pci_dev)
80628c2ecf20Sopenharmony_ci{
80638c2ecf20Sopenharmony_ci	int rc;
80648c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
80658c2ecf20Sopenharmony_ci
80668c2ecf20Sopenharmony_ci	ctrl_info = pci_get_drvdata(pci_dev);
80678c2ecf20Sopenharmony_ci	if (!ctrl_info) {
80688c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev,
80698c2ecf20Sopenharmony_ci			"cache could not be flushed\n");
80708c2ecf20Sopenharmony_ci		return;
80718c2ecf20Sopenharmony_ci	}
80728c2ecf20Sopenharmony_ci
80738c2ecf20Sopenharmony_ci	pqi_disable_events(ctrl_info);
80748c2ecf20Sopenharmony_ci	pqi_wait_until_ofa_finished(ctrl_info);
80758c2ecf20Sopenharmony_ci	pqi_cancel_update_time_worker(ctrl_info);
80768c2ecf20Sopenharmony_ci	pqi_cancel_rescan_worker(ctrl_info);
80778c2ecf20Sopenharmony_ci	pqi_cancel_event_worker(ctrl_info);
80788c2ecf20Sopenharmony_ci
80798c2ecf20Sopenharmony_ci	pqi_ctrl_shutdown_start(ctrl_info);
80808c2ecf20Sopenharmony_ci	pqi_ctrl_wait_until_quiesced(ctrl_info);
80818c2ecf20Sopenharmony_ci
80828c2ecf20Sopenharmony_ci	rc = pqi_ctrl_wait_for_pending_io(ctrl_info, NO_TIMEOUT);
80838c2ecf20Sopenharmony_ci	if (rc) {
80848c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev,
80858c2ecf20Sopenharmony_ci			"wait for pending I/O failed\n");
80868c2ecf20Sopenharmony_ci		return;
80878c2ecf20Sopenharmony_ci	}
80888c2ecf20Sopenharmony_ci
80898c2ecf20Sopenharmony_ci	pqi_ctrl_block_device_reset(ctrl_info);
80908c2ecf20Sopenharmony_ci	pqi_wait_until_lun_reset_finished(ctrl_info);
80918c2ecf20Sopenharmony_ci
80928c2ecf20Sopenharmony_ci	/*
80938c2ecf20Sopenharmony_ci	 * Write all data in the controller's battery-backed cache to
80948c2ecf20Sopenharmony_ci	 * storage.
80958c2ecf20Sopenharmony_ci	 */
80968c2ecf20Sopenharmony_ci	rc = pqi_flush_cache(ctrl_info, SHUTDOWN);
80978c2ecf20Sopenharmony_ci	if (rc)
80988c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev,
80998c2ecf20Sopenharmony_ci			"unable to flush controller cache\n");
81008c2ecf20Sopenharmony_ci
81018c2ecf20Sopenharmony_ci	pqi_ctrl_block_requests(ctrl_info);
81028c2ecf20Sopenharmony_ci
81038c2ecf20Sopenharmony_ci	rc = pqi_ctrl_wait_for_pending_sync_cmds(ctrl_info);
81048c2ecf20Sopenharmony_ci	if (rc) {
81058c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev,
81068c2ecf20Sopenharmony_ci			"wait for pending sync cmds failed\n");
81078c2ecf20Sopenharmony_ci		return;
81088c2ecf20Sopenharmony_ci	}
81098c2ecf20Sopenharmony_ci
81108c2ecf20Sopenharmony_ci	pqi_crash_if_pending_command(ctrl_info);
81118c2ecf20Sopenharmony_ci	pqi_reset(ctrl_info);
81128c2ecf20Sopenharmony_ci}
81138c2ecf20Sopenharmony_ci
81148c2ecf20Sopenharmony_cistatic void pqi_process_lockup_action_param(void)
81158c2ecf20Sopenharmony_ci{
81168c2ecf20Sopenharmony_ci	unsigned int i;
81178c2ecf20Sopenharmony_ci
81188c2ecf20Sopenharmony_ci	if (!pqi_lockup_action_param)
81198c2ecf20Sopenharmony_ci		return;
81208c2ecf20Sopenharmony_ci
81218c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pqi_lockup_actions); i++) {
81228c2ecf20Sopenharmony_ci		if (strcmp(pqi_lockup_action_param,
81238c2ecf20Sopenharmony_ci			pqi_lockup_actions[i].name) == 0) {
81248c2ecf20Sopenharmony_ci			pqi_lockup_action = pqi_lockup_actions[i].action;
81258c2ecf20Sopenharmony_ci			return;
81268c2ecf20Sopenharmony_ci		}
81278c2ecf20Sopenharmony_ci	}
81288c2ecf20Sopenharmony_ci
81298c2ecf20Sopenharmony_ci	pr_warn("%s: invalid lockup action setting \"%s\" - supported settings: none, reboot, panic\n",
81308c2ecf20Sopenharmony_ci		DRIVER_NAME_SHORT, pqi_lockup_action_param);
81318c2ecf20Sopenharmony_ci}
81328c2ecf20Sopenharmony_ci
81338c2ecf20Sopenharmony_cistatic void pqi_process_module_params(void)
81348c2ecf20Sopenharmony_ci{
81358c2ecf20Sopenharmony_ci	pqi_process_lockup_action_param();
81368c2ecf20Sopenharmony_ci}
81378c2ecf20Sopenharmony_ci
81388c2ecf20Sopenharmony_cistatic __maybe_unused int pqi_suspend(struct pci_dev *pci_dev, pm_message_t state)
81398c2ecf20Sopenharmony_ci{
81408c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
81418c2ecf20Sopenharmony_ci
81428c2ecf20Sopenharmony_ci	ctrl_info = pci_get_drvdata(pci_dev);
81438c2ecf20Sopenharmony_ci
81448c2ecf20Sopenharmony_ci	pqi_disable_events(ctrl_info);
81458c2ecf20Sopenharmony_ci	pqi_cancel_update_time_worker(ctrl_info);
81468c2ecf20Sopenharmony_ci	pqi_cancel_rescan_worker(ctrl_info);
81478c2ecf20Sopenharmony_ci	pqi_wait_until_scan_finished(ctrl_info);
81488c2ecf20Sopenharmony_ci	pqi_wait_until_lun_reset_finished(ctrl_info);
81498c2ecf20Sopenharmony_ci	pqi_wait_until_ofa_finished(ctrl_info);
81508c2ecf20Sopenharmony_ci	pqi_flush_cache(ctrl_info, SUSPEND);
81518c2ecf20Sopenharmony_ci	pqi_ctrl_block_requests(ctrl_info);
81528c2ecf20Sopenharmony_ci	pqi_ctrl_wait_until_quiesced(ctrl_info);
81538c2ecf20Sopenharmony_ci	pqi_wait_until_inbound_queues_empty(ctrl_info);
81548c2ecf20Sopenharmony_ci	pqi_ctrl_wait_for_pending_io(ctrl_info, NO_TIMEOUT);
81558c2ecf20Sopenharmony_ci	pqi_stop_heartbeat_timer(ctrl_info);
81568c2ecf20Sopenharmony_ci
81578c2ecf20Sopenharmony_ci	if (state.event == PM_EVENT_FREEZE)
81588c2ecf20Sopenharmony_ci		return 0;
81598c2ecf20Sopenharmony_ci
81608c2ecf20Sopenharmony_ci	pci_save_state(pci_dev);
81618c2ecf20Sopenharmony_ci	pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
81628c2ecf20Sopenharmony_ci
81638c2ecf20Sopenharmony_ci	ctrl_info->controller_online = false;
81648c2ecf20Sopenharmony_ci	ctrl_info->pqi_mode_enabled = false;
81658c2ecf20Sopenharmony_ci
81668c2ecf20Sopenharmony_ci	return 0;
81678c2ecf20Sopenharmony_ci}
81688c2ecf20Sopenharmony_ci
81698c2ecf20Sopenharmony_cistatic __maybe_unused int pqi_resume(struct pci_dev *pci_dev)
81708c2ecf20Sopenharmony_ci{
81718c2ecf20Sopenharmony_ci	int rc;
81728c2ecf20Sopenharmony_ci	struct pqi_ctrl_info *ctrl_info;
81738c2ecf20Sopenharmony_ci
81748c2ecf20Sopenharmony_ci	ctrl_info = pci_get_drvdata(pci_dev);
81758c2ecf20Sopenharmony_ci
81768c2ecf20Sopenharmony_ci	if (pci_dev->current_state != PCI_D0) {
81778c2ecf20Sopenharmony_ci		ctrl_info->max_hw_queue_index = 0;
81788c2ecf20Sopenharmony_ci		pqi_free_interrupts(ctrl_info);
81798c2ecf20Sopenharmony_ci		pqi_change_irq_mode(ctrl_info, IRQ_MODE_INTX);
81808c2ecf20Sopenharmony_ci		rc = request_irq(pci_irq_vector(pci_dev, 0), pqi_irq_handler,
81818c2ecf20Sopenharmony_ci			IRQF_SHARED, DRIVER_NAME_SHORT,
81828c2ecf20Sopenharmony_ci			&ctrl_info->queue_groups[0]);
81838c2ecf20Sopenharmony_ci		if (rc) {
81848c2ecf20Sopenharmony_ci			dev_err(&ctrl_info->pci_dev->dev,
81858c2ecf20Sopenharmony_ci				"irq %u init failed with error %d\n",
81868c2ecf20Sopenharmony_ci				pci_dev->irq, rc);
81878c2ecf20Sopenharmony_ci			return rc;
81888c2ecf20Sopenharmony_ci		}
81898c2ecf20Sopenharmony_ci		pqi_start_heartbeat_timer(ctrl_info);
81908c2ecf20Sopenharmony_ci		pqi_ctrl_unblock_requests(ctrl_info);
81918c2ecf20Sopenharmony_ci		return 0;
81928c2ecf20Sopenharmony_ci	}
81938c2ecf20Sopenharmony_ci
81948c2ecf20Sopenharmony_ci	pci_set_power_state(pci_dev, PCI_D0);
81958c2ecf20Sopenharmony_ci	pci_restore_state(pci_dev);
81968c2ecf20Sopenharmony_ci
81978c2ecf20Sopenharmony_ci	return pqi_ctrl_init_resume(ctrl_info);
81988c2ecf20Sopenharmony_ci}
81998c2ecf20Sopenharmony_ci
82008c2ecf20Sopenharmony_ci/* Define the PCI IDs for the controllers that we support. */
82018c2ecf20Sopenharmony_cistatic const struct pci_device_id pqi_pci_id_table[] = {
82028c2ecf20Sopenharmony_ci	{
82038c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82048c2ecf20Sopenharmony_ci			       0x105b, 0x1211)
82058c2ecf20Sopenharmony_ci	},
82068c2ecf20Sopenharmony_ci	{
82078c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82088c2ecf20Sopenharmony_ci			       0x105b, 0x1321)
82098c2ecf20Sopenharmony_ci	},
82108c2ecf20Sopenharmony_ci	{
82118c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82128c2ecf20Sopenharmony_ci			       0x152d, 0x8a22)
82138c2ecf20Sopenharmony_ci	},
82148c2ecf20Sopenharmony_ci	{
82158c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82168c2ecf20Sopenharmony_ci			       0x152d, 0x8a23)
82178c2ecf20Sopenharmony_ci	},
82188c2ecf20Sopenharmony_ci	{
82198c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82208c2ecf20Sopenharmony_ci			       0x152d, 0x8a24)
82218c2ecf20Sopenharmony_ci	},
82228c2ecf20Sopenharmony_ci	{
82238c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82248c2ecf20Sopenharmony_ci			       0x152d, 0x8a36)
82258c2ecf20Sopenharmony_ci	},
82268c2ecf20Sopenharmony_ci	{
82278c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82288c2ecf20Sopenharmony_ci			       0x152d, 0x8a37)
82298c2ecf20Sopenharmony_ci	},
82308c2ecf20Sopenharmony_ci	{
82318c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82328c2ecf20Sopenharmony_ci			       0x193d, 0x8460)
82338c2ecf20Sopenharmony_ci	},
82348c2ecf20Sopenharmony_ci	{
82358c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82368c2ecf20Sopenharmony_ci			       0x193d, 0x1104)
82378c2ecf20Sopenharmony_ci	},
82388c2ecf20Sopenharmony_ci	{
82398c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82408c2ecf20Sopenharmony_ci			       0x193d, 0x1105)
82418c2ecf20Sopenharmony_ci	},
82428c2ecf20Sopenharmony_ci	{
82438c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82448c2ecf20Sopenharmony_ci			       0x193d, 0x1106)
82458c2ecf20Sopenharmony_ci	},
82468c2ecf20Sopenharmony_ci	{
82478c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82488c2ecf20Sopenharmony_ci			       0x193d, 0x1107)
82498c2ecf20Sopenharmony_ci	},
82508c2ecf20Sopenharmony_ci	{
82518c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82528c2ecf20Sopenharmony_ci			       0x193d, 0x8460)
82538c2ecf20Sopenharmony_ci	},
82548c2ecf20Sopenharmony_ci	{
82558c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82568c2ecf20Sopenharmony_ci			       0x193d, 0x8461)
82578c2ecf20Sopenharmony_ci	},
82588c2ecf20Sopenharmony_ci	{
82598c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82608c2ecf20Sopenharmony_ci			       0x193d, 0xc460)
82618c2ecf20Sopenharmony_ci	},
82628c2ecf20Sopenharmony_ci	{
82638c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82648c2ecf20Sopenharmony_ci			       0x193d, 0xc461)
82658c2ecf20Sopenharmony_ci	},
82668c2ecf20Sopenharmony_ci	{
82678c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82688c2ecf20Sopenharmony_ci			       0x193d, 0xf460)
82698c2ecf20Sopenharmony_ci	},
82708c2ecf20Sopenharmony_ci	{
82718c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82728c2ecf20Sopenharmony_ci			       0x193d, 0xf461)
82738c2ecf20Sopenharmony_ci	},
82748c2ecf20Sopenharmony_ci	{
82758c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82768c2ecf20Sopenharmony_ci			       0x1bd4, 0x0045)
82778c2ecf20Sopenharmony_ci	},
82788c2ecf20Sopenharmony_ci	{
82798c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82808c2ecf20Sopenharmony_ci			       0x1bd4, 0x0046)
82818c2ecf20Sopenharmony_ci	},
82828c2ecf20Sopenharmony_ci	{
82838c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82848c2ecf20Sopenharmony_ci			       0x1bd4, 0x0047)
82858c2ecf20Sopenharmony_ci	},
82868c2ecf20Sopenharmony_ci	{
82878c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82888c2ecf20Sopenharmony_ci			       0x1bd4, 0x0048)
82898c2ecf20Sopenharmony_ci	},
82908c2ecf20Sopenharmony_ci	{
82918c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82928c2ecf20Sopenharmony_ci			       0x1bd4, 0x004a)
82938c2ecf20Sopenharmony_ci	},
82948c2ecf20Sopenharmony_ci	{
82958c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
82968c2ecf20Sopenharmony_ci			       0x1bd4, 0x004b)
82978c2ecf20Sopenharmony_ci	},
82988c2ecf20Sopenharmony_ci	{
82998c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83008c2ecf20Sopenharmony_ci			       0x1bd4, 0x004c)
83018c2ecf20Sopenharmony_ci	},
83028c2ecf20Sopenharmony_ci	{
83038c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83048c2ecf20Sopenharmony_ci			       0x1bd4, 0x004f)
83058c2ecf20Sopenharmony_ci	},
83068c2ecf20Sopenharmony_ci	{
83078c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83088c2ecf20Sopenharmony_ci			       0x1bd4, 0x0051)
83098c2ecf20Sopenharmony_ci	},
83108c2ecf20Sopenharmony_ci	{
83118c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83128c2ecf20Sopenharmony_ci			       0x1bd4, 0x0052)
83138c2ecf20Sopenharmony_ci	},
83148c2ecf20Sopenharmony_ci	{
83158c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83168c2ecf20Sopenharmony_ci			       0x1bd4, 0x0053)
83178c2ecf20Sopenharmony_ci	},
83188c2ecf20Sopenharmony_ci	{
83198c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83208c2ecf20Sopenharmony_ci			       0x1bd4, 0x0054)
83218c2ecf20Sopenharmony_ci	},
83228c2ecf20Sopenharmony_ci	{
83238c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83248c2ecf20Sopenharmony_ci			       0x19e5, 0xd227)
83258c2ecf20Sopenharmony_ci	},
83268c2ecf20Sopenharmony_ci	{
83278c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83288c2ecf20Sopenharmony_ci			       0x19e5, 0xd228)
83298c2ecf20Sopenharmony_ci	},
83308c2ecf20Sopenharmony_ci	{
83318c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83328c2ecf20Sopenharmony_ci			       0x19e5, 0xd229)
83338c2ecf20Sopenharmony_ci	},
83348c2ecf20Sopenharmony_ci	{
83358c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83368c2ecf20Sopenharmony_ci			       0x19e5, 0xd22a)
83378c2ecf20Sopenharmony_ci	},
83388c2ecf20Sopenharmony_ci	{
83398c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83408c2ecf20Sopenharmony_ci			       0x19e5, 0xd22b)
83418c2ecf20Sopenharmony_ci	},
83428c2ecf20Sopenharmony_ci	{
83438c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83448c2ecf20Sopenharmony_ci			       0x19e5, 0xd22c)
83458c2ecf20Sopenharmony_ci	},
83468c2ecf20Sopenharmony_ci	{
83478c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83488c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0110)
83498c2ecf20Sopenharmony_ci	},
83508c2ecf20Sopenharmony_ci	{
83518c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83528c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0608)
83538c2ecf20Sopenharmony_ci	},
83548c2ecf20Sopenharmony_ci	{
83558c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83568c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0800)
83578c2ecf20Sopenharmony_ci	},
83588c2ecf20Sopenharmony_ci	{
83598c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83608c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0801)
83618c2ecf20Sopenharmony_ci	},
83628c2ecf20Sopenharmony_ci	{
83638c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83648c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0802)
83658c2ecf20Sopenharmony_ci	},
83668c2ecf20Sopenharmony_ci	{
83678c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83688c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0803)
83698c2ecf20Sopenharmony_ci	},
83708c2ecf20Sopenharmony_ci	{
83718c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83728c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0804)
83738c2ecf20Sopenharmony_ci	},
83748c2ecf20Sopenharmony_ci	{
83758c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83768c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0805)
83778c2ecf20Sopenharmony_ci	},
83788c2ecf20Sopenharmony_ci	{
83798c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83808c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0806)
83818c2ecf20Sopenharmony_ci	},
83828c2ecf20Sopenharmony_ci	{
83838c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83848c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0807)
83858c2ecf20Sopenharmony_ci	},
83868c2ecf20Sopenharmony_ci	{
83878c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83888c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0808)
83898c2ecf20Sopenharmony_ci	},
83908c2ecf20Sopenharmony_ci	{
83918c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83928c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0809)
83938c2ecf20Sopenharmony_ci	},
83948c2ecf20Sopenharmony_ci	{
83958c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
83968c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x080a)
83978c2ecf20Sopenharmony_ci	},
83988c2ecf20Sopenharmony_ci	{
83998c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84008c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0900)
84018c2ecf20Sopenharmony_ci	},
84028c2ecf20Sopenharmony_ci	{
84038c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84048c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0901)
84058c2ecf20Sopenharmony_ci	},
84068c2ecf20Sopenharmony_ci	{
84078c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84088c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0902)
84098c2ecf20Sopenharmony_ci	},
84108c2ecf20Sopenharmony_ci	{
84118c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84128c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0903)
84138c2ecf20Sopenharmony_ci	},
84148c2ecf20Sopenharmony_ci	{
84158c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84168c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0904)
84178c2ecf20Sopenharmony_ci	},
84188c2ecf20Sopenharmony_ci	{
84198c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84208c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0905)
84218c2ecf20Sopenharmony_ci	},
84228c2ecf20Sopenharmony_ci	{
84238c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84248c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0906)
84258c2ecf20Sopenharmony_ci	},
84268c2ecf20Sopenharmony_ci	{
84278c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84288c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0907)
84298c2ecf20Sopenharmony_ci	},
84308c2ecf20Sopenharmony_ci	{
84318c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84328c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x0908)
84338c2ecf20Sopenharmony_ci	},
84348c2ecf20Sopenharmony_ci	{
84358c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84368c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x090a)
84378c2ecf20Sopenharmony_ci	},
84388c2ecf20Sopenharmony_ci	{
84398c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84408c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1200)
84418c2ecf20Sopenharmony_ci	},
84428c2ecf20Sopenharmony_ci	{
84438c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84448c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1201)
84458c2ecf20Sopenharmony_ci	},
84468c2ecf20Sopenharmony_ci	{
84478c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84488c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1202)
84498c2ecf20Sopenharmony_ci	},
84508c2ecf20Sopenharmony_ci	{
84518c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84528c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1280)
84538c2ecf20Sopenharmony_ci	},
84548c2ecf20Sopenharmony_ci	{
84558c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84568c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1281)
84578c2ecf20Sopenharmony_ci	},
84588c2ecf20Sopenharmony_ci	{
84598c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84608c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1282)
84618c2ecf20Sopenharmony_ci	},
84628c2ecf20Sopenharmony_ci	{
84638c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84648c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1300)
84658c2ecf20Sopenharmony_ci	},
84668c2ecf20Sopenharmony_ci	{
84678c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84688c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1301)
84698c2ecf20Sopenharmony_ci	},
84708c2ecf20Sopenharmony_ci	{
84718c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84728c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1302)
84738c2ecf20Sopenharmony_ci	},
84748c2ecf20Sopenharmony_ci	{
84758c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84768c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1303)
84778c2ecf20Sopenharmony_ci	},
84788c2ecf20Sopenharmony_ci	{
84798c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84808c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1380)
84818c2ecf20Sopenharmony_ci	},
84828c2ecf20Sopenharmony_ci	{
84838c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84848c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1400)
84858c2ecf20Sopenharmony_ci	},
84868c2ecf20Sopenharmony_ci	{
84878c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84888c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1402)
84898c2ecf20Sopenharmony_ci	},
84908c2ecf20Sopenharmony_ci	{
84918c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84928c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1410)
84938c2ecf20Sopenharmony_ci	},
84948c2ecf20Sopenharmony_ci	{
84958c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
84968c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1411)
84978c2ecf20Sopenharmony_ci	},
84988c2ecf20Sopenharmony_ci	{
84998c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85008c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1412)
85018c2ecf20Sopenharmony_ci	},
85028c2ecf20Sopenharmony_ci	{
85038c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85048c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1420)
85058c2ecf20Sopenharmony_ci	},
85068c2ecf20Sopenharmony_ci	{
85078c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85088c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1430)
85098c2ecf20Sopenharmony_ci	},
85108c2ecf20Sopenharmony_ci	{
85118c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85128c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1440)
85138c2ecf20Sopenharmony_ci	},
85148c2ecf20Sopenharmony_ci	{
85158c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85168c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1441)
85178c2ecf20Sopenharmony_ci	},
85188c2ecf20Sopenharmony_ci	{
85198c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85208c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1450)
85218c2ecf20Sopenharmony_ci	},
85228c2ecf20Sopenharmony_ci	{
85238c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85248c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1452)
85258c2ecf20Sopenharmony_ci	},
85268c2ecf20Sopenharmony_ci	{
85278c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85288c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1460)
85298c2ecf20Sopenharmony_ci	},
85308c2ecf20Sopenharmony_ci	{
85318c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85328c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1461)
85338c2ecf20Sopenharmony_ci	},
85348c2ecf20Sopenharmony_ci	{
85358c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85368c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1462)
85378c2ecf20Sopenharmony_ci	},
85388c2ecf20Sopenharmony_ci	{
85398c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85408c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1470)
85418c2ecf20Sopenharmony_ci	},
85428c2ecf20Sopenharmony_ci	{
85438c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85448c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1471)
85458c2ecf20Sopenharmony_ci	},
85468c2ecf20Sopenharmony_ci	{
85478c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85488c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1472)
85498c2ecf20Sopenharmony_ci	},
85508c2ecf20Sopenharmony_ci	{
85518c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85528c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1480)
85538c2ecf20Sopenharmony_ci	},
85548c2ecf20Sopenharmony_ci	{
85558c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85568c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1490)
85578c2ecf20Sopenharmony_ci	},
85588c2ecf20Sopenharmony_ci	{
85598c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85608c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x1491)
85618c2ecf20Sopenharmony_ci	},
85628c2ecf20Sopenharmony_ci	{
85638c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85648c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14a0)
85658c2ecf20Sopenharmony_ci	},
85668c2ecf20Sopenharmony_ci	{
85678c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85688c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14a1)
85698c2ecf20Sopenharmony_ci	},
85708c2ecf20Sopenharmony_ci	{
85718c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85728c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14b0)
85738c2ecf20Sopenharmony_ci	},
85748c2ecf20Sopenharmony_ci	{
85758c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85768c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14b1)
85778c2ecf20Sopenharmony_ci	},
85788c2ecf20Sopenharmony_ci	{
85798c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85808c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14c0)
85818c2ecf20Sopenharmony_ci	},
85828c2ecf20Sopenharmony_ci	{
85838c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85848c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14c1)
85858c2ecf20Sopenharmony_ci	},
85868c2ecf20Sopenharmony_ci	{
85878c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85888c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14d0)
85898c2ecf20Sopenharmony_ci	},
85908c2ecf20Sopenharmony_ci	{
85918c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85928c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14e0)
85938c2ecf20Sopenharmony_ci	},
85948c2ecf20Sopenharmony_ci	{
85958c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
85968c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADAPTEC2, 0x14f0)
85978c2ecf20Sopenharmony_ci	},
85988c2ecf20Sopenharmony_ci	{
85998c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86008c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_ADVANTECH, 0x8312)
86018c2ecf20Sopenharmony_ci	},
86028c2ecf20Sopenharmony_ci	{
86038c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86048c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_DELL, 0x1fe0)
86058c2ecf20Sopenharmony_ci	},
86068c2ecf20Sopenharmony_ci	{
86078c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86088c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0600)
86098c2ecf20Sopenharmony_ci	},
86108c2ecf20Sopenharmony_ci	{
86118c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86128c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0601)
86138c2ecf20Sopenharmony_ci	},
86148c2ecf20Sopenharmony_ci	{
86158c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86168c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0602)
86178c2ecf20Sopenharmony_ci	},
86188c2ecf20Sopenharmony_ci	{
86198c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86208c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0603)
86218c2ecf20Sopenharmony_ci	},
86228c2ecf20Sopenharmony_ci	{
86238c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86248c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0609)
86258c2ecf20Sopenharmony_ci	},
86268c2ecf20Sopenharmony_ci	{
86278c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86288c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0650)
86298c2ecf20Sopenharmony_ci	},
86308c2ecf20Sopenharmony_ci	{
86318c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86328c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0651)
86338c2ecf20Sopenharmony_ci	},
86348c2ecf20Sopenharmony_ci	{
86358c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86368c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0652)
86378c2ecf20Sopenharmony_ci	},
86388c2ecf20Sopenharmony_ci	{
86398c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86408c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0653)
86418c2ecf20Sopenharmony_ci	},
86428c2ecf20Sopenharmony_ci	{
86438c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86448c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0654)
86458c2ecf20Sopenharmony_ci	},
86468c2ecf20Sopenharmony_ci	{
86478c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86488c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0655)
86498c2ecf20Sopenharmony_ci	},
86508c2ecf20Sopenharmony_ci	{
86518c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86528c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0700)
86538c2ecf20Sopenharmony_ci	},
86548c2ecf20Sopenharmony_ci	{
86558c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86568c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x0701)
86578c2ecf20Sopenharmony_ci	},
86588c2ecf20Sopenharmony_ci	{
86598c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86608c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x1001)
86618c2ecf20Sopenharmony_ci	},
86628c2ecf20Sopenharmony_ci	{
86638c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86648c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x1002)
86658c2ecf20Sopenharmony_ci	},
86668c2ecf20Sopenharmony_ci	{
86678c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86688c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x1100)
86698c2ecf20Sopenharmony_ci	},
86708c2ecf20Sopenharmony_ci	{
86718c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86728c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_HP, 0x1101)
86738c2ecf20Sopenharmony_ci	},
86748c2ecf20Sopenharmony_ci	{
86758c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86768c2ecf20Sopenharmony_ci			       0x1590, 0x0294)
86778c2ecf20Sopenharmony_ci	},
86788c2ecf20Sopenharmony_ci	{
86798c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86808c2ecf20Sopenharmony_ci			       0x1590, 0x02db)
86818c2ecf20Sopenharmony_ci	},
86828c2ecf20Sopenharmony_ci	{
86838c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86848c2ecf20Sopenharmony_ci			       0x1590, 0x02dc)
86858c2ecf20Sopenharmony_ci	},
86868c2ecf20Sopenharmony_ci	{
86878c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86888c2ecf20Sopenharmony_ci			       0x1590, 0x032e)
86898c2ecf20Sopenharmony_ci	},
86908c2ecf20Sopenharmony_ci	{
86918c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86928c2ecf20Sopenharmony_ci			       0x1d8d, 0x0800)
86938c2ecf20Sopenharmony_ci	},
86948c2ecf20Sopenharmony_ci	{
86958c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
86968c2ecf20Sopenharmony_ci			       0x1d8d, 0x0908)
86978c2ecf20Sopenharmony_ci	},
86988c2ecf20Sopenharmony_ci	{
86998c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
87008c2ecf20Sopenharmony_ci			       0x1d8d, 0x0806)
87018c2ecf20Sopenharmony_ci	},
87028c2ecf20Sopenharmony_ci	{
87038c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
87048c2ecf20Sopenharmony_ci			       0x1d8d, 0x0916)
87058c2ecf20Sopenharmony_ci	},
87068c2ecf20Sopenharmony_ci	{
87078c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
87088c2ecf20Sopenharmony_ci			       PCI_VENDOR_ID_GIGABYTE, 0x1000)
87098c2ecf20Sopenharmony_ci	},
87108c2ecf20Sopenharmony_ci	{
87118c2ecf20Sopenharmony_ci		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
87128c2ecf20Sopenharmony_ci			       PCI_ANY_ID, PCI_ANY_ID)
87138c2ecf20Sopenharmony_ci	},
87148c2ecf20Sopenharmony_ci	{ 0 }
87158c2ecf20Sopenharmony_ci};
87168c2ecf20Sopenharmony_ci
87178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pqi_pci_id_table);
87188c2ecf20Sopenharmony_ci
87198c2ecf20Sopenharmony_cistatic struct pci_driver pqi_pci_driver = {
87208c2ecf20Sopenharmony_ci	.name = DRIVER_NAME_SHORT,
87218c2ecf20Sopenharmony_ci	.id_table = pqi_pci_id_table,
87228c2ecf20Sopenharmony_ci	.probe = pqi_pci_probe,
87238c2ecf20Sopenharmony_ci	.remove = pqi_pci_remove,
87248c2ecf20Sopenharmony_ci	.shutdown = pqi_shutdown,
87258c2ecf20Sopenharmony_ci#if defined(CONFIG_PM)
87268c2ecf20Sopenharmony_ci	.suspend = pqi_suspend,
87278c2ecf20Sopenharmony_ci	.resume = pqi_resume,
87288c2ecf20Sopenharmony_ci#endif
87298c2ecf20Sopenharmony_ci};
87308c2ecf20Sopenharmony_ci
87318c2ecf20Sopenharmony_cistatic int __init pqi_init(void)
87328c2ecf20Sopenharmony_ci{
87338c2ecf20Sopenharmony_ci	int rc;
87348c2ecf20Sopenharmony_ci
87358c2ecf20Sopenharmony_ci	pr_info(DRIVER_NAME "\n");
87368c2ecf20Sopenharmony_ci
87378c2ecf20Sopenharmony_ci	pqi_sas_transport_template = sas_attach_transport(&pqi_sas_transport_functions);
87388c2ecf20Sopenharmony_ci	if (!pqi_sas_transport_template)
87398c2ecf20Sopenharmony_ci		return -ENODEV;
87408c2ecf20Sopenharmony_ci
87418c2ecf20Sopenharmony_ci	pqi_process_module_params();
87428c2ecf20Sopenharmony_ci
87438c2ecf20Sopenharmony_ci	rc = pci_register_driver(&pqi_pci_driver);
87448c2ecf20Sopenharmony_ci	if (rc)
87458c2ecf20Sopenharmony_ci		sas_release_transport(pqi_sas_transport_template);
87468c2ecf20Sopenharmony_ci
87478c2ecf20Sopenharmony_ci	return rc;
87488c2ecf20Sopenharmony_ci}
87498c2ecf20Sopenharmony_ci
87508c2ecf20Sopenharmony_cistatic void __exit pqi_cleanup(void)
87518c2ecf20Sopenharmony_ci{
87528c2ecf20Sopenharmony_ci	pci_unregister_driver(&pqi_pci_driver);
87538c2ecf20Sopenharmony_ci	sas_release_transport(pqi_sas_transport_template);
87548c2ecf20Sopenharmony_ci}
87558c2ecf20Sopenharmony_ci
87568c2ecf20Sopenharmony_cimodule_init(pqi_init);
87578c2ecf20Sopenharmony_cimodule_exit(pqi_cleanup);
87588c2ecf20Sopenharmony_ci
87598c2ecf20Sopenharmony_cistatic void __attribute__((unused)) verify_structures(void)
87608c2ecf20Sopenharmony_ci{
87618c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87628c2ecf20Sopenharmony_ci		sis_host_to_ctrl_doorbell) != 0x20);
87638c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87648c2ecf20Sopenharmony_ci		sis_interrupt_mask) != 0x34);
87658c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87668c2ecf20Sopenharmony_ci		sis_ctrl_to_host_doorbell) != 0x9c);
87678c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87688c2ecf20Sopenharmony_ci		sis_ctrl_to_host_doorbell_clear) != 0xa0);
87698c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87708c2ecf20Sopenharmony_ci		sis_driver_scratch) != 0xb0);
87718c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87728c2ecf20Sopenharmony_ci		sis_firmware_status) != 0xbc);
87738c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87748c2ecf20Sopenharmony_ci		sis_mailbox) != 0x1000);
87758c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers,
87768c2ecf20Sopenharmony_ci		pqi_registers) != 0x4000);
87778c2ecf20Sopenharmony_ci
87788c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_iu_header,
87798c2ecf20Sopenharmony_ci		iu_type) != 0x0);
87808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_iu_header,
87818c2ecf20Sopenharmony_ci		iu_length) != 0x2);
87828c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_iu_header,
87838c2ecf20Sopenharmony_ci		response_queue_id) != 0x4);
87848c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_iu_header,
87858c2ecf20Sopenharmony_ci		work_area) != 0x6);
87868c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_iu_header) != 0x8);
87878c2ecf20Sopenharmony_ci
87888c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
87898c2ecf20Sopenharmony_ci		status) != 0x0);
87908c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
87918c2ecf20Sopenharmony_ci		service_response) != 0x1);
87928c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
87938c2ecf20Sopenharmony_ci		data_present) != 0x2);
87948c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
87958c2ecf20Sopenharmony_ci		reserved) != 0x3);
87968c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
87978c2ecf20Sopenharmony_ci		residual_count) != 0x4);
87988c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
87998c2ecf20Sopenharmony_ci		data_length) != 0x8);
88008c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
88018c2ecf20Sopenharmony_ci		reserved1) != 0xa);
88028c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_error_info,
88038c2ecf20Sopenharmony_ci		data) != 0xc);
88048c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_aio_error_info) != 0x10c);
88058c2ecf20Sopenharmony_ci
88068c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88078c2ecf20Sopenharmony_ci		data_in_result) != 0x0);
88088c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88098c2ecf20Sopenharmony_ci		data_out_result) != 0x1);
88108c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88118c2ecf20Sopenharmony_ci		reserved) != 0x2);
88128c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88138c2ecf20Sopenharmony_ci		status) != 0x5);
88148c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88158c2ecf20Sopenharmony_ci		status_qualifier) != 0x6);
88168c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88178c2ecf20Sopenharmony_ci		sense_data_length) != 0x8);
88188c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88198c2ecf20Sopenharmony_ci		response_data_length) != 0xa);
88208c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88218c2ecf20Sopenharmony_ci		data_in_transferred) != 0xc);
88228c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88238c2ecf20Sopenharmony_ci		data_out_transferred) != 0x10);
88248c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_error_info,
88258c2ecf20Sopenharmony_ci		data) != 0x14);
88268c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_raid_error_info) != 0x114);
88278c2ecf20Sopenharmony_ci
88288c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88298c2ecf20Sopenharmony_ci		signature) != 0x0);
88308c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88318c2ecf20Sopenharmony_ci		function_and_status_code) != 0x8);
88328c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88338c2ecf20Sopenharmony_ci		max_admin_iq_elements) != 0x10);
88348c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88358c2ecf20Sopenharmony_ci		max_admin_oq_elements) != 0x11);
88368c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88378c2ecf20Sopenharmony_ci		admin_iq_element_length) != 0x12);
88388c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88398c2ecf20Sopenharmony_ci		admin_oq_element_length) != 0x13);
88408c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88418c2ecf20Sopenharmony_ci		max_reset_timeout) != 0x14);
88428c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88438c2ecf20Sopenharmony_ci		legacy_intx_status) != 0x18);
88448c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88458c2ecf20Sopenharmony_ci		legacy_intx_mask_set) != 0x1c);
88468c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88478c2ecf20Sopenharmony_ci		legacy_intx_mask_clear) != 0x20);
88488c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88498c2ecf20Sopenharmony_ci		device_status) != 0x40);
88508c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88518c2ecf20Sopenharmony_ci		admin_iq_pi_offset) != 0x48);
88528c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88538c2ecf20Sopenharmony_ci		admin_oq_ci_offset) != 0x50);
88548c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88558c2ecf20Sopenharmony_ci		admin_iq_element_array_addr) != 0x58);
88568c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88578c2ecf20Sopenharmony_ci		admin_oq_element_array_addr) != 0x60);
88588c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88598c2ecf20Sopenharmony_ci		admin_iq_ci_addr) != 0x68);
88608c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88618c2ecf20Sopenharmony_ci		admin_oq_pi_addr) != 0x70);
88628c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88638c2ecf20Sopenharmony_ci		admin_iq_num_elements) != 0x78);
88648c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88658c2ecf20Sopenharmony_ci		admin_oq_num_elements) != 0x79);
88668c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88678c2ecf20Sopenharmony_ci		admin_queue_int_msg_num) != 0x7a);
88688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88698c2ecf20Sopenharmony_ci		device_error) != 0x80);
88708c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88718c2ecf20Sopenharmony_ci		error_details) != 0x88);
88728c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88738c2ecf20Sopenharmony_ci		device_reset) != 0x90);
88748c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_registers,
88758c2ecf20Sopenharmony_ci		power_action) != 0x94);
88768c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_device_registers) != 0x100);
88778c2ecf20Sopenharmony_ci
88788c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88798c2ecf20Sopenharmony_ci		header.iu_type) != 0);
88808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88818c2ecf20Sopenharmony_ci		header.iu_length) != 2);
88828c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88838c2ecf20Sopenharmony_ci		header.work_area) != 6);
88848c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88858c2ecf20Sopenharmony_ci		request_id) != 8);
88868c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88878c2ecf20Sopenharmony_ci		function_code) != 10);
88888c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88898c2ecf20Sopenharmony_ci		data.report_device_capability.buffer_length) != 44);
88908c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88918c2ecf20Sopenharmony_ci		data.report_device_capability.sg_descriptor) != 48);
88928c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88938c2ecf20Sopenharmony_ci		data.create_operational_iq.queue_id) != 12);
88948c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88958c2ecf20Sopenharmony_ci		data.create_operational_iq.element_array_addr) != 16);
88968c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88978c2ecf20Sopenharmony_ci		data.create_operational_iq.ci_addr) != 24);
88988c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
88998c2ecf20Sopenharmony_ci		data.create_operational_iq.num_elements) != 32);
89008c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89018c2ecf20Sopenharmony_ci		data.create_operational_iq.element_length) != 34);
89028c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89038c2ecf20Sopenharmony_ci		data.create_operational_iq.queue_protocol) != 36);
89048c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89058c2ecf20Sopenharmony_ci		data.create_operational_oq.queue_id) != 12);
89068c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89078c2ecf20Sopenharmony_ci		data.create_operational_oq.element_array_addr) != 16);
89088c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89098c2ecf20Sopenharmony_ci		data.create_operational_oq.pi_addr) != 24);
89108c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89118c2ecf20Sopenharmony_ci		data.create_operational_oq.num_elements) != 32);
89128c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89138c2ecf20Sopenharmony_ci		data.create_operational_oq.element_length) != 34);
89148c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89158c2ecf20Sopenharmony_ci		data.create_operational_oq.queue_protocol) != 36);
89168c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89178c2ecf20Sopenharmony_ci		data.create_operational_oq.int_msg_num) != 40);
89188c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89198c2ecf20Sopenharmony_ci		data.create_operational_oq.coalescing_count) != 42);
89208c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89218c2ecf20Sopenharmony_ci		data.create_operational_oq.min_coalescing_time) != 44);
89228c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89238c2ecf20Sopenharmony_ci		data.create_operational_oq.max_coalescing_time) != 48);
89248c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_request,
89258c2ecf20Sopenharmony_ci		data.delete_operational_queue.queue_id) != 12);
89268c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_general_admin_request) != 64);
89278c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof_field(struct pqi_general_admin_request,
89288c2ecf20Sopenharmony_ci		data.create_operational_iq) != 64 - 11);
89298c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof_field(struct pqi_general_admin_request,
89308c2ecf20Sopenharmony_ci		data.create_operational_oq) != 64 - 11);
89318c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof_field(struct pqi_general_admin_request,
89328c2ecf20Sopenharmony_ci		data.delete_operational_queue) != 64 - 11);
89338c2ecf20Sopenharmony_ci
89348c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89358c2ecf20Sopenharmony_ci		header.iu_type) != 0);
89368c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89378c2ecf20Sopenharmony_ci		header.iu_length) != 2);
89388c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89398c2ecf20Sopenharmony_ci		header.work_area) != 6);
89408c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89418c2ecf20Sopenharmony_ci		request_id) != 8);
89428c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89438c2ecf20Sopenharmony_ci		function_code) != 10);
89448c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89458c2ecf20Sopenharmony_ci		status) != 11);
89468c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89478c2ecf20Sopenharmony_ci		data.create_operational_iq.status_descriptor) != 12);
89488c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89498c2ecf20Sopenharmony_ci		data.create_operational_iq.iq_pi_offset) != 16);
89508c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89518c2ecf20Sopenharmony_ci		data.create_operational_oq.status_descriptor) != 12);
89528c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_admin_response,
89538c2ecf20Sopenharmony_ci		data.create_operational_oq.oq_ci_offset) != 16);
89548c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_general_admin_response) != 64);
89558c2ecf20Sopenharmony_ci
89568c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89578c2ecf20Sopenharmony_ci		header.iu_type) != 0);
89588c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89598c2ecf20Sopenharmony_ci		header.iu_length) != 2);
89608c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89618c2ecf20Sopenharmony_ci		header.response_queue_id) != 4);
89628c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89638c2ecf20Sopenharmony_ci		header.work_area) != 6);
89648c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89658c2ecf20Sopenharmony_ci		request_id) != 8);
89668c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89678c2ecf20Sopenharmony_ci		nexus_id) != 10);
89688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89698c2ecf20Sopenharmony_ci		buffer_length) != 12);
89708c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89718c2ecf20Sopenharmony_ci		lun_number) != 16);
89728c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89738c2ecf20Sopenharmony_ci		protocol_specific) != 24);
89748c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89758c2ecf20Sopenharmony_ci		error_index) != 27);
89768c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89778c2ecf20Sopenharmony_ci		cdb) != 32);
89788c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89798c2ecf20Sopenharmony_ci		timeout) != 60);
89808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_raid_path_request,
89818c2ecf20Sopenharmony_ci		sg_descriptors) != 64);
89828c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_raid_path_request) !=
89838c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
89848c2ecf20Sopenharmony_ci
89858c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89868c2ecf20Sopenharmony_ci		header.iu_type) != 0);
89878c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89888c2ecf20Sopenharmony_ci		header.iu_length) != 2);
89898c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89908c2ecf20Sopenharmony_ci		header.response_queue_id) != 4);
89918c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89928c2ecf20Sopenharmony_ci		header.work_area) != 6);
89938c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89948c2ecf20Sopenharmony_ci		request_id) != 8);
89958c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89968c2ecf20Sopenharmony_ci		nexus_id) != 12);
89978c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
89988c2ecf20Sopenharmony_ci		buffer_length) != 16);
89998c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90008c2ecf20Sopenharmony_ci		data_encryption_key_index) != 22);
90018c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90028c2ecf20Sopenharmony_ci		encrypt_tweak_lower) != 24);
90038c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90048c2ecf20Sopenharmony_ci		encrypt_tweak_upper) != 28);
90058c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90068c2ecf20Sopenharmony_ci		cdb) != 32);
90078c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90088c2ecf20Sopenharmony_ci		error_index) != 48);
90098c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90108c2ecf20Sopenharmony_ci		num_sg_descriptors) != 50);
90118c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90128c2ecf20Sopenharmony_ci		cdb_length) != 51);
90138c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90148c2ecf20Sopenharmony_ci		lun_number) != 52);
90158c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_aio_path_request,
90168c2ecf20Sopenharmony_ci		sg_descriptors) != 64);
90178c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_aio_path_request) !=
90188c2ecf20Sopenharmony_ci		PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
90198c2ecf20Sopenharmony_ci
90208c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_io_response,
90218c2ecf20Sopenharmony_ci		header.iu_type) != 0);
90228c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_io_response,
90238c2ecf20Sopenharmony_ci		header.iu_length) != 2);
90248c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_io_response,
90258c2ecf20Sopenharmony_ci		request_id) != 8);
90268c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_io_response,
90278c2ecf20Sopenharmony_ci		error_index) != 10);
90288c2ecf20Sopenharmony_ci
90298c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90308c2ecf20Sopenharmony_ci		header.iu_type) != 0);
90318c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90328c2ecf20Sopenharmony_ci		header.iu_length) != 2);
90338c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90348c2ecf20Sopenharmony_ci		header.response_queue_id) != 4);
90358c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90368c2ecf20Sopenharmony_ci		request_id) != 8);
90378c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90388c2ecf20Sopenharmony_ci		data.report_event_configuration.buffer_length) != 12);
90398c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90408c2ecf20Sopenharmony_ci		data.report_event_configuration.sg_descriptors) != 16);
90418c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90428c2ecf20Sopenharmony_ci		data.set_event_configuration.global_event_oq_id) != 10);
90438c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90448c2ecf20Sopenharmony_ci		data.set_event_configuration.buffer_length) != 12);
90458c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_general_management_request,
90468c2ecf20Sopenharmony_ci		data.set_event_configuration.sg_descriptors) != 16);
90478c2ecf20Sopenharmony_ci
90488c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_iu_layer_descriptor,
90498c2ecf20Sopenharmony_ci		max_inbound_iu_length) != 6);
90508c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_iu_layer_descriptor,
90518c2ecf20Sopenharmony_ci		max_outbound_iu_length) != 14);
90528c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_iu_layer_descriptor) != 16);
90538c2ecf20Sopenharmony_ci
90548c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90558c2ecf20Sopenharmony_ci		data_length) != 0);
90568c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90578c2ecf20Sopenharmony_ci		iq_arbitration_priority_support_bitmask) != 8);
90588c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90598c2ecf20Sopenharmony_ci		maximum_aw_a) != 9);
90608c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90618c2ecf20Sopenharmony_ci		maximum_aw_b) != 10);
90628c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90638c2ecf20Sopenharmony_ci		maximum_aw_c) != 11);
90648c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90658c2ecf20Sopenharmony_ci		max_inbound_queues) != 16);
90668c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90678c2ecf20Sopenharmony_ci		max_elements_per_iq) != 18);
90688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90698c2ecf20Sopenharmony_ci		max_iq_element_length) != 24);
90708c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90718c2ecf20Sopenharmony_ci		min_iq_element_length) != 26);
90728c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90738c2ecf20Sopenharmony_ci		max_outbound_queues) != 30);
90748c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90758c2ecf20Sopenharmony_ci		max_elements_per_oq) != 32);
90768c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90778c2ecf20Sopenharmony_ci		intr_coalescing_time_granularity) != 34);
90788c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90798c2ecf20Sopenharmony_ci		max_oq_element_length) != 36);
90808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90818c2ecf20Sopenharmony_ci		min_oq_element_length) != 38);
90828c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_device_capability,
90838c2ecf20Sopenharmony_ci		iu_layer_descriptors) != 64);
90848c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_device_capability) != 576);
90858c2ecf20Sopenharmony_ci
90868c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_descriptor,
90878c2ecf20Sopenharmony_ci		event_type) != 0);
90888c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_descriptor,
90898c2ecf20Sopenharmony_ci		oq_id) != 2);
90908c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_event_descriptor) != 4);
90918c2ecf20Sopenharmony_ci
90928c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_config,
90938c2ecf20Sopenharmony_ci		num_event_descriptors) != 2);
90948c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_config,
90958c2ecf20Sopenharmony_ci		descriptors) != 4);
90968c2ecf20Sopenharmony_ci
90978c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_NUM_SUPPORTED_EVENTS !=
90988c2ecf20Sopenharmony_ci		ARRAY_SIZE(pqi_supported_event_types));
90998c2ecf20Sopenharmony_ci
91008c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_response,
91018c2ecf20Sopenharmony_ci		header.iu_type) != 0);
91028c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_response,
91038c2ecf20Sopenharmony_ci		header.iu_length) != 2);
91048c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_response,
91058c2ecf20Sopenharmony_ci		event_type) != 8);
91068c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_response,
91078c2ecf20Sopenharmony_ci		event_id) != 10);
91088c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_response,
91098c2ecf20Sopenharmony_ci		additional_event_id) != 12);
91108c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_response,
91118c2ecf20Sopenharmony_ci		data) != 16);
91128c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_event_response) != 32);
91138c2ecf20Sopenharmony_ci
91148c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request,
91158c2ecf20Sopenharmony_ci		header.iu_type) != 0);
91168c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request,
91178c2ecf20Sopenharmony_ci		header.iu_length) != 2);
91188c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request,
91198c2ecf20Sopenharmony_ci		event_type) != 8);
91208c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request,
91218c2ecf20Sopenharmony_ci		event_id) != 10);
91228c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request,
91238c2ecf20Sopenharmony_ci		additional_event_id) != 12);
91248c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_event_acknowledge_request) != 16);
91258c2ecf20Sopenharmony_ci
91268c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91278c2ecf20Sopenharmony_ci		header.iu_type) != 0);
91288c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91298c2ecf20Sopenharmony_ci		header.iu_length) != 2);
91308c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91318c2ecf20Sopenharmony_ci		request_id) != 8);
91328c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91338c2ecf20Sopenharmony_ci		nexus_id) != 10);
91348c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91358c2ecf20Sopenharmony_ci		timeout) != 14);
91368c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91378c2ecf20Sopenharmony_ci		lun_number) != 16);
91388c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91398c2ecf20Sopenharmony_ci		protocol_specific) != 24);
91408c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91418c2ecf20Sopenharmony_ci		outbound_queue_id_to_manage) != 26);
91428c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91438c2ecf20Sopenharmony_ci		request_id_to_manage) != 28);
91448c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_request,
91458c2ecf20Sopenharmony_ci		task_management_function) != 30);
91468c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_task_management_request) != 32);
91478c2ecf20Sopenharmony_ci
91488c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_response,
91498c2ecf20Sopenharmony_ci		header.iu_type) != 0);
91508c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_response,
91518c2ecf20Sopenharmony_ci		header.iu_length) != 2);
91528c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_response,
91538c2ecf20Sopenharmony_ci		request_id) != 8);
91548c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_response,
91558c2ecf20Sopenharmony_ci		nexus_id) != 10);
91568c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_response,
91578c2ecf20Sopenharmony_ci		additional_response_info) != 12);
91588c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct pqi_task_management_response,
91598c2ecf20Sopenharmony_ci		response_code) != 15);
91608c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct pqi_task_management_response) != 16);
91618c2ecf20Sopenharmony_ci
91628c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_controller,
91638c2ecf20Sopenharmony_ci		configured_logical_drive_count) != 0);
91648c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_controller,
91658c2ecf20Sopenharmony_ci		configuration_signature) != 1);
91668c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_controller,
91678c2ecf20Sopenharmony_ci		firmware_version) != 5);
91688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_controller,
91698c2ecf20Sopenharmony_ci		extended_logical_unit_count) != 154);
91708c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_controller,
91718c2ecf20Sopenharmony_ci		firmware_build_number) != 190);
91728c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_controller,
91738c2ecf20Sopenharmony_ci		controller_mode) != 292);
91748c2ecf20Sopenharmony_ci
91758c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91768c2ecf20Sopenharmony_ci		phys_bay_in_box) != 115);
91778c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91788c2ecf20Sopenharmony_ci		device_type) != 120);
91798c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91808c2ecf20Sopenharmony_ci		redundant_path_present_map) != 1736);
91818c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91828c2ecf20Sopenharmony_ci		active_path_number) != 1738);
91838c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91848c2ecf20Sopenharmony_ci		alternate_paths_phys_connector) != 1739);
91858c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91868c2ecf20Sopenharmony_ci		alternate_paths_phys_box_on_port) != 1755);
91878c2ecf20Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct bmic_identify_physical_device,
91888c2ecf20Sopenharmony_ci		current_queue_depth_limit) != 1796);
91898c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct bmic_identify_physical_device) != 2560);
91908c2ecf20Sopenharmony_ci
91918c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_ADMIN_IQ_NUM_ELEMENTS > 255);
91928c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_ADMIN_OQ_NUM_ELEMENTS > 255);
91938c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_ADMIN_IQ_ELEMENT_LENGTH %
91948c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0);
91958c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_ADMIN_OQ_ELEMENT_LENGTH %
91968c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0);
91978c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH > 1048560);
91988c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH %
91998c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0);
92008c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH > 1048560);
92018c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH %
92028c2ecf20Sopenharmony_ci		PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0);
92038c2ecf20Sopenharmony_ci
92048c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_RESERVED_IO_SLOTS >= PQI_MAX_OUTSTANDING_REQUESTS);
92058c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PQI_RESERVED_IO_SLOTS >=
92068c2ecf20Sopenharmony_ci		PQI_MAX_OUTSTANDING_REQUESTS_KDUMP);
92078c2ecf20Sopenharmony_ci}
9208