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