162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Written By: Anil Ravindranath<anil_ravindranath@pmc-sierra.com>
662306a36Sopenharmony_ci *             PMC-Sierra Inc
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2008, 2009 PMC Sierra Inc
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/ioport.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/wait.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/sched.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/blkdev.h>
2362306a36Sopenharmony_ci#include <linux/firmware.h>
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/moduleparam.h>
2662306a36Sopenharmony_ci#include <linux/hdreg.h>
2762306a36Sopenharmony_ci#include <linux/io.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <asm/irq.h>
3062306a36Sopenharmony_ci#include <asm/processor.h>
3162306a36Sopenharmony_ci#include <linux/libata.h>
3262306a36Sopenharmony_ci#include <linux/mutex.h>
3362306a36Sopenharmony_ci#include <linux/ktime.h>
3462306a36Sopenharmony_ci#include <scsi/scsi.h>
3562306a36Sopenharmony_ci#include <scsi/scsi_host.h>
3662306a36Sopenharmony_ci#include <scsi/scsi_device.h>
3762306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
3862306a36Sopenharmony_ci#include <scsi/scsi_eh.h>
3962306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
4062306a36Sopenharmony_ci#include <scsi/scsicam.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "pmcraid.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci *   Module configuration parameters
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_cistatic unsigned int pmcraid_debug_log;
4862306a36Sopenharmony_cistatic unsigned int pmcraid_disable_aen;
4962306a36Sopenharmony_cistatic unsigned int pmcraid_log_level = IOASC_LOG_LEVEL_MUST;
5062306a36Sopenharmony_cistatic unsigned int pmcraid_enable_msix;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * Data structures to support multiple adapters by the LLD.
5462306a36Sopenharmony_ci * pmcraid_adapter_count - count of configured adapters
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistatic atomic_t pmcraid_adapter_count = ATOMIC_INIT(0);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Supporting user-level control interface through IOCTL commands.
6062306a36Sopenharmony_ci * pmcraid_major - major number to use
6162306a36Sopenharmony_ci * pmcraid_minor - minor number(s) to use
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_cistatic unsigned int pmcraid_major;
6462306a36Sopenharmony_cistatic struct class *pmcraid_class;
6562306a36Sopenharmony_cistatic DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * Module parameters
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ciMODULE_AUTHOR("Anil Ravindranath<anil_ravindranath@pmc-sierra.com>");
7162306a36Sopenharmony_ciMODULE_DESCRIPTION("PMC Sierra MaxRAID Controller Driver");
7262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7362306a36Sopenharmony_ciMODULE_VERSION(PMCRAID_DRIVER_VERSION);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cimodule_param_named(log_level, pmcraid_log_level, uint, (S_IRUGO | S_IWUSR));
7662306a36Sopenharmony_ciMODULE_PARM_DESC(log_level,
7762306a36Sopenharmony_ci		 "Enables firmware error code logging, default :1 high-severity"
7862306a36Sopenharmony_ci		 " errors, 2: all errors including high-severity errors,"
7962306a36Sopenharmony_ci		 " 0: disables logging");
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cimodule_param_named(debug, pmcraid_debug_log, uint, (S_IRUGO | S_IWUSR));
8262306a36Sopenharmony_ciMODULE_PARM_DESC(debug,
8362306a36Sopenharmony_ci		 "Enable driver verbose message logging. Set 1 to enable."
8462306a36Sopenharmony_ci		 "(default: 0)");
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cimodule_param_named(disable_aen, pmcraid_disable_aen, uint, (S_IRUGO | S_IWUSR));
8762306a36Sopenharmony_ciMODULE_PARM_DESC(disable_aen,
8862306a36Sopenharmony_ci		 "Disable driver aen notifications to apps. Set 1 to disable."
8962306a36Sopenharmony_ci		 "(default: 0)");
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* chip specific constants for PMC MaxRAID controllers (same for
9262306a36Sopenharmony_ci * 0x5220 and 0x8010
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic struct pmcraid_chip_details pmcraid_chip_cfg[] = {
9562306a36Sopenharmony_ci	{
9662306a36Sopenharmony_ci	 .ioastatus = 0x0,
9762306a36Sopenharmony_ci	 .ioarrin = 0x00040,
9862306a36Sopenharmony_ci	 .mailbox = 0x7FC30,
9962306a36Sopenharmony_ci	 .global_intr_mask = 0x00034,
10062306a36Sopenharmony_ci	 .ioa_host_intr = 0x0009C,
10162306a36Sopenharmony_ci	 .ioa_host_intr_clr = 0x000A0,
10262306a36Sopenharmony_ci	 .ioa_host_msix_intr = 0x7FC40,
10362306a36Sopenharmony_ci	 .ioa_host_mask = 0x7FC28,
10462306a36Sopenharmony_ci	 .ioa_host_mask_clr = 0x7FC28,
10562306a36Sopenharmony_ci	 .host_ioa_intr = 0x00020,
10662306a36Sopenharmony_ci	 .host_ioa_intr_clr = 0x00020,
10762306a36Sopenharmony_ci	 .transop_timeout = 300
10862306a36Sopenharmony_ci	 }
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * PCI device ids supported by pmcraid driver
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistatic struct pci_device_id pmcraid_pci_table[] = {
11562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID),
11662306a36Sopenharmony_ci	  0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0]
11762306a36Sopenharmony_ci	},
11862306a36Sopenharmony_ci	{}
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pmcraid_pci_table);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/**
12662306a36Sopenharmony_ci * pmcraid_slave_alloc - Prepare for commands to a device
12762306a36Sopenharmony_ci * @scsi_dev: scsi device struct
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * This function is called by mid-layer prior to sending any command to the new
13062306a36Sopenharmony_ci * device. Stores resource entry details of the device in scsi_device struct.
13162306a36Sopenharmony_ci * Queuecommand uses the resource handle and other details to fill up IOARCB
13262306a36Sopenharmony_ci * while sending commands to the device.
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * Return value:
13562306a36Sopenharmony_ci *	  0 on success / -ENXIO if device does not exist
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic int pmcraid_slave_alloc(struct scsi_device *scsi_dev)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct pmcraid_resource_entry *temp, *res = NULL;
14062306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
14162306a36Sopenharmony_ci	u8 target, bus, lun;
14262306a36Sopenharmony_ci	unsigned long lock_flags;
14362306a36Sopenharmony_ci	int rc = -ENXIO;
14462306a36Sopenharmony_ci	u16 fw_version;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	pinstance = shost_priv(scsi_dev->host);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Driver exposes VSET and GSCSI resources only; all other device types
15162306a36Sopenharmony_ci	 * are not exposed. Resource list is synchronized using resource lock
15262306a36Sopenharmony_ci	 * so any traversal or modifications to the list should be done inside
15362306a36Sopenharmony_ci	 * this lock
15462306a36Sopenharmony_ci	 */
15562306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
15662306a36Sopenharmony_ci	list_for_each_entry(temp, &pinstance->used_res_q, queue) {
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		/* do not expose VSETs with order-ids > MAX_VSET_TARGETS */
15962306a36Sopenharmony_ci		if (RES_IS_VSET(temp->cfg_entry)) {
16062306a36Sopenharmony_ci			if (fw_version <= PMCRAID_FW_VERSION_1)
16162306a36Sopenharmony_ci				target = temp->cfg_entry.unique_flags1;
16262306a36Sopenharmony_ci			else
16362306a36Sopenharmony_ci				target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci			if (target > PMCRAID_MAX_VSET_TARGETS)
16662306a36Sopenharmony_ci				continue;
16762306a36Sopenharmony_ci			bus = PMCRAID_VSET_BUS_ID;
16862306a36Sopenharmony_ci			lun = 0;
16962306a36Sopenharmony_ci		} else if (RES_IS_GSCSI(temp->cfg_entry)) {
17062306a36Sopenharmony_ci			target = RES_TARGET(temp->cfg_entry.resource_address);
17162306a36Sopenharmony_ci			bus = PMCRAID_PHYS_BUS_ID;
17262306a36Sopenharmony_ci			lun = RES_LUN(temp->cfg_entry.resource_address);
17362306a36Sopenharmony_ci		} else {
17462306a36Sopenharmony_ci			continue;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		if (bus == scsi_dev->channel &&
17862306a36Sopenharmony_ci		    target == scsi_dev->id &&
17962306a36Sopenharmony_ci		    lun == scsi_dev->lun) {
18062306a36Sopenharmony_ci			res = temp;
18162306a36Sopenharmony_ci			break;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (res) {
18662306a36Sopenharmony_ci		res->scsi_dev = scsi_dev;
18762306a36Sopenharmony_ci		scsi_dev->hostdata = res;
18862306a36Sopenharmony_ci		res->change_detected = 0;
18962306a36Sopenharmony_ci		atomic_set(&res->read_failures, 0);
19062306a36Sopenharmony_ci		atomic_set(&res->write_failures, 0);
19162306a36Sopenharmony_ci		rc = 0;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
19462306a36Sopenharmony_ci	return rc;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * pmcraid_slave_configure - Configures a SCSI device
19962306a36Sopenharmony_ci * @scsi_dev: scsi device struct
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci * This function is executed by SCSI mid layer just after a device is first
20262306a36Sopenharmony_ci * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the
20362306a36Sopenharmony_ci * timeout value (default 30s) will be over-written to a higher value (60s)
20462306a36Sopenharmony_ci * and max_sectors value will be over-written to 512. It also sets queue depth
20562306a36Sopenharmony_ci * to host->cmd_per_lun value
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * Return value:
20862306a36Sopenharmony_ci *	  0 on success
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cistatic int pmcraid_slave_configure(struct scsi_device *scsi_dev)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct pmcraid_resource_entry *res = scsi_dev->hostdata;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (!res)
21562306a36Sopenharmony_ci		return 0;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* LLD exposes VSETs and Enclosure devices only */
21862306a36Sopenharmony_ci	if (RES_IS_GSCSI(res->cfg_entry) &&
21962306a36Sopenharmony_ci	    scsi_dev->type != TYPE_ENCLOSURE)
22062306a36Sopenharmony_ci		return -ENXIO;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	pmcraid_info("configuring %x:%x:%x:%x\n",
22362306a36Sopenharmony_ci		     scsi_dev->host->unique_id,
22462306a36Sopenharmony_ci		     scsi_dev->channel,
22562306a36Sopenharmony_ci		     scsi_dev->id,
22662306a36Sopenharmony_ci		     (u8)scsi_dev->lun);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (RES_IS_GSCSI(res->cfg_entry)) {
22962306a36Sopenharmony_ci		scsi_dev->allow_restart = 1;
23062306a36Sopenharmony_ci	} else if (RES_IS_VSET(res->cfg_entry)) {
23162306a36Sopenharmony_ci		scsi_dev->allow_restart = 1;
23262306a36Sopenharmony_ci		blk_queue_rq_timeout(scsi_dev->request_queue,
23362306a36Sopenharmony_ci				     PMCRAID_VSET_IO_TIMEOUT);
23462306a36Sopenharmony_ci		blk_queue_max_hw_sectors(scsi_dev->request_queue,
23562306a36Sopenharmony_ci				      PMCRAID_VSET_MAX_SECTORS);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * We never want to report TCQ support for these types of devices.
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry))
24262306a36Sopenharmony_ci		scsi_dev->tagged_supported = 0;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/**
24862306a36Sopenharmony_ci * pmcraid_slave_destroy - Unconfigure a SCSI device before removing it
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * @scsi_dev: scsi device struct
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * This is called by mid-layer before removing a device. Pointer assignments
25362306a36Sopenharmony_ci * done in pmcraid_slave_alloc will be reset to NULL here.
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci * Return value
25662306a36Sopenharmony_ci *   none
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct pmcraid_resource_entry *res;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	res = (struct pmcraid_resource_entry *)scsi_dev->hostdata;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (res)
26562306a36Sopenharmony_ci		res->scsi_dev = NULL;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	scsi_dev->hostdata = NULL;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/**
27162306a36Sopenharmony_ci * pmcraid_change_queue_depth - Change the device's queue depth
27262306a36Sopenharmony_ci * @scsi_dev: scsi device struct
27362306a36Sopenharmony_ci * @depth: depth to set
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * Return value
27662306a36Sopenharmony_ci *	actual depth set
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_cistatic int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	if (depth > PMCRAID_MAX_CMD_PER_LUN)
28162306a36Sopenharmony_ci		depth = PMCRAID_MAX_CMD_PER_LUN;
28262306a36Sopenharmony_ci	return scsi_change_queue_depth(scsi_dev, depth);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/**
28662306a36Sopenharmony_ci * pmcraid_init_cmdblk - initializes a command block
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * @cmd: pointer to struct pmcraid_cmd to be initialized
28962306a36Sopenharmony_ci * @index: if >=0 first time initialization; otherwise reinitialization
29062306a36Sopenharmony_ci *
29162306a36Sopenharmony_ci * Return Value
29262306a36Sopenharmony_ci *	 None
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
29762306a36Sopenharmony_ci	dma_addr_t dma_addr = cmd->ioa_cb_bus_addr;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (index >= 0) {
30062306a36Sopenharmony_ci		/* first time initialization (called from  probe) */
30162306a36Sopenharmony_ci		u32 ioasa_offset =
30262306a36Sopenharmony_ci			offsetof(struct pmcraid_control_block, ioasa);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		cmd->index = index;
30562306a36Sopenharmony_ci		ioarcb->response_handle = cpu_to_le32(index << 2);
30662306a36Sopenharmony_ci		ioarcb->ioarcb_bus_addr = cpu_to_le64(dma_addr);
30762306a36Sopenharmony_ci		ioarcb->ioasa_bus_addr = cpu_to_le64(dma_addr + ioasa_offset);
30862306a36Sopenharmony_ci		ioarcb->ioasa_len = cpu_to_le16(sizeof(struct pmcraid_ioasa));
30962306a36Sopenharmony_ci	} else {
31062306a36Sopenharmony_ci		/* re-initialization of various lengths, called once command is
31162306a36Sopenharmony_ci		 * processed by IOA
31262306a36Sopenharmony_ci		 */
31362306a36Sopenharmony_ci		memset(&cmd->ioa_cb->ioarcb.cdb, 0, PMCRAID_MAX_CDB_LEN);
31462306a36Sopenharmony_ci		ioarcb->hrrq_id = 0;
31562306a36Sopenharmony_ci		ioarcb->request_flags0 = 0;
31662306a36Sopenharmony_ci		ioarcb->request_flags1 = 0;
31762306a36Sopenharmony_ci		ioarcb->cmd_timeout = 0;
31862306a36Sopenharmony_ci		ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL);
31962306a36Sopenharmony_ci		ioarcb->ioadl_bus_addr = 0;
32062306a36Sopenharmony_ci		ioarcb->ioadl_length = 0;
32162306a36Sopenharmony_ci		ioarcb->data_transfer_length = 0;
32262306a36Sopenharmony_ci		ioarcb->add_cmd_param_length = 0;
32362306a36Sopenharmony_ci		ioarcb->add_cmd_param_offset = 0;
32462306a36Sopenharmony_ci		cmd->ioa_cb->ioasa.ioasc = 0;
32562306a36Sopenharmony_ci		cmd->ioa_cb->ioasa.residual_data_length = 0;
32662306a36Sopenharmony_ci		cmd->time_left = 0;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	cmd->cmd_done = NULL;
33062306a36Sopenharmony_ci	cmd->scsi_cmd = NULL;
33162306a36Sopenharmony_ci	cmd->release = 0;
33262306a36Sopenharmony_ci	cmd->completion_req = 0;
33362306a36Sopenharmony_ci	cmd->sense_buffer = NULL;
33462306a36Sopenharmony_ci	cmd->sense_buffer_dma = 0;
33562306a36Sopenharmony_ci	cmd->dma_handle = 0;
33662306a36Sopenharmony_ci	timer_setup(&cmd->timer, NULL, 0);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/**
34062306a36Sopenharmony_ci * pmcraid_reinit_cmdblk - reinitialize a command block
34162306a36Sopenharmony_ci *
34262306a36Sopenharmony_ci * @cmd: pointer to struct pmcraid_cmd to be reinitialized
34362306a36Sopenharmony_ci *
34462306a36Sopenharmony_ci * Return Value
34562306a36Sopenharmony_ci *	 None
34662306a36Sopenharmony_ci */
34762306a36Sopenharmony_cistatic void pmcraid_reinit_cmdblk(struct pmcraid_cmd *cmd)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	pmcraid_init_cmdblk(cmd, -1);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/**
35362306a36Sopenharmony_ci * pmcraid_get_free_cmd - get a free cmd block from command block pool
35462306a36Sopenharmony_ci * @pinstance: adapter instance structure
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci * Return Value:
35762306a36Sopenharmony_ci *	returns pointer to cmd block or NULL if no blocks are available
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_cistatic struct pmcraid_cmd *pmcraid_get_free_cmd(
36062306a36Sopenharmony_ci	struct pmcraid_instance *pinstance
36162306a36Sopenharmony_ci)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct pmcraid_cmd *cmd = NULL;
36462306a36Sopenharmony_ci	unsigned long lock_flags;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* free cmd block list is protected by free_pool_lock */
36762306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (!list_empty(&pinstance->free_cmd_pool)) {
37062306a36Sopenharmony_ci		cmd = list_entry(pinstance->free_cmd_pool.next,
37162306a36Sopenharmony_ci				 struct pmcraid_cmd, free_list);
37262306a36Sopenharmony_ci		list_del(&cmd->free_list);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Initialize the command block before giving it the caller */
37762306a36Sopenharmony_ci	if (cmd != NULL)
37862306a36Sopenharmony_ci		pmcraid_reinit_cmdblk(cmd);
37962306a36Sopenharmony_ci	return cmd;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/**
38362306a36Sopenharmony_ci * pmcraid_return_cmd - return a completed command block back into free pool
38462306a36Sopenharmony_ci * @cmd: pointer to the command block
38562306a36Sopenharmony_ci *
38662306a36Sopenharmony_ci * Return Value:
38762306a36Sopenharmony_ci *	nothing
38862306a36Sopenharmony_ci */
38962306a36Sopenharmony_cistatic void pmcraid_return_cmd(struct pmcraid_cmd *cmd)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
39262306a36Sopenharmony_ci	unsigned long lock_flags;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags);
39562306a36Sopenharmony_ci	list_add_tail(&cmd->free_list, &pinstance->free_cmd_pool);
39662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/**
40062306a36Sopenharmony_ci * pmcraid_read_interrupts -  reads IOA interrupts
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * Return value
40562306a36Sopenharmony_ci *	 interrupts read from IOA
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_cistatic u32 pmcraid_read_interrupts(struct pmcraid_instance *pinstance)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	return (pinstance->interrupt_mode) ?
41062306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_msix_interrupt_reg) :
41162306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * pmcraid_disable_interrupts - Masks and clears all specified interrupts
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
41862306a36Sopenharmony_ci * @intrs: interrupts to disable
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * Return Value
42162306a36Sopenharmony_ci *	 None
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_cistatic void pmcraid_disable_interrupts(
42462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
42562306a36Sopenharmony_ci	u32 intrs
42662306a36Sopenharmony_ci)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);
42962306a36Sopenharmony_ci	u32 nmask = gmask | GLOBAL_INTERRUPT_MASK;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_clr_reg);
43262306a36Sopenharmony_ci	iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg);
43362306a36Sopenharmony_ci	ioread32(pinstance->int_regs.global_interrupt_mask_reg);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!pinstance->interrupt_mode) {
43662306a36Sopenharmony_ci		iowrite32(intrs,
43762306a36Sopenharmony_ci			pinstance->int_regs.ioa_host_interrupt_mask_reg);
43862306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/**
44362306a36Sopenharmony_ci * pmcraid_enable_interrupts - Enables specified interrupts
44462306a36Sopenharmony_ci *
44562306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
44662306a36Sopenharmony_ci * @intrs: interrupts to enable
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * Return Value
44962306a36Sopenharmony_ci *	 None
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic void pmcraid_enable_interrupts(
45262306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
45362306a36Sopenharmony_ci	u32 intrs)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);
45662306a36Sopenharmony_ci	u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (!pinstance->interrupt_mode) {
46162306a36Sopenharmony_ci		iowrite32(~intrs,
46262306a36Sopenharmony_ci			 pinstance->int_regs.ioa_host_interrupt_mask_reg);
46362306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	pmcraid_info("enabled interrupts global mask = %x intr_mask = %x\n",
46762306a36Sopenharmony_ci		ioread32(pinstance->int_regs.global_interrupt_mask_reg),
46862306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg));
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/**
47262306a36Sopenharmony_ci * pmcraid_clr_trans_op - clear trans to op interrupt
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci * Return Value
47762306a36Sopenharmony_ci *	 None
47862306a36Sopenharmony_ci */
47962306a36Sopenharmony_cistatic void pmcraid_clr_trans_op(
48062306a36Sopenharmony_ci	struct pmcraid_instance *pinstance
48162306a36Sopenharmony_ci)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	unsigned long lock_flags;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (!pinstance->interrupt_mode) {
48662306a36Sopenharmony_ci		iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
48762306a36Sopenharmony_ci			pinstance->int_regs.ioa_host_interrupt_mask_reg);
48862306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
48962306a36Sopenharmony_ci		iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
49062306a36Sopenharmony_ci			pinstance->int_regs.ioa_host_interrupt_clr_reg);
49162306a36Sopenharmony_ci		ioread32(pinstance->int_regs.ioa_host_interrupt_clr_reg);
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (pinstance->reset_cmd != NULL) {
49562306a36Sopenharmony_ci		del_timer(&pinstance->reset_cmd->timer);
49662306a36Sopenharmony_ci		spin_lock_irqsave(
49762306a36Sopenharmony_ci			pinstance->host->host_lock, lock_flags);
49862306a36Sopenharmony_ci		pinstance->reset_cmd->cmd_done(pinstance->reset_cmd);
49962306a36Sopenharmony_ci		spin_unlock_irqrestore(
50062306a36Sopenharmony_ci			pinstance->host->host_lock, lock_flags);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * pmcraid_reset_type - Determine the required reset type
50662306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci * IOA requires hard reset if any of the following conditions is true.
50962306a36Sopenharmony_ci * 1. If HRRQ valid interrupt is not masked
51062306a36Sopenharmony_ci * 2. IOA reset alert doorbell is set
51162306a36Sopenharmony_ci * 3. If there are any error interrupts
51262306a36Sopenharmony_ci */
51362306a36Sopenharmony_cistatic void pmcraid_reset_type(struct pmcraid_instance *pinstance)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	u32 mask;
51662306a36Sopenharmony_ci	u32 intrs;
51762306a36Sopenharmony_ci	u32 alerts;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	mask = ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
52062306a36Sopenharmony_ci	intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
52162306a36Sopenharmony_ci	alerts = ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if ((mask & INTRS_HRRQ_VALID) == 0 ||
52462306a36Sopenharmony_ci	    (alerts & DOORBELL_IOA_RESET_ALERT) ||
52562306a36Sopenharmony_ci	    (intrs & PMCRAID_ERROR_INTERRUPTS)) {
52662306a36Sopenharmony_ci		pmcraid_info("IOA requires hard reset\n");
52762306a36Sopenharmony_ci		pinstance->ioa_hard_reset = 1;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* If unit check is active, trigger the dump */
53162306a36Sopenharmony_ci	if (intrs & INTRS_IOA_UNIT_CHECK)
53262306a36Sopenharmony_ci		pinstance->ioa_unit_check = 1;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void pmcraid_ioa_reset(struct pmcraid_cmd *);
53662306a36Sopenharmony_ci/**
53762306a36Sopenharmony_ci * pmcraid_bist_done - completion function for PCI BIST
53862306a36Sopenharmony_ci * @t: pointer to reset command
53962306a36Sopenharmony_ci * Return Value
54062306a36Sopenharmony_ci *	none
54162306a36Sopenharmony_ci */
54262306a36Sopenharmony_cistatic void pmcraid_bist_done(struct timer_list *t)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct pmcraid_cmd *cmd = from_timer(cmd, t, timer);
54562306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
54662306a36Sopenharmony_ci	unsigned long lock_flags;
54762306a36Sopenharmony_ci	int rc;
54862306a36Sopenharmony_ci	u16 pci_reg;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* If PCI config space can't be accessed wait for another two secs */
55362306a36Sopenharmony_ci	if ((rc != PCIBIOS_SUCCESSFUL || (!(pci_reg & PCI_COMMAND_MEMORY))) &&
55462306a36Sopenharmony_ci	    cmd->time_left > 0) {
55562306a36Sopenharmony_ci		pmcraid_info("BIST not complete, waiting another 2 secs\n");
55662306a36Sopenharmony_ci		cmd->timer.expires = jiffies + cmd->time_left;
55762306a36Sopenharmony_ci		cmd->time_left = 0;
55862306a36Sopenharmony_ci		add_timer(&cmd->timer);
55962306a36Sopenharmony_ci	} else {
56062306a36Sopenharmony_ci		cmd->time_left = 0;
56162306a36Sopenharmony_ci		pmcraid_info("BIST is complete, proceeding with reset\n");
56262306a36Sopenharmony_ci		spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
56362306a36Sopenharmony_ci		pmcraid_ioa_reset(cmd);
56462306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/**
56962306a36Sopenharmony_ci * pmcraid_start_bist - starts BIST
57062306a36Sopenharmony_ci * @cmd: pointer to reset cmd
57162306a36Sopenharmony_ci * Return Value
57262306a36Sopenharmony_ci *   none
57362306a36Sopenharmony_ci */
57462306a36Sopenharmony_cistatic void pmcraid_start_bist(struct pmcraid_cmd *cmd)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
57762306a36Sopenharmony_ci	u32 doorbells, intrs;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* proceed with bist and wait for 2 seconds */
58062306a36Sopenharmony_ci	iowrite32(DOORBELL_IOA_START_BIST,
58162306a36Sopenharmony_ci		pinstance->int_regs.host_ioa_interrupt_reg);
58262306a36Sopenharmony_ci	doorbells = ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
58362306a36Sopenharmony_ci	intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
58462306a36Sopenharmony_ci	pmcraid_info("doorbells after start bist: %x intrs: %x\n",
58562306a36Sopenharmony_ci		      doorbells, intrs);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	cmd->time_left = msecs_to_jiffies(PMCRAID_BIST_TIMEOUT);
58862306a36Sopenharmony_ci	cmd->timer.expires = jiffies + msecs_to_jiffies(PMCRAID_BIST_TIMEOUT);
58962306a36Sopenharmony_ci	cmd->timer.function = pmcraid_bist_done;
59062306a36Sopenharmony_ci	add_timer(&cmd->timer);
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/**
59462306a36Sopenharmony_ci * pmcraid_reset_alert_done - completion routine for reset_alert
59562306a36Sopenharmony_ci * @t: pointer to command block used in reset sequence
59662306a36Sopenharmony_ci * Return value
59762306a36Sopenharmony_ci *  None
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_cistatic void pmcraid_reset_alert_done(struct timer_list *t)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct pmcraid_cmd *cmd = from_timer(cmd, t, timer);
60262306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
60362306a36Sopenharmony_ci	u32 status = ioread32(pinstance->ioa_status);
60462306a36Sopenharmony_ci	unsigned long lock_flags;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* if the critical operation in progress bit is set or the wait times
60762306a36Sopenharmony_ci	 * out, invoke reset engine to proceed with hard reset. If there is
60862306a36Sopenharmony_ci	 * some more time to wait, restart the timer
60962306a36Sopenharmony_ci	 */
61062306a36Sopenharmony_ci	if (((status & INTRS_CRITICAL_OP_IN_PROGRESS) == 0) ||
61162306a36Sopenharmony_ci	    cmd->time_left <= 0) {
61262306a36Sopenharmony_ci		pmcraid_info("critical op is reset proceeding with reset\n");
61362306a36Sopenharmony_ci		spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
61462306a36Sopenharmony_ci		pmcraid_ioa_reset(cmd);
61562306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
61662306a36Sopenharmony_ci	} else {
61762306a36Sopenharmony_ci		pmcraid_info("critical op is not yet reset waiting again\n");
61862306a36Sopenharmony_ci		/* restart timer if some more time is available to wait */
61962306a36Sopenharmony_ci		cmd->time_left -= PMCRAID_CHECK_FOR_RESET_TIMEOUT;
62062306a36Sopenharmony_ci		cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
62162306a36Sopenharmony_ci		cmd->timer.function = pmcraid_reset_alert_done;
62262306a36Sopenharmony_ci		add_timer(&cmd->timer);
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic void pmcraid_notify_ioastate(struct pmcraid_instance *, u32);
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * pmcraid_reset_alert - alerts IOA for a possible reset
62962306a36Sopenharmony_ci * @cmd: command block to be used for reset sequence.
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * Return Value
63262306a36Sopenharmony_ci *	returns 0 if pci config-space is accessible and RESET_DOORBELL is
63362306a36Sopenharmony_ci *	successfully written to IOA. Returns non-zero in case pci_config_space
63462306a36Sopenharmony_ci *	is not accessible
63562306a36Sopenharmony_ci */
63662306a36Sopenharmony_cistatic void pmcraid_reset_alert(struct pmcraid_cmd *cmd)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
63962306a36Sopenharmony_ci	u32 doorbells;
64062306a36Sopenharmony_ci	int rc;
64162306a36Sopenharmony_ci	u16 pci_reg;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* If we are able to access IOA PCI config space, alert IOA that we are
64462306a36Sopenharmony_ci	 * going to reset it soon. This enables IOA to preserv persistent error
64562306a36Sopenharmony_ci	 * data if any. In case memory space is not accessible, proceed with
64662306a36Sopenharmony_ci	 * BIST or slot_reset
64762306a36Sopenharmony_ci	 */
64862306a36Sopenharmony_ci	rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg);
64962306a36Sopenharmony_ci	if ((rc == PCIBIOS_SUCCESSFUL) && (pci_reg & PCI_COMMAND_MEMORY)) {
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		/* wait for IOA permission i.e until CRITICAL_OPERATION bit is
65262306a36Sopenharmony_ci		 * reset IOA doesn't generate any interrupts when CRITICAL
65362306a36Sopenharmony_ci		 * OPERATION bit is reset. A timer is started to wait for this
65462306a36Sopenharmony_ci		 * bit to be reset.
65562306a36Sopenharmony_ci		 */
65662306a36Sopenharmony_ci		cmd->time_left = PMCRAID_RESET_TIMEOUT;
65762306a36Sopenharmony_ci		cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
65862306a36Sopenharmony_ci		cmd->timer.function = pmcraid_reset_alert_done;
65962306a36Sopenharmony_ci		add_timer(&cmd->timer);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		iowrite32(DOORBELL_IOA_RESET_ALERT,
66262306a36Sopenharmony_ci			pinstance->int_regs.host_ioa_interrupt_reg);
66362306a36Sopenharmony_ci		doorbells =
66462306a36Sopenharmony_ci			ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
66562306a36Sopenharmony_ci		pmcraid_info("doorbells after reset alert: %x\n", doorbells);
66662306a36Sopenharmony_ci	} else {
66762306a36Sopenharmony_ci		pmcraid_info("PCI config is not accessible starting BIST\n");
66862306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_IN_HARD_RESET;
66962306a36Sopenharmony_ci		pmcraid_start_bist(cmd);
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/**
67462306a36Sopenharmony_ci * pmcraid_timeout_handler -  Timeout handler for internally generated ops
67562306a36Sopenharmony_ci *
67662306a36Sopenharmony_ci * @t: pointer to command structure, that got timedout
67762306a36Sopenharmony_ci *
67862306a36Sopenharmony_ci * This function blocks host requests and initiates an adapter reset.
67962306a36Sopenharmony_ci *
68062306a36Sopenharmony_ci * Return value:
68162306a36Sopenharmony_ci *   None
68262306a36Sopenharmony_ci */
68362306a36Sopenharmony_cistatic void pmcraid_timeout_handler(struct timer_list *t)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct pmcraid_cmd *cmd = from_timer(cmd, t, timer);
68662306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
68762306a36Sopenharmony_ci	unsigned long lock_flags;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	dev_info(&pinstance->pdev->dev,
69062306a36Sopenharmony_ci		"Adapter being reset due to cmd(CDB[0] = %x) timeout\n",
69162306a36Sopenharmony_ci		cmd->ioa_cb->ioarcb.cdb[0]);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/* Command timeouts result in hard reset sequence. The command that got
69462306a36Sopenharmony_ci	 * timed out may be the one used as part of reset sequence. In this
69562306a36Sopenharmony_ci	 * case restart reset sequence using the same command block even if
69662306a36Sopenharmony_ci	 * reset is in progress. Otherwise fail this command and get a free
69762306a36Sopenharmony_ci	 * command block to restart the reset sequence.
69862306a36Sopenharmony_ci	 */
69962306a36Sopenharmony_ci	spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
70062306a36Sopenharmony_ci	if (!pinstance->ioa_reset_in_progress) {
70162306a36Sopenharmony_ci		pinstance->ioa_reset_attempts = 0;
70262306a36Sopenharmony_ci		cmd = pmcraid_get_free_cmd(pinstance);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		/* If we are out of command blocks, just return here itself.
70562306a36Sopenharmony_ci		 * Some other command's timeout handler can do the reset job
70662306a36Sopenharmony_ci		 */
70762306a36Sopenharmony_ci		if (cmd == NULL) {
70862306a36Sopenharmony_ci			spin_unlock_irqrestore(pinstance->host->host_lock,
70962306a36Sopenharmony_ci					       lock_flags);
71062306a36Sopenharmony_ci			pmcraid_err("no free cmnd block for timeout handler\n");
71162306a36Sopenharmony_ci			return;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		pinstance->reset_cmd = cmd;
71562306a36Sopenharmony_ci		pinstance->ioa_reset_in_progress = 1;
71662306a36Sopenharmony_ci	} else {
71762306a36Sopenharmony_ci		pmcraid_info("reset is already in progress\n");
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		if (pinstance->reset_cmd != cmd) {
72062306a36Sopenharmony_ci			/* This command should have been given to IOA, this
72162306a36Sopenharmony_ci			 * command will be completed by fail_outstanding_cmds
72262306a36Sopenharmony_ci			 * anyway
72362306a36Sopenharmony_ci			 */
72462306a36Sopenharmony_ci			pmcraid_err("cmd is pending but reset in progress\n");
72562306a36Sopenharmony_ci		}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		/* If this command was being used as part of the reset
72862306a36Sopenharmony_ci		 * sequence, set cmd_done pointer to pmcraid_ioa_reset. This
72962306a36Sopenharmony_ci		 * causes fail_outstanding_commands not to return the command
73062306a36Sopenharmony_ci		 * block back to free pool
73162306a36Sopenharmony_ci		 */
73262306a36Sopenharmony_ci		if (cmd == pinstance->reset_cmd)
73362306a36Sopenharmony_ci			cmd->cmd_done = pmcraid_ioa_reset;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* Notify apps of important IOA bringup/bringdown sequences */
73762306a36Sopenharmony_ci	if (pinstance->scn.ioa_state != PMC_DEVICE_EVENT_RESET_START &&
73862306a36Sopenharmony_ci	    pinstance->scn.ioa_state != PMC_DEVICE_EVENT_SHUTDOWN_START)
73962306a36Sopenharmony_ci		pmcraid_notify_ioastate(pinstance,
74062306a36Sopenharmony_ci					PMC_DEVICE_EVENT_RESET_START);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
74362306a36Sopenharmony_ci	scsi_block_requests(pinstance->host);
74462306a36Sopenharmony_ci	pmcraid_reset_alert(cmd);
74562306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/**
74962306a36Sopenharmony_ci * pmcraid_internal_done - completion routine for internally generated cmds
75062306a36Sopenharmony_ci *
75162306a36Sopenharmony_ci * @cmd: command that got response from IOA
75262306a36Sopenharmony_ci *
75362306a36Sopenharmony_ci * Return Value:
75462306a36Sopenharmony_ci *	 none
75562306a36Sopenharmony_ci */
75662306a36Sopenharmony_cistatic void pmcraid_internal_done(struct pmcraid_cmd *cmd)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n",
75962306a36Sopenharmony_ci		     cmd->ioa_cb->ioarcb.cdb[0],
76062306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* Some of the internal commands are sent with callers blocking for the
76362306a36Sopenharmony_ci	 * response. Same will be indicated as part of cmd->completion_req
76462306a36Sopenharmony_ci	 * field. Response path needs to wake up any waiters waiting for cmd
76562306a36Sopenharmony_ci	 * completion if this flag is set.
76662306a36Sopenharmony_ci	 */
76762306a36Sopenharmony_ci	if (cmd->completion_req) {
76862306a36Sopenharmony_ci		cmd->completion_req = 0;
76962306a36Sopenharmony_ci		complete(&cmd->wait_for_completion);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* most of the internal commands are completed by caller itself, so
77362306a36Sopenharmony_ci	 * no need to return the command block back to free pool until we are
77462306a36Sopenharmony_ci	 * required to do so (e.g once done with initialization).
77562306a36Sopenharmony_ci	 */
77662306a36Sopenharmony_ci	if (cmd->release) {
77762306a36Sopenharmony_ci		cmd->release = 0;
77862306a36Sopenharmony_ci		pmcraid_return_cmd(cmd);
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci/**
78362306a36Sopenharmony_ci * pmcraid_reinit_cfgtable_done - done function for cfg table reinitialization
78462306a36Sopenharmony_ci *
78562306a36Sopenharmony_ci * @cmd: command that got response from IOA
78662306a36Sopenharmony_ci *
78762306a36Sopenharmony_ci * This routine is called after driver re-reads configuration table due to a
78862306a36Sopenharmony_ci * lost CCN. It returns the command block back to free pool and schedules
78962306a36Sopenharmony_ci * worker thread to add/delete devices into the system.
79062306a36Sopenharmony_ci *
79162306a36Sopenharmony_ci * Return Value:
79262306a36Sopenharmony_ci *	 none
79362306a36Sopenharmony_ci */
79462306a36Sopenharmony_cistatic void pmcraid_reinit_cfgtable_done(struct pmcraid_cmd *cmd)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n",
79762306a36Sopenharmony_ci		     cmd->ioa_cb->ioarcb.cdb[0],
79862306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (cmd->release) {
80162306a36Sopenharmony_ci		cmd->release = 0;
80262306a36Sopenharmony_ci		pmcraid_return_cmd(cmd);
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	pmcraid_info("scheduling worker for config table reinitialization\n");
80562306a36Sopenharmony_ci	schedule_work(&cmd->drv_inst->worker_q);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/**
80962306a36Sopenharmony_ci * pmcraid_erp_done - Process completion of SCSI error response from device
81062306a36Sopenharmony_ci * @cmd: pmcraid_command
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * This function copies the sense buffer into the scsi_cmd struct and completes
81362306a36Sopenharmony_ci * scsi_cmd by calling scsi_done function.
81462306a36Sopenharmony_ci *
81562306a36Sopenharmony_ci * Return value:
81662306a36Sopenharmony_ci *  none
81762306a36Sopenharmony_ci */
81862306a36Sopenharmony_cistatic void pmcraid_erp_done(struct pmcraid_cmd *cmd)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
82162306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
82262306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (PMCRAID_IOASC_SENSE_KEY(ioasc) > 0) {
82562306a36Sopenharmony_ci		scsi_cmd->result |= (DID_ERROR << 16);
82662306a36Sopenharmony_ci		scmd_printk(KERN_INFO, scsi_cmd,
82762306a36Sopenharmony_ci			    "command CDB[0] = %x failed with IOASC: 0x%08X\n",
82862306a36Sopenharmony_ci			    cmd->ioa_cb->ioarcb.cdb[0], ioasc);
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (cmd->sense_buffer) {
83262306a36Sopenharmony_ci		dma_unmap_single(&pinstance->pdev->dev, cmd->sense_buffer_dma,
83362306a36Sopenharmony_ci				 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
83462306a36Sopenharmony_ci		cmd->sense_buffer = NULL;
83562306a36Sopenharmony_ci		cmd->sense_buffer_dma = 0;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	scsi_dma_unmap(scsi_cmd);
83962306a36Sopenharmony_ci	pmcraid_return_cmd(cmd);
84062306a36Sopenharmony_ci	scsi_done(scsi_cmd);
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci/**
84462306a36Sopenharmony_ci * _pmcraid_fire_command - sends an IOA command to adapter
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * This function adds the given block into pending command list
84762306a36Sopenharmony_ci * and returns without waiting
84862306a36Sopenharmony_ci *
84962306a36Sopenharmony_ci * @cmd : command to be sent to the device
85062306a36Sopenharmony_ci *
85162306a36Sopenharmony_ci * Return Value
85262306a36Sopenharmony_ci *	None
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic void _pmcraid_fire_command(struct pmcraid_cmd *cmd)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
85762306a36Sopenharmony_ci	unsigned long lock_flags;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* Add this command block to pending cmd pool. We do this prior to
86062306a36Sopenharmony_ci	 * writting IOARCB to ioarrin because IOA might complete the command
86162306a36Sopenharmony_ci	 * by the time we are about to add it to the list. Response handler
86262306a36Sopenharmony_ci	 * (isr/tasklet) looks for cmd block in the pending pending list.
86362306a36Sopenharmony_ci	 */
86462306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
86562306a36Sopenharmony_ci	list_add_tail(&cmd->free_list, &pinstance->pending_cmd_pool);
86662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags);
86762306a36Sopenharmony_ci	atomic_inc(&pinstance->outstanding_cmds);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/* driver writes lower 32-bit value of IOARCB address only */
87062306a36Sopenharmony_ci	mb();
87162306a36Sopenharmony_ci	iowrite32(le64_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr), pinstance->ioarrin);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci/**
87562306a36Sopenharmony_ci * pmcraid_send_cmd - fires a command to IOA
87662306a36Sopenharmony_ci *
87762306a36Sopenharmony_ci * This function also sets up timeout function, and command completion
87862306a36Sopenharmony_ci * function
87962306a36Sopenharmony_ci *
88062306a36Sopenharmony_ci * @cmd: pointer to the command block to be fired to IOA
88162306a36Sopenharmony_ci * @cmd_done: command completion function, called once IOA responds
88262306a36Sopenharmony_ci * @timeout: timeout to wait for this command completion
88362306a36Sopenharmony_ci * @timeout_func: timeout handler
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * Return value
88662306a36Sopenharmony_ci *   none
88762306a36Sopenharmony_ci */
88862306a36Sopenharmony_cistatic void pmcraid_send_cmd(
88962306a36Sopenharmony_ci	struct pmcraid_cmd *cmd,
89062306a36Sopenharmony_ci	void (*cmd_done) (struct pmcraid_cmd *),
89162306a36Sopenharmony_ci	unsigned long timeout,
89262306a36Sopenharmony_ci	void (*timeout_func) (struct timer_list *)
89362306a36Sopenharmony_ci)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	/* initialize done function */
89662306a36Sopenharmony_ci	cmd->cmd_done = cmd_done;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (timeout_func) {
89962306a36Sopenharmony_ci		/* setup timeout handler */
90062306a36Sopenharmony_ci		cmd->timer.expires = jiffies + timeout;
90162306a36Sopenharmony_ci		cmd->timer.function = timeout_func;
90262306a36Sopenharmony_ci		add_timer(&cmd->timer);
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* fire the command to IOA */
90662306a36Sopenharmony_ci	_pmcraid_fire_command(cmd);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/**
91062306a36Sopenharmony_ci * pmcraid_ioa_shutdown_done - completion function for IOA shutdown command
91162306a36Sopenharmony_ci * @cmd: pointer to the command block used for sending IOA shutdown command
91262306a36Sopenharmony_ci *
91362306a36Sopenharmony_ci * Return value
91462306a36Sopenharmony_ci *  None
91562306a36Sopenharmony_ci */
91662306a36Sopenharmony_cistatic void pmcraid_ioa_shutdown_done(struct pmcraid_cmd *cmd)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
91962306a36Sopenharmony_ci	unsigned long lock_flags;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
92262306a36Sopenharmony_ci	pmcraid_ioa_reset(cmd);
92362306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/**
92762306a36Sopenharmony_ci * pmcraid_ioa_shutdown - sends SHUTDOWN command to ioa
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * @cmd: pointer to the command block used as part of reset sequence
93062306a36Sopenharmony_ci *
93162306a36Sopenharmony_ci * Return Value
93262306a36Sopenharmony_ci *  None
93362306a36Sopenharmony_ci */
93462306a36Sopenharmony_cistatic void pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	pmcraid_info("response for Cancel CCN CDB[0] = %x ioasc = %x\n",
93762306a36Sopenharmony_ci		     cmd->ioa_cb->ioarcb.cdb[0],
93862306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* Note that commands sent during reset require next command to be sent
94162306a36Sopenharmony_ci	 * to IOA. Hence reinit the done function as well as timeout function
94262306a36Sopenharmony_ci	 */
94362306a36Sopenharmony_ci	pmcraid_reinit_cmdblk(cmd);
94462306a36Sopenharmony_ci	cmd->ioa_cb->ioarcb.request_type = REQ_TYPE_IOACMD;
94562306a36Sopenharmony_ci	cmd->ioa_cb->ioarcb.resource_handle =
94662306a36Sopenharmony_ci		cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
94762306a36Sopenharmony_ci	cmd->ioa_cb->ioarcb.cdb[0] = PMCRAID_IOA_SHUTDOWN;
94862306a36Sopenharmony_ci	cmd->ioa_cb->ioarcb.cdb[1] = PMCRAID_SHUTDOWN_NORMAL;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* fire shutdown command to hardware. */
95162306a36Sopenharmony_ci	pmcraid_info("firing normal shutdown command (%d) to IOA\n",
95262306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle));
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	pmcraid_notify_ioastate(cmd->drv_inst, PMC_DEVICE_EVENT_SHUTDOWN_START);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, pmcraid_ioa_shutdown_done,
95762306a36Sopenharmony_ci			 PMCRAID_SHUTDOWN_TIMEOUT,
95862306a36Sopenharmony_ci			 pmcraid_timeout_handler);
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic void pmcraid_querycfg(struct pmcraid_cmd *);
96262306a36Sopenharmony_ci/**
96362306a36Sopenharmony_ci * pmcraid_get_fwversion_done - completion function for get_fwversion
96462306a36Sopenharmony_ci *
96562306a36Sopenharmony_ci * @cmd: pointer to command block used to send INQUIRY command
96662306a36Sopenharmony_ci *
96762306a36Sopenharmony_ci * Return Value
96862306a36Sopenharmony_ci *	none
96962306a36Sopenharmony_ci */
97062306a36Sopenharmony_cistatic void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
97362306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
97462306a36Sopenharmony_ci	unsigned long lock_flags;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* configuration table entry size depends on firmware version. If fw
97762306a36Sopenharmony_ci	 * version is not known, it is not possible to interpret IOA config
97862306a36Sopenharmony_ci	 * table
97962306a36Sopenharmony_ci	 */
98062306a36Sopenharmony_ci	if (ioasc) {
98162306a36Sopenharmony_ci		pmcraid_err("IOA Inquiry failed with %x\n", ioasc);
98262306a36Sopenharmony_ci		spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
98362306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
98462306a36Sopenharmony_ci		pmcraid_reset_alert(cmd);
98562306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
98662306a36Sopenharmony_ci	} else  {
98762306a36Sopenharmony_ci		pmcraid_querycfg(cmd);
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci/**
99262306a36Sopenharmony_ci * pmcraid_get_fwversion - reads firmware version information
99362306a36Sopenharmony_ci *
99462306a36Sopenharmony_ci * @cmd: pointer to command block used to send INQUIRY command
99562306a36Sopenharmony_ci *
99662306a36Sopenharmony_ci * Return Value
99762306a36Sopenharmony_ci *	none
99862306a36Sopenharmony_ci */
99962306a36Sopenharmony_cistatic void pmcraid_get_fwversion(struct pmcraid_cmd *cmd)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
100262306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl;
100362306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
100462306a36Sopenharmony_ci	u16 data_size = sizeof(struct pmcraid_inquiry_data);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	pmcraid_reinit_cmdblk(cmd);
100762306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_SCSI;
100862306a36Sopenharmony_ci	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
100962306a36Sopenharmony_ci	ioarcb->cdb[0] = INQUIRY;
101062306a36Sopenharmony_ci	ioarcb->cdb[1] = 1;
101162306a36Sopenharmony_ci	ioarcb->cdb[2] = 0xD0;
101262306a36Sopenharmony_ci	ioarcb->cdb[3] = (data_size >> 8) & 0xFF;
101362306a36Sopenharmony_ci	ioarcb->cdb[4] = data_size & 0xFF;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* Since entire inquiry data it can be part of IOARCB itself
101662306a36Sopenharmony_ci	 */
101762306a36Sopenharmony_ci	ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
101862306a36Sopenharmony_ci					offsetof(struct pmcraid_ioarcb,
101962306a36Sopenharmony_ci						add_data.u.ioadl[0]));
102062306a36Sopenharmony_ci	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
102162306a36Sopenharmony_ci	ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	ioarcb->request_flags0 |= NO_LINK_DESCS;
102462306a36Sopenharmony_ci	ioarcb->data_transfer_length = cpu_to_le32(data_size);
102562306a36Sopenharmony_ci	ioadl = &(ioarcb->add_data.u.ioadl[0]);
102662306a36Sopenharmony_ci	ioadl->flags = IOADL_FLAGS_LAST_DESC;
102762306a36Sopenharmony_ci	ioadl->address = cpu_to_le64(pinstance->inq_data_baddr);
102862306a36Sopenharmony_ci	ioadl->data_len = cpu_to_le32(data_size);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, pmcraid_get_fwversion_done,
103162306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci/**
103562306a36Sopenharmony_ci * pmcraid_identify_hrrq - registers host rrq buffers with IOA
103662306a36Sopenharmony_ci * @cmd: pointer to command block to be used for identify hrrq
103762306a36Sopenharmony_ci *
103862306a36Sopenharmony_ci * Return Value
103962306a36Sopenharmony_ci *	 none
104062306a36Sopenharmony_ci */
104162306a36Sopenharmony_cistatic void pmcraid_identify_hrrq(struct pmcraid_cmd *cmd)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
104462306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
104562306a36Sopenharmony_ci	int index = cmd->hrrq_index;
104662306a36Sopenharmony_ci	__be64 hrrq_addr = cpu_to_be64(pinstance->hrrq_start_bus_addr[index]);
104762306a36Sopenharmony_ci	__be32 hrrq_size = cpu_to_be32(sizeof(u32) * PMCRAID_MAX_CMD);
104862306a36Sopenharmony_ci	void (*done_function)(struct pmcraid_cmd *);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	pmcraid_reinit_cmdblk(cmd);
105162306a36Sopenharmony_ci	cmd->hrrq_index = index + 1;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (cmd->hrrq_index < pinstance->num_hrrq) {
105462306a36Sopenharmony_ci		done_function = pmcraid_identify_hrrq;
105562306a36Sopenharmony_ci	} else {
105662306a36Sopenharmony_ci		cmd->hrrq_index = 0;
105762306a36Sopenharmony_ci		done_function = pmcraid_get_fwversion;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* Initialize ioarcb */
106162306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_IOACMD;
106262306a36Sopenharmony_ci	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* initialize the hrrq number where IOA will respond to this command */
106562306a36Sopenharmony_ci	ioarcb->hrrq_id = index;
106662306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_IDENTIFY_HRRQ;
106762306a36Sopenharmony_ci	ioarcb->cdb[1] = index;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* IOA expects 64-bit pci address to be written in B.E format
107062306a36Sopenharmony_ci	 * (i.e cdb[2]=MSByte..cdb[9]=LSB.
107162306a36Sopenharmony_ci	 */
107262306a36Sopenharmony_ci	pmcraid_info("HRRQ_IDENTIFY with hrrq:ioarcb:index => %llx:%llx:%x\n",
107362306a36Sopenharmony_ci		     hrrq_addr, ioarcb->ioarcb_bus_addr, index);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	memcpy(&(ioarcb->cdb[2]), &hrrq_addr, sizeof(hrrq_addr));
107662306a36Sopenharmony_ci	memcpy(&(ioarcb->cdb[10]), &hrrq_size, sizeof(hrrq_size));
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* Subsequent commands require HRRQ identification to be successful.
107962306a36Sopenharmony_ci	 * Note that this gets called even during reset from SCSI mid-layer
108062306a36Sopenharmony_ci	 * or tasklet
108162306a36Sopenharmony_ci	 */
108262306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, done_function,
108362306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT,
108462306a36Sopenharmony_ci			 pmcraid_timeout_handler);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic void pmcraid_process_ccn(struct pmcraid_cmd *cmd);
108862306a36Sopenharmony_cistatic void pmcraid_process_ldn(struct pmcraid_cmd *cmd);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci/**
109162306a36Sopenharmony_ci * pmcraid_send_hcam_cmd - send an initialized command block(HCAM) to IOA
109262306a36Sopenharmony_ci *
109362306a36Sopenharmony_ci * @cmd: initialized command block pointer
109462306a36Sopenharmony_ci *
109562306a36Sopenharmony_ci * Return Value
109662306a36Sopenharmony_ci *   none
109762306a36Sopenharmony_ci */
109862306a36Sopenharmony_cistatic void pmcraid_send_hcam_cmd(struct pmcraid_cmd *cmd)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	if (cmd->ioa_cb->ioarcb.cdb[1] == PMCRAID_HCAM_CODE_CONFIG_CHANGE)
110162306a36Sopenharmony_ci		atomic_set(&(cmd->drv_inst->ccn.ignore), 0);
110262306a36Sopenharmony_ci	else
110362306a36Sopenharmony_ci		atomic_set(&(cmd->drv_inst->ldn.ignore), 0);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, cmd->cmd_done, 0, NULL);
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/**
110962306a36Sopenharmony_ci * pmcraid_init_hcam - send an initialized command block(HCAM) to IOA
111062306a36Sopenharmony_ci *
111162306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
111262306a36Sopenharmony_ci * @type: HCAM type
111362306a36Sopenharmony_ci *
111462306a36Sopenharmony_ci * Return Value
111562306a36Sopenharmony_ci *   pointer to initialized pmcraid_cmd structure or NULL
111662306a36Sopenharmony_ci */
111762306a36Sopenharmony_cistatic struct pmcraid_cmd *pmcraid_init_hcam
111862306a36Sopenharmony_ci(
111962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
112062306a36Sopenharmony_ci	u8 type
112162306a36Sopenharmony_ci)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	struct pmcraid_cmd *cmd;
112462306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb;
112562306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl;
112662306a36Sopenharmony_ci	struct pmcraid_hostrcb *hcam;
112762306a36Sopenharmony_ci	void (*cmd_done) (struct pmcraid_cmd *);
112862306a36Sopenharmony_ci	dma_addr_t dma;
112962306a36Sopenharmony_ci	int rcb_size;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	cmd = pmcraid_get_free_cmd(pinstance);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (!cmd) {
113462306a36Sopenharmony_ci		pmcraid_err("no free command blocks for hcam\n");
113562306a36Sopenharmony_ci		return cmd;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (type == PMCRAID_HCAM_CODE_CONFIG_CHANGE) {
113962306a36Sopenharmony_ci		rcb_size = sizeof(struct pmcraid_hcam_ccn_ext);
114062306a36Sopenharmony_ci		cmd_done = pmcraid_process_ccn;
114162306a36Sopenharmony_ci		dma = pinstance->ccn.baddr + PMCRAID_AEN_HDR_SIZE;
114262306a36Sopenharmony_ci		hcam = &pinstance->ccn;
114362306a36Sopenharmony_ci	} else {
114462306a36Sopenharmony_ci		rcb_size = sizeof(struct pmcraid_hcam_ldn);
114562306a36Sopenharmony_ci		cmd_done = pmcraid_process_ldn;
114662306a36Sopenharmony_ci		dma = pinstance->ldn.baddr + PMCRAID_AEN_HDR_SIZE;
114762306a36Sopenharmony_ci		hcam = &pinstance->ldn;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* initialize command pointer used for HCAM registration */
115162306a36Sopenharmony_ci	hcam->cmd = cmd;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	ioarcb = &cmd->ioa_cb->ioarcb;
115462306a36Sopenharmony_ci	ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
115562306a36Sopenharmony_ci					offsetof(struct pmcraid_ioarcb,
115662306a36Sopenharmony_ci						add_data.u.ioadl[0]));
115762306a36Sopenharmony_ci	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
115862306a36Sopenharmony_ci	ioadl = ioarcb->add_data.u.ioadl;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	/* Initialize ioarcb */
116162306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_HCAM;
116262306a36Sopenharmony_ci	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
116362306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_HOST_CONTROLLED_ASYNC;
116462306a36Sopenharmony_ci	ioarcb->cdb[1] = type;
116562306a36Sopenharmony_ci	ioarcb->cdb[7] = (rcb_size >> 8) & 0xFF;
116662306a36Sopenharmony_ci	ioarcb->cdb[8] = (rcb_size) & 0xFF;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	ioarcb->data_transfer_length = cpu_to_le32(rcb_size);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	ioadl[0].flags |= IOADL_FLAGS_READ_LAST;
117162306a36Sopenharmony_ci	ioadl[0].data_len = cpu_to_le32(rcb_size);
117262306a36Sopenharmony_ci	ioadl[0].address = cpu_to_le64(dma);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	cmd->cmd_done = cmd_done;
117562306a36Sopenharmony_ci	return cmd;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci/**
117962306a36Sopenharmony_ci * pmcraid_send_hcam - Send an HCAM to IOA
118062306a36Sopenharmony_ci * @pinstance: ioa config struct
118162306a36Sopenharmony_ci * @type: HCAM type
118262306a36Sopenharmony_ci *
118362306a36Sopenharmony_ci * This function will send a Host Controlled Async command to IOA.
118462306a36Sopenharmony_ci *
118562306a36Sopenharmony_ci * Return value:
118662306a36Sopenharmony_ci *	none
118762306a36Sopenharmony_ci */
118862306a36Sopenharmony_cistatic void pmcraid_send_hcam(struct pmcraid_instance *pinstance, u8 type)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	struct pmcraid_cmd *cmd = pmcraid_init_hcam(pinstance, type);
119162306a36Sopenharmony_ci	pmcraid_send_hcam_cmd(cmd);
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci/**
119662306a36Sopenharmony_ci * pmcraid_prepare_cancel_cmd - prepares a command block to abort another
119762306a36Sopenharmony_ci *
119862306a36Sopenharmony_ci * @cmd: pointer to cmd that is used as cancelling command
119962306a36Sopenharmony_ci * @cmd_to_cancel: pointer to the command that needs to be cancelled
120062306a36Sopenharmony_ci */
120162306a36Sopenharmony_cistatic void pmcraid_prepare_cancel_cmd(
120262306a36Sopenharmony_ci	struct pmcraid_cmd *cmd,
120362306a36Sopenharmony_ci	struct pmcraid_cmd *cmd_to_cancel
120462306a36Sopenharmony_ci)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
120762306a36Sopenharmony_ci	__be64 ioarcb_addr;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* IOARCB address of the command to be cancelled is given in
121062306a36Sopenharmony_ci	 * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in
121162306a36Sopenharmony_ci	 * IOARCB address are not masked.
121262306a36Sopenharmony_ci	 */
121362306a36Sopenharmony_ci	ioarcb_addr = cpu_to_be64(le64_to_cpu(cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr));
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/* Get the resource handle to where the command to be aborted has been
121662306a36Sopenharmony_ci	 * sent.
121762306a36Sopenharmony_ci	 */
121862306a36Sopenharmony_ci	ioarcb->resource_handle = cmd_to_cancel->ioa_cb->ioarcb.resource_handle;
121962306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_IOACMD;
122062306a36Sopenharmony_ci	memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
122162306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_ABORT_CMD;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	memcpy(&(ioarcb->cdb[2]), &ioarcb_addr, sizeof(ioarcb_addr));
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci/**
122762306a36Sopenharmony_ci * pmcraid_cancel_hcam - sends ABORT task to abort a given HCAM
122862306a36Sopenharmony_ci *
122962306a36Sopenharmony_ci * @cmd: command to be used as cancelling command
123062306a36Sopenharmony_ci * @type: HCAM type
123162306a36Sopenharmony_ci * @cmd_done: op done function for the cancelling command
123262306a36Sopenharmony_ci */
123362306a36Sopenharmony_cistatic void pmcraid_cancel_hcam(
123462306a36Sopenharmony_ci	struct pmcraid_cmd *cmd,
123562306a36Sopenharmony_ci	u8 type,
123662306a36Sopenharmony_ci	void (*cmd_done) (struct pmcraid_cmd *)
123762306a36Sopenharmony_ci)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
124062306a36Sopenharmony_ci	struct pmcraid_hostrcb  *hcam;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	pinstance = cmd->drv_inst;
124362306a36Sopenharmony_ci	hcam =  (type == PMCRAID_HCAM_CODE_LOG_DATA) ?
124462306a36Sopenharmony_ci		&pinstance->ldn : &pinstance->ccn;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	/* prepare for cancelling previous hcam command. If the HCAM is
124762306a36Sopenharmony_ci	 * currently not pending with IOA, we would have hcam->cmd as non-null
124862306a36Sopenharmony_ci	 */
124962306a36Sopenharmony_ci	if (hcam->cmd == NULL)
125062306a36Sopenharmony_ci		return;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	pmcraid_prepare_cancel_cmd(cmd, hcam->cmd);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* writing to IOARRIN must be protected by host_lock, as mid-layer
125562306a36Sopenharmony_ci	 * schedule queuecommand while we are doing this
125662306a36Sopenharmony_ci	 */
125762306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, cmd_done,
125862306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT,
125962306a36Sopenharmony_ci			 pmcraid_timeout_handler);
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci/**
126362306a36Sopenharmony_ci * pmcraid_cancel_ccn - cancel CCN HCAM already registered with IOA
126462306a36Sopenharmony_ci *
126562306a36Sopenharmony_ci * @cmd: command block to be used for cancelling the HCAM
126662306a36Sopenharmony_ci */
126762306a36Sopenharmony_cistatic void pmcraid_cancel_ccn(struct pmcraid_cmd *cmd)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	pmcraid_info("response for Cancel LDN CDB[0] = %x ioasc = %x\n",
127062306a36Sopenharmony_ci		     cmd->ioa_cb->ioarcb.cdb[0],
127162306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	pmcraid_reinit_cmdblk(cmd);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	pmcraid_cancel_hcam(cmd,
127662306a36Sopenharmony_ci			    PMCRAID_HCAM_CODE_CONFIG_CHANGE,
127762306a36Sopenharmony_ci			    pmcraid_ioa_shutdown);
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci/**
128162306a36Sopenharmony_ci * pmcraid_cancel_ldn - cancel LDN HCAM already registered with IOA
128262306a36Sopenharmony_ci *
128362306a36Sopenharmony_ci * @cmd: command block to be used for cancelling the HCAM
128462306a36Sopenharmony_ci */
128562306a36Sopenharmony_cistatic void pmcraid_cancel_ldn(struct pmcraid_cmd *cmd)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	pmcraid_cancel_hcam(cmd,
128862306a36Sopenharmony_ci			    PMCRAID_HCAM_CODE_LOG_DATA,
128962306a36Sopenharmony_ci			    pmcraid_cancel_ccn);
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci/**
129362306a36Sopenharmony_ci * pmcraid_expose_resource - check if the resource can be exposed to OS
129462306a36Sopenharmony_ci *
129562306a36Sopenharmony_ci * @fw_version: firmware version code
129662306a36Sopenharmony_ci * @cfgte: pointer to configuration table entry of the resource
129762306a36Sopenharmony_ci *
129862306a36Sopenharmony_ci * Return value:
129962306a36Sopenharmony_ci *	true if resource can be added to midlayer, false(0) otherwise
130062306a36Sopenharmony_ci */
130162306a36Sopenharmony_cistatic int pmcraid_expose_resource(u16 fw_version,
130262306a36Sopenharmony_ci				   struct pmcraid_config_table_entry *cfgte)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	int retval = 0;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (cfgte->resource_type == RES_TYPE_VSET) {
130762306a36Sopenharmony_ci		if (fw_version <= PMCRAID_FW_VERSION_1)
130862306a36Sopenharmony_ci			retval = ((cfgte->unique_flags1 & 0x80) == 0);
130962306a36Sopenharmony_ci		else
131062306a36Sopenharmony_ci			retval = ((cfgte->unique_flags0 & 0x80) == 0 &&
131162306a36Sopenharmony_ci				  (cfgte->unique_flags1 & 0x80) == 0);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	} else if (cfgte->resource_type == RES_TYPE_GSCSI)
131462306a36Sopenharmony_ci		retval = (RES_BUS(cfgte->resource_address) !=
131562306a36Sopenharmony_ci				PMCRAID_VIRTUAL_ENCL_BUS_ID);
131662306a36Sopenharmony_ci	return retval;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci/* attributes supported by pmcraid_event_family */
132062306a36Sopenharmony_cienum {
132162306a36Sopenharmony_ci	PMCRAID_AEN_ATTR_UNSPEC,
132262306a36Sopenharmony_ci	PMCRAID_AEN_ATTR_EVENT,
132362306a36Sopenharmony_ci	__PMCRAID_AEN_ATTR_MAX,
132462306a36Sopenharmony_ci};
132562306a36Sopenharmony_ci#define PMCRAID_AEN_ATTR_MAX (__PMCRAID_AEN_ATTR_MAX - 1)
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci/* commands supported by pmcraid_event_family */
132862306a36Sopenharmony_cienum {
132962306a36Sopenharmony_ci	PMCRAID_AEN_CMD_UNSPEC,
133062306a36Sopenharmony_ci	PMCRAID_AEN_CMD_EVENT,
133162306a36Sopenharmony_ci	__PMCRAID_AEN_CMD_MAX,
133262306a36Sopenharmony_ci};
133362306a36Sopenharmony_ci#define PMCRAID_AEN_CMD_MAX (__PMCRAID_AEN_CMD_MAX - 1)
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic struct genl_multicast_group pmcraid_mcgrps[] = {
133662306a36Sopenharmony_ci	{ .name = "events", /* not really used - see ID discussion below */ },
133762306a36Sopenharmony_ci};
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic struct genl_family pmcraid_event_family __ro_after_init = {
134062306a36Sopenharmony_ci	.module = THIS_MODULE,
134162306a36Sopenharmony_ci	.name = "pmcraid",
134262306a36Sopenharmony_ci	.version = 1,
134362306a36Sopenharmony_ci	.maxattr = PMCRAID_AEN_ATTR_MAX,
134462306a36Sopenharmony_ci	.mcgrps = pmcraid_mcgrps,
134562306a36Sopenharmony_ci	.n_mcgrps = ARRAY_SIZE(pmcraid_mcgrps),
134662306a36Sopenharmony_ci};
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci/**
134962306a36Sopenharmony_ci * pmcraid_netlink_init - registers pmcraid_event_family
135062306a36Sopenharmony_ci *
135162306a36Sopenharmony_ci * Return value:
135262306a36Sopenharmony_ci *	0 if the pmcraid_event_family is successfully registered
135362306a36Sopenharmony_ci *	with netlink generic, non-zero otherwise
135462306a36Sopenharmony_ci */
135562306a36Sopenharmony_cistatic int __init pmcraid_netlink_init(void)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	int result;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	result = genl_register_family(&pmcraid_event_family);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	if (result)
136262306a36Sopenharmony_ci		return result;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	pmcraid_info("registered NETLINK GENERIC group: %d\n",
136562306a36Sopenharmony_ci		     pmcraid_event_family.id);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	return result;
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci/**
137162306a36Sopenharmony_ci * pmcraid_netlink_release - unregisters pmcraid_event_family
137262306a36Sopenharmony_ci *
137362306a36Sopenharmony_ci * Return value:
137462306a36Sopenharmony_ci *	none
137562306a36Sopenharmony_ci */
137662306a36Sopenharmony_cistatic void pmcraid_netlink_release(void)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	genl_unregister_family(&pmcraid_event_family);
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci/*
138262306a36Sopenharmony_ci * pmcraid_notify_aen - sends event msg to user space application
138362306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
138462306a36Sopenharmony_ci *
138562306a36Sopenharmony_ci * Return value:
138662306a36Sopenharmony_ci *	0 if success, error value in case of any failure.
138762306a36Sopenharmony_ci */
138862306a36Sopenharmony_cistatic int pmcraid_notify_aen(
138962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
139062306a36Sopenharmony_ci	struct pmcraid_aen_msg  *aen_msg,
139162306a36Sopenharmony_ci	u32    data_size)
139262306a36Sopenharmony_ci{
139362306a36Sopenharmony_ci	struct sk_buff *skb;
139462306a36Sopenharmony_ci	void *msg_header;
139562306a36Sopenharmony_ci	u32  total_size, nla_genl_hdr_total_size;
139662306a36Sopenharmony_ci	int result;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	aen_msg->hostno = (pinstance->host->unique_id << 16 |
139962306a36Sopenharmony_ci			   MINOR(pinstance->cdev.dev));
140062306a36Sopenharmony_ci	aen_msg->length = data_size;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	data_size += sizeof(*aen_msg);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	total_size = nla_total_size(data_size);
140562306a36Sopenharmony_ci	/* Add GENL_HDR to total_size */
140662306a36Sopenharmony_ci	nla_genl_hdr_total_size =
140762306a36Sopenharmony_ci		(total_size + (GENL_HDRLEN +
140862306a36Sopenharmony_ci		((struct genl_family *)&pmcraid_event_family)->hdrsize)
140962306a36Sopenharmony_ci		 + NLMSG_HDRLEN);
141062306a36Sopenharmony_ci	skb = genlmsg_new(nla_genl_hdr_total_size, GFP_ATOMIC);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	if (!skb) {
141462306a36Sopenharmony_ci		pmcraid_err("Failed to allocate aen data SKB of size: %x\n",
141562306a36Sopenharmony_ci			     total_size);
141662306a36Sopenharmony_ci		return -ENOMEM;
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	/* add the genetlink message header */
142062306a36Sopenharmony_ci	msg_header = genlmsg_put(skb, 0, 0,
142162306a36Sopenharmony_ci				 &pmcraid_event_family, 0,
142262306a36Sopenharmony_ci				 PMCRAID_AEN_CMD_EVENT);
142362306a36Sopenharmony_ci	if (!msg_header) {
142462306a36Sopenharmony_ci		pmcraid_err("failed to copy command details\n");
142562306a36Sopenharmony_ci		nlmsg_free(skb);
142662306a36Sopenharmony_ci		return -ENOMEM;
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	result = nla_put(skb, PMCRAID_AEN_ATTR_EVENT, data_size, aen_msg);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	if (result) {
143262306a36Sopenharmony_ci		pmcraid_err("failed to copy AEN attribute data\n");
143362306a36Sopenharmony_ci		nlmsg_free(skb);
143462306a36Sopenharmony_ci		return -EINVAL;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	/* send genetlink multicast message to notify applications */
143862306a36Sopenharmony_ci	genlmsg_end(skb, msg_header);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	result = genlmsg_multicast(&pmcraid_event_family, skb,
144162306a36Sopenharmony_ci				   0, 0, GFP_ATOMIC);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	/* If there are no listeners, genlmsg_multicast may return non-zero
144462306a36Sopenharmony_ci	 * value.
144562306a36Sopenharmony_ci	 */
144662306a36Sopenharmony_ci	if (result)
144762306a36Sopenharmony_ci		pmcraid_info("error (%x) sending aen event message\n", result);
144862306a36Sopenharmony_ci	return result;
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci/**
145262306a36Sopenharmony_ci * pmcraid_notify_ccn - notifies about CCN event msg to user space
145362306a36Sopenharmony_ci * @pinstance: pointer adapter instance structure
145462306a36Sopenharmony_ci *
145562306a36Sopenharmony_ci * Return value:
145662306a36Sopenharmony_ci *	0 if success, error value in case of any failure
145762306a36Sopenharmony_ci */
145862306a36Sopenharmony_cistatic int pmcraid_notify_ccn(struct pmcraid_instance *pinstance)
145962306a36Sopenharmony_ci{
146062306a36Sopenharmony_ci	return pmcraid_notify_aen(pinstance,
146162306a36Sopenharmony_ci				pinstance->ccn.msg,
146262306a36Sopenharmony_ci				le32_to_cpu(pinstance->ccn.hcam->data_len) +
146362306a36Sopenharmony_ci				sizeof(struct pmcraid_hcam_hdr));
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci/**
146762306a36Sopenharmony_ci * pmcraid_notify_ldn - notifies about CCN event msg to user space
146862306a36Sopenharmony_ci * @pinstance: pointer adapter instance structure
146962306a36Sopenharmony_ci *
147062306a36Sopenharmony_ci * Return value:
147162306a36Sopenharmony_ci *	0 if success, error value in case of any failure
147262306a36Sopenharmony_ci */
147362306a36Sopenharmony_cistatic int pmcraid_notify_ldn(struct pmcraid_instance *pinstance)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	return pmcraid_notify_aen(pinstance,
147662306a36Sopenharmony_ci				pinstance->ldn.msg,
147762306a36Sopenharmony_ci				le32_to_cpu(pinstance->ldn.hcam->data_len) +
147862306a36Sopenharmony_ci				sizeof(struct pmcraid_hcam_hdr));
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci/**
148262306a36Sopenharmony_ci * pmcraid_notify_ioastate - sends IOA state event msg to user space
148362306a36Sopenharmony_ci * @pinstance: pointer adapter instance structure
148462306a36Sopenharmony_ci * @evt: controller state event to be sent
148562306a36Sopenharmony_ci *
148662306a36Sopenharmony_ci * Return value:
148762306a36Sopenharmony_ci *	0 if success, error value in case of any failure
148862306a36Sopenharmony_ci */
148962306a36Sopenharmony_cistatic void pmcraid_notify_ioastate(struct pmcraid_instance *pinstance, u32 evt)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	pinstance->scn.ioa_state = evt;
149262306a36Sopenharmony_ci	pmcraid_notify_aen(pinstance,
149362306a36Sopenharmony_ci			  &pinstance->scn.msg,
149462306a36Sopenharmony_ci			  sizeof(u32));
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci/**
149862306a36Sopenharmony_ci * pmcraid_handle_config_change - Handle a config change from the adapter
149962306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
150062306a36Sopenharmony_ci *
150162306a36Sopenharmony_ci * Return value:
150262306a36Sopenharmony_ci *  none
150362306a36Sopenharmony_ci */
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cistatic void pmcraid_handle_config_change(struct pmcraid_instance *pinstance)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	struct pmcraid_config_table_entry *cfg_entry;
150862306a36Sopenharmony_ci	struct pmcraid_hcam_ccn *ccn_hcam;
150962306a36Sopenharmony_ci	struct pmcraid_cmd *cmd;
151062306a36Sopenharmony_ci	struct pmcraid_cmd *cfgcmd;
151162306a36Sopenharmony_ci	struct pmcraid_resource_entry *res = NULL;
151262306a36Sopenharmony_ci	unsigned long lock_flags;
151362306a36Sopenharmony_ci	unsigned long host_lock_flags;
151462306a36Sopenharmony_ci	u32 new_entry = 1;
151562306a36Sopenharmony_ci	u32 hidden_entry = 0;
151662306a36Sopenharmony_ci	u16 fw_version;
151762306a36Sopenharmony_ci	int rc;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	ccn_hcam = (struct pmcraid_hcam_ccn *)pinstance->ccn.hcam;
152062306a36Sopenharmony_ci	cfg_entry = &ccn_hcam->cfg_entry;
152162306a36Sopenharmony_ci	fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	pmcraid_info("CCN(%x): %x timestamp: %llx type: %x lost: %x flags: %x \
152462306a36Sopenharmony_ci		 res: %x:%x:%x:%x\n",
152562306a36Sopenharmony_ci		 le32_to_cpu(pinstance->ccn.hcam->ilid),
152662306a36Sopenharmony_ci		 pinstance->ccn.hcam->op_code,
152762306a36Sopenharmony_ci		(le32_to_cpu(pinstance->ccn.hcam->timestamp1) |
152862306a36Sopenharmony_ci		((le32_to_cpu(pinstance->ccn.hcam->timestamp2) & 0xffffffffLL) << 32)),
152962306a36Sopenharmony_ci		 pinstance->ccn.hcam->notification_type,
153062306a36Sopenharmony_ci		 pinstance->ccn.hcam->notification_lost,
153162306a36Sopenharmony_ci		 pinstance->ccn.hcam->flags,
153262306a36Sopenharmony_ci		 pinstance->host->unique_id,
153362306a36Sopenharmony_ci		 RES_IS_VSET(*cfg_entry) ? PMCRAID_VSET_BUS_ID :
153462306a36Sopenharmony_ci		 (RES_IS_GSCSI(*cfg_entry) ? PMCRAID_PHYS_BUS_ID :
153562306a36Sopenharmony_ci			RES_BUS(cfg_entry->resource_address)),
153662306a36Sopenharmony_ci		 RES_IS_VSET(*cfg_entry) ?
153762306a36Sopenharmony_ci			(fw_version <= PMCRAID_FW_VERSION_1 ?
153862306a36Sopenharmony_ci				cfg_entry->unique_flags1 :
153962306a36Sopenharmony_ci				le16_to_cpu(cfg_entry->array_id) & 0xFF) :
154062306a36Sopenharmony_ci			RES_TARGET(cfg_entry->resource_address),
154162306a36Sopenharmony_ci		 RES_LUN(cfg_entry->resource_address));
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	/* If this HCAM indicates a lost notification, read the config table */
154562306a36Sopenharmony_ci	if (pinstance->ccn.hcam->notification_lost) {
154662306a36Sopenharmony_ci		cfgcmd = pmcraid_get_free_cmd(pinstance);
154762306a36Sopenharmony_ci		if (cfgcmd) {
154862306a36Sopenharmony_ci			pmcraid_info("lost CCN, reading config table\b");
154962306a36Sopenharmony_ci			pinstance->reinit_cfg_table = 1;
155062306a36Sopenharmony_ci			pmcraid_querycfg(cfgcmd);
155162306a36Sopenharmony_ci		} else {
155262306a36Sopenharmony_ci			pmcraid_err("lost CCN, no free cmd for querycfg\n");
155362306a36Sopenharmony_ci		}
155462306a36Sopenharmony_ci		goto out_notify_apps;
155562306a36Sopenharmony_ci	}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	/* If this resource is not going to be added to mid-layer, just notify
155862306a36Sopenharmony_ci	 * applications and return. If this notification is about hiding a VSET
155962306a36Sopenharmony_ci	 * resource, check if it was exposed already.
156062306a36Sopenharmony_ci	 */
156162306a36Sopenharmony_ci	if (pinstance->ccn.hcam->notification_type ==
156262306a36Sopenharmony_ci	    NOTIFICATION_TYPE_ENTRY_CHANGED &&
156362306a36Sopenharmony_ci	    cfg_entry->resource_type == RES_TYPE_VSET) {
156462306a36Sopenharmony_ci		hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0;
156562306a36Sopenharmony_ci	} else if (!pmcraid_expose_resource(fw_version, cfg_entry)) {
156662306a36Sopenharmony_ci		goto out_notify_apps;
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
157062306a36Sopenharmony_ci	list_for_each_entry(res, &pinstance->used_res_q, queue) {
157162306a36Sopenharmony_ci		rc = memcmp(&res->cfg_entry.resource_address,
157262306a36Sopenharmony_ci			    &cfg_entry->resource_address,
157362306a36Sopenharmony_ci			    sizeof(cfg_entry->resource_address));
157462306a36Sopenharmony_ci		if (!rc) {
157562306a36Sopenharmony_ci			new_entry = 0;
157662306a36Sopenharmony_ci			break;
157762306a36Sopenharmony_ci		}
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (new_entry) {
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci		if (hidden_entry) {
158362306a36Sopenharmony_ci			spin_unlock_irqrestore(&pinstance->resource_lock,
158462306a36Sopenharmony_ci						lock_flags);
158562306a36Sopenharmony_ci			goto out_notify_apps;
158662306a36Sopenharmony_ci		}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci		/* If there are more number of resources than what driver can
158962306a36Sopenharmony_ci		 * manage, do not notify the applications about the CCN. Just
159062306a36Sopenharmony_ci		 * ignore this notifications and re-register the same HCAM
159162306a36Sopenharmony_ci		 */
159262306a36Sopenharmony_ci		if (list_empty(&pinstance->free_res_q)) {
159362306a36Sopenharmony_ci			spin_unlock_irqrestore(&pinstance->resource_lock,
159462306a36Sopenharmony_ci						lock_flags);
159562306a36Sopenharmony_ci			pmcraid_err("too many resources attached\n");
159662306a36Sopenharmony_ci			spin_lock_irqsave(pinstance->host->host_lock,
159762306a36Sopenharmony_ci					  host_lock_flags);
159862306a36Sopenharmony_ci			pmcraid_send_hcam(pinstance,
159962306a36Sopenharmony_ci					  PMCRAID_HCAM_CODE_CONFIG_CHANGE);
160062306a36Sopenharmony_ci			spin_unlock_irqrestore(pinstance->host->host_lock,
160162306a36Sopenharmony_ci					       host_lock_flags);
160262306a36Sopenharmony_ci			return;
160362306a36Sopenharmony_ci		}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci		res = list_entry(pinstance->free_res_q.next,
160662306a36Sopenharmony_ci				 struct pmcraid_resource_entry, queue);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci		list_del(&res->queue);
160962306a36Sopenharmony_ci		res->scsi_dev = NULL;
161062306a36Sopenharmony_ci		res->reset_progress = 0;
161162306a36Sopenharmony_ci		list_add_tail(&res->queue, &pinstance->used_res_q);
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	memcpy(&res->cfg_entry, cfg_entry, pinstance->config_table_entry_size);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	if (pinstance->ccn.hcam->notification_type ==
161762306a36Sopenharmony_ci	    NOTIFICATION_TYPE_ENTRY_DELETED || hidden_entry) {
161862306a36Sopenharmony_ci		if (res->scsi_dev) {
161962306a36Sopenharmony_ci			if (fw_version <= PMCRAID_FW_VERSION_1)
162062306a36Sopenharmony_ci				res->cfg_entry.unique_flags1 &= 0x7F;
162162306a36Sopenharmony_ci			else
162262306a36Sopenharmony_ci				res->cfg_entry.array_id &= cpu_to_le16(0xFF);
162362306a36Sopenharmony_ci			res->change_detected = RES_CHANGE_DEL;
162462306a36Sopenharmony_ci			res->cfg_entry.resource_handle =
162562306a36Sopenharmony_ci				PMCRAID_INVALID_RES_HANDLE;
162662306a36Sopenharmony_ci			schedule_work(&pinstance->worker_q);
162762306a36Sopenharmony_ci		} else {
162862306a36Sopenharmony_ci			/* This may be one of the non-exposed resources */
162962306a36Sopenharmony_ci			list_move_tail(&res->queue, &pinstance->free_res_q);
163062306a36Sopenharmony_ci		}
163162306a36Sopenharmony_ci	} else if (!res->scsi_dev) {
163262306a36Sopenharmony_ci		res->change_detected = RES_CHANGE_ADD;
163362306a36Sopenharmony_ci		schedule_work(&pinstance->worker_q);
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ciout_notify_apps:
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	/* Notify configuration changes to registered applications.*/
164062306a36Sopenharmony_ci	if (!pmcraid_disable_aen)
164162306a36Sopenharmony_ci		pmcraid_notify_ccn(pinstance);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
164462306a36Sopenharmony_ci	if (cmd)
164562306a36Sopenharmony_ci		pmcraid_send_hcam_cmd(cmd);
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci/**
164962306a36Sopenharmony_ci * pmcraid_get_error_info - return error string for an ioasc
165062306a36Sopenharmony_ci * @ioasc: ioasc code
165162306a36Sopenharmony_ci * Return Value
165262306a36Sopenharmony_ci *	 none
165362306a36Sopenharmony_ci */
165462306a36Sopenharmony_cistatic struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	int i;
165762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pmcraid_ioasc_error_table); i++) {
165862306a36Sopenharmony_ci		if (pmcraid_ioasc_error_table[i].ioasc_code == ioasc)
165962306a36Sopenharmony_ci			return &pmcraid_ioasc_error_table[i];
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci	return NULL;
166262306a36Sopenharmony_ci}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci/**
166562306a36Sopenharmony_ci * pmcraid_ioasc_logger - log IOASC information based user-settings
166662306a36Sopenharmony_ci * @ioasc: ioasc code
166762306a36Sopenharmony_ci * @cmd: pointer to command that resulted in 'ioasc'
166862306a36Sopenharmony_ci */
166962306a36Sopenharmony_cistatic void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (error_info == NULL ||
167462306a36Sopenharmony_ci		cmd->drv_inst->current_log_level < error_info->log_level)
167562306a36Sopenharmony_ci		return;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	/* log the error string */
167862306a36Sopenharmony_ci	pmcraid_err("cmd [%x] for resource %x failed with %x(%s)\n",
167962306a36Sopenharmony_ci		cmd->ioa_cb->ioarcb.cdb[0],
168062306a36Sopenharmony_ci		le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle),
168162306a36Sopenharmony_ci		ioasc, error_info->error_string);
168262306a36Sopenharmony_ci}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci/**
168562306a36Sopenharmony_ci * pmcraid_handle_error_log - Handle a config change (error log) from the IOA
168662306a36Sopenharmony_ci *
168762306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
168862306a36Sopenharmony_ci *
168962306a36Sopenharmony_ci * Return value:
169062306a36Sopenharmony_ci *  none
169162306a36Sopenharmony_ci */
169262306a36Sopenharmony_cistatic void pmcraid_handle_error_log(struct pmcraid_instance *pinstance)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	struct pmcraid_hcam_ldn *hcam_ldn;
169562306a36Sopenharmony_ci	u32 ioasc;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	hcam_ldn = (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	pmcraid_info
170062306a36Sopenharmony_ci		("LDN(%x): %x type: %x lost: %x flags: %x overlay id: %x\n",
170162306a36Sopenharmony_ci		 pinstance->ldn.hcam->ilid,
170262306a36Sopenharmony_ci		 pinstance->ldn.hcam->op_code,
170362306a36Sopenharmony_ci		 pinstance->ldn.hcam->notification_type,
170462306a36Sopenharmony_ci		 pinstance->ldn.hcam->notification_lost,
170562306a36Sopenharmony_ci		 pinstance->ldn.hcam->flags,
170662306a36Sopenharmony_ci		 pinstance->ldn.hcam->overlay_id);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	/* log only the errors, no need to log informational log entries */
170962306a36Sopenharmony_ci	if (pinstance->ldn.hcam->notification_type !=
171062306a36Sopenharmony_ci	    NOTIFICATION_TYPE_ERROR_LOG)
171162306a36Sopenharmony_ci		return;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (pinstance->ldn.hcam->notification_lost ==
171462306a36Sopenharmony_ci	    HOSTRCB_NOTIFICATIONS_LOST)
171562306a36Sopenharmony_ci		dev_info(&pinstance->pdev->dev, "Error notifications lost\n");
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	ioasc = le32_to_cpu(hcam_ldn->error_log.fd_ioasc);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET ||
172062306a36Sopenharmony_ci		ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) {
172162306a36Sopenharmony_ci		dev_info(&pinstance->pdev->dev,
172262306a36Sopenharmony_ci			"UnitAttention due to IOA Bus Reset\n");
172362306a36Sopenharmony_ci		scsi_report_bus_reset(
172462306a36Sopenharmony_ci			pinstance->host,
172562306a36Sopenharmony_ci			RES_BUS(hcam_ldn->error_log.fd_ra));
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	return;
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci/**
173262306a36Sopenharmony_ci * pmcraid_process_ccn - Op done function for a CCN.
173362306a36Sopenharmony_ci * @cmd: pointer to command struct
173462306a36Sopenharmony_ci *
173562306a36Sopenharmony_ci * This function is the op done function for a configuration
173662306a36Sopenharmony_ci * change notification
173762306a36Sopenharmony_ci *
173862306a36Sopenharmony_ci * Return value:
173962306a36Sopenharmony_ci * none
174062306a36Sopenharmony_ci */
174162306a36Sopenharmony_cistatic void pmcraid_process_ccn(struct pmcraid_cmd *cmd)
174262306a36Sopenharmony_ci{
174362306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
174462306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
174562306a36Sopenharmony_ci	unsigned long lock_flags;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	pinstance->ccn.cmd = NULL;
174862306a36Sopenharmony_ci	pmcraid_return_cmd(cmd);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/* If driver initiated IOA reset happened while this hcam was pending
175162306a36Sopenharmony_ci	 * with IOA, or IOA bringdown sequence is in progress, no need to
175262306a36Sopenharmony_ci	 * re-register the hcam
175362306a36Sopenharmony_ci	 */
175462306a36Sopenharmony_ci	if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
175562306a36Sopenharmony_ci	    atomic_read(&pinstance->ccn.ignore) == 1) {
175662306a36Sopenharmony_ci		return;
175762306a36Sopenharmony_ci	} else if (ioasc) {
175862306a36Sopenharmony_ci		dev_info(&pinstance->pdev->dev,
175962306a36Sopenharmony_ci			"Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc);
176062306a36Sopenharmony_ci		spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
176162306a36Sopenharmony_ci		pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
176262306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
176362306a36Sopenharmony_ci	} else {
176462306a36Sopenharmony_ci		pmcraid_handle_config_change(pinstance);
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic void pmcraid_initiate_reset(struct pmcraid_instance *);
176962306a36Sopenharmony_cistatic void pmcraid_set_timestamp(struct pmcraid_cmd *cmd);
177062306a36Sopenharmony_ci/**
177162306a36Sopenharmony_ci * pmcraid_process_ldn - op done function for an LDN
177262306a36Sopenharmony_ci * @cmd: pointer to command block
177362306a36Sopenharmony_ci *
177462306a36Sopenharmony_ci * Return value
177562306a36Sopenharmony_ci *   none
177662306a36Sopenharmony_ci */
177762306a36Sopenharmony_cistatic void pmcraid_process_ldn(struct pmcraid_cmd *cmd)
177862306a36Sopenharmony_ci{
177962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
178062306a36Sopenharmony_ci	struct pmcraid_hcam_ldn *ldn_hcam =
178162306a36Sopenharmony_ci			(struct pmcraid_hcam_ldn *)pinstance->ldn.hcam;
178262306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
178362306a36Sopenharmony_ci	u32 fd_ioasc = le32_to_cpu(ldn_hcam->error_log.fd_ioasc);
178462306a36Sopenharmony_ci	unsigned long lock_flags;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* return the command block back to freepool */
178762306a36Sopenharmony_ci	pinstance->ldn.cmd = NULL;
178862306a36Sopenharmony_ci	pmcraid_return_cmd(cmd);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	/* If driver initiated IOA reset happened while this hcam was pending
179162306a36Sopenharmony_ci	 * with IOA, no need to re-register the hcam as reset engine will do it
179262306a36Sopenharmony_ci	 * once reset sequence is complete
179362306a36Sopenharmony_ci	 */
179462306a36Sopenharmony_ci	if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
179562306a36Sopenharmony_ci	    atomic_read(&pinstance->ccn.ignore) == 1) {
179662306a36Sopenharmony_ci		return;
179762306a36Sopenharmony_ci	} else if (!ioasc) {
179862306a36Sopenharmony_ci		pmcraid_handle_error_log(pinstance);
179962306a36Sopenharmony_ci		if (fd_ioasc == PMCRAID_IOASC_NR_IOA_RESET_REQUIRED) {
180062306a36Sopenharmony_ci			spin_lock_irqsave(pinstance->host->host_lock,
180162306a36Sopenharmony_ci					  lock_flags);
180262306a36Sopenharmony_ci			pmcraid_initiate_reset(pinstance);
180362306a36Sopenharmony_ci			spin_unlock_irqrestore(pinstance->host->host_lock,
180462306a36Sopenharmony_ci					       lock_flags);
180562306a36Sopenharmony_ci			return;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci		if (fd_ioasc == PMCRAID_IOASC_TIME_STAMP_OUT_OF_SYNC) {
180862306a36Sopenharmony_ci			pinstance->timestamp_error = 1;
180962306a36Sopenharmony_ci			pmcraid_set_timestamp(cmd);
181062306a36Sopenharmony_ci		}
181162306a36Sopenharmony_ci	} else {
181262306a36Sopenharmony_ci		dev_info(&pinstance->pdev->dev,
181362306a36Sopenharmony_ci			"Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc);
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci	/* send netlink message for HCAM notification if enabled */
181662306a36Sopenharmony_ci	if (!pmcraid_disable_aen)
181762306a36Sopenharmony_ci		pmcraid_notify_ldn(pinstance);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA);
182062306a36Sopenharmony_ci	if (cmd)
182162306a36Sopenharmony_ci		pmcraid_send_hcam_cmd(cmd);
182262306a36Sopenharmony_ci}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci/**
182562306a36Sopenharmony_ci * pmcraid_register_hcams - register HCAMs for CCN and LDN
182662306a36Sopenharmony_ci *
182762306a36Sopenharmony_ci * @pinstance: pointer per adapter instance structure
182862306a36Sopenharmony_ci *
182962306a36Sopenharmony_ci * Return Value
183062306a36Sopenharmony_ci *   none
183162306a36Sopenharmony_ci */
183262306a36Sopenharmony_cistatic void pmcraid_register_hcams(struct pmcraid_instance *pinstance)
183362306a36Sopenharmony_ci{
183462306a36Sopenharmony_ci	pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
183562306a36Sopenharmony_ci	pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA);
183662306a36Sopenharmony_ci}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci/**
183962306a36Sopenharmony_ci * pmcraid_unregister_hcams - cancel HCAMs registered already
184062306a36Sopenharmony_ci * @cmd: pointer to command used as part of reset sequence
184162306a36Sopenharmony_ci */
184262306a36Sopenharmony_cistatic void pmcraid_unregister_hcams(struct pmcraid_cmd *cmd)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	/* During IOA bringdown, HCAM gets fired and tasklet proceeds with
184762306a36Sopenharmony_ci	 * handling hcam response though it is not necessary. In order to
184862306a36Sopenharmony_ci	 * prevent this, set 'ignore', so that bring-down sequence doesn't
184962306a36Sopenharmony_ci	 * re-send any more hcams
185062306a36Sopenharmony_ci	 */
185162306a36Sopenharmony_ci	atomic_set(&pinstance->ccn.ignore, 1);
185262306a36Sopenharmony_ci	atomic_set(&pinstance->ldn.ignore, 1);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	/* If adapter reset was forced as part of runtime reset sequence,
185562306a36Sopenharmony_ci	 * start the reset sequence. Reset will be triggered even in case
185662306a36Sopenharmony_ci	 * IOA unit_check.
185762306a36Sopenharmony_ci	 */
185862306a36Sopenharmony_ci	if ((pinstance->force_ioa_reset && !pinstance->ioa_bringdown) ||
185962306a36Sopenharmony_ci	     pinstance->ioa_unit_check) {
186062306a36Sopenharmony_ci		pinstance->force_ioa_reset = 0;
186162306a36Sopenharmony_ci		pinstance->ioa_unit_check = 0;
186262306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
186362306a36Sopenharmony_ci		pmcraid_reset_alert(cmd);
186462306a36Sopenharmony_ci		return;
186562306a36Sopenharmony_ci	}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	/* Driver tries to cancel HCAMs by sending ABORT TASK for each HCAM
186862306a36Sopenharmony_ci	 * one after the other. So CCN cancellation will be triggered by
186962306a36Sopenharmony_ci	 * pmcraid_cancel_ldn itself.
187062306a36Sopenharmony_ci	 */
187162306a36Sopenharmony_ci	pmcraid_cancel_ldn(cmd);
187262306a36Sopenharmony_ci}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_cistatic void pmcraid_reinit_buffers(struct pmcraid_instance *);
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci/**
187762306a36Sopenharmony_ci * pmcraid_reset_enable_ioa - re-enable IOA after a hard reset
187862306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
187962306a36Sopenharmony_ci * Return Value
188062306a36Sopenharmony_ci *  1 if TRANSITION_TO_OPERATIONAL is active, otherwise 0
188162306a36Sopenharmony_ci */
188262306a36Sopenharmony_cistatic int pmcraid_reset_enable_ioa(struct pmcraid_instance *pinstance)
188362306a36Sopenharmony_ci{
188462306a36Sopenharmony_ci	u32 intrs;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	pmcraid_reinit_buffers(pinstance);
188762306a36Sopenharmony_ci	intrs = pmcraid_read_interrupts(pinstance);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) {
189262306a36Sopenharmony_ci		if (!pinstance->interrupt_mode) {
189362306a36Sopenharmony_ci			iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
189462306a36Sopenharmony_ci				pinstance->int_regs.
189562306a36Sopenharmony_ci				ioa_host_interrupt_mask_reg);
189662306a36Sopenharmony_ci			iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
189762306a36Sopenharmony_ci				pinstance->int_regs.ioa_host_interrupt_clr_reg);
189862306a36Sopenharmony_ci		}
189962306a36Sopenharmony_ci		return 1;
190062306a36Sopenharmony_ci	} else {
190162306a36Sopenharmony_ci		return 0;
190262306a36Sopenharmony_ci	}
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci/**
190662306a36Sopenharmony_ci * pmcraid_soft_reset - performs a soft reset and makes IOA become ready
190762306a36Sopenharmony_ci * @cmd : pointer to reset command block
190862306a36Sopenharmony_ci *
190962306a36Sopenharmony_ci * Return Value
191062306a36Sopenharmony_ci *	none
191162306a36Sopenharmony_ci */
191262306a36Sopenharmony_cistatic void pmcraid_soft_reset(struct pmcraid_cmd *cmd)
191362306a36Sopenharmony_ci{
191462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
191562306a36Sopenharmony_ci	u32 int_reg;
191662306a36Sopenharmony_ci	u32 doorbell;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	/* There will be an interrupt when Transition to Operational bit is
191962306a36Sopenharmony_ci	 * set so tasklet would execute next reset task. The timeout handler
192062306a36Sopenharmony_ci	 * would re-initiate a reset
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	cmd->cmd_done = pmcraid_ioa_reset;
192362306a36Sopenharmony_ci	cmd->timer.expires = jiffies +
192462306a36Sopenharmony_ci			     msecs_to_jiffies(PMCRAID_TRANSOP_TIMEOUT);
192562306a36Sopenharmony_ci	cmd->timer.function = pmcraid_timeout_handler;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (!timer_pending(&cmd->timer))
192862306a36Sopenharmony_ci		add_timer(&cmd->timer);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	/* Enable destructive diagnostics on IOA if it is not yet in
193162306a36Sopenharmony_ci	 * operational state
193262306a36Sopenharmony_ci	 */
193362306a36Sopenharmony_ci	doorbell = DOORBELL_RUNTIME_RESET |
193462306a36Sopenharmony_ci		   DOORBELL_ENABLE_DESTRUCTIVE_DIAGS;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	/* Since we do RESET_ALERT and Start BIST we have to again write
193762306a36Sopenharmony_ci	 * MSIX Doorbell to indicate the interrupt mode
193862306a36Sopenharmony_ci	 */
193962306a36Sopenharmony_ci	if (pinstance->interrupt_mode) {
194062306a36Sopenharmony_ci		iowrite32(DOORBELL_INTR_MODE_MSIX,
194162306a36Sopenharmony_ci			  pinstance->int_regs.host_ioa_interrupt_reg);
194262306a36Sopenharmony_ci		ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	iowrite32(doorbell, pinstance->int_regs.host_ioa_interrupt_reg);
194662306a36Sopenharmony_ci	ioread32(pinstance->int_regs.host_ioa_interrupt_reg),
194762306a36Sopenharmony_ci	int_reg = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	pmcraid_info("Waiting for IOA to become operational %x:%x\n",
195062306a36Sopenharmony_ci		     ioread32(pinstance->int_regs.host_ioa_interrupt_reg),
195162306a36Sopenharmony_ci		     int_reg);
195262306a36Sopenharmony_ci}
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci/**
195562306a36Sopenharmony_ci * pmcraid_get_dump - retrieves IOA dump in case of Unit Check interrupt
195662306a36Sopenharmony_ci *
195762306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
195862306a36Sopenharmony_ci *
195962306a36Sopenharmony_ci * Return Value
196062306a36Sopenharmony_ci *	none
196162306a36Sopenharmony_ci */
196262306a36Sopenharmony_cistatic void pmcraid_get_dump(struct pmcraid_instance *pinstance)
196362306a36Sopenharmony_ci{
196462306a36Sopenharmony_ci	pmcraid_info("%s is not yet implemented\n", __func__);
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci/**
196862306a36Sopenharmony_ci * pmcraid_fail_outstanding_cmds - Fails all outstanding ops.
196962306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
197062306a36Sopenharmony_ci *
197162306a36Sopenharmony_ci * This function fails all outstanding ops. If they are submitted to IOA
197262306a36Sopenharmony_ci * already, it sends cancel all messages if IOA is still accepting IOARCBs,
197362306a36Sopenharmony_ci * otherwise just completes the commands and returns the cmd blocks to free
197462306a36Sopenharmony_ci * pool.
197562306a36Sopenharmony_ci *
197662306a36Sopenharmony_ci * Return value:
197762306a36Sopenharmony_ci *	 none
197862306a36Sopenharmony_ci */
197962306a36Sopenharmony_cistatic void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance)
198062306a36Sopenharmony_ci{
198162306a36Sopenharmony_ci	struct pmcraid_cmd *cmd, *temp;
198262306a36Sopenharmony_ci	unsigned long lock_flags;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	/* pending command list is protected by pending_pool_lock. Its
198562306a36Sopenharmony_ci	 * traversal must be done as within this lock
198662306a36Sopenharmony_ci	 */
198762306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
198862306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, temp, &pinstance->pending_cmd_pool,
198962306a36Sopenharmony_ci				 free_list) {
199062306a36Sopenharmony_ci		list_del(&cmd->free_list);
199162306a36Sopenharmony_ci		spin_unlock_irqrestore(&pinstance->pending_pool_lock,
199262306a36Sopenharmony_ci					lock_flags);
199362306a36Sopenharmony_ci		cmd->ioa_cb->ioasa.ioasc =
199462306a36Sopenharmony_ci			cpu_to_le32(PMCRAID_IOASC_IOA_WAS_RESET);
199562306a36Sopenharmony_ci		cmd->ioa_cb->ioasa.ilid =
199662306a36Sopenharmony_ci			cpu_to_le32(PMCRAID_DRIVER_ILID);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci		/* In case the command timer is still running */
199962306a36Sopenharmony_ci		del_timer(&cmd->timer);
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci		/* If this is an IO command, complete it by invoking scsi_done
200262306a36Sopenharmony_ci		 * function. If this is one of the internal commands other
200362306a36Sopenharmony_ci		 * than pmcraid_ioa_reset and HCAM commands invoke cmd_done to
200462306a36Sopenharmony_ci		 * complete it
200562306a36Sopenharmony_ci		 */
200662306a36Sopenharmony_ci		if (cmd->scsi_cmd) {
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci			struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
200962306a36Sopenharmony_ci			__le32 resp = cmd->ioa_cb->ioarcb.response_handle;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci			scsi_cmd->result |= DID_ERROR << 16;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci			scsi_dma_unmap(scsi_cmd);
201462306a36Sopenharmony_ci			pmcraid_return_cmd(cmd);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci			pmcraid_info("failing(%d) CDB[0] = %x result: %x\n",
201762306a36Sopenharmony_ci				     le32_to_cpu(resp) >> 2,
201862306a36Sopenharmony_ci				     cmd->ioa_cb->ioarcb.cdb[0],
201962306a36Sopenharmony_ci				     scsi_cmd->result);
202062306a36Sopenharmony_ci			scsi_done(scsi_cmd);
202162306a36Sopenharmony_ci		} else if (cmd->cmd_done == pmcraid_internal_done ||
202262306a36Sopenharmony_ci			   cmd->cmd_done == pmcraid_erp_done) {
202362306a36Sopenharmony_ci			cmd->cmd_done(cmd);
202462306a36Sopenharmony_ci		} else if (cmd->cmd_done != pmcraid_ioa_reset &&
202562306a36Sopenharmony_ci			   cmd->cmd_done != pmcraid_ioa_shutdown_done) {
202662306a36Sopenharmony_ci			pmcraid_return_cmd(cmd);
202762306a36Sopenharmony_ci		}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci		atomic_dec(&pinstance->outstanding_cmds);
203062306a36Sopenharmony_ci		spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
203162306a36Sopenharmony_ci	}
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags);
203462306a36Sopenharmony_ci}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci/**
203762306a36Sopenharmony_ci * pmcraid_ioa_reset - Implementation of IOA reset logic
203862306a36Sopenharmony_ci *
203962306a36Sopenharmony_ci * @cmd: pointer to the cmd block to be used for entire reset process
204062306a36Sopenharmony_ci *
204162306a36Sopenharmony_ci * This function executes most of the steps required for IOA reset. This gets
204262306a36Sopenharmony_ci * called by user threads (modprobe/insmod/rmmod) timer, tasklet and midlayer's
204362306a36Sopenharmony_ci * 'eh_' thread. Access to variables used for controlling the reset sequence is
204462306a36Sopenharmony_ci * synchronized using host lock. Various functions called during reset process
204562306a36Sopenharmony_ci * would make use of a single command block, pointer to which is also stored in
204662306a36Sopenharmony_ci * adapter instance structure.
204762306a36Sopenharmony_ci *
204862306a36Sopenharmony_ci * Return Value
204962306a36Sopenharmony_ci *	 None
205062306a36Sopenharmony_ci */
205162306a36Sopenharmony_cistatic void pmcraid_ioa_reset(struct pmcraid_cmd *cmd)
205262306a36Sopenharmony_ci{
205362306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
205462306a36Sopenharmony_ci	u8 reset_complete = 0;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	pinstance->ioa_reset_in_progress = 1;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	if (pinstance->reset_cmd != cmd) {
205962306a36Sopenharmony_ci		pmcraid_err("reset is called with different command block\n");
206062306a36Sopenharmony_ci		pinstance->reset_cmd = cmd;
206162306a36Sopenharmony_ci	}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	pmcraid_info("reset_engine: state = %d, command = %p\n",
206462306a36Sopenharmony_ci		      pinstance->ioa_state, cmd);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	switch (pinstance->ioa_state) {
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	case IOA_STATE_DEAD:
206962306a36Sopenharmony_ci		/* If IOA is offline, whatever may be the reset reason, just
207062306a36Sopenharmony_ci		 * return. callers might be waiting on the reset wait_q, wake
207162306a36Sopenharmony_ci		 * up them
207262306a36Sopenharmony_ci		 */
207362306a36Sopenharmony_ci		pmcraid_err("IOA is offline no reset is possible\n");
207462306a36Sopenharmony_ci		reset_complete = 1;
207562306a36Sopenharmony_ci		break;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	case IOA_STATE_IN_BRINGDOWN:
207862306a36Sopenharmony_ci		/* we enter here, once ioa shutdown command is processed by IOA
207962306a36Sopenharmony_ci		 * Alert IOA for a possible reset. If reset alert fails, IOA
208062306a36Sopenharmony_ci		 * goes through hard-reset
208162306a36Sopenharmony_ci		 */
208262306a36Sopenharmony_ci		pmcraid_disable_interrupts(pinstance, ~0);
208362306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
208462306a36Sopenharmony_ci		pmcraid_reset_alert(cmd);
208562306a36Sopenharmony_ci		break;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	case IOA_STATE_UNKNOWN:
208862306a36Sopenharmony_ci		/* We may be called during probe or resume. Some pre-processing
208962306a36Sopenharmony_ci		 * is required for prior to reset
209062306a36Sopenharmony_ci		 */
209162306a36Sopenharmony_ci		scsi_block_requests(pinstance->host);
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci		/* If asked to reset while IOA was processing responses or
209462306a36Sopenharmony_ci		 * there are any error responses then IOA may require
209562306a36Sopenharmony_ci		 * hard-reset.
209662306a36Sopenharmony_ci		 */
209762306a36Sopenharmony_ci		if (pinstance->ioa_hard_reset == 0) {
209862306a36Sopenharmony_ci			if (ioread32(pinstance->ioa_status) &
209962306a36Sopenharmony_ci			    INTRS_TRANSITION_TO_OPERATIONAL) {
210062306a36Sopenharmony_ci				pmcraid_info("sticky bit set, bring-up\n");
210162306a36Sopenharmony_ci				pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
210262306a36Sopenharmony_ci				pmcraid_reinit_cmdblk(cmd);
210362306a36Sopenharmony_ci				pmcraid_identify_hrrq(cmd);
210462306a36Sopenharmony_ci			} else {
210562306a36Sopenharmony_ci				pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET;
210662306a36Sopenharmony_ci				pmcraid_soft_reset(cmd);
210762306a36Sopenharmony_ci			}
210862306a36Sopenharmony_ci		} else {
210962306a36Sopenharmony_ci			/* Alert IOA of a possible reset and wait for critical
211062306a36Sopenharmony_ci			 * operation in progress bit to reset
211162306a36Sopenharmony_ci			 */
211262306a36Sopenharmony_ci			pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
211362306a36Sopenharmony_ci			pmcraid_reset_alert(cmd);
211462306a36Sopenharmony_ci		}
211562306a36Sopenharmony_ci		break;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	case IOA_STATE_IN_RESET_ALERT:
211862306a36Sopenharmony_ci		/* If critical operation in progress bit is reset or wait gets
211962306a36Sopenharmony_ci		 * timed out, reset proceeds with starting BIST on the IOA.
212062306a36Sopenharmony_ci		 * pmcraid_ioa_hard_reset keeps a count of reset attempts. If
212162306a36Sopenharmony_ci		 * they are 3 or more, reset engine marks IOA dead and returns
212262306a36Sopenharmony_ci		 */
212362306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_IN_HARD_RESET;
212462306a36Sopenharmony_ci		pmcraid_start_bist(cmd);
212562306a36Sopenharmony_ci		break;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	case IOA_STATE_IN_HARD_RESET:
212862306a36Sopenharmony_ci		pinstance->ioa_reset_attempts++;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci		/* retry reset if we haven't reached maximum allowed limit */
213162306a36Sopenharmony_ci		if (pinstance->ioa_reset_attempts > PMCRAID_RESET_ATTEMPTS) {
213262306a36Sopenharmony_ci			pinstance->ioa_reset_attempts = 0;
213362306a36Sopenharmony_ci			pmcraid_err("IOA didn't respond marking it as dead\n");
213462306a36Sopenharmony_ci			pinstance->ioa_state = IOA_STATE_DEAD;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci			if (pinstance->ioa_bringdown)
213762306a36Sopenharmony_ci				pmcraid_notify_ioastate(pinstance,
213862306a36Sopenharmony_ci					PMC_DEVICE_EVENT_SHUTDOWN_FAILED);
213962306a36Sopenharmony_ci			else
214062306a36Sopenharmony_ci				pmcraid_notify_ioastate(pinstance,
214162306a36Sopenharmony_ci						PMC_DEVICE_EVENT_RESET_FAILED);
214262306a36Sopenharmony_ci			reset_complete = 1;
214362306a36Sopenharmony_ci			break;
214462306a36Sopenharmony_ci		}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci		/* Once either bist or pci reset is done, restore PCI config
214762306a36Sopenharmony_ci		 * space. If this fails, proceed with hard reset again
214862306a36Sopenharmony_ci		 */
214962306a36Sopenharmony_ci		pci_restore_state(pinstance->pdev);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci		/* fail all pending commands */
215262306a36Sopenharmony_ci		pmcraid_fail_outstanding_cmds(pinstance);
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci		/* check if unit check is active, if so extract dump */
215562306a36Sopenharmony_ci		if (pinstance->ioa_unit_check) {
215662306a36Sopenharmony_ci			pmcraid_info("unit check is active\n");
215762306a36Sopenharmony_ci			pinstance->ioa_unit_check = 0;
215862306a36Sopenharmony_ci			pmcraid_get_dump(pinstance);
215962306a36Sopenharmony_ci			pinstance->ioa_reset_attempts--;
216062306a36Sopenharmony_ci			pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
216162306a36Sopenharmony_ci			pmcraid_reset_alert(cmd);
216262306a36Sopenharmony_ci			break;
216362306a36Sopenharmony_ci		}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci		/* if the reset reason is to bring-down the ioa, we might be
216662306a36Sopenharmony_ci		 * done with the reset restore pci_config_space and complete
216762306a36Sopenharmony_ci		 * the reset
216862306a36Sopenharmony_ci		 */
216962306a36Sopenharmony_ci		if (pinstance->ioa_bringdown) {
217062306a36Sopenharmony_ci			pmcraid_info("bringing down the adapter\n");
217162306a36Sopenharmony_ci			pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
217262306a36Sopenharmony_ci			pinstance->ioa_bringdown = 0;
217362306a36Sopenharmony_ci			pinstance->ioa_state = IOA_STATE_UNKNOWN;
217462306a36Sopenharmony_ci			pmcraid_notify_ioastate(pinstance,
217562306a36Sopenharmony_ci					PMC_DEVICE_EVENT_SHUTDOWN_SUCCESS);
217662306a36Sopenharmony_ci			reset_complete = 1;
217762306a36Sopenharmony_ci		} else {
217862306a36Sopenharmony_ci			/* bring-up IOA, so proceed with soft reset
217962306a36Sopenharmony_ci			 * Reinitialize hrrq_buffers and their indices also
218062306a36Sopenharmony_ci			 * enable interrupts after a pci_restore_state
218162306a36Sopenharmony_ci			 */
218262306a36Sopenharmony_ci			if (pmcraid_reset_enable_ioa(pinstance)) {
218362306a36Sopenharmony_ci				pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
218462306a36Sopenharmony_ci				pmcraid_info("bringing up the adapter\n");
218562306a36Sopenharmony_ci				pmcraid_reinit_cmdblk(cmd);
218662306a36Sopenharmony_ci				pmcraid_identify_hrrq(cmd);
218762306a36Sopenharmony_ci			} else {
218862306a36Sopenharmony_ci				pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET;
218962306a36Sopenharmony_ci				pmcraid_soft_reset(cmd);
219062306a36Sopenharmony_ci			}
219162306a36Sopenharmony_ci		}
219262306a36Sopenharmony_ci		break;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	case IOA_STATE_IN_SOFT_RESET:
219562306a36Sopenharmony_ci		/* TRANSITION TO OPERATIONAL is on so start initialization
219662306a36Sopenharmony_ci		 * sequence
219762306a36Sopenharmony_ci		 */
219862306a36Sopenharmony_ci		pmcraid_info("In softreset proceeding with bring-up\n");
219962306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci		/* Initialization commands start with HRRQ identification. From
220262306a36Sopenharmony_ci		 * now on tasklet completes most of the commands as IOA is up
220362306a36Sopenharmony_ci		 * and intrs are enabled
220462306a36Sopenharmony_ci		 */
220562306a36Sopenharmony_ci		pmcraid_identify_hrrq(cmd);
220662306a36Sopenharmony_ci		break;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	case IOA_STATE_IN_BRINGUP:
220962306a36Sopenharmony_ci		/* we are done with bringing up of IOA, change the ioa_state to
221062306a36Sopenharmony_ci		 * operational and wake up any waiters
221162306a36Sopenharmony_ci		 */
221262306a36Sopenharmony_ci		pinstance->ioa_state = IOA_STATE_OPERATIONAL;
221362306a36Sopenharmony_ci		reset_complete = 1;
221462306a36Sopenharmony_ci		break;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	case IOA_STATE_OPERATIONAL:
221762306a36Sopenharmony_ci	default:
221862306a36Sopenharmony_ci		/* When IOA is operational and a reset is requested, check for
221962306a36Sopenharmony_ci		 * the reset reason. If reset is to bring down IOA, unregister
222062306a36Sopenharmony_ci		 * HCAMs and initiate shutdown; if adapter reset is forced then
222162306a36Sopenharmony_ci		 * restart reset sequence again
222262306a36Sopenharmony_ci		 */
222362306a36Sopenharmony_ci		if (pinstance->ioa_shutdown_type == SHUTDOWN_NONE &&
222462306a36Sopenharmony_ci		    pinstance->force_ioa_reset == 0) {
222562306a36Sopenharmony_ci			pmcraid_notify_ioastate(pinstance,
222662306a36Sopenharmony_ci						PMC_DEVICE_EVENT_RESET_SUCCESS);
222762306a36Sopenharmony_ci			reset_complete = 1;
222862306a36Sopenharmony_ci		} else {
222962306a36Sopenharmony_ci			if (pinstance->ioa_shutdown_type != SHUTDOWN_NONE)
223062306a36Sopenharmony_ci				pinstance->ioa_state = IOA_STATE_IN_BRINGDOWN;
223162306a36Sopenharmony_ci			pmcraid_reinit_cmdblk(cmd);
223262306a36Sopenharmony_ci			pmcraid_unregister_hcams(cmd);
223362306a36Sopenharmony_ci		}
223462306a36Sopenharmony_ci		break;
223562306a36Sopenharmony_ci	}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	/* reset will be completed if ioa_state is either DEAD or UNKNOWN or
223862306a36Sopenharmony_ci	 * OPERATIONAL. Reset all control variables used during reset, wake up
223962306a36Sopenharmony_ci	 * any waiting threads and let the SCSI mid-layer send commands. Note
224062306a36Sopenharmony_ci	 * that host_lock must be held before invoking scsi_report_bus_reset.
224162306a36Sopenharmony_ci	 */
224262306a36Sopenharmony_ci	if (reset_complete) {
224362306a36Sopenharmony_ci		pinstance->ioa_reset_in_progress = 0;
224462306a36Sopenharmony_ci		pinstance->ioa_reset_attempts = 0;
224562306a36Sopenharmony_ci		pinstance->reset_cmd = NULL;
224662306a36Sopenharmony_ci		pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
224762306a36Sopenharmony_ci		pinstance->ioa_bringdown = 0;
224862306a36Sopenharmony_ci		pmcraid_return_cmd(cmd);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci		/* If target state is to bring up the adapter, proceed with
225162306a36Sopenharmony_ci		 * hcam registration and resource exposure to mid-layer.
225262306a36Sopenharmony_ci		 */
225362306a36Sopenharmony_ci		if (pinstance->ioa_state == IOA_STATE_OPERATIONAL)
225462306a36Sopenharmony_ci			pmcraid_register_hcams(pinstance);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci		wake_up_all(&pinstance->reset_wait_q);
225762306a36Sopenharmony_ci	}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	return;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci/**
226362306a36Sopenharmony_ci * pmcraid_initiate_reset - initiates reset sequence. This is called from
226462306a36Sopenharmony_ci * ISR/tasklet during error interrupts including IOA unit check. If reset
226562306a36Sopenharmony_ci * is already in progress, it just returns, otherwise initiates IOA reset
226662306a36Sopenharmony_ci * to bring IOA up to operational state.
226762306a36Sopenharmony_ci *
226862306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
226962306a36Sopenharmony_ci *
227062306a36Sopenharmony_ci * Return value
227162306a36Sopenharmony_ci *	 none
227262306a36Sopenharmony_ci */
227362306a36Sopenharmony_cistatic void pmcraid_initiate_reset(struct pmcraid_instance *pinstance)
227462306a36Sopenharmony_ci{
227562306a36Sopenharmony_ci	struct pmcraid_cmd *cmd;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	/* If the reset is already in progress, just return, otherwise start
227862306a36Sopenharmony_ci	 * reset sequence and return
227962306a36Sopenharmony_ci	 */
228062306a36Sopenharmony_ci	if (!pinstance->ioa_reset_in_progress) {
228162306a36Sopenharmony_ci		scsi_block_requests(pinstance->host);
228262306a36Sopenharmony_ci		cmd = pmcraid_get_free_cmd(pinstance);
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci		if (cmd == NULL) {
228562306a36Sopenharmony_ci			pmcraid_err("no cmnd blocks for initiate_reset\n");
228662306a36Sopenharmony_ci			return;
228762306a36Sopenharmony_ci		}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci		pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
229062306a36Sopenharmony_ci		pinstance->reset_cmd = cmd;
229162306a36Sopenharmony_ci		pinstance->force_ioa_reset = 1;
229262306a36Sopenharmony_ci		pmcraid_notify_ioastate(pinstance,
229362306a36Sopenharmony_ci					PMC_DEVICE_EVENT_RESET_START);
229462306a36Sopenharmony_ci		pmcraid_ioa_reset(cmd);
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci}
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci/**
229962306a36Sopenharmony_ci * pmcraid_reset_reload - utility routine for doing IOA reset either to bringup
230062306a36Sopenharmony_ci *			  or bringdown IOA
230162306a36Sopenharmony_ci * @pinstance: pointer adapter instance structure
230262306a36Sopenharmony_ci * @shutdown_type: shutdown type to be used NONE, NORMAL or ABRREV
230362306a36Sopenharmony_ci * @target_state: expected target state after reset
230462306a36Sopenharmony_ci *
230562306a36Sopenharmony_ci * Note: This command initiates reset and waits for its completion. Hence this
230662306a36Sopenharmony_ci * should not be called from isr/timer/tasklet functions (timeout handlers,
230762306a36Sopenharmony_ci * error response handlers and interrupt handlers).
230862306a36Sopenharmony_ci *
230962306a36Sopenharmony_ci * Return Value
231062306a36Sopenharmony_ci *	 1 in case ioa_state is not target_state, 0 otherwise.
231162306a36Sopenharmony_ci */
231262306a36Sopenharmony_cistatic int pmcraid_reset_reload(
231362306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
231462306a36Sopenharmony_ci	u8 shutdown_type,
231562306a36Sopenharmony_ci	u8 target_state
231662306a36Sopenharmony_ci)
231762306a36Sopenharmony_ci{
231862306a36Sopenharmony_ci	struct pmcraid_cmd *reset_cmd = NULL;
231962306a36Sopenharmony_ci	unsigned long lock_flags;
232062306a36Sopenharmony_ci	int reset = 1;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	if (pinstance->ioa_reset_in_progress) {
232562306a36Sopenharmony_ci		pmcraid_info("reset_reload: reset is already in progress\n");
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci		wait_event(pinstance->reset_wait_q,
233062306a36Sopenharmony_ci			   !pinstance->ioa_reset_in_progress);
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci		spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci		if (pinstance->ioa_state == IOA_STATE_DEAD) {
233562306a36Sopenharmony_ci			pmcraid_info("reset_reload: IOA is dead\n");
233662306a36Sopenharmony_ci			goto out_unlock;
233762306a36Sopenharmony_ci		}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci		if (pinstance->ioa_state == target_state) {
234062306a36Sopenharmony_ci			reset = 0;
234162306a36Sopenharmony_ci			goto out_unlock;
234262306a36Sopenharmony_ci		}
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	pmcraid_info("reset_reload: proceeding with reset\n");
234662306a36Sopenharmony_ci	scsi_block_requests(pinstance->host);
234762306a36Sopenharmony_ci	reset_cmd = pmcraid_get_free_cmd(pinstance);
234862306a36Sopenharmony_ci	if (reset_cmd == NULL) {
234962306a36Sopenharmony_ci		pmcraid_err("no free cmnd for reset_reload\n");
235062306a36Sopenharmony_ci		goto out_unlock;
235162306a36Sopenharmony_ci	}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	if (shutdown_type == SHUTDOWN_NORMAL)
235462306a36Sopenharmony_ci		pinstance->ioa_bringdown = 1;
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	pinstance->ioa_shutdown_type = shutdown_type;
235762306a36Sopenharmony_ci	pinstance->reset_cmd = reset_cmd;
235862306a36Sopenharmony_ci	pinstance->force_ioa_reset = reset;
235962306a36Sopenharmony_ci	pmcraid_info("reset_reload: initiating reset\n");
236062306a36Sopenharmony_ci	pmcraid_ioa_reset(reset_cmd);
236162306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
236262306a36Sopenharmony_ci	pmcraid_info("reset_reload: waiting for reset to complete\n");
236362306a36Sopenharmony_ci	wait_event(pinstance->reset_wait_q,
236462306a36Sopenharmony_ci		   !pinstance->ioa_reset_in_progress);
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	pmcraid_info("reset_reload: reset is complete !!\n");
236762306a36Sopenharmony_ci	scsi_unblock_requests(pinstance->host);
236862306a36Sopenharmony_ci	return pinstance->ioa_state != target_state;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ciout_unlock:
237162306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
237262306a36Sopenharmony_ci	return reset;
237362306a36Sopenharmony_ci}
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci/**
237662306a36Sopenharmony_ci * pmcraid_reset_bringdown - wrapper over pmcraid_reset_reload to bringdown IOA
237762306a36Sopenharmony_ci *
237862306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
237962306a36Sopenharmony_ci *
238062306a36Sopenharmony_ci * Return Value
238162306a36Sopenharmony_ci *	 whatever is returned from pmcraid_reset_reload
238262306a36Sopenharmony_ci */
238362306a36Sopenharmony_cistatic int pmcraid_reset_bringdown(struct pmcraid_instance *pinstance)
238462306a36Sopenharmony_ci{
238562306a36Sopenharmony_ci	return pmcraid_reset_reload(pinstance,
238662306a36Sopenharmony_ci				    SHUTDOWN_NORMAL,
238762306a36Sopenharmony_ci				    IOA_STATE_UNKNOWN);
238862306a36Sopenharmony_ci}
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci/**
239162306a36Sopenharmony_ci * pmcraid_reset_bringup - wrapper over pmcraid_reset_reload to bring up IOA
239262306a36Sopenharmony_ci *
239362306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
239462306a36Sopenharmony_ci *
239562306a36Sopenharmony_ci * Return Value
239662306a36Sopenharmony_ci *	 whatever is returned from pmcraid_reset_reload
239762306a36Sopenharmony_ci */
239862306a36Sopenharmony_cistatic int pmcraid_reset_bringup(struct pmcraid_instance *pinstance)
239962306a36Sopenharmony_ci{
240062306a36Sopenharmony_ci	pmcraid_notify_ioastate(pinstance, PMC_DEVICE_EVENT_RESET_START);
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	return pmcraid_reset_reload(pinstance,
240362306a36Sopenharmony_ci				    SHUTDOWN_NONE,
240462306a36Sopenharmony_ci				    IOA_STATE_OPERATIONAL);
240562306a36Sopenharmony_ci}
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci/**
240862306a36Sopenharmony_ci * pmcraid_request_sense - Send request sense to a device
240962306a36Sopenharmony_ci * @cmd: pmcraid command struct
241062306a36Sopenharmony_ci *
241162306a36Sopenharmony_ci * This function sends a request sense to a device as a result of a check
241262306a36Sopenharmony_ci * condition. This method re-uses the same command block that failed earlier.
241362306a36Sopenharmony_ci */
241462306a36Sopenharmony_cistatic void pmcraid_request_sense(struct pmcraid_cmd *cmd)
241562306a36Sopenharmony_ci{
241662306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
241762306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
241862306a36Sopenharmony_ci	struct device *dev = &cmd->drv_inst->pdev->dev;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	cmd->sense_buffer = cmd->scsi_cmd->sense_buffer;
242162306a36Sopenharmony_ci	cmd->sense_buffer_dma = dma_map_single(dev, cmd->sense_buffer,
242262306a36Sopenharmony_ci			SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
242362306a36Sopenharmony_ci	if (dma_mapping_error(dev, cmd->sense_buffer_dma)) {
242462306a36Sopenharmony_ci		pmcraid_err
242562306a36Sopenharmony_ci			("couldn't allocate sense buffer for request sense\n");
242662306a36Sopenharmony_ci		pmcraid_erp_done(cmd);
242762306a36Sopenharmony_ci		return;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	/* re-use the command block */
243162306a36Sopenharmony_ci	memset(&cmd->ioa_cb->ioasa, 0, sizeof(struct pmcraid_ioasa));
243262306a36Sopenharmony_ci	memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
243362306a36Sopenharmony_ci	ioarcb->request_flags0 = (SYNC_COMPLETE |
243462306a36Sopenharmony_ci				  NO_LINK_DESCS |
243562306a36Sopenharmony_ci				  INHIBIT_UL_CHECK);
243662306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_SCSI;
243762306a36Sopenharmony_ci	ioarcb->cdb[0] = REQUEST_SENSE;
243862306a36Sopenharmony_ci	ioarcb->cdb[4] = SCSI_SENSE_BUFFERSIZE;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
244162306a36Sopenharmony_ci					offsetof(struct pmcraid_ioarcb,
244262306a36Sopenharmony_ci						add_data.u.ioadl[0]));
244362306a36Sopenharmony_ci	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	ioarcb->data_transfer_length = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	ioadl->address = cpu_to_le64(cmd->sense_buffer_dma);
244862306a36Sopenharmony_ci	ioadl->data_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
244962306a36Sopenharmony_ci	ioadl->flags = IOADL_FLAGS_LAST_DESC;
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	/* request sense might be called as part of error response processing
245262306a36Sopenharmony_ci	 * which runs in tasklets context. It is possible that mid-layer might
245362306a36Sopenharmony_ci	 * schedule queuecommand during this time, hence, writting to IOARRIN
245462306a36Sopenharmony_ci	 * must be protect by host_lock
245562306a36Sopenharmony_ci	 */
245662306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, pmcraid_erp_done,
245762306a36Sopenharmony_ci			 PMCRAID_REQUEST_SENSE_TIMEOUT,
245862306a36Sopenharmony_ci			 pmcraid_timeout_handler);
245962306a36Sopenharmony_ci}
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci/**
246262306a36Sopenharmony_ci * pmcraid_cancel_all - cancel all outstanding IOARCBs as part of error recovery
246362306a36Sopenharmony_ci * @cmd: command that failed
246462306a36Sopenharmony_ci * @need_sense: true if request_sense is required after cancel all
246562306a36Sopenharmony_ci *
246662306a36Sopenharmony_ci * This function sends a cancel all to a device to clear the queue.
246762306a36Sopenharmony_ci */
246862306a36Sopenharmony_cistatic void pmcraid_cancel_all(struct pmcraid_cmd *cmd, bool need_sense)
246962306a36Sopenharmony_ci{
247062306a36Sopenharmony_ci	struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
247162306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
247262306a36Sopenharmony_ci	struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
247562306a36Sopenharmony_ci	ioarcb->request_flags0 = SYNC_OVERRIDE;
247662306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_IOACMD;
247762306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_CANCEL_ALL_REQUESTS;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	if (RES_IS_GSCSI(res->cfg_entry))
248062306a36Sopenharmony_ci		ioarcb->cdb[1] = PMCRAID_SYNC_COMPLETE_AFTER_CANCEL;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	ioarcb->ioadl_bus_addr = 0;
248362306a36Sopenharmony_ci	ioarcb->ioadl_length = 0;
248462306a36Sopenharmony_ci	ioarcb->data_transfer_length = 0;
248562306a36Sopenharmony_ci	ioarcb->ioarcb_bus_addr &= cpu_to_le64((~0x1FULL));
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	/* writing to IOARRIN must be protected by host_lock, as mid-layer
248862306a36Sopenharmony_ci	 * schedule queuecommand while we are doing this
248962306a36Sopenharmony_ci	 */
249062306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, need_sense ?
249162306a36Sopenharmony_ci			 pmcraid_erp_done : pmcraid_request_sense,
249262306a36Sopenharmony_ci			 PMCRAID_REQUEST_SENSE_TIMEOUT,
249362306a36Sopenharmony_ci			 pmcraid_timeout_handler);
249462306a36Sopenharmony_ci}
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci/**
249762306a36Sopenharmony_ci * pmcraid_frame_auto_sense: frame fixed format sense information
249862306a36Sopenharmony_ci *
249962306a36Sopenharmony_ci * @cmd: pointer to failing command block
250062306a36Sopenharmony_ci *
250162306a36Sopenharmony_ci * Return value
250262306a36Sopenharmony_ci *  none
250362306a36Sopenharmony_ci */
250462306a36Sopenharmony_cistatic void pmcraid_frame_auto_sense(struct pmcraid_cmd *cmd)
250562306a36Sopenharmony_ci{
250662306a36Sopenharmony_ci	u8 *sense_buf = cmd->scsi_cmd->sense_buffer;
250762306a36Sopenharmony_ci	struct pmcraid_resource_entry *res = cmd->scsi_cmd->device->hostdata;
250862306a36Sopenharmony_ci	struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
250962306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(ioasa->ioasc);
251062306a36Sopenharmony_ci	u32 failing_lba = 0;
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci	memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
251362306a36Sopenharmony_ci	cmd->scsi_cmd->result = SAM_STAT_CHECK_CONDITION;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	if (RES_IS_VSET(res->cfg_entry) &&
251662306a36Sopenharmony_ci	    ioasc == PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC &&
251762306a36Sopenharmony_ci	    ioasa->u.vset.failing_lba_hi != 0) {
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci		sense_buf[0] = 0x72;
252062306a36Sopenharmony_ci		sense_buf[1] = PMCRAID_IOASC_SENSE_KEY(ioasc);
252162306a36Sopenharmony_ci		sense_buf[2] = PMCRAID_IOASC_SENSE_CODE(ioasc);
252262306a36Sopenharmony_ci		sense_buf[3] = PMCRAID_IOASC_SENSE_QUAL(ioasc);
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci		sense_buf[7] = 12;
252562306a36Sopenharmony_ci		sense_buf[8] = 0;
252662306a36Sopenharmony_ci		sense_buf[9] = 0x0A;
252762306a36Sopenharmony_ci		sense_buf[10] = 0x80;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci		failing_lba = le32_to_cpu(ioasa->u.vset.failing_lba_hi);
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci		sense_buf[12] = (failing_lba & 0xff000000) >> 24;
253262306a36Sopenharmony_ci		sense_buf[13] = (failing_lba & 0x00ff0000) >> 16;
253362306a36Sopenharmony_ci		sense_buf[14] = (failing_lba & 0x0000ff00) >> 8;
253462306a36Sopenharmony_ci		sense_buf[15] = failing_lba & 0x000000ff;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci		failing_lba = le32_to_cpu(ioasa->u.vset.failing_lba_lo);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci		sense_buf[16] = (failing_lba & 0xff000000) >> 24;
253962306a36Sopenharmony_ci		sense_buf[17] = (failing_lba & 0x00ff0000) >> 16;
254062306a36Sopenharmony_ci		sense_buf[18] = (failing_lba & 0x0000ff00) >> 8;
254162306a36Sopenharmony_ci		sense_buf[19] = failing_lba & 0x000000ff;
254262306a36Sopenharmony_ci	} else {
254362306a36Sopenharmony_ci		sense_buf[0] = 0x70;
254462306a36Sopenharmony_ci		sense_buf[2] = PMCRAID_IOASC_SENSE_KEY(ioasc);
254562306a36Sopenharmony_ci		sense_buf[12] = PMCRAID_IOASC_SENSE_CODE(ioasc);
254662306a36Sopenharmony_ci		sense_buf[13] = PMCRAID_IOASC_SENSE_QUAL(ioasc);
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci		if (ioasc == PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC) {
254962306a36Sopenharmony_ci			if (RES_IS_VSET(res->cfg_entry))
255062306a36Sopenharmony_ci				failing_lba =
255162306a36Sopenharmony_ci					le32_to_cpu(ioasa->u.
255262306a36Sopenharmony_ci						 vset.failing_lba_lo);
255362306a36Sopenharmony_ci			sense_buf[0] |= 0x80;
255462306a36Sopenharmony_ci			sense_buf[3] = (failing_lba >> 24) & 0xff;
255562306a36Sopenharmony_ci			sense_buf[4] = (failing_lba >> 16) & 0xff;
255662306a36Sopenharmony_ci			sense_buf[5] = (failing_lba >> 8) & 0xff;
255762306a36Sopenharmony_ci			sense_buf[6] = failing_lba & 0xff;
255862306a36Sopenharmony_ci		}
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci		sense_buf[7] = 6; /* additional length */
256162306a36Sopenharmony_ci	}
256262306a36Sopenharmony_ci}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci/**
256562306a36Sopenharmony_ci * pmcraid_error_handler - Error response handlers for a SCSI op
256662306a36Sopenharmony_ci * @cmd: pointer to pmcraid_cmd that has failed
256762306a36Sopenharmony_ci *
256862306a36Sopenharmony_ci * This function determines whether or not to initiate ERP on the affected
256962306a36Sopenharmony_ci * device. This is called from a tasklet, which doesn't hold any locks.
257062306a36Sopenharmony_ci *
257162306a36Sopenharmony_ci * Return value:
257262306a36Sopenharmony_ci *	 0 it caller can complete the request, otherwise 1 where in error
257362306a36Sopenharmony_ci *	 handler itself completes the request and returns the command block
257462306a36Sopenharmony_ci *	 back to free-pool
257562306a36Sopenharmony_ci */
257662306a36Sopenharmony_cistatic int pmcraid_error_handler(struct pmcraid_cmd *cmd)
257762306a36Sopenharmony_ci{
257862306a36Sopenharmony_ci	struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
257962306a36Sopenharmony_ci	struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata;
258062306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
258162306a36Sopenharmony_ci	struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
258262306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(ioasa->ioasc);
258362306a36Sopenharmony_ci	u32 masked_ioasc = ioasc & PMCRAID_IOASC_SENSE_MASK;
258462306a36Sopenharmony_ci	bool sense_copied = false;
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	if (!res) {
258762306a36Sopenharmony_ci		pmcraid_info("resource pointer is NULL\n");
258862306a36Sopenharmony_ci		return 0;
258962306a36Sopenharmony_ci	}
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	/* If this was a SCSI read/write command keep count of errors */
259262306a36Sopenharmony_ci	if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_READ_CMD)
259362306a36Sopenharmony_ci		atomic_inc(&res->read_failures);
259462306a36Sopenharmony_ci	else if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_WRITE_CMD)
259562306a36Sopenharmony_ci		atomic_inc(&res->write_failures);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	if (!RES_IS_GSCSI(res->cfg_entry) &&
259862306a36Sopenharmony_ci		masked_ioasc != PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR) {
259962306a36Sopenharmony_ci		pmcraid_frame_auto_sense(cmd);
260062306a36Sopenharmony_ci	}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	/* Log IOASC/IOASA information based on user settings */
260362306a36Sopenharmony_ci	pmcraid_ioasc_logger(ioasc, cmd);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	switch (masked_ioasc) {
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	case PMCRAID_IOASC_AC_TERMINATED_BY_HOST:
260862306a36Sopenharmony_ci		scsi_cmd->result |= (DID_ABORT << 16);
260962306a36Sopenharmony_ci		break;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	case PMCRAID_IOASC_IR_INVALID_RESOURCE_HANDLE:
261262306a36Sopenharmony_ci	case PMCRAID_IOASC_HW_CANNOT_COMMUNICATE:
261362306a36Sopenharmony_ci		scsi_cmd->result |= (DID_NO_CONNECT << 16);
261462306a36Sopenharmony_ci		break;
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	case PMCRAID_IOASC_NR_SYNC_REQUIRED:
261762306a36Sopenharmony_ci		res->sync_reqd = 1;
261862306a36Sopenharmony_ci		scsi_cmd->result |= (DID_IMM_RETRY << 16);
261962306a36Sopenharmony_ci		break;
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	case PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC:
262262306a36Sopenharmony_ci		scsi_cmd->result |= (DID_PASSTHROUGH << 16);
262362306a36Sopenharmony_ci		break;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	case PMCRAID_IOASC_UA_BUS_WAS_RESET:
262662306a36Sopenharmony_ci	case PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER:
262762306a36Sopenharmony_ci		if (!res->reset_progress)
262862306a36Sopenharmony_ci			scsi_report_bus_reset(pinstance->host,
262962306a36Sopenharmony_ci					      scsi_cmd->device->channel);
263062306a36Sopenharmony_ci		scsi_cmd->result |= (DID_ERROR << 16);
263162306a36Sopenharmony_ci		break;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	case PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR:
263462306a36Sopenharmony_ci		scsi_cmd->result |= PMCRAID_IOASC_SENSE_STATUS(ioasc);
263562306a36Sopenharmony_ci		res->sync_reqd = 1;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci		/* if check_condition is not active return with error otherwise
263862306a36Sopenharmony_ci		 * get/frame the sense buffer
263962306a36Sopenharmony_ci		 */
264062306a36Sopenharmony_ci		if (PMCRAID_IOASC_SENSE_STATUS(ioasc) !=
264162306a36Sopenharmony_ci		    SAM_STAT_CHECK_CONDITION &&
264262306a36Sopenharmony_ci		    PMCRAID_IOASC_SENSE_STATUS(ioasc) != SAM_STAT_ACA_ACTIVE)
264362306a36Sopenharmony_ci			return 0;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci		/* If we have auto sense data as part of IOASA pass it to
264662306a36Sopenharmony_ci		 * mid-layer
264762306a36Sopenharmony_ci		 */
264862306a36Sopenharmony_ci		if (ioasa->auto_sense_length != 0) {
264962306a36Sopenharmony_ci			short sense_len = le16_to_cpu(ioasa->auto_sense_length);
265062306a36Sopenharmony_ci			int data_size = min_t(u16, sense_len,
265162306a36Sopenharmony_ci					      SCSI_SENSE_BUFFERSIZE);
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci			memcpy(scsi_cmd->sense_buffer,
265462306a36Sopenharmony_ci			       ioasa->sense_data,
265562306a36Sopenharmony_ci			       data_size);
265662306a36Sopenharmony_ci			sense_copied = true;
265762306a36Sopenharmony_ci		}
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci		if (RES_IS_GSCSI(res->cfg_entry))
266062306a36Sopenharmony_ci			pmcraid_cancel_all(cmd, sense_copied);
266162306a36Sopenharmony_ci		else if (sense_copied)
266262306a36Sopenharmony_ci			pmcraid_erp_done(cmd);
266362306a36Sopenharmony_ci		else
266462306a36Sopenharmony_ci			pmcraid_request_sense(cmd);
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci		return 1;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	case PMCRAID_IOASC_NR_INIT_CMD_REQUIRED:
266962306a36Sopenharmony_ci		break;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	default:
267262306a36Sopenharmony_ci		if (PMCRAID_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
267362306a36Sopenharmony_ci			scsi_cmd->result |= (DID_ERROR << 16);
267462306a36Sopenharmony_ci		break;
267562306a36Sopenharmony_ci	}
267662306a36Sopenharmony_ci	return 0;
267762306a36Sopenharmony_ci}
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci/**
268062306a36Sopenharmony_ci * pmcraid_reset_device - device reset handler functions
268162306a36Sopenharmony_ci *
268262306a36Sopenharmony_ci * @scsi_cmd: scsi command struct
268362306a36Sopenharmony_ci * @timeout: command timeout
268462306a36Sopenharmony_ci * @modifier: reset modifier indicating the reset sequence to be performed
268562306a36Sopenharmony_ci *
268662306a36Sopenharmony_ci * This function issues a device reset to the affected device.
268762306a36Sopenharmony_ci * A LUN reset will be sent to the device first. If that does
268862306a36Sopenharmony_ci * not work, a target reset will be sent.
268962306a36Sopenharmony_ci *
269062306a36Sopenharmony_ci * Return value:
269162306a36Sopenharmony_ci *	SUCCESS / FAILED
269262306a36Sopenharmony_ci */
269362306a36Sopenharmony_cistatic int pmcraid_reset_device(
269462306a36Sopenharmony_ci	struct scsi_cmnd *scsi_cmd,
269562306a36Sopenharmony_ci	unsigned long timeout,
269662306a36Sopenharmony_ci	u8 modifier)
269762306a36Sopenharmony_ci{
269862306a36Sopenharmony_ci	struct pmcraid_cmd *cmd;
269962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
270062306a36Sopenharmony_ci	struct pmcraid_resource_entry *res;
270162306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb;
270262306a36Sopenharmony_ci	unsigned long lock_flags;
270362306a36Sopenharmony_ci	u32 ioasc;
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci	pinstance =
270662306a36Sopenharmony_ci		(struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
270762306a36Sopenharmony_ci	res = scsi_cmd->device->hostdata;
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	if (!res) {
271062306a36Sopenharmony_ci		sdev_printk(KERN_ERR, scsi_cmd->device,
271162306a36Sopenharmony_ci			    "reset_device: NULL resource pointer\n");
271262306a36Sopenharmony_ci		return FAILED;
271362306a36Sopenharmony_ci	}
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	/* If adapter is currently going through reset/reload, return failed.
271662306a36Sopenharmony_ci	 * This will force the mid-layer to call _eh_bus/host reset, which
271762306a36Sopenharmony_ci	 * will then go to sleep and wait for the reset to complete
271862306a36Sopenharmony_ci	 */
271962306a36Sopenharmony_ci	spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
272062306a36Sopenharmony_ci	if (pinstance->ioa_reset_in_progress ||
272162306a36Sopenharmony_ci	    pinstance->ioa_state == IOA_STATE_DEAD) {
272262306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
272362306a36Sopenharmony_ci		return FAILED;
272462306a36Sopenharmony_ci	}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	res->reset_progress = 1;
272762306a36Sopenharmony_ci	pmcraid_info("Resetting %s resource with addr %x\n",
272862306a36Sopenharmony_ci		     ((modifier & RESET_DEVICE_LUN) ? "LUN" :
272962306a36Sopenharmony_ci		     ((modifier & RESET_DEVICE_TARGET) ? "TARGET" : "BUS")),
273062306a36Sopenharmony_ci		     le32_to_cpu(res->cfg_entry.resource_address));
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	/* get a free cmd block */
273362306a36Sopenharmony_ci	cmd = pmcraid_get_free_cmd(pinstance);
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	if (cmd == NULL) {
273662306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
273762306a36Sopenharmony_ci		pmcraid_err("%s: no cmd blocks are available\n", __func__);
273862306a36Sopenharmony_ci		return FAILED;
273962306a36Sopenharmony_ci	}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	ioarcb = &cmd->ioa_cb->ioarcb;
274262306a36Sopenharmony_ci	ioarcb->resource_handle = res->cfg_entry.resource_handle;
274362306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_IOACMD;
274462306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_RESET_DEVICE;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	/* Initialize reset modifier bits */
274762306a36Sopenharmony_ci	if (modifier)
274862306a36Sopenharmony_ci		modifier = ENABLE_RESET_MODIFIER | modifier;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	ioarcb->cdb[1] = modifier;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	init_completion(&cmd->wait_for_completion);
275362306a36Sopenharmony_ci	cmd->completion_req = 1;
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	pmcraid_info("cmd(CDB[0] = %x) for %x with index = %d\n",
275662306a36Sopenharmony_ci		     cmd->ioa_cb->ioarcb.cdb[0],
275762306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle),
275862306a36Sopenharmony_ci		     le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2);
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	pmcraid_send_cmd(cmd,
276162306a36Sopenharmony_ci			 pmcraid_internal_done,
276262306a36Sopenharmony_ci			 timeout,
276362306a36Sopenharmony_ci			 pmcraid_timeout_handler);
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	/* RESET_DEVICE command completes after all pending IOARCBs are
276862306a36Sopenharmony_ci	 * completed. Once this command is completed, pmcraind_internal_done
276962306a36Sopenharmony_ci	 * will wake up the 'completion' queue.
277062306a36Sopenharmony_ci	 */
277162306a36Sopenharmony_ci	wait_for_completion(&cmd->wait_for_completion);
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	/* complete the command here itself and return the command block
277462306a36Sopenharmony_ci	 * to free list
277562306a36Sopenharmony_ci	 */
277662306a36Sopenharmony_ci	pmcraid_return_cmd(cmd);
277762306a36Sopenharmony_ci	res->reset_progress = 0;
277862306a36Sopenharmony_ci	ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	/* set the return value based on the returned ioasc */
278162306a36Sopenharmony_ci	return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
278262306a36Sopenharmony_ci}
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci/**
278562306a36Sopenharmony_ci * _pmcraid_io_done - helper for pmcraid_io_done function
278662306a36Sopenharmony_ci *
278762306a36Sopenharmony_ci * @cmd: pointer to pmcraid command struct
278862306a36Sopenharmony_ci * @reslen: residual data length to be set in the ioasa
278962306a36Sopenharmony_ci * @ioasc: ioasc either returned by IOA or set by driver itself.
279062306a36Sopenharmony_ci *
279162306a36Sopenharmony_ci * This function is invoked by pmcraid_io_done to complete mid-layer
279262306a36Sopenharmony_ci * scsi ops.
279362306a36Sopenharmony_ci *
279462306a36Sopenharmony_ci * Return value:
279562306a36Sopenharmony_ci *	  0 if caller is required to return it to free_pool. Returns 1 if
279662306a36Sopenharmony_ci *	  caller need not worry about freeing command block as error handler
279762306a36Sopenharmony_ci *	  will take care of that.
279862306a36Sopenharmony_ci */
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_cistatic int _pmcraid_io_done(struct pmcraid_cmd *cmd, int reslen, int ioasc)
280162306a36Sopenharmony_ci{
280262306a36Sopenharmony_ci	struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
280362306a36Sopenharmony_ci	int rc = 0;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	scsi_set_resid(scsi_cmd, reslen);
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci	pmcraid_info("response(%d) CDB[0] = %x ioasc:result: %x:%x\n",
280862306a36Sopenharmony_ci		le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2,
280962306a36Sopenharmony_ci		cmd->ioa_cb->ioarcb.cdb[0],
281062306a36Sopenharmony_ci		ioasc, scsi_cmd->result);
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	if (PMCRAID_IOASC_SENSE_KEY(ioasc) != 0)
281362306a36Sopenharmony_ci		rc = pmcraid_error_handler(cmd);
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci	if (rc == 0) {
281662306a36Sopenharmony_ci		scsi_dma_unmap(scsi_cmd);
281762306a36Sopenharmony_ci		scsi_done(scsi_cmd);
281862306a36Sopenharmony_ci	}
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	return rc;
282162306a36Sopenharmony_ci}
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci/**
282462306a36Sopenharmony_ci * pmcraid_io_done - SCSI completion function
282562306a36Sopenharmony_ci *
282662306a36Sopenharmony_ci * @cmd: pointer to pmcraid command struct
282762306a36Sopenharmony_ci *
282862306a36Sopenharmony_ci * This function is invoked by tasklet/mid-layer error handler to completing
282962306a36Sopenharmony_ci * the SCSI ops sent from mid-layer.
283062306a36Sopenharmony_ci *
283162306a36Sopenharmony_ci * Return value
283262306a36Sopenharmony_ci *	  none
283362306a36Sopenharmony_ci */
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_cistatic void pmcraid_io_done(struct pmcraid_cmd *cmd)
283662306a36Sopenharmony_ci{
283762306a36Sopenharmony_ci	u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
283862306a36Sopenharmony_ci	u32 reslen = le32_to_cpu(cmd->ioa_cb->ioasa.residual_data_length);
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	if (_pmcraid_io_done(cmd, reslen, ioasc) == 0)
284162306a36Sopenharmony_ci		pmcraid_return_cmd(cmd);
284262306a36Sopenharmony_ci}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci/**
284562306a36Sopenharmony_ci * pmcraid_abort_cmd - Aborts a single IOARCB already submitted to IOA
284662306a36Sopenharmony_ci *
284762306a36Sopenharmony_ci * @cmd: command block of the command to be aborted
284862306a36Sopenharmony_ci *
284962306a36Sopenharmony_ci * Return Value:
285062306a36Sopenharmony_ci *	 returns pointer to command structure used as cancelling cmd
285162306a36Sopenharmony_ci */
285262306a36Sopenharmony_cistatic struct pmcraid_cmd *pmcraid_abort_cmd(struct pmcraid_cmd *cmd)
285362306a36Sopenharmony_ci{
285462306a36Sopenharmony_ci	struct pmcraid_cmd *cancel_cmd;
285562306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci	pinstance = (struct pmcraid_instance *)cmd->drv_inst;
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	cancel_cmd = pmcraid_get_free_cmd(pinstance);
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci	if (cancel_cmd == NULL) {
286262306a36Sopenharmony_ci		pmcraid_err("%s: no cmd blocks are available\n", __func__);
286362306a36Sopenharmony_ci		return NULL;
286462306a36Sopenharmony_ci	}
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	pmcraid_prepare_cancel_cmd(cancel_cmd, cmd);
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	pmcraid_info("aborting command CDB[0]= %x with index = %d\n",
286962306a36Sopenharmony_ci		cmd->ioa_cb->ioarcb.cdb[0],
287062306a36Sopenharmony_ci		le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2);
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	init_completion(&cancel_cmd->wait_for_completion);
287362306a36Sopenharmony_ci	cancel_cmd->completion_req = 1;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	pmcraid_info("command (%d) CDB[0] = %x for %x\n",
287662306a36Sopenharmony_ci		le32_to_cpu(cancel_cmd->ioa_cb->ioarcb.response_handle) >> 2,
287762306a36Sopenharmony_ci		cancel_cmd->ioa_cb->ioarcb.cdb[0],
287862306a36Sopenharmony_ci		le32_to_cpu(cancel_cmd->ioa_cb->ioarcb.resource_handle));
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	pmcraid_send_cmd(cancel_cmd,
288162306a36Sopenharmony_ci			 pmcraid_internal_done,
288262306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT,
288362306a36Sopenharmony_ci			 pmcraid_timeout_handler);
288462306a36Sopenharmony_ci	return cancel_cmd;
288562306a36Sopenharmony_ci}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci/**
288862306a36Sopenharmony_ci * pmcraid_abort_complete - Waits for ABORT TASK completion
288962306a36Sopenharmony_ci *
289062306a36Sopenharmony_ci * @cancel_cmd: command block use as cancelling command
289162306a36Sopenharmony_ci *
289262306a36Sopenharmony_ci * Return Value:
289362306a36Sopenharmony_ci *	 returns SUCCESS if ABORT TASK has good completion
289462306a36Sopenharmony_ci *	 otherwise FAILED
289562306a36Sopenharmony_ci */
289662306a36Sopenharmony_cistatic int pmcraid_abort_complete(struct pmcraid_cmd *cancel_cmd)
289762306a36Sopenharmony_ci{
289862306a36Sopenharmony_ci	struct pmcraid_resource_entry *res;
289962306a36Sopenharmony_ci	u32 ioasc;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	wait_for_completion(&cancel_cmd->wait_for_completion);
290262306a36Sopenharmony_ci	res = cancel_cmd->res;
290362306a36Sopenharmony_ci	cancel_cmd->res = NULL;
290462306a36Sopenharmony_ci	ioasc = le32_to_cpu(cancel_cmd->ioa_cb->ioasa.ioasc);
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	/* If the abort task is not timed out we will get a Good completion
290762306a36Sopenharmony_ci	 * as sense_key, otherwise we may get one the following responses
290862306a36Sopenharmony_ci	 * due to subsequent bus reset or device reset. In case IOASC is
290962306a36Sopenharmony_ci	 * NR_SYNC_REQUIRED, set sync_reqd flag for the corresponding resource
291062306a36Sopenharmony_ci	 */
291162306a36Sopenharmony_ci	if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET ||
291262306a36Sopenharmony_ci	    ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED) {
291362306a36Sopenharmony_ci		if (ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED)
291462306a36Sopenharmony_ci			res->sync_reqd = 1;
291562306a36Sopenharmony_ci		ioasc = 0;
291662306a36Sopenharmony_ci	}
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	/* complete the command here itself */
291962306a36Sopenharmony_ci	pmcraid_return_cmd(cancel_cmd);
292062306a36Sopenharmony_ci	return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
292162306a36Sopenharmony_ci}
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci/**
292462306a36Sopenharmony_ci * pmcraid_eh_abort_handler - entry point for aborting a single task on errors
292562306a36Sopenharmony_ci *
292662306a36Sopenharmony_ci * @scsi_cmd:   scsi command struct given by mid-layer. When this is called
292762306a36Sopenharmony_ci *		mid-layer ensures that no other commands are queued. This
292862306a36Sopenharmony_ci *		never gets called under interrupt, but a separate eh thread.
292962306a36Sopenharmony_ci *
293062306a36Sopenharmony_ci * Return value:
293162306a36Sopenharmony_ci *	 SUCCESS / FAILED
293262306a36Sopenharmony_ci */
293362306a36Sopenharmony_cistatic int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd)
293462306a36Sopenharmony_ci{
293562306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
293662306a36Sopenharmony_ci	struct pmcraid_cmd *cmd;
293762306a36Sopenharmony_ci	struct pmcraid_resource_entry *res;
293862306a36Sopenharmony_ci	unsigned long host_lock_flags;
293962306a36Sopenharmony_ci	unsigned long pending_lock_flags;
294062306a36Sopenharmony_ci	struct pmcraid_cmd *cancel_cmd = NULL;
294162306a36Sopenharmony_ci	int cmd_found = 0;
294262306a36Sopenharmony_ci	int rc = FAILED;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	pinstance =
294562306a36Sopenharmony_ci		(struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	scmd_printk(KERN_INFO, scsi_cmd,
294862306a36Sopenharmony_ci		    "I/O command timed out, aborting it.\n");
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	res = scsi_cmd->device->hostdata;
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	if (res == NULL)
295362306a36Sopenharmony_ci		return rc;
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci	/* If we are currently going through reset/reload, return failed.
295662306a36Sopenharmony_ci	 * This will force the mid-layer to eventually call
295762306a36Sopenharmony_ci	 * pmcraid_eh_host_reset which will then go to sleep and wait for the
295862306a36Sopenharmony_ci	 * reset to complete
295962306a36Sopenharmony_ci	 */
296062306a36Sopenharmony_ci	spin_lock_irqsave(pinstance->host->host_lock, host_lock_flags);
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci	if (pinstance->ioa_reset_in_progress ||
296362306a36Sopenharmony_ci	    pinstance->ioa_state == IOA_STATE_DEAD) {
296462306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock,
296562306a36Sopenharmony_ci				       host_lock_flags);
296662306a36Sopenharmony_ci		return rc;
296762306a36Sopenharmony_ci	}
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_ci	/* loop over pending cmd list to find cmd corresponding to this
297062306a36Sopenharmony_ci	 * scsi_cmd. Note that this command might not have been completed
297162306a36Sopenharmony_ci	 * already. locking: all pending commands are protected with
297262306a36Sopenharmony_ci	 * pending_pool_lock.
297362306a36Sopenharmony_ci	 */
297462306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->pending_pool_lock, pending_lock_flags);
297562306a36Sopenharmony_ci	list_for_each_entry(cmd, &pinstance->pending_cmd_pool, free_list) {
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci		if (cmd->scsi_cmd == scsi_cmd) {
297862306a36Sopenharmony_ci			cmd_found = 1;
297962306a36Sopenharmony_ci			break;
298062306a36Sopenharmony_ci		}
298162306a36Sopenharmony_ci	}
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->pending_pool_lock,
298462306a36Sopenharmony_ci				pending_lock_flags);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	/* If the command to be aborted was given to IOA and still pending with
298762306a36Sopenharmony_ci	 * it, send ABORT_TASK to abort this and wait for its completion
298862306a36Sopenharmony_ci	 */
298962306a36Sopenharmony_ci	if (cmd_found)
299062306a36Sopenharmony_ci		cancel_cmd = pmcraid_abort_cmd(cmd);
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock,
299362306a36Sopenharmony_ci			       host_lock_flags);
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	if (cancel_cmd) {
299662306a36Sopenharmony_ci		cancel_cmd->res = cmd->scsi_cmd->device->hostdata;
299762306a36Sopenharmony_ci		rc = pmcraid_abort_complete(cancel_cmd);
299862306a36Sopenharmony_ci	}
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	return cmd_found ? rc : SUCCESS;
300162306a36Sopenharmony_ci}
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci/**
300462306a36Sopenharmony_ci * pmcraid_eh_device_reset_handler - bus/target/device reset handler callbacks
300562306a36Sopenharmony_ci *
300662306a36Sopenharmony_ci * @scmd: pointer to scsi_cmd that was sent to the resource to be reset.
300762306a36Sopenharmony_ci *
300862306a36Sopenharmony_ci * All these routines invokve pmcraid_reset_device with appropriate parameters.
300962306a36Sopenharmony_ci * Since these are called from mid-layer EH thread, no other IO will be queued
301062306a36Sopenharmony_ci * to the resource being reset. However, control path (IOCTL) may be active so
301162306a36Sopenharmony_ci * it is necessary to synchronize IOARRIN writes which pmcraid_reset_device
301262306a36Sopenharmony_ci * takes care by locking/unlocking host_lock.
301362306a36Sopenharmony_ci *
301462306a36Sopenharmony_ci * Return value
301562306a36Sopenharmony_ci *	SUCCESS or FAILED
301662306a36Sopenharmony_ci */
301762306a36Sopenharmony_cistatic int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd)
301862306a36Sopenharmony_ci{
301962306a36Sopenharmony_ci	scmd_printk(KERN_INFO, scmd,
302062306a36Sopenharmony_ci		    "resetting device due to an I/O command timeout.\n");
302162306a36Sopenharmony_ci	return pmcraid_reset_device(scmd,
302262306a36Sopenharmony_ci				    PMCRAID_INTERNAL_TIMEOUT,
302362306a36Sopenharmony_ci				    RESET_DEVICE_LUN);
302462306a36Sopenharmony_ci}
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_cistatic int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd)
302762306a36Sopenharmony_ci{
302862306a36Sopenharmony_ci	scmd_printk(KERN_INFO, scmd,
302962306a36Sopenharmony_ci		    "Doing bus reset due to an I/O command timeout.\n");
303062306a36Sopenharmony_ci	return pmcraid_reset_device(scmd,
303162306a36Sopenharmony_ci				    PMCRAID_RESET_BUS_TIMEOUT,
303262306a36Sopenharmony_ci				    RESET_DEVICE_BUS);
303362306a36Sopenharmony_ci}
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_cistatic int pmcraid_eh_target_reset_handler(struct scsi_cmnd *scmd)
303662306a36Sopenharmony_ci{
303762306a36Sopenharmony_ci	scmd_printk(KERN_INFO, scmd,
303862306a36Sopenharmony_ci		    "Doing target reset due to an I/O command timeout.\n");
303962306a36Sopenharmony_ci	return pmcraid_reset_device(scmd,
304062306a36Sopenharmony_ci				    PMCRAID_INTERNAL_TIMEOUT,
304162306a36Sopenharmony_ci				    RESET_DEVICE_TARGET);
304262306a36Sopenharmony_ci}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci/**
304562306a36Sopenharmony_ci * pmcraid_eh_host_reset_handler - adapter reset handler callback
304662306a36Sopenharmony_ci *
304762306a36Sopenharmony_ci * @scmd: pointer to scsi_cmd that was sent to a resource of adapter
304862306a36Sopenharmony_ci *
304962306a36Sopenharmony_ci * Initiates adapter reset to bring it up to operational state
305062306a36Sopenharmony_ci *
305162306a36Sopenharmony_ci * Return value
305262306a36Sopenharmony_ci *	SUCCESS or FAILED
305362306a36Sopenharmony_ci */
305462306a36Sopenharmony_cistatic int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd)
305562306a36Sopenharmony_ci{
305662306a36Sopenharmony_ci	unsigned long interval = 10000; /* 10 seconds interval */
305762306a36Sopenharmony_ci	int waits = jiffies_to_msecs(PMCRAID_RESET_HOST_TIMEOUT) / interval;
305862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance =
305962306a36Sopenharmony_ci		(struct pmcraid_instance *)(scmd->device->host->hostdata);
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	/* wait for an additional 150 seconds just in case firmware could come
306362306a36Sopenharmony_ci	 * up and if it could complete all the pending commands excluding the
306462306a36Sopenharmony_ci	 * two HCAM (CCN and LDN).
306562306a36Sopenharmony_ci	 */
306662306a36Sopenharmony_ci	while (waits--) {
306762306a36Sopenharmony_ci		if (atomic_read(&pinstance->outstanding_cmds) <=
306862306a36Sopenharmony_ci		    PMCRAID_MAX_HCAM_CMD)
306962306a36Sopenharmony_ci			return SUCCESS;
307062306a36Sopenharmony_ci		msleep(interval);
307162306a36Sopenharmony_ci	}
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci	dev_err(&pinstance->pdev->dev,
307462306a36Sopenharmony_ci		"Adapter being reset due to an I/O command timeout.\n");
307562306a36Sopenharmony_ci	return pmcraid_reset_bringup(pinstance) == 0 ? SUCCESS : FAILED;
307662306a36Sopenharmony_ci}
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci/**
307962306a36Sopenharmony_ci * pmcraid_init_ioadls - initializes IOADL related fields in IOARCB
308062306a36Sopenharmony_ci * @cmd: pmcraid command struct
308162306a36Sopenharmony_ci * @sgcount: count of scatter-gather elements
308262306a36Sopenharmony_ci *
308362306a36Sopenharmony_ci * Return value
308462306a36Sopenharmony_ci *   returns pointer pmcraid_ioadl_desc, initialized to point to internal
308562306a36Sopenharmony_ci *   or external IOADLs
308662306a36Sopenharmony_ci */
308762306a36Sopenharmony_cistatic struct pmcraid_ioadl_desc *
308862306a36Sopenharmony_cipmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
308962306a36Sopenharmony_ci{
309062306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl;
309162306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
309262306a36Sopenharmony_ci	int ioadl_count = 0;
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_ci	if (ioarcb->add_cmd_param_length)
309562306a36Sopenharmony_ci		ioadl_count = DIV_ROUND_UP(le16_to_cpu(ioarcb->add_cmd_param_length), 16);
309662306a36Sopenharmony_ci	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc) * sgcount);
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	if ((sgcount + ioadl_count) > (ARRAY_SIZE(ioarcb->add_data.u.ioadl))) {
309962306a36Sopenharmony_ci		/* external ioadls start at offset 0x80 from control_block
310062306a36Sopenharmony_ci		 * structure, re-using 24 out of 27 ioadls part of IOARCB.
310162306a36Sopenharmony_ci		 * It is necessary to indicate to firmware that driver is
310262306a36Sopenharmony_ci		 * using ioadls to be treated as external to IOARCB.
310362306a36Sopenharmony_ci		 */
310462306a36Sopenharmony_ci		ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
310562306a36Sopenharmony_ci		ioarcb->ioadl_bus_addr =
310662306a36Sopenharmony_ci			cpu_to_le64((cmd->ioa_cb_bus_addr) +
310762306a36Sopenharmony_ci				offsetof(struct pmcraid_ioarcb,
310862306a36Sopenharmony_ci					add_data.u.ioadl[3]));
310962306a36Sopenharmony_ci		ioadl = &ioarcb->add_data.u.ioadl[3];
311062306a36Sopenharmony_ci	} else {
311162306a36Sopenharmony_ci		ioarcb->ioadl_bus_addr =
311262306a36Sopenharmony_ci			cpu_to_le64((cmd->ioa_cb_bus_addr) +
311362306a36Sopenharmony_ci				offsetof(struct pmcraid_ioarcb,
311462306a36Sopenharmony_ci					add_data.u.ioadl[ioadl_count]));
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci		ioadl = &ioarcb->add_data.u.ioadl[ioadl_count];
311762306a36Sopenharmony_ci		ioarcb->ioarcb_bus_addr |=
311862306a36Sopenharmony_ci			cpu_to_le64(DIV_ROUND_CLOSEST(sgcount + ioadl_count, 8));
311962306a36Sopenharmony_ci	}
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	return ioadl;
312262306a36Sopenharmony_ci}
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci/**
312562306a36Sopenharmony_ci * pmcraid_build_ioadl - Build a scatter/gather list and map the buffer
312662306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
312762306a36Sopenharmony_ci * @cmd: pmcraid command struct
312862306a36Sopenharmony_ci *
312962306a36Sopenharmony_ci * This function is invoked by queuecommand entry point while sending a command
313062306a36Sopenharmony_ci * to firmware. This builds ioadl descriptors and sets up ioarcb fields.
313162306a36Sopenharmony_ci *
313262306a36Sopenharmony_ci * Return value:
313362306a36Sopenharmony_ci *	0 on success or -1 on failure
313462306a36Sopenharmony_ci */
313562306a36Sopenharmony_cistatic int pmcraid_build_ioadl(
313662306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
313762306a36Sopenharmony_ci	struct pmcraid_cmd *cmd
313862306a36Sopenharmony_ci)
313962306a36Sopenharmony_ci{
314062306a36Sopenharmony_ci	int i, nseg;
314162306a36Sopenharmony_ci	struct scatterlist *sglist;
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
314462306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
314562306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl;
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci	u32 length = scsi_bufflen(scsi_cmd);
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	if (!length)
315062306a36Sopenharmony_ci		return 0;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	nseg = scsi_dma_map(scsi_cmd);
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	if (nseg < 0) {
315562306a36Sopenharmony_ci		scmd_printk(KERN_ERR, scsi_cmd, "scsi_map_dma failed!\n");
315662306a36Sopenharmony_ci		return -1;
315762306a36Sopenharmony_ci	} else if (nseg > PMCRAID_MAX_IOADLS) {
315862306a36Sopenharmony_ci		scsi_dma_unmap(scsi_cmd);
315962306a36Sopenharmony_ci		scmd_printk(KERN_ERR, scsi_cmd,
316062306a36Sopenharmony_ci			"sg count is (%d) more than allowed!\n", nseg);
316162306a36Sopenharmony_ci		return -1;
316262306a36Sopenharmony_ci	}
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	/* Initialize IOARCB data transfer length fields */
316562306a36Sopenharmony_ci	if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE)
316662306a36Sopenharmony_ci		ioarcb->request_flags0 |= TRANSFER_DIR_WRITE;
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	ioarcb->request_flags0 |= NO_LINK_DESCS;
316962306a36Sopenharmony_ci	ioarcb->data_transfer_length = cpu_to_le32(length);
317062306a36Sopenharmony_ci	ioadl = pmcraid_init_ioadls(cmd, nseg);
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	/* Initialize IOADL descriptor addresses */
317362306a36Sopenharmony_ci	scsi_for_each_sg(scsi_cmd, sglist, nseg, i) {
317462306a36Sopenharmony_ci		ioadl[i].data_len = cpu_to_le32(sg_dma_len(sglist));
317562306a36Sopenharmony_ci		ioadl[i].address = cpu_to_le64(sg_dma_address(sglist));
317662306a36Sopenharmony_ci		ioadl[i].flags = 0;
317762306a36Sopenharmony_ci	}
317862306a36Sopenharmony_ci	/* setup last descriptor */
317962306a36Sopenharmony_ci	ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC;
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci	return 0;
318262306a36Sopenharmony_ci}
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci/**
318562306a36Sopenharmony_ci * pmcraid_queuecommand_lck - Queue a mid-layer request
318662306a36Sopenharmony_ci * @scsi_cmd: scsi command struct
318762306a36Sopenharmony_ci *
318862306a36Sopenharmony_ci * This function queues a request generated by the mid-layer. Midlayer calls
318962306a36Sopenharmony_ci * this routine within host->lock. Some of the functions called by queuecommand
319062306a36Sopenharmony_ci * would use cmd block queue locks (free_pool_lock and pending_pool_lock)
319162306a36Sopenharmony_ci *
319262306a36Sopenharmony_ci * Return value:
319362306a36Sopenharmony_ci *	  0 on success
319462306a36Sopenharmony_ci *	  SCSI_MLQUEUE_DEVICE_BUSY if device is busy
319562306a36Sopenharmony_ci *	  SCSI_MLQUEUE_HOST_BUSY if host is busy
319662306a36Sopenharmony_ci */
319762306a36Sopenharmony_cistatic int pmcraid_queuecommand_lck(struct scsi_cmnd *scsi_cmd)
319862306a36Sopenharmony_ci{
319962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
320062306a36Sopenharmony_ci	struct pmcraid_resource_entry *res;
320162306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb;
320262306a36Sopenharmony_ci	struct pmcraid_cmd *cmd;
320362306a36Sopenharmony_ci	u32 fw_version;
320462306a36Sopenharmony_ci	int rc = 0;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	pinstance =
320762306a36Sopenharmony_ci		(struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
320862306a36Sopenharmony_ci	fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
320962306a36Sopenharmony_ci	res = scsi_cmd->device->hostdata;
321062306a36Sopenharmony_ci	scsi_cmd->result = (DID_OK << 16);
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	/* if adapter is marked as dead, set result to DID_NO_CONNECT complete
321362306a36Sopenharmony_ci	 * the command
321462306a36Sopenharmony_ci	 */
321562306a36Sopenharmony_ci	if (pinstance->ioa_state == IOA_STATE_DEAD) {
321662306a36Sopenharmony_ci		pmcraid_info("IOA is dead, but queuecommand is scheduled\n");
321762306a36Sopenharmony_ci		scsi_cmd->result = (DID_NO_CONNECT << 16);
321862306a36Sopenharmony_ci		scsi_done(scsi_cmd);
321962306a36Sopenharmony_ci		return 0;
322062306a36Sopenharmony_ci	}
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	/* If IOA reset is in progress, can't queue the commands */
322362306a36Sopenharmony_ci	if (pinstance->ioa_reset_in_progress)
322462306a36Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	/* Firmware doesn't support SYNCHRONIZE_CACHE command (0x35), complete
322762306a36Sopenharmony_ci	 * the command here itself with success return
322862306a36Sopenharmony_ci	 */
322962306a36Sopenharmony_ci	if (scsi_cmd->cmnd[0] == SYNCHRONIZE_CACHE) {
323062306a36Sopenharmony_ci		pmcraid_info("SYNC_CACHE(0x35), completing in driver itself\n");
323162306a36Sopenharmony_ci		scsi_done(scsi_cmd);
323262306a36Sopenharmony_ci		return 0;
323362306a36Sopenharmony_ci	}
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	/* initialize the command and IOARCB to be sent to IOA */
323662306a36Sopenharmony_ci	cmd = pmcraid_get_free_cmd(pinstance);
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	if (cmd == NULL) {
323962306a36Sopenharmony_ci		pmcraid_err("free command block is not available\n");
324062306a36Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
324162306a36Sopenharmony_ci	}
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	cmd->scsi_cmd = scsi_cmd;
324462306a36Sopenharmony_ci	ioarcb = &(cmd->ioa_cb->ioarcb);
324562306a36Sopenharmony_ci	memcpy(ioarcb->cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len);
324662306a36Sopenharmony_ci	ioarcb->resource_handle = res->cfg_entry.resource_handle;
324762306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_SCSI;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci	/* set hrrq number where the IOA should respond to. Note that all cmds
325062306a36Sopenharmony_ci	 * generated internally uses hrrq_id 0, exception to this is the cmd
325162306a36Sopenharmony_ci	 * block of scsi_cmd which is re-used (e.g. cancel/abort), which uses
325262306a36Sopenharmony_ci	 * hrrq_id assigned here in queuecommand
325362306a36Sopenharmony_ci	 */
325462306a36Sopenharmony_ci	ioarcb->hrrq_id = atomic_add_return(1, &(pinstance->last_message_id)) %
325562306a36Sopenharmony_ci			  pinstance->num_hrrq;
325662306a36Sopenharmony_ci	cmd->cmd_done = pmcraid_io_done;
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	if (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry)) {
325962306a36Sopenharmony_ci		if (scsi_cmd->underflow == 0)
326062306a36Sopenharmony_ci			ioarcb->request_flags0 |= INHIBIT_UL_CHECK;
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci		if (res->sync_reqd) {
326362306a36Sopenharmony_ci			ioarcb->request_flags0 |= SYNC_COMPLETE;
326462306a36Sopenharmony_ci			res->sync_reqd = 0;
326562306a36Sopenharmony_ci		}
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci		ioarcb->request_flags0 |= NO_LINK_DESCS;
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci		if (scsi_cmd->flags & SCMD_TAGGED)
327062306a36Sopenharmony_ci			ioarcb->request_flags1 |= TASK_TAG_SIMPLE;
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci		if (RES_IS_GSCSI(res->cfg_entry))
327362306a36Sopenharmony_ci			ioarcb->request_flags1 |= DELAY_AFTER_RESET;
327462306a36Sopenharmony_ci	}
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	rc = pmcraid_build_ioadl(pinstance, cmd);
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	pmcraid_info("command (%d) CDB[0] = %x for %x:%x:%x:%x\n",
327962306a36Sopenharmony_ci		     le32_to_cpu(ioarcb->response_handle) >> 2,
328062306a36Sopenharmony_ci		     scsi_cmd->cmnd[0], pinstance->host->unique_id,
328162306a36Sopenharmony_ci		     RES_IS_VSET(res->cfg_entry) ? PMCRAID_VSET_BUS_ID :
328262306a36Sopenharmony_ci			PMCRAID_PHYS_BUS_ID,
328362306a36Sopenharmony_ci		     RES_IS_VSET(res->cfg_entry) ?
328462306a36Sopenharmony_ci			(fw_version <= PMCRAID_FW_VERSION_1 ?
328562306a36Sopenharmony_ci				res->cfg_entry.unique_flags1 :
328662306a36Sopenharmony_ci				le16_to_cpu(res->cfg_entry.array_id) & 0xFF) :
328762306a36Sopenharmony_ci			RES_TARGET(res->cfg_entry.resource_address),
328862306a36Sopenharmony_ci		     RES_LUN(res->cfg_entry.resource_address));
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	if (likely(rc == 0)) {
329162306a36Sopenharmony_ci		_pmcraid_fire_command(cmd);
329262306a36Sopenharmony_ci	} else {
329362306a36Sopenharmony_ci		pmcraid_err("queuecommand could not build ioadl\n");
329462306a36Sopenharmony_ci		pmcraid_return_cmd(cmd);
329562306a36Sopenharmony_ci		rc = SCSI_MLQUEUE_HOST_BUSY;
329662306a36Sopenharmony_ci	}
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	return rc;
329962306a36Sopenharmony_ci}
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_cistatic DEF_SCSI_QCMD(pmcraid_queuecommand)
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci/*
330462306a36Sopenharmony_ci * pmcraid_open -char node "open" entry, allowed only users with admin access
330562306a36Sopenharmony_ci */
330662306a36Sopenharmony_cistatic int pmcraid_chr_open(struct inode *inode, struct file *filep)
330762306a36Sopenharmony_ci{
330862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
331162306a36Sopenharmony_ci		return -EACCES;
331262306a36Sopenharmony_ci
331362306a36Sopenharmony_ci	/* Populate adapter instance * pointer for use by ioctl */
331462306a36Sopenharmony_ci	pinstance = container_of(inode->i_cdev, struct pmcraid_instance, cdev);
331562306a36Sopenharmony_ci	filep->private_data = pinstance;
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci	return 0;
331862306a36Sopenharmony_ci}
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci/*
332162306a36Sopenharmony_ci * pmcraid_fasync - Async notifier registration from applications
332262306a36Sopenharmony_ci *
332362306a36Sopenharmony_ci * This function adds the calling process to a driver global queue. When an
332462306a36Sopenharmony_ci * event occurs, SIGIO will be sent to all processes in this queue.
332562306a36Sopenharmony_ci */
332662306a36Sopenharmony_cistatic int pmcraid_chr_fasync(int fd, struct file *filep, int mode)
332762306a36Sopenharmony_ci{
332862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
332962306a36Sopenharmony_ci	int rc;
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	pinstance = filep->private_data;
333262306a36Sopenharmony_ci	mutex_lock(&pinstance->aen_queue_lock);
333362306a36Sopenharmony_ci	rc = fasync_helper(fd, filep, mode, &pinstance->aen_queue);
333462306a36Sopenharmony_ci	mutex_unlock(&pinstance->aen_queue_lock);
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	return rc;
333762306a36Sopenharmony_ci}
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci/**
334062306a36Sopenharmony_ci * pmcraid_ioctl_driver - ioctl handler for commands handled by driver itself
334162306a36Sopenharmony_ci *
334262306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
334362306a36Sopenharmony_ci * @cmd: ioctl command passed in
334462306a36Sopenharmony_ci * @buflen: length of user_buffer
334562306a36Sopenharmony_ci * @user_buffer: user buffer pointer
334662306a36Sopenharmony_ci *
334762306a36Sopenharmony_ci * Return Value
334862306a36Sopenharmony_ci *   0 in case of success, otherwise appropriate error code
334962306a36Sopenharmony_ci */
335062306a36Sopenharmony_cistatic long pmcraid_ioctl_driver(
335162306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
335262306a36Sopenharmony_ci	unsigned int cmd,
335362306a36Sopenharmony_ci	unsigned int buflen,
335462306a36Sopenharmony_ci	void __user *user_buffer
335562306a36Sopenharmony_ci)
335662306a36Sopenharmony_ci{
335762306a36Sopenharmony_ci	int rc = -ENOSYS;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	switch (cmd) {
336062306a36Sopenharmony_ci	case PMCRAID_IOCTL_RESET_ADAPTER:
336162306a36Sopenharmony_ci		pmcraid_reset_bringup(pinstance);
336262306a36Sopenharmony_ci		rc = 0;
336362306a36Sopenharmony_ci		break;
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_ci	default:
336662306a36Sopenharmony_ci		break;
336762306a36Sopenharmony_ci	}
336862306a36Sopenharmony_ci
336962306a36Sopenharmony_ci	return rc;
337062306a36Sopenharmony_ci}
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci/**
337362306a36Sopenharmony_ci * pmcraid_check_ioctl_buffer - check for proper access to user buffer
337462306a36Sopenharmony_ci *
337562306a36Sopenharmony_ci * @cmd: ioctl command
337662306a36Sopenharmony_ci * @arg: user buffer
337762306a36Sopenharmony_ci * @hdr: pointer to kernel memory for pmcraid_ioctl_header
337862306a36Sopenharmony_ci *
337962306a36Sopenharmony_ci * Return Value
338062306a36Sopenharmony_ci *	negetive error code if there are access issues, otherwise zero.
338162306a36Sopenharmony_ci *	Upon success, returns ioctl header copied out of user buffer.
338262306a36Sopenharmony_ci */
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_cistatic int pmcraid_check_ioctl_buffer(
338562306a36Sopenharmony_ci	int cmd,
338662306a36Sopenharmony_ci	void __user *arg,
338762306a36Sopenharmony_ci	struct pmcraid_ioctl_header *hdr
338862306a36Sopenharmony_ci)
338962306a36Sopenharmony_ci{
339062306a36Sopenharmony_ci	int rc;
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	if (copy_from_user(hdr, arg, sizeof(struct pmcraid_ioctl_header))) {
339362306a36Sopenharmony_ci		pmcraid_err("couldn't copy ioctl header from user buffer\n");
339462306a36Sopenharmony_ci		return -EFAULT;
339562306a36Sopenharmony_ci	}
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	/* check for valid driver signature */
339862306a36Sopenharmony_ci	rc = memcmp(hdr->signature,
339962306a36Sopenharmony_ci		    PMCRAID_IOCTL_SIGNATURE,
340062306a36Sopenharmony_ci		    sizeof(hdr->signature));
340162306a36Sopenharmony_ci	if (rc) {
340262306a36Sopenharmony_ci		pmcraid_err("signature verification failed\n");
340362306a36Sopenharmony_ci		return -EINVAL;
340462306a36Sopenharmony_ci	}
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_ci	return 0;
340762306a36Sopenharmony_ci}
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci/*
341062306a36Sopenharmony_ci *  pmcraid_ioctl - char node ioctl entry point
341162306a36Sopenharmony_ci */
341262306a36Sopenharmony_cistatic long pmcraid_chr_ioctl(
341362306a36Sopenharmony_ci	struct file *filep,
341462306a36Sopenharmony_ci	unsigned int cmd,
341562306a36Sopenharmony_ci	unsigned long arg
341662306a36Sopenharmony_ci)
341762306a36Sopenharmony_ci{
341862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = NULL;
341962306a36Sopenharmony_ci	struct pmcraid_ioctl_header *hdr = NULL;
342062306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
342162306a36Sopenharmony_ci	int retval = -ENOTTY;
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	hdr = kmalloc(sizeof(struct pmcraid_ioctl_header), GFP_KERNEL);
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	if (!hdr) {
342662306a36Sopenharmony_ci		pmcraid_err("failed to allocate memory for ioctl header\n");
342762306a36Sopenharmony_ci		return -ENOMEM;
342862306a36Sopenharmony_ci	}
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci	retval = pmcraid_check_ioctl_buffer(cmd, argp, hdr);
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_ci	if (retval) {
343362306a36Sopenharmony_ci		pmcraid_info("chr_ioctl: header check failed\n");
343462306a36Sopenharmony_ci		kfree(hdr);
343562306a36Sopenharmony_ci		return retval;
343662306a36Sopenharmony_ci	}
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	pinstance = filep->private_data;
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_ci	if (!pinstance) {
344162306a36Sopenharmony_ci		pmcraid_info("adapter instance is not found\n");
344262306a36Sopenharmony_ci		kfree(hdr);
344362306a36Sopenharmony_ci		return -ENOTTY;
344462306a36Sopenharmony_ci	}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci	switch (_IOC_TYPE(cmd)) {
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci	case PMCRAID_DRIVER_IOCTL:
344962306a36Sopenharmony_ci		arg += sizeof(struct pmcraid_ioctl_header);
345062306a36Sopenharmony_ci		retval = pmcraid_ioctl_driver(pinstance, cmd,
345162306a36Sopenharmony_ci					      hdr->buffer_length, argp);
345262306a36Sopenharmony_ci		break;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	default:
345562306a36Sopenharmony_ci		retval = -ENOTTY;
345662306a36Sopenharmony_ci		break;
345762306a36Sopenharmony_ci	}
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	kfree(hdr);
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci	return retval;
346262306a36Sopenharmony_ci}
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci/*
346562306a36Sopenharmony_ci * File operations structure for management interface
346662306a36Sopenharmony_ci */
346762306a36Sopenharmony_cistatic const struct file_operations pmcraid_fops = {
346862306a36Sopenharmony_ci	.owner = THIS_MODULE,
346962306a36Sopenharmony_ci	.open = pmcraid_chr_open,
347062306a36Sopenharmony_ci	.fasync = pmcraid_chr_fasync,
347162306a36Sopenharmony_ci	.unlocked_ioctl = pmcraid_chr_ioctl,
347262306a36Sopenharmony_ci	.compat_ioctl = compat_ptr_ioctl,
347362306a36Sopenharmony_ci	.llseek = noop_llseek,
347462306a36Sopenharmony_ci};
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci/**
348062306a36Sopenharmony_ci * pmcraid_show_log_level - Display adapter's error logging level
348162306a36Sopenharmony_ci * @dev: class device struct
348262306a36Sopenharmony_ci * @attr: unused
348362306a36Sopenharmony_ci * @buf: buffer
348462306a36Sopenharmony_ci *
348562306a36Sopenharmony_ci * Return value:
348662306a36Sopenharmony_ci *  number of bytes printed to buffer
348762306a36Sopenharmony_ci */
348862306a36Sopenharmony_cistatic ssize_t pmcraid_show_log_level(
348962306a36Sopenharmony_ci	struct device *dev,
349062306a36Sopenharmony_ci	struct device_attribute *attr,
349162306a36Sopenharmony_ci	char *buf)
349262306a36Sopenharmony_ci{
349362306a36Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
349462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance =
349562306a36Sopenharmony_ci		(struct pmcraid_instance *)shost->hostdata;
349662306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", pinstance->current_log_level);
349762306a36Sopenharmony_ci}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci/**
350062306a36Sopenharmony_ci * pmcraid_store_log_level - Change the adapter's error logging level
350162306a36Sopenharmony_ci * @dev: class device struct
350262306a36Sopenharmony_ci * @attr: unused
350362306a36Sopenharmony_ci * @buf: buffer
350462306a36Sopenharmony_ci * @count: not used
350562306a36Sopenharmony_ci *
350662306a36Sopenharmony_ci * Return value:
350762306a36Sopenharmony_ci *  number of bytes printed to buffer
350862306a36Sopenharmony_ci */
350962306a36Sopenharmony_cistatic ssize_t pmcraid_store_log_level(
351062306a36Sopenharmony_ci	struct device *dev,
351162306a36Sopenharmony_ci	struct device_attribute *attr,
351262306a36Sopenharmony_ci	const char *buf,
351362306a36Sopenharmony_ci	size_t count
351462306a36Sopenharmony_ci)
351562306a36Sopenharmony_ci{
351662306a36Sopenharmony_ci	struct Scsi_Host *shost;
351762306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
351862306a36Sopenharmony_ci	u8 val;
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci	if (kstrtou8(buf, 10, &val))
352162306a36Sopenharmony_ci		return -EINVAL;
352262306a36Sopenharmony_ci	/* log-level should be from 0 to 2 */
352362306a36Sopenharmony_ci	if (val > 2)
352462306a36Sopenharmony_ci		return -EINVAL;
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ci	shost = class_to_shost(dev);
352762306a36Sopenharmony_ci	pinstance = (struct pmcraid_instance *)shost->hostdata;
352862306a36Sopenharmony_ci	pinstance->current_log_level = val;
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_ci	return strlen(buf);
353162306a36Sopenharmony_ci}
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_cistatic struct device_attribute pmcraid_log_level_attr = {
353462306a36Sopenharmony_ci	.attr = {
353562306a36Sopenharmony_ci		 .name = "log_level",
353662306a36Sopenharmony_ci		 .mode = S_IRUGO | S_IWUSR,
353762306a36Sopenharmony_ci		 },
353862306a36Sopenharmony_ci	.show = pmcraid_show_log_level,
353962306a36Sopenharmony_ci	.store = pmcraid_store_log_level,
354062306a36Sopenharmony_ci};
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci/**
354362306a36Sopenharmony_ci * pmcraid_show_drv_version - Display driver version
354462306a36Sopenharmony_ci * @dev: class device struct
354562306a36Sopenharmony_ci * @attr: unused
354662306a36Sopenharmony_ci * @buf: buffer
354762306a36Sopenharmony_ci *
354862306a36Sopenharmony_ci * Return value:
354962306a36Sopenharmony_ci *  number of bytes printed to buffer
355062306a36Sopenharmony_ci */
355162306a36Sopenharmony_cistatic ssize_t pmcraid_show_drv_version(
355262306a36Sopenharmony_ci	struct device *dev,
355362306a36Sopenharmony_ci	struct device_attribute *attr,
355462306a36Sopenharmony_ci	char *buf
355562306a36Sopenharmony_ci)
355662306a36Sopenharmony_ci{
355762306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "version: %s\n",
355862306a36Sopenharmony_ci			PMCRAID_DRIVER_VERSION);
355962306a36Sopenharmony_ci}
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_cistatic struct device_attribute pmcraid_driver_version_attr = {
356262306a36Sopenharmony_ci	.attr = {
356362306a36Sopenharmony_ci		 .name = "drv_version",
356462306a36Sopenharmony_ci		 .mode = S_IRUGO,
356562306a36Sopenharmony_ci		 },
356662306a36Sopenharmony_ci	.show = pmcraid_show_drv_version,
356762306a36Sopenharmony_ci};
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci/**
357062306a36Sopenharmony_ci * pmcraid_show_adapter_id - Display driver assigned adapter id
357162306a36Sopenharmony_ci * @dev: class device struct
357262306a36Sopenharmony_ci * @attr: unused
357362306a36Sopenharmony_ci * @buf: buffer
357462306a36Sopenharmony_ci *
357562306a36Sopenharmony_ci * Return value:
357662306a36Sopenharmony_ci *  number of bytes printed to buffer
357762306a36Sopenharmony_ci */
357862306a36Sopenharmony_cistatic ssize_t pmcraid_show_adapter_id(
357962306a36Sopenharmony_ci	struct device *dev,
358062306a36Sopenharmony_ci	struct device_attribute *attr,
358162306a36Sopenharmony_ci	char *buf
358262306a36Sopenharmony_ci)
358362306a36Sopenharmony_ci{
358462306a36Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
358562306a36Sopenharmony_ci	struct pmcraid_instance *pinstance =
358662306a36Sopenharmony_ci		(struct pmcraid_instance *)shost->hostdata;
358762306a36Sopenharmony_ci	u32 adapter_id = pci_dev_id(pinstance->pdev);
358862306a36Sopenharmony_ci	u32 aen_group = pmcraid_event_family.id;
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE,
359162306a36Sopenharmony_ci			"adapter id: %d\nminor: %d\naen group: %d\n",
359262306a36Sopenharmony_ci			adapter_id, MINOR(pinstance->cdev.dev), aen_group);
359362306a36Sopenharmony_ci}
359462306a36Sopenharmony_ci
359562306a36Sopenharmony_cistatic struct device_attribute pmcraid_adapter_id_attr = {
359662306a36Sopenharmony_ci	.attr = {
359762306a36Sopenharmony_ci		 .name = "adapter_id",
359862306a36Sopenharmony_ci		 .mode = S_IRUGO,
359962306a36Sopenharmony_ci		 },
360062306a36Sopenharmony_ci	.show = pmcraid_show_adapter_id,
360162306a36Sopenharmony_ci};
360262306a36Sopenharmony_ci
360362306a36Sopenharmony_cistatic struct attribute *pmcraid_host_attrs[] = {
360462306a36Sopenharmony_ci	&pmcraid_log_level_attr.attr,
360562306a36Sopenharmony_ci	&pmcraid_driver_version_attr.attr,
360662306a36Sopenharmony_ci	&pmcraid_adapter_id_attr.attr,
360762306a36Sopenharmony_ci	NULL,
360862306a36Sopenharmony_ci};
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ciATTRIBUTE_GROUPS(pmcraid_host);
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci/* host template structure for pmcraid driver */
361362306a36Sopenharmony_cistatic const struct scsi_host_template pmcraid_host_template = {
361462306a36Sopenharmony_ci	.module = THIS_MODULE,
361562306a36Sopenharmony_ci	.name = PMCRAID_DRIVER_NAME,
361662306a36Sopenharmony_ci	.queuecommand = pmcraid_queuecommand,
361762306a36Sopenharmony_ci	.eh_abort_handler = pmcraid_eh_abort_handler,
361862306a36Sopenharmony_ci	.eh_bus_reset_handler = pmcraid_eh_bus_reset_handler,
361962306a36Sopenharmony_ci	.eh_target_reset_handler = pmcraid_eh_target_reset_handler,
362062306a36Sopenharmony_ci	.eh_device_reset_handler = pmcraid_eh_device_reset_handler,
362162306a36Sopenharmony_ci	.eh_host_reset_handler = pmcraid_eh_host_reset_handler,
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci	.slave_alloc = pmcraid_slave_alloc,
362462306a36Sopenharmony_ci	.slave_configure = pmcraid_slave_configure,
362562306a36Sopenharmony_ci	.slave_destroy = pmcraid_slave_destroy,
362662306a36Sopenharmony_ci	.change_queue_depth = pmcraid_change_queue_depth,
362762306a36Sopenharmony_ci	.can_queue = PMCRAID_MAX_IO_CMD,
362862306a36Sopenharmony_ci	.this_id = -1,
362962306a36Sopenharmony_ci	.sg_tablesize = PMCRAID_MAX_IOADLS,
363062306a36Sopenharmony_ci	.max_sectors = PMCRAID_IOA_MAX_SECTORS,
363162306a36Sopenharmony_ci	.no_write_same = 1,
363262306a36Sopenharmony_ci	.cmd_per_lun = PMCRAID_MAX_CMD_PER_LUN,
363362306a36Sopenharmony_ci	.shost_groups = pmcraid_host_groups,
363462306a36Sopenharmony_ci	.proc_name = PMCRAID_DRIVER_NAME,
363562306a36Sopenharmony_ci};
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci/*
363862306a36Sopenharmony_ci * pmcraid_isr_msix - implements MSI-X interrupt handling routine
363962306a36Sopenharmony_ci * @irq: interrupt vector number
364062306a36Sopenharmony_ci * @dev_id: pointer hrrq_vector
364162306a36Sopenharmony_ci *
364262306a36Sopenharmony_ci * Return Value
364362306a36Sopenharmony_ci *	 IRQ_HANDLED if interrupt is handled or IRQ_NONE if ignored
364462306a36Sopenharmony_ci */
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_cistatic irqreturn_t pmcraid_isr_msix(int irq, void *dev_id)
364762306a36Sopenharmony_ci{
364862306a36Sopenharmony_ci	struct pmcraid_isr_param *hrrq_vector;
364962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
365062306a36Sopenharmony_ci	unsigned long lock_flags;
365162306a36Sopenharmony_ci	u32 intrs_val;
365262306a36Sopenharmony_ci	int hrrq_id;
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci	hrrq_vector = (struct pmcraid_isr_param *)dev_id;
365562306a36Sopenharmony_ci	hrrq_id = hrrq_vector->hrrq_id;
365662306a36Sopenharmony_ci	pinstance = hrrq_vector->drv_inst;
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci	if (!hrrq_id) {
365962306a36Sopenharmony_ci		/* Read the interrupt */
366062306a36Sopenharmony_ci		intrs_val = pmcraid_read_interrupts(pinstance);
366162306a36Sopenharmony_ci		if (intrs_val &&
366262306a36Sopenharmony_ci			((ioread32(pinstance->int_regs.host_ioa_interrupt_reg)
366362306a36Sopenharmony_ci			& DOORBELL_INTR_MSIX_CLR) == 0)) {
366462306a36Sopenharmony_ci			/* Any error interrupts including unit_check,
366562306a36Sopenharmony_ci			 * initiate IOA reset.In case of unit check indicate
366662306a36Sopenharmony_ci			 * to reset_sequence that IOA unit checked and prepare
366762306a36Sopenharmony_ci			 * for a dump during reset sequence
366862306a36Sopenharmony_ci			 */
366962306a36Sopenharmony_ci			if (intrs_val & PMCRAID_ERROR_INTERRUPTS) {
367062306a36Sopenharmony_ci				if (intrs_val & INTRS_IOA_UNIT_CHECK)
367162306a36Sopenharmony_ci					pinstance->ioa_unit_check = 1;
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci				pmcraid_err("ISR: error interrupts: %x \
367462306a36Sopenharmony_ci					initiating reset\n", intrs_val);
367562306a36Sopenharmony_ci				spin_lock_irqsave(pinstance->host->host_lock,
367662306a36Sopenharmony_ci					lock_flags);
367762306a36Sopenharmony_ci				pmcraid_initiate_reset(pinstance);
367862306a36Sopenharmony_ci				spin_unlock_irqrestore(
367962306a36Sopenharmony_ci					pinstance->host->host_lock,
368062306a36Sopenharmony_ci					lock_flags);
368162306a36Sopenharmony_ci			}
368262306a36Sopenharmony_ci			/* If interrupt was as part of the ioa initialization,
368362306a36Sopenharmony_ci			 * clear it. Delete the timer and wakeup the
368462306a36Sopenharmony_ci			 * reset engine to proceed with reset sequence
368562306a36Sopenharmony_ci			 */
368662306a36Sopenharmony_ci			if (intrs_val & INTRS_TRANSITION_TO_OPERATIONAL)
368762306a36Sopenharmony_ci				pmcraid_clr_trans_op(pinstance);
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci			/* Clear the interrupt register by writing
369062306a36Sopenharmony_ci			 * to host to ioa doorbell. Once done
369162306a36Sopenharmony_ci			 * FW will clear the interrupt.
369262306a36Sopenharmony_ci			 */
369362306a36Sopenharmony_ci			iowrite32(DOORBELL_INTR_MSIX_CLR,
369462306a36Sopenharmony_ci				pinstance->int_regs.host_ioa_interrupt_reg);
369562306a36Sopenharmony_ci			ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci
369862306a36Sopenharmony_ci		}
369962306a36Sopenharmony_ci	}
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_ci	tasklet_schedule(&(pinstance->isr_tasklet[hrrq_id]));
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci	return IRQ_HANDLED;
370462306a36Sopenharmony_ci}
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci/**
370762306a36Sopenharmony_ci * pmcraid_isr  - implements legacy interrupt handling routine
370862306a36Sopenharmony_ci *
370962306a36Sopenharmony_ci * @irq: interrupt vector number
371062306a36Sopenharmony_ci * @dev_id: pointer hrrq_vector
371162306a36Sopenharmony_ci *
371262306a36Sopenharmony_ci * Return Value
371362306a36Sopenharmony_ci *	 IRQ_HANDLED if interrupt is handled or IRQ_NONE if ignored
371462306a36Sopenharmony_ci */
371562306a36Sopenharmony_cistatic irqreturn_t pmcraid_isr(int irq, void *dev_id)
371662306a36Sopenharmony_ci{
371762306a36Sopenharmony_ci	struct pmcraid_isr_param *hrrq_vector;
371862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
371962306a36Sopenharmony_ci	u32 intrs;
372062306a36Sopenharmony_ci	unsigned long lock_flags;
372162306a36Sopenharmony_ci	int hrrq_id = 0;
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	/* In case of legacy interrupt mode where interrupts are shared across
372462306a36Sopenharmony_ci	 * isrs, it may be possible that the current interrupt is not from IOA
372562306a36Sopenharmony_ci	 */
372662306a36Sopenharmony_ci	if (!dev_id) {
372762306a36Sopenharmony_ci		printk(KERN_INFO "%s(): NULL host pointer\n", __func__);
372862306a36Sopenharmony_ci		return IRQ_NONE;
372962306a36Sopenharmony_ci	}
373062306a36Sopenharmony_ci	hrrq_vector = (struct pmcraid_isr_param *)dev_id;
373162306a36Sopenharmony_ci	pinstance = hrrq_vector->drv_inst;
373262306a36Sopenharmony_ci
373362306a36Sopenharmony_ci	intrs = pmcraid_read_interrupts(pinstance);
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	if (unlikely((intrs & PMCRAID_PCI_INTERRUPTS) == 0))
373662306a36Sopenharmony_ci		return IRQ_NONE;
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_ci	/* Any error interrupts including unit_check, initiate IOA reset.
373962306a36Sopenharmony_ci	 * In case of unit check indicate to reset_sequence that IOA unit
374062306a36Sopenharmony_ci	 * checked and prepare for a dump during reset sequence
374162306a36Sopenharmony_ci	 */
374262306a36Sopenharmony_ci	if (intrs & PMCRAID_ERROR_INTERRUPTS) {
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_ci		if (intrs & INTRS_IOA_UNIT_CHECK)
374562306a36Sopenharmony_ci			pinstance->ioa_unit_check = 1;
374662306a36Sopenharmony_ci
374762306a36Sopenharmony_ci		iowrite32(intrs,
374862306a36Sopenharmony_ci			  pinstance->int_regs.ioa_host_interrupt_clr_reg);
374962306a36Sopenharmony_ci		pmcraid_err("ISR: error interrupts: %x initiating reset\n",
375062306a36Sopenharmony_ci			    intrs);
375162306a36Sopenharmony_ci		intrs = ioread32(
375262306a36Sopenharmony_ci				pinstance->int_regs.ioa_host_interrupt_clr_reg);
375362306a36Sopenharmony_ci		spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
375462306a36Sopenharmony_ci		pmcraid_initiate_reset(pinstance);
375562306a36Sopenharmony_ci		spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
375662306a36Sopenharmony_ci	} else {
375762306a36Sopenharmony_ci		/* If interrupt was as part of the ioa initialization,
375862306a36Sopenharmony_ci		 * clear. Delete the timer and wakeup the
375962306a36Sopenharmony_ci		 * reset engine to proceed with reset sequence
376062306a36Sopenharmony_ci		 */
376162306a36Sopenharmony_ci		if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) {
376262306a36Sopenharmony_ci			pmcraid_clr_trans_op(pinstance);
376362306a36Sopenharmony_ci		} else {
376462306a36Sopenharmony_ci			iowrite32(intrs,
376562306a36Sopenharmony_ci				pinstance->int_regs.ioa_host_interrupt_clr_reg);
376662306a36Sopenharmony_ci			ioread32(
376762306a36Sopenharmony_ci				pinstance->int_regs.ioa_host_interrupt_clr_reg);
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_ci			tasklet_schedule(
377062306a36Sopenharmony_ci					&(pinstance->isr_tasklet[hrrq_id]));
377162306a36Sopenharmony_ci		}
377262306a36Sopenharmony_ci	}
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	return IRQ_HANDLED;
377562306a36Sopenharmony_ci}
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci/**
377962306a36Sopenharmony_ci * pmcraid_worker_function -  worker thread function
378062306a36Sopenharmony_ci *
378162306a36Sopenharmony_ci * @workp: pointer to struct work queue
378262306a36Sopenharmony_ci *
378362306a36Sopenharmony_ci * Return Value
378462306a36Sopenharmony_ci *	 None
378562306a36Sopenharmony_ci */
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_cistatic void pmcraid_worker_function(struct work_struct *workp)
378862306a36Sopenharmony_ci{
378962306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
379062306a36Sopenharmony_ci	struct pmcraid_resource_entry *res;
379162306a36Sopenharmony_ci	struct pmcraid_resource_entry *temp;
379262306a36Sopenharmony_ci	struct scsi_device *sdev;
379362306a36Sopenharmony_ci	unsigned long lock_flags;
379462306a36Sopenharmony_ci	unsigned long host_lock_flags;
379562306a36Sopenharmony_ci	u16 fw_version;
379662306a36Sopenharmony_ci	u8 bus, target, lun;
379762306a36Sopenharmony_ci
379862306a36Sopenharmony_ci	pinstance = container_of(workp, struct pmcraid_instance, worker_q);
379962306a36Sopenharmony_ci	/* add resources only after host is added into system */
380062306a36Sopenharmony_ci	if (!atomic_read(&pinstance->expose_resources))
380162306a36Sopenharmony_ci		return;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
380662306a36Sopenharmony_ci	list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue) {
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci		if (res->change_detected == RES_CHANGE_DEL && res->scsi_dev) {
380962306a36Sopenharmony_ci			sdev = res->scsi_dev;
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci			/* host_lock must be held before calling
381262306a36Sopenharmony_ci			 * scsi_device_get
381362306a36Sopenharmony_ci			 */
381462306a36Sopenharmony_ci			spin_lock_irqsave(pinstance->host->host_lock,
381562306a36Sopenharmony_ci					  host_lock_flags);
381662306a36Sopenharmony_ci			if (!scsi_device_get(sdev)) {
381762306a36Sopenharmony_ci				spin_unlock_irqrestore(
381862306a36Sopenharmony_ci						pinstance->host->host_lock,
381962306a36Sopenharmony_ci						host_lock_flags);
382062306a36Sopenharmony_ci				pmcraid_info("deleting %x from midlayer\n",
382162306a36Sopenharmony_ci					     res->cfg_entry.resource_address);
382262306a36Sopenharmony_ci				list_move_tail(&res->queue,
382362306a36Sopenharmony_ci						&pinstance->free_res_q);
382462306a36Sopenharmony_ci				spin_unlock_irqrestore(
382562306a36Sopenharmony_ci					&pinstance->resource_lock,
382662306a36Sopenharmony_ci					lock_flags);
382762306a36Sopenharmony_ci				scsi_remove_device(sdev);
382862306a36Sopenharmony_ci				scsi_device_put(sdev);
382962306a36Sopenharmony_ci				spin_lock_irqsave(&pinstance->resource_lock,
383062306a36Sopenharmony_ci						   lock_flags);
383162306a36Sopenharmony_ci				res->change_detected = 0;
383262306a36Sopenharmony_ci			} else {
383362306a36Sopenharmony_ci				spin_unlock_irqrestore(
383462306a36Sopenharmony_ci						pinstance->host->host_lock,
383562306a36Sopenharmony_ci						host_lock_flags);
383662306a36Sopenharmony_ci			}
383762306a36Sopenharmony_ci		}
383862306a36Sopenharmony_ci	}
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_ci	list_for_each_entry(res, &pinstance->used_res_q, queue) {
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci		if (res->change_detected == RES_CHANGE_ADD) {
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci			if (!pmcraid_expose_resource(fw_version,
384562306a36Sopenharmony_ci						     &res->cfg_entry))
384662306a36Sopenharmony_ci				continue;
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci			if (RES_IS_VSET(res->cfg_entry)) {
384962306a36Sopenharmony_ci				bus = PMCRAID_VSET_BUS_ID;
385062306a36Sopenharmony_ci				if (fw_version <= PMCRAID_FW_VERSION_1)
385162306a36Sopenharmony_ci					target = res->cfg_entry.unique_flags1;
385262306a36Sopenharmony_ci				else
385362306a36Sopenharmony_ci					target = le16_to_cpu(res->cfg_entry.array_id) & 0xFF;
385462306a36Sopenharmony_ci				lun = PMCRAID_VSET_LUN_ID;
385562306a36Sopenharmony_ci			} else {
385662306a36Sopenharmony_ci				bus = PMCRAID_PHYS_BUS_ID;
385762306a36Sopenharmony_ci				target =
385862306a36Sopenharmony_ci				     RES_TARGET(
385962306a36Sopenharmony_ci					res->cfg_entry.resource_address);
386062306a36Sopenharmony_ci				lun = RES_LUN(res->cfg_entry.resource_address);
386162306a36Sopenharmony_ci			}
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci			res->change_detected = 0;
386462306a36Sopenharmony_ci			spin_unlock_irqrestore(&pinstance->resource_lock,
386562306a36Sopenharmony_ci						lock_flags);
386662306a36Sopenharmony_ci			scsi_add_device(pinstance->host, bus, target, lun);
386762306a36Sopenharmony_ci			spin_lock_irqsave(&pinstance->resource_lock,
386862306a36Sopenharmony_ci					   lock_flags);
386962306a36Sopenharmony_ci		}
387062306a36Sopenharmony_ci	}
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
387362306a36Sopenharmony_ci}
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci/**
387662306a36Sopenharmony_ci * pmcraid_tasklet_function - Tasklet function
387762306a36Sopenharmony_ci *
387862306a36Sopenharmony_ci * @instance: pointer to msix param structure
387962306a36Sopenharmony_ci *
388062306a36Sopenharmony_ci * Return Value
388162306a36Sopenharmony_ci *	None
388262306a36Sopenharmony_ci */
388362306a36Sopenharmony_cistatic void pmcraid_tasklet_function(unsigned long instance)
388462306a36Sopenharmony_ci{
388562306a36Sopenharmony_ci	struct pmcraid_isr_param *hrrq_vector;
388662306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
388762306a36Sopenharmony_ci	unsigned long hrrq_lock_flags;
388862306a36Sopenharmony_ci	unsigned long pending_lock_flags;
388962306a36Sopenharmony_ci	unsigned long host_lock_flags;
389062306a36Sopenharmony_ci	spinlock_t *lockp; /* hrrq buffer lock */
389162306a36Sopenharmony_ci	int id;
389262306a36Sopenharmony_ci	u32 resp;
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	hrrq_vector = (struct pmcraid_isr_param *)instance;
389562306a36Sopenharmony_ci	pinstance = hrrq_vector->drv_inst;
389662306a36Sopenharmony_ci	id = hrrq_vector->hrrq_id;
389762306a36Sopenharmony_ci	lockp = &(pinstance->hrrq_lock[id]);
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	/* loop through each of the commands responded by IOA. Each HRRQ buf is
390062306a36Sopenharmony_ci	 * protected by its own lock. Traversals must be done within this lock
390162306a36Sopenharmony_ci	 * as there may be multiple tasklets running on multiple CPUs. Note
390262306a36Sopenharmony_ci	 * that the lock is held just for picking up the response handle and
390362306a36Sopenharmony_ci	 * manipulating hrrq_curr/toggle_bit values.
390462306a36Sopenharmony_ci	 */
390562306a36Sopenharmony_ci	spin_lock_irqsave(lockp, hrrq_lock_flags);
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_ci	resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	while ((resp & HRRQ_TOGGLE_BIT) ==
391062306a36Sopenharmony_ci		pinstance->host_toggle_bit[id]) {
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci		int cmd_index = resp >> 2;
391362306a36Sopenharmony_ci		struct pmcraid_cmd *cmd = NULL;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci		if (pinstance->hrrq_curr[id] < pinstance->hrrq_end[id]) {
391662306a36Sopenharmony_ci			pinstance->hrrq_curr[id]++;
391762306a36Sopenharmony_ci		} else {
391862306a36Sopenharmony_ci			pinstance->hrrq_curr[id] = pinstance->hrrq_start[id];
391962306a36Sopenharmony_ci			pinstance->host_toggle_bit[id] ^= 1u;
392062306a36Sopenharmony_ci		}
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci		if (cmd_index >= PMCRAID_MAX_CMD) {
392362306a36Sopenharmony_ci			/* In case of invalid response handle, log message */
392462306a36Sopenharmony_ci			pmcraid_err("Invalid response handle %d\n", cmd_index);
392562306a36Sopenharmony_ci			resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
392662306a36Sopenharmony_ci			continue;
392762306a36Sopenharmony_ci		}
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci		cmd = pinstance->cmd_list[cmd_index];
393062306a36Sopenharmony_ci		spin_unlock_irqrestore(lockp, hrrq_lock_flags);
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci		spin_lock_irqsave(&pinstance->pending_pool_lock,
393362306a36Sopenharmony_ci				   pending_lock_flags);
393462306a36Sopenharmony_ci		list_del(&cmd->free_list);
393562306a36Sopenharmony_ci		spin_unlock_irqrestore(&pinstance->pending_pool_lock,
393662306a36Sopenharmony_ci					pending_lock_flags);
393762306a36Sopenharmony_ci		del_timer(&cmd->timer);
393862306a36Sopenharmony_ci		atomic_dec(&pinstance->outstanding_cmds);
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci		if (cmd->cmd_done == pmcraid_ioa_reset) {
394162306a36Sopenharmony_ci			spin_lock_irqsave(pinstance->host->host_lock,
394262306a36Sopenharmony_ci					  host_lock_flags);
394362306a36Sopenharmony_ci			cmd->cmd_done(cmd);
394462306a36Sopenharmony_ci			spin_unlock_irqrestore(pinstance->host->host_lock,
394562306a36Sopenharmony_ci					       host_lock_flags);
394662306a36Sopenharmony_ci		} else if (cmd->cmd_done != NULL) {
394762306a36Sopenharmony_ci			cmd->cmd_done(cmd);
394862306a36Sopenharmony_ci		}
394962306a36Sopenharmony_ci		/* loop over until we are done with all responses */
395062306a36Sopenharmony_ci		spin_lock_irqsave(lockp, hrrq_lock_flags);
395162306a36Sopenharmony_ci		resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
395262306a36Sopenharmony_ci	}
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci	spin_unlock_irqrestore(lockp, hrrq_lock_flags);
395562306a36Sopenharmony_ci}
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci/**
395862306a36Sopenharmony_ci * pmcraid_unregister_interrupt_handler - de-register interrupts handlers
395962306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
396062306a36Sopenharmony_ci *
396162306a36Sopenharmony_ci * This routine un-registers registered interrupt handler and
396262306a36Sopenharmony_ci * also frees irqs/vectors.
396362306a36Sopenharmony_ci *
396462306a36Sopenharmony_ci * Retun Value
396562306a36Sopenharmony_ci *	None
396662306a36Sopenharmony_ci */
396762306a36Sopenharmony_cistatic
396862306a36Sopenharmony_civoid pmcraid_unregister_interrupt_handler(struct pmcraid_instance *pinstance)
396962306a36Sopenharmony_ci{
397062306a36Sopenharmony_ci	struct pci_dev *pdev = pinstance->pdev;
397162306a36Sopenharmony_ci	int i;
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_ci	for (i = 0; i < pinstance->num_hrrq; i++)
397462306a36Sopenharmony_ci		free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]);
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	pinstance->interrupt_mode = 0;
397762306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
397862306a36Sopenharmony_ci}
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci/**
398162306a36Sopenharmony_ci * pmcraid_register_interrupt_handler - registers interrupt handler
398262306a36Sopenharmony_ci * @pinstance: pointer to per-adapter instance structure
398362306a36Sopenharmony_ci *
398462306a36Sopenharmony_ci * Return Value
398562306a36Sopenharmony_ci *	0 on success, non-zero error code otherwise.
398662306a36Sopenharmony_ci */
398762306a36Sopenharmony_cistatic int
398862306a36Sopenharmony_cipmcraid_register_interrupt_handler(struct pmcraid_instance *pinstance)
398962306a36Sopenharmony_ci{
399062306a36Sopenharmony_ci	struct pci_dev *pdev = pinstance->pdev;
399162306a36Sopenharmony_ci	unsigned int irq_flag = PCI_IRQ_LEGACY, flag;
399262306a36Sopenharmony_ci	int num_hrrq, rc, i;
399362306a36Sopenharmony_ci	irq_handler_t isr;
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_ci	if (pmcraid_enable_msix)
399662306a36Sopenharmony_ci		irq_flag |= PCI_IRQ_MSIX;
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	num_hrrq = pci_alloc_irq_vectors(pdev, 1, PMCRAID_NUM_MSIX_VECTORS,
399962306a36Sopenharmony_ci			irq_flag);
400062306a36Sopenharmony_ci	if (num_hrrq < 0)
400162306a36Sopenharmony_ci		return num_hrrq;
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ci	if (pdev->msix_enabled) {
400462306a36Sopenharmony_ci		flag = 0;
400562306a36Sopenharmony_ci		isr = pmcraid_isr_msix;
400662306a36Sopenharmony_ci	} else {
400762306a36Sopenharmony_ci		flag = IRQF_SHARED;
400862306a36Sopenharmony_ci		isr = pmcraid_isr;
400962306a36Sopenharmony_ci	}
401062306a36Sopenharmony_ci
401162306a36Sopenharmony_ci	for (i = 0; i < num_hrrq; i++) {
401262306a36Sopenharmony_ci		struct pmcraid_isr_param *vec = &pinstance->hrrq_vector[i];
401362306a36Sopenharmony_ci
401462306a36Sopenharmony_ci		vec->hrrq_id = i;
401562306a36Sopenharmony_ci		vec->drv_inst = pinstance;
401662306a36Sopenharmony_ci		rc = request_irq(pci_irq_vector(pdev, i), isr, flag,
401762306a36Sopenharmony_ci				PMCRAID_DRIVER_NAME, vec);
401862306a36Sopenharmony_ci		if (rc)
401962306a36Sopenharmony_ci			goto out_unwind;
402062306a36Sopenharmony_ci	}
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci	pinstance->num_hrrq = num_hrrq;
402362306a36Sopenharmony_ci	if (pdev->msix_enabled) {
402462306a36Sopenharmony_ci		pinstance->interrupt_mode = 1;
402562306a36Sopenharmony_ci		iowrite32(DOORBELL_INTR_MODE_MSIX,
402662306a36Sopenharmony_ci			  pinstance->int_regs.host_ioa_interrupt_reg);
402762306a36Sopenharmony_ci		ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
402862306a36Sopenharmony_ci	}
402962306a36Sopenharmony_ci
403062306a36Sopenharmony_ci	return 0;
403162306a36Sopenharmony_ci
403262306a36Sopenharmony_ciout_unwind:
403362306a36Sopenharmony_ci	while (--i >= 0)
403462306a36Sopenharmony_ci		free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]);
403562306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
403662306a36Sopenharmony_ci	return rc;
403762306a36Sopenharmony_ci}
403862306a36Sopenharmony_ci
403962306a36Sopenharmony_ci/**
404062306a36Sopenharmony_ci * pmcraid_release_cmd_blocks - release buufers allocated for command blocks
404162306a36Sopenharmony_ci * @pinstance: per adapter instance structure pointer
404262306a36Sopenharmony_ci * @max_index: number of buffer blocks to release
404362306a36Sopenharmony_ci *
404462306a36Sopenharmony_ci * Return Value
404562306a36Sopenharmony_ci *  None
404662306a36Sopenharmony_ci */
404762306a36Sopenharmony_cistatic void
404862306a36Sopenharmony_cipmcraid_release_cmd_blocks(struct pmcraid_instance *pinstance, int max_index)
404962306a36Sopenharmony_ci{
405062306a36Sopenharmony_ci	int i;
405162306a36Sopenharmony_ci	for (i = 0; i < max_index; i++) {
405262306a36Sopenharmony_ci		kmem_cache_free(pinstance->cmd_cachep, pinstance->cmd_list[i]);
405362306a36Sopenharmony_ci		pinstance->cmd_list[i] = NULL;
405462306a36Sopenharmony_ci	}
405562306a36Sopenharmony_ci	kmem_cache_destroy(pinstance->cmd_cachep);
405662306a36Sopenharmony_ci	pinstance->cmd_cachep = NULL;
405762306a36Sopenharmony_ci}
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_ci/**
406062306a36Sopenharmony_ci * pmcraid_release_control_blocks - releases buffers alloced for control blocks
406162306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
406262306a36Sopenharmony_ci * @max_index: number of buffers (from 0 onwards) to release
406362306a36Sopenharmony_ci *
406462306a36Sopenharmony_ci * This function assumes that the command blocks for which control blocks are
406562306a36Sopenharmony_ci * linked are not released.
406662306a36Sopenharmony_ci *
406762306a36Sopenharmony_ci * Return Value
406862306a36Sopenharmony_ci *	 None
406962306a36Sopenharmony_ci */
407062306a36Sopenharmony_cistatic void
407162306a36Sopenharmony_cipmcraid_release_control_blocks(
407262306a36Sopenharmony_ci	struct pmcraid_instance *pinstance,
407362306a36Sopenharmony_ci	int max_index
407462306a36Sopenharmony_ci)
407562306a36Sopenharmony_ci{
407662306a36Sopenharmony_ci	int i;
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_ci	if (pinstance->control_pool == NULL)
407962306a36Sopenharmony_ci		return;
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_ci	for (i = 0; i < max_index; i++) {
408262306a36Sopenharmony_ci		dma_pool_free(pinstance->control_pool,
408362306a36Sopenharmony_ci			      pinstance->cmd_list[i]->ioa_cb,
408462306a36Sopenharmony_ci			      pinstance->cmd_list[i]->ioa_cb_bus_addr);
408562306a36Sopenharmony_ci		pinstance->cmd_list[i]->ioa_cb = NULL;
408662306a36Sopenharmony_ci		pinstance->cmd_list[i]->ioa_cb_bus_addr = 0;
408762306a36Sopenharmony_ci	}
408862306a36Sopenharmony_ci	dma_pool_destroy(pinstance->control_pool);
408962306a36Sopenharmony_ci	pinstance->control_pool = NULL;
409062306a36Sopenharmony_ci}
409162306a36Sopenharmony_ci
409262306a36Sopenharmony_ci/**
409362306a36Sopenharmony_ci * pmcraid_allocate_cmd_blocks - allocate memory for cmd block structures
409462306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
409562306a36Sopenharmony_ci *
409662306a36Sopenharmony_ci * Allocates memory for command blocks using kernel slab allocator.
409762306a36Sopenharmony_ci *
409862306a36Sopenharmony_ci * Return Value
409962306a36Sopenharmony_ci *	0 in case of success; -ENOMEM in case of failure
410062306a36Sopenharmony_ci */
410162306a36Sopenharmony_cistatic int pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance)
410262306a36Sopenharmony_ci{
410362306a36Sopenharmony_ci	int i;
410462306a36Sopenharmony_ci
410562306a36Sopenharmony_ci	sprintf(pinstance->cmd_pool_name, "pmcraid_cmd_pool_%d",
410662306a36Sopenharmony_ci		pinstance->host->unique_id);
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci	pinstance->cmd_cachep = kmem_cache_create(
411062306a36Sopenharmony_ci					pinstance->cmd_pool_name,
411162306a36Sopenharmony_ci					sizeof(struct pmcraid_cmd), 0,
411262306a36Sopenharmony_ci					SLAB_HWCACHE_ALIGN, NULL);
411362306a36Sopenharmony_ci	if (!pinstance->cmd_cachep)
411462306a36Sopenharmony_ci		return -ENOMEM;
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	for (i = 0; i < PMCRAID_MAX_CMD; i++) {
411762306a36Sopenharmony_ci		pinstance->cmd_list[i] =
411862306a36Sopenharmony_ci			kmem_cache_alloc(pinstance->cmd_cachep, GFP_KERNEL);
411962306a36Sopenharmony_ci		if (!pinstance->cmd_list[i]) {
412062306a36Sopenharmony_ci			pmcraid_release_cmd_blocks(pinstance, i);
412162306a36Sopenharmony_ci			return -ENOMEM;
412262306a36Sopenharmony_ci		}
412362306a36Sopenharmony_ci	}
412462306a36Sopenharmony_ci	return 0;
412562306a36Sopenharmony_ci}
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci/**
412862306a36Sopenharmony_ci * pmcraid_allocate_control_blocks - allocates memory control blocks
412962306a36Sopenharmony_ci * @pinstance : pointer to per adapter instance structure
413062306a36Sopenharmony_ci *
413162306a36Sopenharmony_ci * This function allocates PCI memory for DMAable buffers like IOARCB, IOADLs
413262306a36Sopenharmony_ci * and IOASAs. This is called after command blocks are already allocated.
413362306a36Sopenharmony_ci *
413462306a36Sopenharmony_ci * Return Value
413562306a36Sopenharmony_ci *  0 in case it can allocate all control blocks, otherwise -ENOMEM
413662306a36Sopenharmony_ci */
413762306a36Sopenharmony_cistatic int pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
413862306a36Sopenharmony_ci{
413962306a36Sopenharmony_ci	int i;
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci	sprintf(pinstance->ctl_pool_name, "pmcraid_control_pool_%d",
414262306a36Sopenharmony_ci		pinstance->host->unique_id);
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci	pinstance->control_pool =
414562306a36Sopenharmony_ci		dma_pool_create(pinstance->ctl_pool_name,
414662306a36Sopenharmony_ci				&pinstance->pdev->dev,
414762306a36Sopenharmony_ci				sizeof(struct pmcraid_control_block),
414862306a36Sopenharmony_ci				PMCRAID_IOARCB_ALIGNMENT, 0);
414962306a36Sopenharmony_ci
415062306a36Sopenharmony_ci	if (!pinstance->control_pool)
415162306a36Sopenharmony_ci		return -ENOMEM;
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	for (i = 0; i < PMCRAID_MAX_CMD; i++) {
415462306a36Sopenharmony_ci		pinstance->cmd_list[i]->ioa_cb =
415562306a36Sopenharmony_ci			dma_pool_zalloc(
415662306a36Sopenharmony_ci				pinstance->control_pool,
415762306a36Sopenharmony_ci				GFP_KERNEL,
415862306a36Sopenharmony_ci				&(pinstance->cmd_list[i]->ioa_cb_bus_addr));
415962306a36Sopenharmony_ci
416062306a36Sopenharmony_ci		if (!pinstance->cmd_list[i]->ioa_cb) {
416162306a36Sopenharmony_ci			pmcraid_release_control_blocks(pinstance, i);
416262306a36Sopenharmony_ci			return -ENOMEM;
416362306a36Sopenharmony_ci		}
416462306a36Sopenharmony_ci	}
416562306a36Sopenharmony_ci	return 0;
416662306a36Sopenharmony_ci}
416762306a36Sopenharmony_ci
416862306a36Sopenharmony_ci/**
416962306a36Sopenharmony_ci * pmcraid_release_host_rrqs - release memory allocated for hrrq buffer(s)
417062306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
417162306a36Sopenharmony_ci * @maxindex: size of hrrq buffer pointer array
417262306a36Sopenharmony_ci *
417362306a36Sopenharmony_ci * Return Value
417462306a36Sopenharmony_ci *	None
417562306a36Sopenharmony_ci */
417662306a36Sopenharmony_cistatic void
417762306a36Sopenharmony_cipmcraid_release_host_rrqs(struct pmcraid_instance *pinstance, int maxindex)
417862306a36Sopenharmony_ci{
417962306a36Sopenharmony_ci	int i;
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	for (i = 0; i < maxindex; i++) {
418262306a36Sopenharmony_ci		dma_free_coherent(&pinstance->pdev->dev,
418362306a36Sopenharmony_ci				    HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD,
418462306a36Sopenharmony_ci				    pinstance->hrrq_start[i],
418562306a36Sopenharmony_ci				    pinstance->hrrq_start_bus_addr[i]);
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci		/* reset pointers and toggle bit to zeros */
418862306a36Sopenharmony_ci		pinstance->hrrq_start[i] = NULL;
418962306a36Sopenharmony_ci		pinstance->hrrq_start_bus_addr[i] = 0;
419062306a36Sopenharmony_ci		pinstance->host_toggle_bit[i] = 0;
419162306a36Sopenharmony_ci	}
419262306a36Sopenharmony_ci}
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci/**
419562306a36Sopenharmony_ci * pmcraid_allocate_host_rrqs - Allocate and initialize host RRQ buffers
419662306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
419762306a36Sopenharmony_ci *
419862306a36Sopenharmony_ci * Return value
419962306a36Sopenharmony_ci *	0 hrrq buffers are allocated, -ENOMEM otherwise.
420062306a36Sopenharmony_ci */
420162306a36Sopenharmony_cistatic int pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
420262306a36Sopenharmony_ci{
420362306a36Sopenharmony_ci	int i, buffer_size;
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD;
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	for (i = 0; i < pinstance->num_hrrq; i++) {
420862306a36Sopenharmony_ci		pinstance->hrrq_start[i] =
420962306a36Sopenharmony_ci			dma_alloc_coherent(&pinstance->pdev->dev, buffer_size,
421062306a36Sopenharmony_ci					   &pinstance->hrrq_start_bus_addr[i],
421162306a36Sopenharmony_ci					   GFP_KERNEL);
421262306a36Sopenharmony_ci		if (!pinstance->hrrq_start[i]) {
421362306a36Sopenharmony_ci			pmcraid_err("pci_alloc failed for hrrq vector : %d\n",
421462306a36Sopenharmony_ci				    i);
421562306a36Sopenharmony_ci			pmcraid_release_host_rrqs(pinstance, i);
421662306a36Sopenharmony_ci			return -ENOMEM;
421762306a36Sopenharmony_ci		}
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci		pinstance->hrrq_curr[i] = pinstance->hrrq_start[i];
422062306a36Sopenharmony_ci		pinstance->hrrq_end[i] =
422162306a36Sopenharmony_ci			pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1;
422262306a36Sopenharmony_ci		pinstance->host_toggle_bit[i] = 1;
422362306a36Sopenharmony_ci		spin_lock_init(&pinstance->hrrq_lock[i]);
422462306a36Sopenharmony_ci	}
422562306a36Sopenharmony_ci	return 0;
422662306a36Sopenharmony_ci}
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci/**
422962306a36Sopenharmony_ci * pmcraid_release_hcams - release HCAM buffers
423062306a36Sopenharmony_ci *
423162306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
423262306a36Sopenharmony_ci *
423362306a36Sopenharmony_ci * Return value
423462306a36Sopenharmony_ci *  none
423562306a36Sopenharmony_ci */
423662306a36Sopenharmony_cistatic void pmcraid_release_hcams(struct pmcraid_instance *pinstance)
423762306a36Sopenharmony_ci{
423862306a36Sopenharmony_ci	if (pinstance->ccn.msg != NULL) {
423962306a36Sopenharmony_ci		dma_free_coherent(&pinstance->pdev->dev,
424062306a36Sopenharmony_ci				    PMCRAID_AEN_HDR_SIZE +
424162306a36Sopenharmony_ci				    sizeof(struct pmcraid_hcam_ccn_ext),
424262306a36Sopenharmony_ci				    pinstance->ccn.msg,
424362306a36Sopenharmony_ci				    pinstance->ccn.baddr);
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci		pinstance->ccn.msg = NULL;
424662306a36Sopenharmony_ci		pinstance->ccn.hcam = NULL;
424762306a36Sopenharmony_ci		pinstance->ccn.baddr = 0;
424862306a36Sopenharmony_ci	}
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_ci	if (pinstance->ldn.msg != NULL) {
425162306a36Sopenharmony_ci		dma_free_coherent(&pinstance->pdev->dev,
425262306a36Sopenharmony_ci				    PMCRAID_AEN_HDR_SIZE +
425362306a36Sopenharmony_ci				    sizeof(struct pmcraid_hcam_ldn),
425462306a36Sopenharmony_ci				    pinstance->ldn.msg,
425562306a36Sopenharmony_ci				    pinstance->ldn.baddr);
425662306a36Sopenharmony_ci
425762306a36Sopenharmony_ci		pinstance->ldn.msg = NULL;
425862306a36Sopenharmony_ci		pinstance->ldn.hcam = NULL;
425962306a36Sopenharmony_ci		pinstance->ldn.baddr = 0;
426062306a36Sopenharmony_ci	}
426162306a36Sopenharmony_ci}
426262306a36Sopenharmony_ci
426362306a36Sopenharmony_ci/**
426462306a36Sopenharmony_ci * pmcraid_allocate_hcams - allocates HCAM buffers
426562306a36Sopenharmony_ci * @pinstance : pointer to per adapter instance structure
426662306a36Sopenharmony_ci *
426762306a36Sopenharmony_ci * Return Value:
426862306a36Sopenharmony_ci *   0 in case of successful allocation, non-zero otherwise
426962306a36Sopenharmony_ci */
427062306a36Sopenharmony_cistatic int pmcraid_allocate_hcams(struct pmcraid_instance *pinstance)
427162306a36Sopenharmony_ci{
427262306a36Sopenharmony_ci	pinstance->ccn.msg = dma_alloc_coherent(&pinstance->pdev->dev,
427362306a36Sopenharmony_ci					PMCRAID_AEN_HDR_SIZE +
427462306a36Sopenharmony_ci					sizeof(struct pmcraid_hcam_ccn_ext),
427562306a36Sopenharmony_ci					&pinstance->ccn.baddr, GFP_KERNEL);
427662306a36Sopenharmony_ci
427762306a36Sopenharmony_ci	pinstance->ldn.msg = dma_alloc_coherent(&pinstance->pdev->dev,
427862306a36Sopenharmony_ci					PMCRAID_AEN_HDR_SIZE +
427962306a36Sopenharmony_ci					sizeof(struct pmcraid_hcam_ldn),
428062306a36Sopenharmony_ci					&pinstance->ldn.baddr, GFP_KERNEL);
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	if (pinstance->ldn.msg == NULL || pinstance->ccn.msg == NULL) {
428362306a36Sopenharmony_ci		pmcraid_release_hcams(pinstance);
428462306a36Sopenharmony_ci	} else {
428562306a36Sopenharmony_ci		pinstance->ccn.hcam =
428662306a36Sopenharmony_ci			(void *)pinstance->ccn.msg + PMCRAID_AEN_HDR_SIZE;
428762306a36Sopenharmony_ci		pinstance->ldn.hcam =
428862306a36Sopenharmony_ci			(void *)pinstance->ldn.msg + PMCRAID_AEN_HDR_SIZE;
428962306a36Sopenharmony_ci
429062306a36Sopenharmony_ci		atomic_set(&pinstance->ccn.ignore, 0);
429162306a36Sopenharmony_ci		atomic_set(&pinstance->ldn.ignore, 0);
429262306a36Sopenharmony_ci	}
429362306a36Sopenharmony_ci
429462306a36Sopenharmony_ci	return (pinstance->ldn.msg == NULL) ? -ENOMEM : 0;
429562306a36Sopenharmony_ci}
429662306a36Sopenharmony_ci
429762306a36Sopenharmony_ci/**
429862306a36Sopenharmony_ci * pmcraid_release_config_buffers - release config.table buffers
429962306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
430062306a36Sopenharmony_ci *
430162306a36Sopenharmony_ci * Return Value
430262306a36Sopenharmony_ci *	 none
430362306a36Sopenharmony_ci */
430462306a36Sopenharmony_cistatic void pmcraid_release_config_buffers(struct pmcraid_instance *pinstance)
430562306a36Sopenharmony_ci{
430662306a36Sopenharmony_ci	if (pinstance->cfg_table != NULL &&
430762306a36Sopenharmony_ci	    pinstance->cfg_table_bus_addr != 0) {
430862306a36Sopenharmony_ci		dma_free_coherent(&pinstance->pdev->dev,
430962306a36Sopenharmony_ci				    sizeof(struct pmcraid_config_table),
431062306a36Sopenharmony_ci				    pinstance->cfg_table,
431162306a36Sopenharmony_ci				    pinstance->cfg_table_bus_addr);
431262306a36Sopenharmony_ci		pinstance->cfg_table = NULL;
431362306a36Sopenharmony_ci		pinstance->cfg_table_bus_addr = 0;
431462306a36Sopenharmony_ci	}
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	if (pinstance->res_entries != NULL) {
431762306a36Sopenharmony_ci		int i;
431862306a36Sopenharmony_ci
431962306a36Sopenharmony_ci		for (i = 0; i < PMCRAID_MAX_RESOURCES; i++)
432062306a36Sopenharmony_ci			list_del(&pinstance->res_entries[i].queue);
432162306a36Sopenharmony_ci		kfree(pinstance->res_entries);
432262306a36Sopenharmony_ci		pinstance->res_entries = NULL;
432362306a36Sopenharmony_ci	}
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci	pmcraid_release_hcams(pinstance);
432662306a36Sopenharmony_ci}
432762306a36Sopenharmony_ci
432862306a36Sopenharmony_ci/**
432962306a36Sopenharmony_ci * pmcraid_allocate_config_buffers - allocates DMAable memory for config table
433062306a36Sopenharmony_ci * @pinstance : pointer to per adapter instance structure
433162306a36Sopenharmony_ci *
433262306a36Sopenharmony_ci * Return Value
433362306a36Sopenharmony_ci *	0 for successful allocation, -ENOMEM for any failure
433462306a36Sopenharmony_ci */
433562306a36Sopenharmony_cistatic int pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
433662306a36Sopenharmony_ci{
433762306a36Sopenharmony_ci	int i;
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci	pinstance->res_entries =
434062306a36Sopenharmony_ci			kcalloc(PMCRAID_MAX_RESOURCES,
434162306a36Sopenharmony_ci				sizeof(struct pmcraid_resource_entry),
434262306a36Sopenharmony_ci				GFP_KERNEL);
434362306a36Sopenharmony_ci
434462306a36Sopenharmony_ci	if (NULL == pinstance->res_entries) {
434562306a36Sopenharmony_ci		pmcraid_err("failed to allocate memory for resource table\n");
434662306a36Sopenharmony_ci		return -ENOMEM;
434762306a36Sopenharmony_ci	}
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_ci	for (i = 0; i < PMCRAID_MAX_RESOURCES; i++)
435062306a36Sopenharmony_ci		list_add_tail(&pinstance->res_entries[i].queue,
435162306a36Sopenharmony_ci			      &pinstance->free_res_q);
435262306a36Sopenharmony_ci
435362306a36Sopenharmony_ci	pinstance->cfg_table = dma_alloc_coherent(&pinstance->pdev->dev,
435462306a36Sopenharmony_ci				     sizeof(struct pmcraid_config_table),
435562306a36Sopenharmony_ci				     &pinstance->cfg_table_bus_addr,
435662306a36Sopenharmony_ci				     GFP_KERNEL);
435762306a36Sopenharmony_ci
435862306a36Sopenharmony_ci	if (NULL == pinstance->cfg_table) {
435962306a36Sopenharmony_ci		pmcraid_err("couldn't alloc DMA memory for config table\n");
436062306a36Sopenharmony_ci		pmcraid_release_config_buffers(pinstance);
436162306a36Sopenharmony_ci		return -ENOMEM;
436262306a36Sopenharmony_ci	}
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	if (pmcraid_allocate_hcams(pinstance)) {
436562306a36Sopenharmony_ci		pmcraid_err("could not alloc DMA memory for HCAMS\n");
436662306a36Sopenharmony_ci		pmcraid_release_config_buffers(pinstance);
436762306a36Sopenharmony_ci		return -ENOMEM;
436862306a36Sopenharmony_ci	}
436962306a36Sopenharmony_ci
437062306a36Sopenharmony_ci	return 0;
437162306a36Sopenharmony_ci}
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci/**
437462306a36Sopenharmony_ci * pmcraid_init_tasklets - registers tasklets for response handling
437562306a36Sopenharmony_ci *
437662306a36Sopenharmony_ci * @pinstance: pointer adapter instance structure
437762306a36Sopenharmony_ci *
437862306a36Sopenharmony_ci * Return value
437962306a36Sopenharmony_ci *	none
438062306a36Sopenharmony_ci */
438162306a36Sopenharmony_cistatic void pmcraid_init_tasklets(struct pmcraid_instance *pinstance)
438262306a36Sopenharmony_ci{
438362306a36Sopenharmony_ci	int i;
438462306a36Sopenharmony_ci	for (i = 0; i < pinstance->num_hrrq; i++)
438562306a36Sopenharmony_ci		tasklet_init(&pinstance->isr_tasklet[i],
438662306a36Sopenharmony_ci			     pmcraid_tasklet_function,
438762306a36Sopenharmony_ci			     (unsigned long)&pinstance->hrrq_vector[i]);
438862306a36Sopenharmony_ci}
438962306a36Sopenharmony_ci
439062306a36Sopenharmony_ci/**
439162306a36Sopenharmony_ci * pmcraid_kill_tasklets - destroys tasklets registered for response handling
439262306a36Sopenharmony_ci *
439362306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
439462306a36Sopenharmony_ci *
439562306a36Sopenharmony_ci * Return value
439662306a36Sopenharmony_ci *	none
439762306a36Sopenharmony_ci */
439862306a36Sopenharmony_cistatic void pmcraid_kill_tasklets(struct pmcraid_instance *pinstance)
439962306a36Sopenharmony_ci{
440062306a36Sopenharmony_ci	int i;
440162306a36Sopenharmony_ci	for (i = 0; i < pinstance->num_hrrq; i++)
440262306a36Sopenharmony_ci		tasklet_kill(&pinstance->isr_tasklet[i]);
440362306a36Sopenharmony_ci}
440462306a36Sopenharmony_ci
440562306a36Sopenharmony_ci/**
440662306a36Sopenharmony_ci * pmcraid_release_buffers - release per-adapter buffers allocated
440762306a36Sopenharmony_ci *
440862306a36Sopenharmony_ci * @pinstance: pointer to adapter soft state
440962306a36Sopenharmony_ci *
441062306a36Sopenharmony_ci * Return Value
441162306a36Sopenharmony_ci *	none
441262306a36Sopenharmony_ci */
441362306a36Sopenharmony_cistatic void pmcraid_release_buffers(struct pmcraid_instance *pinstance)
441462306a36Sopenharmony_ci{
441562306a36Sopenharmony_ci	pmcraid_release_config_buffers(pinstance);
441662306a36Sopenharmony_ci	pmcraid_release_control_blocks(pinstance, PMCRAID_MAX_CMD);
441762306a36Sopenharmony_ci	pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD);
441862306a36Sopenharmony_ci	pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci	if (pinstance->inq_data != NULL) {
442162306a36Sopenharmony_ci		dma_free_coherent(&pinstance->pdev->dev,
442262306a36Sopenharmony_ci				    sizeof(struct pmcraid_inquiry_data),
442362306a36Sopenharmony_ci				    pinstance->inq_data,
442462306a36Sopenharmony_ci				    pinstance->inq_data_baddr);
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ci		pinstance->inq_data = NULL;
442762306a36Sopenharmony_ci		pinstance->inq_data_baddr = 0;
442862306a36Sopenharmony_ci	}
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	if (pinstance->timestamp_data != NULL) {
443162306a36Sopenharmony_ci		dma_free_coherent(&pinstance->pdev->dev,
443262306a36Sopenharmony_ci				    sizeof(struct pmcraid_timestamp_data),
443362306a36Sopenharmony_ci				    pinstance->timestamp_data,
443462306a36Sopenharmony_ci				    pinstance->timestamp_data_baddr);
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci		pinstance->timestamp_data = NULL;
443762306a36Sopenharmony_ci		pinstance->timestamp_data_baddr = 0;
443862306a36Sopenharmony_ci	}
443962306a36Sopenharmony_ci}
444062306a36Sopenharmony_ci
444162306a36Sopenharmony_ci/**
444262306a36Sopenharmony_ci * pmcraid_init_buffers - allocates memory and initializes various structures
444362306a36Sopenharmony_ci * @pinstance: pointer to per adapter instance structure
444462306a36Sopenharmony_ci *
444562306a36Sopenharmony_ci * This routine pre-allocates memory based on the type of block as below:
444662306a36Sopenharmony_ci * cmdblocks(PMCRAID_MAX_CMD): kernel memory using kernel's slab_allocator,
444762306a36Sopenharmony_ci * IOARCBs(PMCRAID_MAX_CMD)  : DMAable memory, using pci pool allocator
444862306a36Sopenharmony_ci * config-table entries      : DMAable memory using dma_alloc_coherent
444962306a36Sopenharmony_ci * HostRRQs                  : DMAable memory, using dma_alloc_coherent
445062306a36Sopenharmony_ci *
445162306a36Sopenharmony_ci * Return Value
445262306a36Sopenharmony_ci *	 0 in case all of the blocks are allocated, -ENOMEM otherwise.
445362306a36Sopenharmony_ci */
445462306a36Sopenharmony_cistatic int pmcraid_init_buffers(struct pmcraid_instance *pinstance)
445562306a36Sopenharmony_ci{
445662306a36Sopenharmony_ci	int i;
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_ci	if (pmcraid_allocate_host_rrqs(pinstance)) {
445962306a36Sopenharmony_ci		pmcraid_err("couldn't allocate memory for %d host rrqs\n",
446062306a36Sopenharmony_ci			     pinstance->num_hrrq);
446162306a36Sopenharmony_ci		return -ENOMEM;
446262306a36Sopenharmony_ci	}
446362306a36Sopenharmony_ci
446462306a36Sopenharmony_ci	if (pmcraid_allocate_config_buffers(pinstance)) {
446562306a36Sopenharmony_ci		pmcraid_err("couldn't allocate memory for config buffers\n");
446662306a36Sopenharmony_ci		pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
446762306a36Sopenharmony_ci		return -ENOMEM;
446862306a36Sopenharmony_ci	}
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_ci	if (pmcraid_allocate_cmd_blocks(pinstance)) {
447162306a36Sopenharmony_ci		pmcraid_err("couldn't allocate memory for cmd blocks\n");
447262306a36Sopenharmony_ci		pmcraid_release_config_buffers(pinstance);
447362306a36Sopenharmony_ci		pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
447462306a36Sopenharmony_ci		return -ENOMEM;
447562306a36Sopenharmony_ci	}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci	if (pmcraid_allocate_control_blocks(pinstance)) {
447862306a36Sopenharmony_ci		pmcraid_err("couldn't allocate memory control blocks\n");
447962306a36Sopenharmony_ci		pmcraid_release_config_buffers(pinstance);
448062306a36Sopenharmony_ci		pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD);
448162306a36Sopenharmony_ci		pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
448262306a36Sopenharmony_ci		return -ENOMEM;
448362306a36Sopenharmony_ci	}
448462306a36Sopenharmony_ci
448562306a36Sopenharmony_ci	/* allocate DMAable memory for page D0 INQUIRY buffer */
448662306a36Sopenharmony_ci	pinstance->inq_data = dma_alloc_coherent(&pinstance->pdev->dev,
448762306a36Sopenharmony_ci					sizeof(struct pmcraid_inquiry_data),
448862306a36Sopenharmony_ci					&pinstance->inq_data_baddr, GFP_KERNEL);
448962306a36Sopenharmony_ci	if (pinstance->inq_data == NULL) {
449062306a36Sopenharmony_ci		pmcraid_err("couldn't allocate DMA memory for INQUIRY\n");
449162306a36Sopenharmony_ci		pmcraid_release_buffers(pinstance);
449262306a36Sopenharmony_ci		return -ENOMEM;
449362306a36Sopenharmony_ci	}
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	/* allocate DMAable memory for set timestamp data buffer */
449662306a36Sopenharmony_ci	pinstance->timestamp_data = dma_alloc_coherent(&pinstance->pdev->dev,
449762306a36Sopenharmony_ci					sizeof(struct pmcraid_timestamp_data),
449862306a36Sopenharmony_ci					&pinstance->timestamp_data_baddr,
449962306a36Sopenharmony_ci					GFP_KERNEL);
450062306a36Sopenharmony_ci	if (pinstance->timestamp_data == NULL) {
450162306a36Sopenharmony_ci		pmcraid_err("couldn't allocate DMA memory for \
450262306a36Sopenharmony_ci				set time_stamp \n");
450362306a36Sopenharmony_ci		pmcraid_release_buffers(pinstance);
450462306a36Sopenharmony_ci		return -ENOMEM;
450562306a36Sopenharmony_ci	}
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci
450862306a36Sopenharmony_ci	/* Initialize all the command blocks and add them to free pool. No
450962306a36Sopenharmony_ci	 * need to lock (free_pool_lock) as this is done in initialization
451062306a36Sopenharmony_ci	 * itself
451162306a36Sopenharmony_ci	 */
451262306a36Sopenharmony_ci	for (i = 0; i < PMCRAID_MAX_CMD; i++) {
451362306a36Sopenharmony_ci		struct pmcraid_cmd *cmdp = pinstance->cmd_list[i];
451462306a36Sopenharmony_ci		pmcraid_init_cmdblk(cmdp, i);
451562306a36Sopenharmony_ci		cmdp->drv_inst = pinstance;
451662306a36Sopenharmony_ci		list_add_tail(&cmdp->free_list, &pinstance->free_cmd_pool);
451762306a36Sopenharmony_ci	}
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci	return 0;
452062306a36Sopenharmony_ci}
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci/**
452362306a36Sopenharmony_ci * pmcraid_reinit_buffers - resets various buffer pointers
452462306a36Sopenharmony_ci * @pinstance: pointer to adapter instance
452562306a36Sopenharmony_ci * Return value
452662306a36Sopenharmony_ci *	none
452762306a36Sopenharmony_ci */
452862306a36Sopenharmony_cistatic void pmcraid_reinit_buffers(struct pmcraid_instance *pinstance)
452962306a36Sopenharmony_ci{
453062306a36Sopenharmony_ci	int i;
453162306a36Sopenharmony_ci	int buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD;
453262306a36Sopenharmony_ci
453362306a36Sopenharmony_ci	for (i = 0; i < pinstance->num_hrrq; i++) {
453462306a36Sopenharmony_ci		memset(pinstance->hrrq_start[i], 0, buffer_size);
453562306a36Sopenharmony_ci		pinstance->hrrq_curr[i] = pinstance->hrrq_start[i];
453662306a36Sopenharmony_ci		pinstance->hrrq_end[i] =
453762306a36Sopenharmony_ci			pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1;
453862306a36Sopenharmony_ci		pinstance->host_toggle_bit[i] = 1;
453962306a36Sopenharmony_ci	}
454062306a36Sopenharmony_ci}
454162306a36Sopenharmony_ci
454262306a36Sopenharmony_ci/**
454362306a36Sopenharmony_ci * pmcraid_init_instance - initialize per instance data structure
454462306a36Sopenharmony_ci * @pdev: pointer to pci device structure
454562306a36Sopenharmony_ci * @host: pointer to Scsi_Host structure
454662306a36Sopenharmony_ci * @mapped_pci_addr: memory mapped IOA configuration registers
454762306a36Sopenharmony_ci *
454862306a36Sopenharmony_ci * Return Value
454962306a36Sopenharmony_ci *	 0 on success, non-zero in case of any failure
455062306a36Sopenharmony_ci */
455162306a36Sopenharmony_cistatic int pmcraid_init_instance(struct pci_dev *pdev, struct Scsi_Host *host,
455262306a36Sopenharmony_ci				 void __iomem *mapped_pci_addr)
455362306a36Sopenharmony_ci{
455462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance =
455562306a36Sopenharmony_ci		(struct pmcraid_instance *)host->hostdata;
455662306a36Sopenharmony_ci
455762306a36Sopenharmony_ci	pinstance->host = host;
455862306a36Sopenharmony_ci	pinstance->pdev = pdev;
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_ci	/* Initialize register addresses */
456162306a36Sopenharmony_ci	pinstance->mapped_dma_addr = mapped_pci_addr;
456262306a36Sopenharmony_ci
456362306a36Sopenharmony_ci	/* Initialize chip-specific details */
456462306a36Sopenharmony_ci	{
456562306a36Sopenharmony_ci		struct pmcraid_chip_details *chip_cfg = pinstance->chip_cfg;
456662306a36Sopenharmony_ci		struct pmcraid_interrupts *pint_regs = &pinstance->int_regs;
456762306a36Sopenharmony_ci
456862306a36Sopenharmony_ci		pinstance->ioarrin = mapped_pci_addr + chip_cfg->ioarrin;
456962306a36Sopenharmony_ci
457062306a36Sopenharmony_ci		pint_regs->ioa_host_interrupt_reg =
457162306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->ioa_host_intr;
457262306a36Sopenharmony_ci		pint_regs->ioa_host_interrupt_clr_reg =
457362306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->ioa_host_intr_clr;
457462306a36Sopenharmony_ci		pint_regs->ioa_host_msix_interrupt_reg =
457562306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->ioa_host_msix_intr;
457662306a36Sopenharmony_ci		pint_regs->host_ioa_interrupt_reg =
457762306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->host_ioa_intr;
457862306a36Sopenharmony_ci		pint_regs->host_ioa_interrupt_clr_reg =
457962306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->host_ioa_intr_clr;
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci		/* Current version of firmware exposes interrupt mask set
458262306a36Sopenharmony_ci		 * and mask clr registers through memory mapped bar0.
458362306a36Sopenharmony_ci		 */
458462306a36Sopenharmony_ci		pinstance->mailbox = mapped_pci_addr + chip_cfg->mailbox;
458562306a36Sopenharmony_ci		pinstance->ioa_status = mapped_pci_addr + chip_cfg->ioastatus;
458662306a36Sopenharmony_ci		pint_regs->ioa_host_interrupt_mask_reg =
458762306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->ioa_host_mask;
458862306a36Sopenharmony_ci		pint_regs->ioa_host_interrupt_mask_clr_reg =
458962306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->ioa_host_mask_clr;
459062306a36Sopenharmony_ci		pint_regs->global_interrupt_mask_reg =
459162306a36Sopenharmony_ci			mapped_pci_addr + chip_cfg->global_intr_mask;
459262306a36Sopenharmony_ci	}
459362306a36Sopenharmony_ci
459462306a36Sopenharmony_ci	pinstance->ioa_reset_attempts = 0;
459562306a36Sopenharmony_ci	init_waitqueue_head(&pinstance->reset_wait_q);
459662306a36Sopenharmony_ci
459762306a36Sopenharmony_ci	atomic_set(&pinstance->outstanding_cmds, 0);
459862306a36Sopenharmony_ci	atomic_set(&pinstance->last_message_id, 0);
459962306a36Sopenharmony_ci	atomic_set(&pinstance->expose_resources, 0);
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci	INIT_LIST_HEAD(&pinstance->free_res_q);
460262306a36Sopenharmony_ci	INIT_LIST_HEAD(&pinstance->used_res_q);
460362306a36Sopenharmony_ci	INIT_LIST_HEAD(&pinstance->free_cmd_pool);
460462306a36Sopenharmony_ci	INIT_LIST_HEAD(&pinstance->pending_cmd_pool);
460562306a36Sopenharmony_ci
460662306a36Sopenharmony_ci	spin_lock_init(&pinstance->free_pool_lock);
460762306a36Sopenharmony_ci	spin_lock_init(&pinstance->pending_pool_lock);
460862306a36Sopenharmony_ci	spin_lock_init(&pinstance->resource_lock);
460962306a36Sopenharmony_ci	mutex_init(&pinstance->aen_queue_lock);
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_ci	/* Work-queue (Shared) for deferred processing error handling */
461262306a36Sopenharmony_ci	INIT_WORK(&pinstance->worker_q, pmcraid_worker_function);
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	/* Initialize the default log_level */
461562306a36Sopenharmony_ci	pinstance->current_log_level = pmcraid_log_level;
461662306a36Sopenharmony_ci
461762306a36Sopenharmony_ci	/* Setup variables required for reset engine */
461862306a36Sopenharmony_ci	pinstance->ioa_state = IOA_STATE_UNKNOWN;
461962306a36Sopenharmony_ci	pinstance->reset_cmd = NULL;
462062306a36Sopenharmony_ci	return 0;
462162306a36Sopenharmony_ci}
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci/**
462462306a36Sopenharmony_ci * pmcraid_shutdown - shutdown adapter controller.
462562306a36Sopenharmony_ci * @pdev: pci device struct
462662306a36Sopenharmony_ci *
462762306a36Sopenharmony_ci * Issues an adapter shutdown to the card waits for its completion
462862306a36Sopenharmony_ci *
462962306a36Sopenharmony_ci * Return value
463062306a36Sopenharmony_ci *	  none
463162306a36Sopenharmony_ci */
463262306a36Sopenharmony_cistatic void pmcraid_shutdown(struct pci_dev *pdev)
463362306a36Sopenharmony_ci{
463462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
463562306a36Sopenharmony_ci	pmcraid_reset_bringdown(pinstance);
463662306a36Sopenharmony_ci}
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci
463962306a36Sopenharmony_ci/*
464062306a36Sopenharmony_ci * pmcraid_get_minor - returns unused minor number from minor number bitmap
464162306a36Sopenharmony_ci */
464262306a36Sopenharmony_cistatic unsigned short pmcraid_get_minor(void)
464362306a36Sopenharmony_ci{
464462306a36Sopenharmony_ci	int minor;
464562306a36Sopenharmony_ci
464662306a36Sopenharmony_ci	minor = find_first_zero_bit(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
464762306a36Sopenharmony_ci	__set_bit(minor, pmcraid_minor);
464862306a36Sopenharmony_ci	return minor;
464962306a36Sopenharmony_ci}
465062306a36Sopenharmony_ci
465162306a36Sopenharmony_ci/*
465262306a36Sopenharmony_ci * pmcraid_release_minor - releases given minor back to minor number bitmap
465362306a36Sopenharmony_ci */
465462306a36Sopenharmony_cistatic void pmcraid_release_minor(unsigned short minor)
465562306a36Sopenharmony_ci{
465662306a36Sopenharmony_ci	__clear_bit(minor, pmcraid_minor);
465762306a36Sopenharmony_ci}
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci/**
466062306a36Sopenharmony_ci * pmcraid_setup_chrdev - allocates a minor number and registers a char device
466162306a36Sopenharmony_ci *
466262306a36Sopenharmony_ci * @pinstance: pointer to adapter instance for which to register device
466362306a36Sopenharmony_ci *
466462306a36Sopenharmony_ci * Return value
466562306a36Sopenharmony_ci *	0 in case of success, otherwise non-zero
466662306a36Sopenharmony_ci */
466762306a36Sopenharmony_cistatic int pmcraid_setup_chrdev(struct pmcraid_instance *pinstance)
466862306a36Sopenharmony_ci{
466962306a36Sopenharmony_ci	int minor;
467062306a36Sopenharmony_ci	int error;
467162306a36Sopenharmony_ci
467262306a36Sopenharmony_ci	minor = pmcraid_get_minor();
467362306a36Sopenharmony_ci	cdev_init(&pinstance->cdev, &pmcraid_fops);
467462306a36Sopenharmony_ci	pinstance->cdev.owner = THIS_MODULE;
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci	error = cdev_add(&pinstance->cdev, MKDEV(pmcraid_major, minor), 1);
467762306a36Sopenharmony_ci
467862306a36Sopenharmony_ci	if (error)
467962306a36Sopenharmony_ci		pmcraid_release_minor(minor);
468062306a36Sopenharmony_ci	else
468162306a36Sopenharmony_ci		device_create(pmcraid_class, NULL, MKDEV(pmcraid_major, minor),
468262306a36Sopenharmony_ci			      NULL, "%s%u", PMCRAID_DEVFILE, minor);
468362306a36Sopenharmony_ci	return error;
468462306a36Sopenharmony_ci}
468562306a36Sopenharmony_ci
468662306a36Sopenharmony_ci/**
468762306a36Sopenharmony_ci * pmcraid_release_chrdev - unregisters per-adapter management interface
468862306a36Sopenharmony_ci *
468962306a36Sopenharmony_ci * @pinstance: pointer to adapter instance structure
469062306a36Sopenharmony_ci *
469162306a36Sopenharmony_ci * Return value
469262306a36Sopenharmony_ci *  none
469362306a36Sopenharmony_ci */
469462306a36Sopenharmony_cistatic void pmcraid_release_chrdev(struct pmcraid_instance *pinstance)
469562306a36Sopenharmony_ci{
469662306a36Sopenharmony_ci	pmcraid_release_minor(MINOR(pinstance->cdev.dev));
469762306a36Sopenharmony_ci	device_destroy(pmcraid_class,
469862306a36Sopenharmony_ci		       MKDEV(pmcraid_major, MINOR(pinstance->cdev.dev)));
469962306a36Sopenharmony_ci	cdev_del(&pinstance->cdev);
470062306a36Sopenharmony_ci}
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_ci/**
470362306a36Sopenharmony_ci * pmcraid_remove - IOA hot plug remove entry point
470462306a36Sopenharmony_ci * @pdev: pci device struct
470562306a36Sopenharmony_ci *
470662306a36Sopenharmony_ci * Return value
470762306a36Sopenharmony_ci *	  none
470862306a36Sopenharmony_ci */
470962306a36Sopenharmony_cistatic void pmcraid_remove(struct pci_dev *pdev)
471062306a36Sopenharmony_ci{
471162306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
471262306a36Sopenharmony_ci
471362306a36Sopenharmony_ci	/* remove the management interface (/dev file) for this device */
471462306a36Sopenharmony_ci	pmcraid_release_chrdev(pinstance);
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_ci	/* remove host template from scsi midlayer */
471762306a36Sopenharmony_ci	scsi_remove_host(pinstance->host);
471862306a36Sopenharmony_ci
471962306a36Sopenharmony_ci	/* block requests from mid-layer */
472062306a36Sopenharmony_ci	scsi_block_requests(pinstance->host);
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_ci	/* initiate shutdown adapter */
472362306a36Sopenharmony_ci	pmcraid_shutdown(pdev);
472462306a36Sopenharmony_ci
472562306a36Sopenharmony_ci	pmcraid_disable_interrupts(pinstance, ~0);
472662306a36Sopenharmony_ci	flush_work(&pinstance->worker_q);
472762306a36Sopenharmony_ci
472862306a36Sopenharmony_ci	pmcraid_kill_tasklets(pinstance);
472962306a36Sopenharmony_ci	pmcraid_unregister_interrupt_handler(pinstance);
473062306a36Sopenharmony_ci	pmcraid_release_buffers(pinstance);
473162306a36Sopenharmony_ci	iounmap(pinstance->mapped_dma_addr);
473262306a36Sopenharmony_ci	pci_release_regions(pdev);
473362306a36Sopenharmony_ci	scsi_host_put(pinstance->host);
473462306a36Sopenharmony_ci	pci_disable_device(pdev);
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_ci	return;
473762306a36Sopenharmony_ci}
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_ci/**
474062306a36Sopenharmony_ci * pmcraid_suspend - driver suspend entry point for power management
474162306a36Sopenharmony_ci * @dev:   Device structure
474262306a36Sopenharmony_ci *
474362306a36Sopenharmony_ci * Return Value - 0 always
474462306a36Sopenharmony_ci */
474562306a36Sopenharmony_cistatic int __maybe_unused pmcraid_suspend(struct device *dev)
474662306a36Sopenharmony_ci{
474762306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
474862306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
474962306a36Sopenharmony_ci
475062306a36Sopenharmony_ci	pmcraid_shutdown(pdev);
475162306a36Sopenharmony_ci	pmcraid_disable_interrupts(pinstance, ~0);
475262306a36Sopenharmony_ci	pmcraid_kill_tasklets(pinstance);
475362306a36Sopenharmony_ci	pmcraid_unregister_interrupt_handler(pinstance);
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ci	return 0;
475662306a36Sopenharmony_ci}
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci/**
475962306a36Sopenharmony_ci * pmcraid_resume - driver resume entry point PCI power management
476062306a36Sopenharmony_ci * @dev: Device structure
476162306a36Sopenharmony_ci *
476262306a36Sopenharmony_ci * Return Value - 0 in case of success. Error code in case of any failure
476362306a36Sopenharmony_ci */
476462306a36Sopenharmony_cistatic int __maybe_unused pmcraid_resume(struct device *dev)
476562306a36Sopenharmony_ci{
476662306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
476762306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
476862306a36Sopenharmony_ci	struct Scsi_Host *host = pinstance->host;
476962306a36Sopenharmony_ci	int rc = 0;
477062306a36Sopenharmony_ci
477162306a36Sopenharmony_ci	if (sizeof(dma_addr_t) == 4 ||
477262306a36Sopenharmony_ci	    dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
477362306a36Sopenharmony_ci		rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	if (rc == 0)
477662306a36Sopenharmony_ci		rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	if (rc != 0) {
477962306a36Sopenharmony_ci		dev_err(&pdev->dev, "resume: Failed to set PCI DMA mask\n");
478062306a36Sopenharmony_ci		goto disable_device;
478162306a36Sopenharmony_ci	}
478262306a36Sopenharmony_ci
478362306a36Sopenharmony_ci	pmcraid_disable_interrupts(pinstance, ~0);
478462306a36Sopenharmony_ci	atomic_set(&pinstance->outstanding_cmds, 0);
478562306a36Sopenharmony_ci	rc = pmcraid_register_interrupt_handler(pinstance);
478662306a36Sopenharmony_ci
478762306a36Sopenharmony_ci	if (rc) {
478862306a36Sopenharmony_ci		dev_err(&pdev->dev,
478962306a36Sopenharmony_ci			"resume: couldn't register interrupt handlers\n");
479062306a36Sopenharmony_ci		rc = -ENODEV;
479162306a36Sopenharmony_ci		goto release_host;
479262306a36Sopenharmony_ci	}
479362306a36Sopenharmony_ci
479462306a36Sopenharmony_ci	pmcraid_init_tasklets(pinstance);
479562306a36Sopenharmony_ci	pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_ci	/* Start with hard reset sequence which brings up IOA to operational
479862306a36Sopenharmony_ci	 * state as well as completes the reset sequence.
479962306a36Sopenharmony_ci	 */
480062306a36Sopenharmony_ci	pinstance->ioa_hard_reset = 1;
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci	/* Start IOA firmware initialization and bring card to Operational
480362306a36Sopenharmony_ci	 * state.
480462306a36Sopenharmony_ci	 */
480562306a36Sopenharmony_ci	if (pmcraid_reset_bringup(pinstance)) {
480662306a36Sopenharmony_ci		dev_err(&pdev->dev, "couldn't initialize IOA\n");
480762306a36Sopenharmony_ci		rc = -ENODEV;
480862306a36Sopenharmony_ci		goto release_tasklets;
480962306a36Sopenharmony_ci	}
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	return 0;
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_cirelease_tasklets:
481462306a36Sopenharmony_ci	pmcraid_disable_interrupts(pinstance, ~0);
481562306a36Sopenharmony_ci	pmcraid_kill_tasklets(pinstance);
481662306a36Sopenharmony_ci	pmcraid_unregister_interrupt_handler(pinstance);
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_cirelease_host:
481962306a36Sopenharmony_ci	scsi_host_put(host);
482062306a36Sopenharmony_ci
482162306a36Sopenharmony_cidisable_device:
482262306a36Sopenharmony_ci
482362306a36Sopenharmony_ci	return rc;
482462306a36Sopenharmony_ci}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci/**
482762306a36Sopenharmony_ci * pmcraid_complete_ioa_reset - Called by either timer or tasklet during
482862306a36Sopenharmony_ci *				completion of the ioa reset
482962306a36Sopenharmony_ci * @cmd: pointer to reset command block
483062306a36Sopenharmony_ci */
483162306a36Sopenharmony_cistatic void pmcraid_complete_ioa_reset(struct pmcraid_cmd *cmd)
483262306a36Sopenharmony_ci{
483362306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
483462306a36Sopenharmony_ci	unsigned long flags;
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci	spin_lock_irqsave(pinstance->host->host_lock, flags);
483762306a36Sopenharmony_ci	pmcraid_ioa_reset(cmd);
483862306a36Sopenharmony_ci	spin_unlock_irqrestore(pinstance->host->host_lock, flags);
483962306a36Sopenharmony_ci	scsi_unblock_requests(pinstance->host);
484062306a36Sopenharmony_ci	schedule_work(&pinstance->worker_q);
484162306a36Sopenharmony_ci}
484262306a36Sopenharmony_ci
484362306a36Sopenharmony_ci/**
484462306a36Sopenharmony_ci * pmcraid_set_supported_devs - sends SET SUPPORTED DEVICES to IOAFP
484562306a36Sopenharmony_ci *
484662306a36Sopenharmony_ci * @cmd: pointer to pmcraid_cmd structure
484762306a36Sopenharmony_ci *
484862306a36Sopenharmony_ci * Return Value
484962306a36Sopenharmony_ci *  0 for success or non-zero for failure cases
485062306a36Sopenharmony_ci */
485162306a36Sopenharmony_cistatic void pmcraid_set_supported_devs(struct pmcraid_cmd *cmd)
485262306a36Sopenharmony_ci{
485362306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
485462306a36Sopenharmony_ci	void (*cmd_done) (struct pmcraid_cmd *) = pmcraid_complete_ioa_reset;
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ci	pmcraid_reinit_cmdblk(cmd);
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_ci	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
485962306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_IOACMD;
486062306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_SET_SUPPORTED_DEVICES;
486162306a36Sopenharmony_ci	ioarcb->cdb[1] = ALL_DEVICES_SUPPORTED;
486262306a36Sopenharmony_ci
486362306a36Sopenharmony_ci	/* If this was called as part of resource table reinitialization due to
486462306a36Sopenharmony_ci	 * lost CCN, it is enough to return the command block back to free pool
486562306a36Sopenharmony_ci	 * as part of set_supported_devs completion function.
486662306a36Sopenharmony_ci	 */
486762306a36Sopenharmony_ci	if (cmd->drv_inst->reinit_cfg_table) {
486862306a36Sopenharmony_ci		cmd->drv_inst->reinit_cfg_table = 0;
486962306a36Sopenharmony_ci		cmd->release = 1;
487062306a36Sopenharmony_ci		cmd_done = pmcraid_reinit_cfgtable_done;
487162306a36Sopenharmony_ci	}
487262306a36Sopenharmony_ci
487362306a36Sopenharmony_ci	/* we will be done with the reset sequence after set supported devices,
487462306a36Sopenharmony_ci	 * setup the done function to return the command block back to free
487562306a36Sopenharmony_ci	 * pool
487662306a36Sopenharmony_ci	 */
487762306a36Sopenharmony_ci	pmcraid_send_cmd(cmd,
487862306a36Sopenharmony_ci			 cmd_done,
487962306a36Sopenharmony_ci			 PMCRAID_SET_SUP_DEV_TIMEOUT,
488062306a36Sopenharmony_ci			 pmcraid_timeout_handler);
488162306a36Sopenharmony_ci	return;
488262306a36Sopenharmony_ci}
488362306a36Sopenharmony_ci
488462306a36Sopenharmony_ci/**
488562306a36Sopenharmony_ci * pmcraid_set_timestamp - set the timestamp to IOAFP
488662306a36Sopenharmony_ci *
488762306a36Sopenharmony_ci * @cmd: pointer to pmcraid_cmd structure
488862306a36Sopenharmony_ci *
488962306a36Sopenharmony_ci * Return Value
489062306a36Sopenharmony_ci *  0 for success or non-zero for failure cases
489162306a36Sopenharmony_ci */
489262306a36Sopenharmony_cistatic void pmcraid_set_timestamp(struct pmcraid_cmd *cmd)
489362306a36Sopenharmony_ci{
489462306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
489562306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
489662306a36Sopenharmony_ci	__be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN);
489762306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl;
489862306a36Sopenharmony_ci	u64 timestamp;
489962306a36Sopenharmony_ci
490062306a36Sopenharmony_ci	timestamp = ktime_get_real_seconds() * 1000;
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci	pinstance->timestamp_data->timestamp[0] = (__u8)(timestamp);
490362306a36Sopenharmony_ci	pinstance->timestamp_data->timestamp[1] = (__u8)((timestamp) >> 8);
490462306a36Sopenharmony_ci	pinstance->timestamp_data->timestamp[2] = (__u8)((timestamp) >> 16);
490562306a36Sopenharmony_ci	pinstance->timestamp_data->timestamp[3] = (__u8)((timestamp) >> 24);
490662306a36Sopenharmony_ci	pinstance->timestamp_data->timestamp[4] = (__u8)((timestamp) >> 32);
490762306a36Sopenharmony_ci	pinstance->timestamp_data->timestamp[5] = (__u8)((timestamp)  >> 40);
490862306a36Sopenharmony_ci
490962306a36Sopenharmony_ci	pmcraid_reinit_cmdblk(cmd);
491062306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_SCSI;
491162306a36Sopenharmony_ci	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
491262306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_SCSI_SET_TIMESTAMP;
491362306a36Sopenharmony_ci	ioarcb->cdb[1] = PMCRAID_SCSI_SERVICE_ACTION;
491462306a36Sopenharmony_ci	memcpy(&(ioarcb->cdb[6]), &time_stamp_len, sizeof(time_stamp_len));
491562306a36Sopenharmony_ci
491662306a36Sopenharmony_ci	ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
491762306a36Sopenharmony_ci					offsetof(struct pmcraid_ioarcb,
491862306a36Sopenharmony_ci						add_data.u.ioadl[0]));
491962306a36Sopenharmony_ci	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
492062306a36Sopenharmony_ci	ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_ci	ioarcb->request_flags0 |= NO_LINK_DESCS;
492362306a36Sopenharmony_ci	ioarcb->request_flags0 |= TRANSFER_DIR_WRITE;
492462306a36Sopenharmony_ci	ioarcb->data_transfer_length =
492562306a36Sopenharmony_ci		cpu_to_le32(sizeof(struct pmcraid_timestamp_data));
492662306a36Sopenharmony_ci	ioadl = &(ioarcb->add_data.u.ioadl[0]);
492762306a36Sopenharmony_ci	ioadl->flags = IOADL_FLAGS_LAST_DESC;
492862306a36Sopenharmony_ci	ioadl->address = cpu_to_le64(pinstance->timestamp_data_baddr);
492962306a36Sopenharmony_ci	ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_timestamp_data));
493062306a36Sopenharmony_ci
493162306a36Sopenharmony_ci	if (!pinstance->timestamp_error) {
493262306a36Sopenharmony_ci		pinstance->timestamp_error = 0;
493362306a36Sopenharmony_ci		pmcraid_send_cmd(cmd, pmcraid_set_supported_devs,
493462306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
493562306a36Sopenharmony_ci	} else {
493662306a36Sopenharmony_ci		pmcraid_send_cmd(cmd, pmcraid_return_cmd,
493762306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
493862306a36Sopenharmony_ci		return;
493962306a36Sopenharmony_ci	}
494062306a36Sopenharmony_ci}
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_ci
494362306a36Sopenharmony_ci/**
494462306a36Sopenharmony_ci * pmcraid_init_res_table - Initialize the resource table
494562306a36Sopenharmony_ci * @cmd:  pointer to pmcraid command struct
494662306a36Sopenharmony_ci *
494762306a36Sopenharmony_ci * This function looks through the existing resource table, comparing
494862306a36Sopenharmony_ci * it with the config table. This function will take care of old/new
494962306a36Sopenharmony_ci * devices and schedule adding/removing them from the mid-layer
495062306a36Sopenharmony_ci * as appropriate.
495162306a36Sopenharmony_ci *
495262306a36Sopenharmony_ci * Return value
495362306a36Sopenharmony_ci *	 None
495462306a36Sopenharmony_ci */
495562306a36Sopenharmony_cistatic void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
495662306a36Sopenharmony_ci{
495762306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
495862306a36Sopenharmony_ci	struct pmcraid_resource_entry *res, *temp;
495962306a36Sopenharmony_ci	struct pmcraid_config_table_entry *cfgte;
496062306a36Sopenharmony_ci	unsigned long lock_flags;
496162306a36Sopenharmony_ci	int found, rc, i;
496262306a36Sopenharmony_ci	u16 fw_version;
496362306a36Sopenharmony_ci	LIST_HEAD(old_res);
496462306a36Sopenharmony_ci
496562306a36Sopenharmony_ci	if (pinstance->cfg_table->flags & MICROCODE_UPDATE_REQUIRED)
496662306a36Sopenharmony_ci		pmcraid_err("IOA requires microcode download\n");
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci	fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci	/* resource list is protected by pinstance->resource_lock.
497162306a36Sopenharmony_ci	 * init_res_table can be called from probe (user-thread) or runtime
497262306a36Sopenharmony_ci	 * reset (timer/tasklet)
497362306a36Sopenharmony_ci	 */
497462306a36Sopenharmony_ci	spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
497562306a36Sopenharmony_ci
497662306a36Sopenharmony_ci	list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue)
497762306a36Sopenharmony_ci		list_move_tail(&res->queue, &old_res);
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	for (i = 0; i < le16_to_cpu(pinstance->cfg_table->num_entries); i++) {
498062306a36Sopenharmony_ci		if (be16_to_cpu(pinstance->inq_data->fw_version) <=
498162306a36Sopenharmony_ci						PMCRAID_FW_VERSION_1)
498262306a36Sopenharmony_ci			cfgte = &pinstance->cfg_table->entries[i];
498362306a36Sopenharmony_ci		else
498462306a36Sopenharmony_ci			cfgte = (struct pmcraid_config_table_entry *)
498562306a36Sopenharmony_ci					&pinstance->cfg_table->entries_ext[i];
498662306a36Sopenharmony_ci
498762306a36Sopenharmony_ci		if (!pmcraid_expose_resource(fw_version, cfgte))
498862306a36Sopenharmony_ci			continue;
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci		found = 0;
499162306a36Sopenharmony_ci
499262306a36Sopenharmony_ci		/* If this entry was already detected and initialized */
499362306a36Sopenharmony_ci		list_for_each_entry_safe(res, temp, &old_res, queue) {
499462306a36Sopenharmony_ci
499562306a36Sopenharmony_ci			rc = memcmp(&res->cfg_entry.resource_address,
499662306a36Sopenharmony_ci				    &cfgte->resource_address,
499762306a36Sopenharmony_ci				    sizeof(cfgte->resource_address));
499862306a36Sopenharmony_ci			if (!rc) {
499962306a36Sopenharmony_ci				list_move_tail(&res->queue,
500062306a36Sopenharmony_ci						&pinstance->used_res_q);
500162306a36Sopenharmony_ci				found = 1;
500262306a36Sopenharmony_ci				break;
500362306a36Sopenharmony_ci			}
500462306a36Sopenharmony_ci		}
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_ci		/* If this is new entry, initialize it and add it the queue */
500762306a36Sopenharmony_ci		if (!found) {
500862306a36Sopenharmony_ci
500962306a36Sopenharmony_ci			if (list_empty(&pinstance->free_res_q)) {
501062306a36Sopenharmony_ci				pmcraid_err("Too many devices attached\n");
501162306a36Sopenharmony_ci				break;
501262306a36Sopenharmony_ci			}
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci			found = 1;
501562306a36Sopenharmony_ci			res = list_entry(pinstance->free_res_q.next,
501662306a36Sopenharmony_ci					 struct pmcraid_resource_entry, queue);
501762306a36Sopenharmony_ci
501862306a36Sopenharmony_ci			res->scsi_dev = NULL;
501962306a36Sopenharmony_ci			res->change_detected = RES_CHANGE_ADD;
502062306a36Sopenharmony_ci			res->reset_progress = 0;
502162306a36Sopenharmony_ci			list_move_tail(&res->queue, &pinstance->used_res_q);
502262306a36Sopenharmony_ci		}
502362306a36Sopenharmony_ci
502462306a36Sopenharmony_ci		/* copy new configuration table entry details into driver
502562306a36Sopenharmony_ci		 * maintained resource entry
502662306a36Sopenharmony_ci		 */
502762306a36Sopenharmony_ci		if (found) {
502862306a36Sopenharmony_ci			memcpy(&res->cfg_entry, cfgte,
502962306a36Sopenharmony_ci					pinstance->config_table_entry_size);
503062306a36Sopenharmony_ci			pmcraid_info("New res type:%x, vset:%x, addr:%x:\n",
503162306a36Sopenharmony_ci				 res->cfg_entry.resource_type,
503262306a36Sopenharmony_ci				 (fw_version <= PMCRAID_FW_VERSION_1 ?
503362306a36Sopenharmony_ci					res->cfg_entry.unique_flags1 :
503462306a36Sopenharmony_ci					le16_to_cpu(res->cfg_entry.array_id) & 0xFF),
503562306a36Sopenharmony_ci				 le32_to_cpu(res->cfg_entry.resource_address));
503662306a36Sopenharmony_ci		}
503762306a36Sopenharmony_ci	}
503862306a36Sopenharmony_ci
503962306a36Sopenharmony_ci	/* Detect any deleted entries, mark them for deletion from mid-layer */
504062306a36Sopenharmony_ci	list_for_each_entry_safe(res, temp, &old_res, queue) {
504162306a36Sopenharmony_ci
504262306a36Sopenharmony_ci		if (res->scsi_dev) {
504362306a36Sopenharmony_ci			res->change_detected = RES_CHANGE_DEL;
504462306a36Sopenharmony_ci			res->cfg_entry.resource_handle =
504562306a36Sopenharmony_ci				PMCRAID_INVALID_RES_HANDLE;
504662306a36Sopenharmony_ci			list_move_tail(&res->queue, &pinstance->used_res_q);
504762306a36Sopenharmony_ci		} else {
504862306a36Sopenharmony_ci			list_move_tail(&res->queue, &pinstance->free_res_q);
504962306a36Sopenharmony_ci		}
505062306a36Sopenharmony_ci	}
505162306a36Sopenharmony_ci
505262306a36Sopenharmony_ci	/* release the resource list lock */
505362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
505462306a36Sopenharmony_ci	pmcraid_set_timestamp(cmd);
505562306a36Sopenharmony_ci}
505662306a36Sopenharmony_ci
505762306a36Sopenharmony_ci/**
505862306a36Sopenharmony_ci * pmcraid_querycfg - Send a Query IOA Config to the adapter.
505962306a36Sopenharmony_ci * @cmd: pointer pmcraid_cmd struct
506062306a36Sopenharmony_ci *
506162306a36Sopenharmony_ci * This function sends a Query IOA Configuration command to the adapter to
506262306a36Sopenharmony_ci * retrieve the IOA configuration table.
506362306a36Sopenharmony_ci *
506462306a36Sopenharmony_ci * Return value:
506562306a36Sopenharmony_ci *	none
506662306a36Sopenharmony_ci */
506762306a36Sopenharmony_cistatic void pmcraid_querycfg(struct pmcraid_cmd *cmd)
506862306a36Sopenharmony_ci{
506962306a36Sopenharmony_ci	struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
507062306a36Sopenharmony_ci	struct pmcraid_ioadl_desc *ioadl;
507162306a36Sopenharmony_ci	struct pmcraid_instance *pinstance = cmd->drv_inst;
507262306a36Sopenharmony_ci	__be32 cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table));
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ci	if (be16_to_cpu(pinstance->inq_data->fw_version) <=
507562306a36Sopenharmony_ci					PMCRAID_FW_VERSION_1)
507662306a36Sopenharmony_ci		pinstance->config_table_entry_size =
507762306a36Sopenharmony_ci			sizeof(struct pmcraid_config_table_entry);
507862306a36Sopenharmony_ci	else
507962306a36Sopenharmony_ci		pinstance->config_table_entry_size =
508062306a36Sopenharmony_ci			sizeof(struct pmcraid_config_table_entry_ext);
508162306a36Sopenharmony_ci
508262306a36Sopenharmony_ci	ioarcb->request_type = REQ_TYPE_IOACMD;
508362306a36Sopenharmony_ci	ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
508462306a36Sopenharmony_ci
508562306a36Sopenharmony_ci	ioarcb->cdb[0] = PMCRAID_QUERY_IOA_CONFIG;
508662306a36Sopenharmony_ci
508762306a36Sopenharmony_ci	/* firmware requires 4-byte length field, specified in B.E format */
508862306a36Sopenharmony_ci	memcpy(&(ioarcb->cdb[10]), &cfg_table_size, sizeof(cfg_table_size));
508962306a36Sopenharmony_ci
509062306a36Sopenharmony_ci	/* Since entire config table can be described by single IOADL, it can
509162306a36Sopenharmony_ci	 * be part of IOARCB itself
509262306a36Sopenharmony_ci	 */
509362306a36Sopenharmony_ci	ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
509462306a36Sopenharmony_ci					offsetof(struct pmcraid_ioarcb,
509562306a36Sopenharmony_ci						add_data.u.ioadl[0]));
509662306a36Sopenharmony_ci	ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
509762306a36Sopenharmony_ci	ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL);
509862306a36Sopenharmony_ci
509962306a36Sopenharmony_ci	ioarcb->request_flags0 |= NO_LINK_DESCS;
510062306a36Sopenharmony_ci	ioarcb->data_transfer_length =
510162306a36Sopenharmony_ci		cpu_to_le32(sizeof(struct pmcraid_config_table));
510262306a36Sopenharmony_ci
510362306a36Sopenharmony_ci	ioadl = &(ioarcb->add_data.u.ioadl[0]);
510462306a36Sopenharmony_ci	ioadl->flags = IOADL_FLAGS_LAST_DESC;
510562306a36Sopenharmony_ci	ioadl->address = cpu_to_le64(pinstance->cfg_table_bus_addr);
510662306a36Sopenharmony_ci	ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_config_table));
510762306a36Sopenharmony_ci
510862306a36Sopenharmony_ci	pmcraid_send_cmd(cmd, pmcraid_init_res_table,
510962306a36Sopenharmony_ci			 PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
511062306a36Sopenharmony_ci}
511162306a36Sopenharmony_ci
511262306a36Sopenharmony_ci
511362306a36Sopenharmony_ci/**
511462306a36Sopenharmony_ci * pmcraid_probe - PCI probe entry pointer for PMC MaxRAID controller driver
511562306a36Sopenharmony_ci * @pdev: pointer to pci device structure
511662306a36Sopenharmony_ci * @dev_id: pointer to device ids structure
511762306a36Sopenharmony_ci *
511862306a36Sopenharmony_ci * Return Value
511962306a36Sopenharmony_ci *	returns 0 if the device is claimed and successfully configured.
512062306a36Sopenharmony_ci *	returns non-zero error code in case of any failure
512162306a36Sopenharmony_ci */
512262306a36Sopenharmony_cistatic int pmcraid_probe(struct pci_dev *pdev,
512362306a36Sopenharmony_ci			 const struct pci_device_id *dev_id)
512462306a36Sopenharmony_ci{
512562306a36Sopenharmony_ci	struct pmcraid_instance *pinstance;
512662306a36Sopenharmony_ci	struct Scsi_Host *host;
512762306a36Sopenharmony_ci	void __iomem *mapped_pci_addr;
512862306a36Sopenharmony_ci	int rc = PCIBIOS_SUCCESSFUL;
512962306a36Sopenharmony_ci
513062306a36Sopenharmony_ci	if (atomic_read(&pmcraid_adapter_count) >= PMCRAID_MAX_ADAPTERS) {
513162306a36Sopenharmony_ci		pmcraid_err
513262306a36Sopenharmony_ci			("maximum number(%d) of supported adapters reached\n",
513362306a36Sopenharmony_ci			 atomic_read(&pmcraid_adapter_count));
513462306a36Sopenharmony_ci		return -ENOMEM;
513562306a36Sopenharmony_ci	}
513662306a36Sopenharmony_ci
513762306a36Sopenharmony_ci	atomic_inc(&pmcraid_adapter_count);
513862306a36Sopenharmony_ci	rc = pci_enable_device(pdev);
513962306a36Sopenharmony_ci
514062306a36Sopenharmony_ci	if (rc) {
514162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable adapter\n");
514262306a36Sopenharmony_ci		atomic_dec(&pmcraid_adapter_count);
514362306a36Sopenharmony_ci		return rc;
514462306a36Sopenharmony_ci	}
514562306a36Sopenharmony_ci
514662306a36Sopenharmony_ci	dev_info(&pdev->dev,
514762306a36Sopenharmony_ci		"Found new IOA(%x:%x), Total IOA count: %d\n",
514862306a36Sopenharmony_ci		 pdev->vendor, pdev->device,
514962306a36Sopenharmony_ci		 atomic_read(&pmcraid_adapter_count));
515062306a36Sopenharmony_ci
515162306a36Sopenharmony_ci	rc = pci_request_regions(pdev, PMCRAID_DRIVER_NAME);
515262306a36Sopenharmony_ci
515362306a36Sopenharmony_ci	if (rc < 0) {
515462306a36Sopenharmony_ci		dev_err(&pdev->dev,
515562306a36Sopenharmony_ci			"Couldn't register memory range of registers\n");
515662306a36Sopenharmony_ci		goto out_disable_device;
515762306a36Sopenharmony_ci	}
515862306a36Sopenharmony_ci
515962306a36Sopenharmony_ci	mapped_pci_addr = pci_iomap(pdev, 0, 0);
516062306a36Sopenharmony_ci
516162306a36Sopenharmony_ci	if (!mapped_pci_addr) {
516262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't map PCI registers memory\n");
516362306a36Sopenharmony_ci		rc = -ENOMEM;
516462306a36Sopenharmony_ci		goto out_release_regions;
516562306a36Sopenharmony_ci	}
516662306a36Sopenharmony_ci
516762306a36Sopenharmony_ci	pci_set_master(pdev);
516862306a36Sopenharmony_ci
516962306a36Sopenharmony_ci	/* Firmware requires the system bus address of IOARCB to be within
517062306a36Sopenharmony_ci	 * 32-bit addressable range though it has 64-bit IOARRIN register.
517162306a36Sopenharmony_ci	 * However, firmware supports 64-bit streaming DMA buffers, whereas
517262306a36Sopenharmony_ci	 * coherent buffers are to be 32-bit. Since dma_alloc_coherent always
517362306a36Sopenharmony_ci	 * returns memory within 4GB (if not, change this logic), coherent
517462306a36Sopenharmony_ci	 * buffers are within firmware acceptable address ranges.
517562306a36Sopenharmony_ci	 */
517662306a36Sopenharmony_ci	if (sizeof(dma_addr_t) == 4 ||
517762306a36Sopenharmony_ci	    dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
517862306a36Sopenharmony_ci		rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
517962306a36Sopenharmony_ci
518062306a36Sopenharmony_ci	/* firmware expects 32-bit DMA addresses for IOARRIN register; set 32
518162306a36Sopenharmony_ci	 * bit mask for dma_alloc_coherent to return addresses within 4GB
518262306a36Sopenharmony_ci	 */
518362306a36Sopenharmony_ci	if (rc == 0)
518462306a36Sopenharmony_ci		rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
518562306a36Sopenharmony_ci
518662306a36Sopenharmony_ci	if (rc != 0) {
518762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
518862306a36Sopenharmony_ci		goto cleanup_nomem;
518962306a36Sopenharmony_ci	}
519062306a36Sopenharmony_ci
519162306a36Sopenharmony_ci	host = scsi_host_alloc(&pmcraid_host_template,
519262306a36Sopenharmony_ci				sizeof(struct pmcraid_instance));
519362306a36Sopenharmony_ci
519462306a36Sopenharmony_ci	if (!host) {
519562306a36Sopenharmony_ci		dev_err(&pdev->dev, "scsi_host_alloc failed!\n");
519662306a36Sopenharmony_ci		rc = -ENOMEM;
519762306a36Sopenharmony_ci		goto cleanup_nomem;
519862306a36Sopenharmony_ci	}
519962306a36Sopenharmony_ci
520062306a36Sopenharmony_ci	host->max_id = PMCRAID_MAX_NUM_TARGETS_PER_BUS;
520162306a36Sopenharmony_ci	host->max_lun = PMCRAID_MAX_NUM_LUNS_PER_TARGET;
520262306a36Sopenharmony_ci	host->unique_id = host->host_no;
520362306a36Sopenharmony_ci	host->max_channel = PMCRAID_MAX_BUS_TO_SCAN;
520462306a36Sopenharmony_ci	host->max_cmd_len = PMCRAID_MAX_CDB_LEN;
520562306a36Sopenharmony_ci
520662306a36Sopenharmony_ci	/* zero out entire instance structure */
520762306a36Sopenharmony_ci	pinstance = (struct pmcraid_instance *)host->hostdata;
520862306a36Sopenharmony_ci	memset(pinstance, 0, sizeof(*pinstance));
520962306a36Sopenharmony_ci
521062306a36Sopenharmony_ci	pinstance->chip_cfg =
521162306a36Sopenharmony_ci		(struct pmcraid_chip_details *)(dev_id->driver_data);
521262306a36Sopenharmony_ci
521362306a36Sopenharmony_ci	rc = pmcraid_init_instance(pdev, host, mapped_pci_addr);
521462306a36Sopenharmony_ci
521562306a36Sopenharmony_ci	if (rc < 0) {
521662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize adapter instance\n");
521762306a36Sopenharmony_ci		goto out_scsi_host_put;
521862306a36Sopenharmony_ci	}
521962306a36Sopenharmony_ci
522062306a36Sopenharmony_ci	pci_set_drvdata(pdev, pinstance);
522162306a36Sopenharmony_ci
522262306a36Sopenharmony_ci	/* Save PCI config-space for use following the reset */
522362306a36Sopenharmony_ci	rc = pci_save_state(pinstance->pdev);
522462306a36Sopenharmony_ci
522562306a36Sopenharmony_ci	if (rc != 0) {
522662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to save PCI config space\n");
522762306a36Sopenharmony_ci		goto out_scsi_host_put;
522862306a36Sopenharmony_ci	}
522962306a36Sopenharmony_ci
523062306a36Sopenharmony_ci	pmcraid_disable_interrupts(pinstance, ~0);
523162306a36Sopenharmony_ci
523262306a36Sopenharmony_ci	rc = pmcraid_register_interrupt_handler(pinstance);
523362306a36Sopenharmony_ci
523462306a36Sopenharmony_ci	if (rc) {
523562306a36Sopenharmony_ci		dev_err(&pdev->dev, "couldn't register interrupt handler\n");
523662306a36Sopenharmony_ci		goto out_scsi_host_put;
523762306a36Sopenharmony_ci	}
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci	pmcraid_init_tasklets(pinstance);
524062306a36Sopenharmony_ci
524162306a36Sopenharmony_ci	/* allocate verious buffers used by LLD.*/
524262306a36Sopenharmony_ci	rc = pmcraid_init_buffers(pinstance);
524362306a36Sopenharmony_ci
524462306a36Sopenharmony_ci	if (rc) {
524562306a36Sopenharmony_ci		pmcraid_err("couldn't allocate memory blocks\n");
524662306a36Sopenharmony_ci		goto out_unregister_isr;
524762306a36Sopenharmony_ci	}
524862306a36Sopenharmony_ci
524962306a36Sopenharmony_ci	/* check the reset type required */
525062306a36Sopenharmony_ci	pmcraid_reset_type(pinstance);
525162306a36Sopenharmony_ci
525262306a36Sopenharmony_ci	pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
525362306a36Sopenharmony_ci
525462306a36Sopenharmony_ci	/* Start IOA firmware initialization and bring card to Operational
525562306a36Sopenharmony_ci	 * state.
525662306a36Sopenharmony_ci	 */
525762306a36Sopenharmony_ci	pmcraid_info("starting IOA initialization sequence\n");
525862306a36Sopenharmony_ci	if (pmcraid_reset_bringup(pinstance)) {
525962306a36Sopenharmony_ci		dev_err(&pdev->dev, "couldn't initialize IOA\n");
526062306a36Sopenharmony_ci		rc = 1;
526162306a36Sopenharmony_ci		goto out_release_bufs;
526262306a36Sopenharmony_ci	}
526362306a36Sopenharmony_ci
526462306a36Sopenharmony_ci	/* Add adapter instance into mid-layer list */
526562306a36Sopenharmony_ci	rc = scsi_add_host(pinstance->host, &pdev->dev);
526662306a36Sopenharmony_ci	if (rc != 0) {
526762306a36Sopenharmony_ci		pmcraid_err("couldn't add host into mid-layer: %d\n", rc);
526862306a36Sopenharmony_ci		goto out_release_bufs;
526962306a36Sopenharmony_ci	}
527062306a36Sopenharmony_ci
527162306a36Sopenharmony_ci	scsi_scan_host(pinstance->host);
527262306a36Sopenharmony_ci
527362306a36Sopenharmony_ci	rc = pmcraid_setup_chrdev(pinstance);
527462306a36Sopenharmony_ci
527562306a36Sopenharmony_ci	if (rc != 0) {
527662306a36Sopenharmony_ci		pmcraid_err("couldn't create mgmt interface, error: %x\n",
527762306a36Sopenharmony_ci			     rc);
527862306a36Sopenharmony_ci		goto out_remove_host;
527962306a36Sopenharmony_ci	}
528062306a36Sopenharmony_ci
528162306a36Sopenharmony_ci	/* Schedule worker thread to handle CCN and take care of adding and
528262306a36Sopenharmony_ci	 * removing devices to OS
528362306a36Sopenharmony_ci	 */
528462306a36Sopenharmony_ci	atomic_set(&pinstance->expose_resources, 1);
528562306a36Sopenharmony_ci	schedule_work(&pinstance->worker_q);
528662306a36Sopenharmony_ci	return rc;
528762306a36Sopenharmony_ci
528862306a36Sopenharmony_ciout_remove_host:
528962306a36Sopenharmony_ci	scsi_remove_host(host);
529062306a36Sopenharmony_ci
529162306a36Sopenharmony_ciout_release_bufs:
529262306a36Sopenharmony_ci	pmcraid_release_buffers(pinstance);
529362306a36Sopenharmony_ci
529462306a36Sopenharmony_ciout_unregister_isr:
529562306a36Sopenharmony_ci	pmcraid_kill_tasklets(pinstance);
529662306a36Sopenharmony_ci	pmcraid_unregister_interrupt_handler(pinstance);
529762306a36Sopenharmony_ci
529862306a36Sopenharmony_ciout_scsi_host_put:
529962306a36Sopenharmony_ci	scsi_host_put(host);
530062306a36Sopenharmony_ci
530162306a36Sopenharmony_cicleanup_nomem:
530262306a36Sopenharmony_ci	iounmap(mapped_pci_addr);
530362306a36Sopenharmony_ci
530462306a36Sopenharmony_ciout_release_regions:
530562306a36Sopenharmony_ci	pci_release_regions(pdev);
530662306a36Sopenharmony_ci
530762306a36Sopenharmony_ciout_disable_device:
530862306a36Sopenharmony_ci	atomic_dec(&pmcraid_adapter_count);
530962306a36Sopenharmony_ci	pci_disable_device(pdev);
531062306a36Sopenharmony_ci	return -ENODEV;
531162306a36Sopenharmony_ci}
531262306a36Sopenharmony_ci
531362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pmcraid_pm_ops, pmcraid_suspend, pmcraid_resume);
531462306a36Sopenharmony_ci
531562306a36Sopenharmony_ci/*
531662306a36Sopenharmony_ci * PCI driver structure of pmcraid driver
531762306a36Sopenharmony_ci */
531862306a36Sopenharmony_cistatic struct pci_driver pmcraid_driver = {
531962306a36Sopenharmony_ci	.name = PMCRAID_DRIVER_NAME,
532062306a36Sopenharmony_ci	.id_table = pmcraid_pci_table,
532162306a36Sopenharmony_ci	.probe = pmcraid_probe,
532262306a36Sopenharmony_ci	.remove = pmcraid_remove,
532362306a36Sopenharmony_ci	.driver.pm = &pmcraid_pm_ops,
532462306a36Sopenharmony_ci	.shutdown = pmcraid_shutdown
532562306a36Sopenharmony_ci};
532662306a36Sopenharmony_ci
532762306a36Sopenharmony_ci/**
532862306a36Sopenharmony_ci * pmcraid_init - module load entry point
532962306a36Sopenharmony_ci */
533062306a36Sopenharmony_cistatic int __init pmcraid_init(void)
533162306a36Sopenharmony_ci{
533262306a36Sopenharmony_ci	dev_t dev;
533362306a36Sopenharmony_ci	int error;
533462306a36Sopenharmony_ci
533562306a36Sopenharmony_ci	pmcraid_info("%s Device Driver version: %s\n",
533662306a36Sopenharmony_ci			 PMCRAID_DRIVER_NAME, PMCRAID_DRIVER_VERSION);
533762306a36Sopenharmony_ci
533862306a36Sopenharmony_ci	error = alloc_chrdev_region(&dev, 0,
533962306a36Sopenharmony_ci				    PMCRAID_MAX_ADAPTERS,
534062306a36Sopenharmony_ci				    PMCRAID_DEVFILE);
534162306a36Sopenharmony_ci
534262306a36Sopenharmony_ci	if (error) {
534362306a36Sopenharmony_ci		pmcraid_err("failed to get a major number for adapters\n");
534462306a36Sopenharmony_ci		goto out_init;
534562306a36Sopenharmony_ci	}
534662306a36Sopenharmony_ci
534762306a36Sopenharmony_ci	pmcraid_major = MAJOR(dev);
534862306a36Sopenharmony_ci	pmcraid_class = class_create(PMCRAID_DEVFILE);
534962306a36Sopenharmony_ci
535062306a36Sopenharmony_ci	if (IS_ERR(pmcraid_class)) {
535162306a36Sopenharmony_ci		error = PTR_ERR(pmcraid_class);
535262306a36Sopenharmony_ci		pmcraid_err("failed to register with sysfs, error = %x\n",
535362306a36Sopenharmony_ci			    error);
535462306a36Sopenharmony_ci		goto out_unreg_chrdev;
535562306a36Sopenharmony_ci	}
535662306a36Sopenharmony_ci
535762306a36Sopenharmony_ci	error = pmcraid_netlink_init();
535862306a36Sopenharmony_ci
535962306a36Sopenharmony_ci	if (error) {
536062306a36Sopenharmony_ci		class_destroy(pmcraid_class);
536162306a36Sopenharmony_ci		goto out_unreg_chrdev;
536262306a36Sopenharmony_ci	}
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_ci	error = pci_register_driver(&pmcraid_driver);
536562306a36Sopenharmony_ci
536662306a36Sopenharmony_ci	if (error == 0)
536762306a36Sopenharmony_ci		goto out_init;
536862306a36Sopenharmony_ci
536962306a36Sopenharmony_ci	pmcraid_err("failed to register pmcraid driver, error = %x\n",
537062306a36Sopenharmony_ci		     error);
537162306a36Sopenharmony_ci	class_destroy(pmcraid_class);
537262306a36Sopenharmony_ci	pmcraid_netlink_release();
537362306a36Sopenharmony_ci
537462306a36Sopenharmony_ciout_unreg_chrdev:
537562306a36Sopenharmony_ci	unregister_chrdev_region(MKDEV(pmcraid_major, 0), PMCRAID_MAX_ADAPTERS);
537662306a36Sopenharmony_ci
537762306a36Sopenharmony_ciout_init:
537862306a36Sopenharmony_ci	return error;
537962306a36Sopenharmony_ci}
538062306a36Sopenharmony_ci
538162306a36Sopenharmony_ci/**
538262306a36Sopenharmony_ci * pmcraid_exit - module unload entry point
538362306a36Sopenharmony_ci */
538462306a36Sopenharmony_cistatic void __exit pmcraid_exit(void)
538562306a36Sopenharmony_ci{
538662306a36Sopenharmony_ci	pmcraid_netlink_release();
538762306a36Sopenharmony_ci	unregister_chrdev_region(MKDEV(pmcraid_major, 0),
538862306a36Sopenharmony_ci				 PMCRAID_MAX_ADAPTERS);
538962306a36Sopenharmony_ci	pci_unregister_driver(&pmcraid_driver);
539062306a36Sopenharmony_ci	class_destroy(pmcraid_class);
539162306a36Sopenharmony_ci}
539262306a36Sopenharmony_ci
539362306a36Sopenharmony_cimodule_init(pmcraid_init);
539462306a36Sopenharmony_cimodule_exit(pmcraid_exit);
5395