18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Written By: Anil Ravindranath<anil_ravindranath@pmc-sierra.com> 68c2ecf20Sopenharmony_ci * PMC-Sierra Inc 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2009 PMC Sierra Inc 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/wait.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 238c2ecf20Sopenharmony_ci#include <linux/firmware.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 268c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 278c2ecf20Sopenharmony_ci#include <linux/io.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <asm/irq.h> 308c2ecf20Sopenharmony_ci#include <asm/processor.h> 318c2ecf20Sopenharmony_ci#include <linux/libata.h> 328c2ecf20Sopenharmony_ci#include <linux/mutex.h> 338c2ecf20Sopenharmony_ci#include <linux/ktime.h> 348c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 358c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 368c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 378c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 388c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h> 398c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 408c2ecf20Sopenharmony_ci#include <scsi/scsicam.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "pmcraid.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Module configuration parameters 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistatic unsigned int pmcraid_debug_log; 488c2ecf20Sopenharmony_cistatic unsigned int pmcraid_disable_aen; 498c2ecf20Sopenharmony_cistatic unsigned int pmcraid_log_level = IOASC_LOG_LEVEL_MUST; 508c2ecf20Sopenharmony_cistatic unsigned int pmcraid_enable_msix; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Data structures to support multiple adapters by the LLD. 548c2ecf20Sopenharmony_ci * pmcraid_adapter_count - count of configured adapters 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistatic atomic_t pmcraid_adapter_count = ATOMIC_INIT(0); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Supporting user-level control interface through IOCTL commands. 608c2ecf20Sopenharmony_ci * pmcraid_major - major number to use 618c2ecf20Sopenharmony_ci * pmcraid_minor - minor number(s) to use 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic unsigned int pmcraid_major; 648c2ecf20Sopenharmony_cistatic struct class *pmcraid_class; 658c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Module parameters 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anil Ravindranath<anil_ravindranath@pmc-sierra.com>"); 718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMC Sierra MaxRAID Controller Driver"); 728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 738c2ecf20Sopenharmony_ciMODULE_VERSION(PMCRAID_DRIVER_VERSION); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cimodule_param_named(log_level, pmcraid_log_level, uint, (S_IRUGO | S_IWUSR)); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(log_level, 778c2ecf20Sopenharmony_ci "Enables firmware error code logging, default :1 high-severity" 788c2ecf20Sopenharmony_ci " errors, 2: all errors including high-severity errors," 798c2ecf20Sopenharmony_ci " 0: disables logging"); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cimodule_param_named(debug, pmcraid_debug_log, uint, (S_IRUGO | S_IWUSR)); 828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, 838c2ecf20Sopenharmony_ci "Enable driver verbose message logging. Set 1 to enable." 848c2ecf20Sopenharmony_ci "(default: 0)"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cimodule_param_named(disable_aen, pmcraid_disable_aen, uint, (S_IRUGO | S_IWUSR)); 878c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_aen, 888c2ecf20Sopenharmony_ci "Disable driver aen notifications to apps. Set 1 to disable." 898c2ecf20Sopenharmony_ci "(default: 0)"); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* chip specific constants for PMC MaxRAID controllers (same for 928c2ecf20Sopenharmony_ci * 0x5220 and 0x8010 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic struct pmcraid_chip_details pmcraid_chip_cfg[] = { 958c2ecf20Sopenharmony_ci { 968c2ecf20Sopenharmony_ci .ioastatus = 0x0, 978c2ecf20Sopenharmony_ci .ioarrin = 0x00040, 988c2ecf20Sopenharmony_ci .mailbox = 0x7FC30, 998c2ecf20Sopenharmony_ci .global_intr_mask = 0x00034, 1008c2ecf20Sopenharmony_ci .ioa_host_intr = 0x0009C, 1018c2ecf20Sopenharmony_ci .ioa_host_intr_clr = 0x000A0, 1028c2ecf20Sopenharmony_ci .ioa_host_msix_intr = 0x7FC40, 1038c2ecf20Sopenharmony_ci .ioa_host_mask = 0x7FC28, 1048c2ecf20Sopenharmony_ci .ioa_host_mask_clr = 0x7FC28, 1058c2ecf20Sopenharmony_ci .host_ioa_intr = 0x00020, 1068c2ecf20Sopenharmony_ci .host_ioa_intr_clr = 0x00020, 1078c2ecf20Sopenharmony_ci .transop_timeout = 300 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * PCI device ids supported by pmcraid driver 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic struct pci_device_id pmcraid_pci_table[] = { 1158c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID), 1168c2ecf20Sopenharmony_ci 0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0] 1178c2ecf20Sopenharmony_ci }, 1188c2ecf20Sopenharmony_ci {} 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pmcraid_pci_table); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * pmcraid_slave_alloc - Prepare for commands to a device 1278c2ecf20Sopenharmony_ci * @scsi_dev: scsi device struct 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * This function is called by mid-layer prior to sending any command to the new 1308c2ecf20Sopenharmony_ci * device. Stores resource entry details of the device in scsi_device struct. 1318c2ecf20Sopenharmony_ci * Queuecommand uses the resource handle and other details to fill up IOARCB 1328c2ecf20Sopenharmony_ci * while sending commands to the device. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Return value: 1358c2ecf20Sopenharmony_ci * 0 on success / -ENXIO if device does not exist 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cistatic int pmcraid_slave_alloc(struct scsi_device *scsi_dev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *temp, *res = NULL; 1408c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 1418c2ecf20Sopenharmony_ci u8 target, bus, lun; 1428c2ecf20Sopenharmony_ci unsigned long lock_flags; 1438c2ecf20Sopenharmony_ci int rc = -ENXIO; 1448c2ecf20Sopenharmony_ci u16 fw_version; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pinstance = shost_priv(scsi_dev->host); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci fw_version = be16_to_cpu(pinstance->inq_data->fw_version); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Driver exposes VSET and GSCSI resources only; all other device types 1518c2ecf20Sopenharmony_ci * are not exposed. Resource list is synchronized using resource lock 1528c2ecf20Sopenharmony_ci * so any traversal or modifications to the list should be done inside 1538c2ecf20Sopenharmony_ci * this lock 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->resource_lock, lock_flags); 1568c2ecf20Sopenharmony_ci list_for_each_entry(temp, &pinstance->used_res_q, queue) { 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* do not expose VSETs with order-ids > MAX_VSET_TARGETS */ 1598c2ecf20Sopenharmony_ci if (RES_IS_VSET(temp->cfg_entry)) { 1608c2ecf20Sopenharmony_ci if (fw_version <= PMCRAID_FW_VERSION_1) 1618c2ecf20Sopenharmony_ci target = temp->cfg_entry.unique_flags1; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (target > PMCRAID_MAX_VSET_TARGETS) 1668c2ecf20Sopenharmony_ci continue; 1678c2ecf20Sopenharmony_ci bus = PMCRAID_VSET_BUS_ID; 1688c2ecf20Sopenharmony_ci lun = 0; 1698c2ecf20Sopenharmony_ci } else if (RES_IS_GSCSI(temp->cfg_entry)) { 1708c2ecf20Sopenharmony_ci target = RES_TARGET(temp->cfg_entry.resource_address); 1718c2ecf20Sopenharmony_ci bus = PMCRAID_PHYS_BUS_ID; 1728c2ecf20Sopenharmony_ci lun = RES_LUN(temp->cfg_entry.resource_address); 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci continue; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (bus == scsi_dev->channel && 1788c2ecf20Sopenharmony_ci target == scsi_dev->id && 1798c2ecf20Sopenharmony_ci lun == scsi_dev->lun) { 1808c2ecf20Sopenharmony_ci res = temp; 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (res) { 1868c2ecf20Sopenharmony_ci res->scsi_dev = scsi_dev; 1878c2ecf20Sopenharmony_ci scsi_dev->hostdata = res; 1888c2ecf20Sopenharmony_ci res->change_detected = 0; 1898c2ecf20Sopenharmony_ci atomic_set(&res->read_failures, 0); 1908c2ecf20Sopenharmony_ci atomic_set(&res->write_failures, 0); 1918c2ecf20Sopenharmony_ci rc = 0; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); 1948c2ecf20Sopenharmony_ci return rc; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/** 1988c2ecf20Sopenharmony_ci * pmcraid_slave_configure - Configures a SCSI device 1998c2ecf20Sopenharmony_ci * @scsi_dev: scsi device struct 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * This function is executed by SCSI mid layer just after a device is first 2028c2ecf20Sopenharmony_ci * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the 2038c2ecf20Sopenharmony_ci * timeout value (default 30s) will be over-written to a higher value (60s) 2048c2ecf20Sopenharmony_ci * and max_sectors value will be over-written to 512. It also sets queue depth 2058c2ecf20Sopenharmony_ci * to host->cmd_per_lun value 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * Return value: 2088c2ecf20Sopenharmony_ci * 0 on success 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_cistatic int pmcraid_slave_configure(struct scsi_device *scsi_dev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res = scsi_dev->hostdata; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (!res) 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* LLD exposes VSETs and Enclosure devices only */ 2188c2ecf20Sopenharmony_ci if (RES_IS_GSCSI(res->cfg_entry) && 2198c2ecf20Sopenharmony_ci scsi_dev->type != TYPE_ENCLOSURE) 2208c2ecf20Sopenharmony_ci return -ENXIO; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pmcraid_info("configuring %x:%x:%x:%x\n", 2238c2ecf20Sopenharmony_ci scsi_dev->host->unique_id, 2248c2ecf20Sopenharmony_ci scsi_dev->channel, 2258c2ecf20Sopenharmony_ci scsi_dev->id, 2268c2ecf20Sopenharmony_ci (u8)scsi_dev->lun); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (RES_IS_GSCSI(res->cfg_entry)) { 2298c2ecf20Sopenharmony_ci scsi_dev->allow_restart = 1; 2308c2ecf20Sopenharmony_ci } else if (RES_IS_VSET(res->cfg_entry)) { 2318c2ecf20Sopenharmony_ci scsi_dev->allow_restart = 1; 2328c2ecf20Sopenharmony_ci blk_queue_rq_timeout(scsi_dev->request_queue, 2338c2ecf20Sopenharmony_ci PMCRAID_VSET_IO_TIMEOUT); 2348c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(scsi_dev->request_queue, 2358c2ecf20Sopenharmony_ci PMCRAID_VSET_MAX_SECTORS); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * We never want to report TCQ support for these types of devices. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry)) 2428c2ecf20Sopenharmony_ci scsi_dev->tagged_supported = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/** 2488c2ecf20Sopenharmony_ci * pmcraid_slave_destroy - Unconfigure a SCSI device before removing it 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * @scsi_dev: scsi device struct 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * This is called by mid-layer before removing a device. Pointer assignments 2538c2ecf20Sopenharmony_ci * done in pmcraid_slave_alloc will be reset to NULL here. 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * Return value 2568c2ecf20Sopenharmony_ci * none 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_cistatic void pmcraid_slave_destroy(struct scsi_device *scsi_dev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci res = (struct pmcraid_resource_entry *)scsi_dev->hostdata; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (res) 2658c2ecf20Sopenharmony_ci res->scsi_dev = NULL; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci scsi_dev->hostdata = NULL; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * pmcraid_change_queue_depth - Change the device's queue depth 2728c2ecf20Sopenharmony_ci * @scsi_dev: scsi device struct 2738c2ecf20Sopenharmony_ci * @depth: depth to set 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Return value 2768c2ecf20Sopenharmony_ci * actual depth set 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci if (depth > PMCRAID_MAX_CMD_PER_LUN) 2818c2ecf20Sopenharmony_ci depth = PMCRAID_MAX_CMD_PER_LUN; 2828c2ecf20Sopenharmony_ci return scsi_change_queue_depth(scsi_dev, depth); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * pmcraid_init_cmdblk - initializes a command block 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * @cmd: pointer to struct pmcraid_cmd to be initialized 2898c2ecf20Sopenharmony_ci * @index: if >=0 first time initialization; otherwise reinitialization 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * Return Value 2928c2ecf20Sopenharmony_ci * None 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); 2978c2ecf20Sopenharmony_ci dma_addr_t dma_addr = cmd->ioa_cb_bus_addr; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (index >= 0) { 3008c2ecf20Sopenharmony_ci /* first time initialization (called from probe) */ 3018c2ecf20Sopenharmony_ci u32 ioasa_offset = 3028c2ecf20Sopenharmony_ci offsetof(struct pmcraid_control_block, ioasa); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci cmd->index = index; 3058c2ecf20Sopenharmony_ci ioarcb->response_handle = cpu_to_le32(index << 2); 3068c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr = cpu_to_le64(dma_addr); 3078c2ecf20Sopenharmony_ci ioarcb->ioasa_bus_addr = cpu_to_le64(dma_addr + ioasa_offset); 3088c2ecf20Sopenharmony_ci ioarcb->ioasa_len = cpu_to_le16(sizeof(struct pmcraid_ioasa)); 3098c2ecf20Sopenharmony_ci } else { 3108c2ecf20Sopenharmony_ci /* re-initialization of various lengths, called once command is 3118c2ecf20Sopenharmony_ci * processed by IOA 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci memset(&cmd->ioa_cb->ioarcb.cdb, 0, PMCRAID_MAX_CDB_LEN); 3148c2ecf20Sopenharmony_ci ioarcb->hrrq_id = 0; 3158c2ecf20Sopenharmony_ci ioarcb->request_flags0 = 0; 3168c2ecf20Sopenharmony_ci ioarcb->request_flags1 = 0; 3178c2ecf20Sopenharmony_ci ioarcb->cmd_timeout = 0; 3188c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL); 3198c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = 0; 3208c2ecf20Sopenharmony_ci ioarcb->ioadl_length = 0; 3218c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = 0; 3228c2ecf20Sopenharmony_ci ioarcb->add_cmd_param_length = 0; 3238c2ecf20Sopenharmony_ci ioarcb->add_cmd_param_offset = 0; 3248c2ecf20Sopenharmony_ci cmd->ioa_cb->ioasa.ioasc = 0; 3258c2ecf20Sopenharmony_ci cmd->ioa_cb->ioasa.residual_data_length = 0; 3268c2ecf20Sopenharmony_ci cmd->time_left = 0; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci cmd->cmd_done = NULL; 3308c2ecf20Sopenharmony_ci cmd->scsi_cmd = NULL; 3318c2ecf20Sopenharmony_ci cmd->release = 0; 3328c2ecf20Sopenharmony_ci cmd->completion_req = 0; 3338c2ecf20Sopenharmony_ci cmd->sense_buffer = NULL; 3348c2ecf20Sopenharmony_ci cmd->sense_buffer_dma = 0; 3358c2ecf20Sopenharmony_ci cmd->dma_handle = 0; 3368c2ecf20Sopenharmony_ci timer_setup(&cmd->timer, NULL, 0); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * pmcraid_reinit_cmdblk - reinitialize a command block 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * @cmd: pointer to struct pmcraid_cmd to be reinitialized 3438c2ecf20Sopenharmony_ci * 3448c2ecf20Sopenharmony_ci * Return Value 3458c2ecf20Sopenharmony_ci * None 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_cistatic void pmcraid_reinit_cmdblk(struct pmcraid_cmd *cmd) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci pmcraid_init_cmdblk(cmd, -1); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/** 3538c2ecf20Sopenharmony_ci * pmcraid_get_free_cmd - get a free cmd block from command block pool 3548c2ecf20Sopenharmony_ci * @pinstance: adapter instance structure 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * Return Value: 3578c2ecf20Sopenharmony_ci * returns pointer to cmd block or NULL if no blocks are available 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_cistatic struct pmcraid_cmd *pmcraid_get_free_cmd( 3608c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance 3618c2ecf20Sopenharmony_ci) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd = NULL; 3648c2ecf20Sopenharmony_ci unsigned long lock_flags; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* free cmd block list is protected by free_pool_lock */ 3678c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!list_empty(&pinstance->free_cmd_pool)) { 3708c2ecf20Sopenharmony_ci cmd = list_entry(pinstance->free_cmd_pool.next, 3718c2ecf20Sopenharmony_ci struct pmcraid_cmd, free_list); 3728c2ecf20Sopenharmony_ci list_del(&cmd->free_list); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Initialize the command block before giving it the caller */ 3778c2ecf20Sopenharmony_ci if (cmd != NULL) 3788c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 3798c2ecf20Sopenharmony_ci return cmd; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/** 3838c2ecf20Sopenharmony_ci * pmcraid_return_cmd - return a completed command block back into free pool 3848c2ecf20Sopenharmony_ci * @cmd: pointer to the command block 3858c2ecf20Sopenharmony_ci * 3868c2ecf20Sopenharmony_ci * Return Value: 3878c2ecf20Sopenharmony_ci * nothing 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic void pmcraid_return_cmd(struct pmcraid_cmd *cmd) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 3928c2ecf20Sopenharmony_ci unsigned long lock_flags; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); 3958c2ecf20Sopenharmony_ci list_add_tail(&cmd->free_list, &pinstance->free_cmd_pool); 3968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * pmcraid_read_interrupts - reads IOA interrupts 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 4038c2ecf20Sopenharmony_ci * 4048c2ecf20Sopenharmony_ci * Return value 4058c2ecf20Sopenharmony_ci * interrupts read from IOA 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic u32 pmcraid_read_interrupts(struct pmcraid_instance *pinstance) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci return (pinstance->interrupt_mode) ? 4108c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_msix_interrupt_reg) : 4118c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_interrupt_reg); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * pmcraid_disable_interrupts - Masks and clears all specified interrupts 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 4188c2ecf20Sopenharmony_ci * @intrs: interrupts to disable 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Return Value 4218c2ecf20Sopenharmony_ci * None 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_cistatic void pmcraid_disable_interrupts( 4248c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 4258c2ecf20Sopenharmony_ci u32 intrs 4268c2ecf20Sopenharmony_ci) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg); 4298c2ecf20Sopenharmony_ci u32 nmask = gmask | GLOBAL_INTERRUPT_MASK; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_clr_reg); 4328c2ecf20Sopenharmony_ci iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); 4338c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.global_interrupt_mask_reg); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (!pinstance->interrupt_mode) { 4368c2ecf20Sopenharmony_ci iowrite32(intrs, 4378c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_mask_reg); 4388c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci/** 4438c2ecf20Sopenharmony_ci * pmcraid_enable_interrupts - Enables specified interrupts 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 4468c2ecf20Sopenharmony_ci * @intr: interrupts to enable 4478c2ecf20Sopenharmony_ci * 4488c2ecf20Sopenharmony_ci * Return Value 4498c2ecf20Sopenharmony_ci * None 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cistatic void pmcraid_enable_interrupts( 4528c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 4538c2ecf20Sopenharmony_ci u32 intrs 4548c2ecf20Sopenharmony_ci) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg); 4578c2ecf20Sopenharmony_ci u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (!pinstance->interrupt_mode) { 4628c2ecf20Sopenharmony_ci iowrite32(~intrs, 4638c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_mask_reg); 4648c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci pmcraid_info("enabled interrupts global mask = %x intr_mask = %x\n", 4688c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.global_interrupt_mask_reg), 4698c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg)); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/** 4738c2ecf20Sopenharmony_ci * pmcraid_clr_trans_op - clear trans to op interrupt 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * Return Value 4788c2ecf20Sopenharmony_ci * None 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_cistatic void pmcraid_clr_trans_op( 4818c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance 4828c2ecf20Sopenharmony_ci) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci unsigned long lock_flags; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (!pinstance->interrupt_mode) { 4878c2ecf20Sopenharmony_ci iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, 4888c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_mask_reg); 4898c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); 4908c2ecf20Sopenharmony_ci iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, 4918c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_clr_reg); 4928c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.ioa_host_interrupt_clr_reg); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (pinstance->reset_cmd != NULL) { 4968c2ecf20Sopenharmony_ci del_timer(&pinstance->reset_cmd->timer); 4978c2ecf20Sopenharmony_ci spin_lock_irqsave( 4988c2ecf20Sopenharmony_ci pinstance->host->host_lock, lock_flags); 4998c2ecf20Sopenharmony_ci pinstance->reset_cmd->cmd_done(pinstance->reset_cmd); 5008c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 5018c2ecf20Sopenharmony_ci pinstance->host->host_lock, lock_flags); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/** 5068c2ecf20Sopenharmony_ci * pmcraid_reset_type - Determine the required reset type 5078c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * IOA requires hard reset if any of the following conditions is true. 5108c2ecf20Sopenharmony_ci * 1. If HRRQ valid interrupt is not masked 5118c2ecf20Sopenharmony_ci * 2. IOA reset alert doorbell is set 5128c2ecf20Sopenharmony_ci * 3. If there are any error interrupts 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_cistatic void pmcraid_reset_type(struct pmcraid_instance *pinstance) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci u32 mask; 5178c2ecf20Sopenharmony_ci u32 intrs; 5188c2ecf20Sopenharmony_ci u32 alerts; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci mask = ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); 5218c2ecf20Sopenharmony_ci intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg); 5228c2ecf20Sopenharmony_ci alerts = ioread32(pinstance->int_regs.host_ioa_interrupt_reg); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if ((mask & INTRS_HRRQ_VALID) == 0 || 5258c2ecf20Sopenharmony_ci (alerts & DOORBELL_IOA_RESET_ALERT) || 5268c2ecf20Sopenharmony_ci (intrs & PMCRAID_ERROR_INTERRUPTS)) { 5278c2ecf20Sopenharmony_ci pmcraid_info("IOA requires hard reset\n"); 5288c2ecf20Sopenharmony_ci pinstance->ioa_hard_reset = 1; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* If unit check is active, trigger the dump */ 5328c2ecf20Sopenharmony_ci if (intrs & INTRS_IOA_UNIT_CHECK) 5338c2ecf20Sopenharmony_ci pinstance->ioa_unit_check = 1; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/** 5378c2ecf20Sopenharmony_ci * pmcraid_bist_done - completion function for PCI BIST 5388c2ecf20Sopenharmony_ci * @cmd: pointer to reset command 5398c2ecf20Sopenharmony_ci * Return Value 5408c2ecf20Sopenharmony_ci * none 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic void pmcraid_ioa_reset(struct pmcraid_cmd *); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void pmcraid_bist_done(struct timer_list *t) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd = from_timer(cmd, t, timer); 5488c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 5498c2ecf20Sopenharmony_ci unsigned long lock_flags; 5508c2ecf20Sopenharmony_ci int rc; 5518c2ecf20Sopenharmony_ci u16 pci_reg; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* If PCI config space can't be accessed wait for another two secs */ 5568c2ecf20Sopenharmony_ci if ((rc != PCIBIOS_SUCCESSFUL || (!(pci_reg & PCI_COMMAND_MEMORY))) && 5578c2ecf20Sopenharmony_ci cmd->time_left > 0) { 5588c2ecf20Sopenharmony_ci pmcraid_info("BIST not complete, waiting another 2 secs\n"); 5598c2ecf20Sopenharmony_ci cmd->timer.expires = jiffies + cmd->time_left; 5608c2ecf20Sopenharmony_ci cmd->time_left = 0; 5618c2ecf20Sopenharmony_ci add_timer(&cmd->timer); 5628c2ecf20Sopenharmony_ci } else { 5638c2ecf20Sopenharmony_ci cmd->time_left = 0; 5648c2ecf20Sopenharmony_ci pmcraid_info("BIST is complete, proceeding with reset\n"); 5658c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 5668c2ecf20Sopenharmony_ci pmcraid_ioa_reset(cmd); 5678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/** 5728c2ecf20Sopenharmony_ci * pmcraid_start_bist - starts BIST 5738c2ecf20Sopenharmony_ci * @cmd: pointer to reset cmd 5748c2ecf20Sopenharmony_ci * Return Value 5758c2ecf20Sopenharmony_ci * none 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_cistatic void pmcraid_start_bist(struct pmcraid_cmd *cmd) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 5808c2ecf20Sopenharmony_ci u32 doorbells, intrs; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* proceed with bist and wait for 2 seconds */ 5838c2ecf20Sopenharmony_ci iowrite32(DOORBELL_IOA_START_BIST, 5848c2ecf20Sopenharmony_ci pinstance->int_regs.host_ioa_interrupt_reg); 5858c2ecf20Sopenharmony_ci doorbells = ioread32(pinstance->int_regs.host_ioa_interrupt_reg); 5868c2ecf20Sopenharmony_ci intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg); 5878c2ecf20Sopenharmony_ci pmcraid_info("doorbells after start bist: %x intrs: %x\n", 5888c2ecf20Sopenharmony_ci doorbells, intrs); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci cmd->time_left = msecs_to_jiffies(PMCRAID_BIST_TIMEOUT); 5918c2ecf20Sopenharmony_ci cmd->timer.expires = jiffies + msecs_to_jiffies(PMCRAID_BIST_TIMEOUT); 5928c2ecf20Sopenharmony_ci cmd->timer.function = pmcraid_bist_done; 5938c2ecf20Sopenharmony_ci add_timer(&cmd->timer); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/** 5978c2ecf20Sopenharmony_ci * pmcraid_reset_alert_done - completion routine for reset_alert 5988c2ecf20Sopenharmony_ci * @cmd: pointer to command block used in reset sequence 5998c2ecf20Sopenharmony_ci * Return value 6008c2ecf20Sopenharmony_ci * None 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_cistatic void pmcraid_reset_alert_done(struct timer_list *t) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd = from_timer(cmd, t, timer); 6058c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 6068c2ecf20Sopenharmony_ci u32 status = ioread32(pinstance->ioa_status); 6078c2ecf20Sopenharmony_ci unsigned long lock_flags; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* if the critical operation in progress bit is set or the wait times 6108c2ecf20Sopenharmony_ci * out, invoke reset engine to proceed with hard reset. If there is 6118c2ecf20Sopenharmony_ci * some more time to wait, restart the timer 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci if (((status & INTRS_CRITICAL_OP_IN_PROGRESS) == 0) || 6148c2ecf20Sopenharmony_ci cmd->time_left <= 0) { 6158c2ecf20Sopenharmony_ci pmcraid_info("critical op is reset proceeding with reset\n"); 6168c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 6178c2ecf20Sopenharmony_ci pmcraid_ioa_reset(cmd); 6188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 6198c2ecf20Sopenharmony_ci } else { 6208c2ecf20Sopenharmony_ci pmcraid_info("critical op is not yet reset waiting again\n"); 6218c2ecf20Sopenharmony_ci /* restart timer if some more time is available to wait */ 6228c2ecf20Sopenharmony_ci cmd->time_left -= PMCRAID_CHECK_FOR_RESET_TIMEOUT; 6238c2ecf20Sopenharmony_ci cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT; 6248c2ecf20Sopenharmony_ci cmd->timer.function = pmcraid_reset_alert_done; 6258c2ecf20Sopenharmony_ci add_timer(&cmd->timer); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/** 6308c2ecf20Sopenharmony_ci * pmcraid_reset_alert - alerts IOA for a possible reset 6318c2ecf20Sopenharmony_ci * @cmd : command block to be used for reset sequence. 6328c2ecf20Sopenharmony_ci * 6338c2ecf20Sopenharmony_ci * Return Value 6348c2ecf20Sopenharmony_ci * returns 0 if pci config-space is accessible and RESET_DOORBELL is 6358c2ecf20Sopenharmony_ci * successfully written to IOA. Returns non-zero in case pci_config_space 6368c2ecf20Sopenharmony_ci * is not accessible 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_cistatic void pmcraid_notify_ioastate(struct pmcraid_instance *, u32); 6398c2ecf20Sopenharmony_cistatic void pmcraid_reset_alert(struct pmcraid_cmd *cmd) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 6428c2ecf20Sopenharmony_ci u32 doorbells; 6438c2ecf20Sopenharmony_ci int rc; 6448c2ecf20Sopenharmony_ci u16 pci_reg; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* If we are able to access IOA PCI config space, alert IOA that we are 6478c2ecf20Sopenharmony_ci * going to reset it soon. This enables IOA to preserv persistent error 6488c2ecf20Sopenharmony_ci * data if any. In case memory space is not accessible, proceed with 6498c2ecf20Sopenharmony_ci * BIST or slot_reset 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg); 6528c2ecf20Sopenharmony_ci if ((rc == PCIBIOS_SUCCESSFUL) && (pci_reg & PCI_COMMAND_MEMORY)) { 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* wait for IOA permission i.e until CRITICAL_OPERATION bit is 6558c2ecf20Sopenharmony_ci * reset IOA doesn't generate any interrupts when CRITICAL 6568c2ecf20Sopenharmony_ci * OPERATION bit is reset. A timer is started to wait for this 6578c2ecf20Sopenharmony_ci * bit to be reset. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci cmd->time_left = PMCRAID_RESET_TIMEOUT; 6608c2ecf20Sopenharmony_ci cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT; 6618c2ecf20Sopenharmony_ci cmd->timer.function = pmcraid_reset_alert_done; 6628c2ecf20Sopenharmony_ci add_timer(&cmd->timer); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci iowrite32(DOORBELL_IOA_RESET_ALERT, 6658c2ecf20Sopenharmony_ci pinstance->int_regs.host_ioa_interrupt_reg); 6668c2ecf20Sopenharmony_ci doorbells = 6678c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.host_ioa_interrupt_reg); 6688c2ecf20Sopenharmony_ci pmcraid_info("doorbells after reset alert: %x\n", doorbells); 6698c2ecf20Sopenharmony_ci } else { 6708c2ecf20Sopenharmony_ci pmcraid_info("PCI config is not accessible starting BIST\n"); 6718c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_HARD_RESET; 6728c2ecf20Sopenharmony_ci pmcraid_start_bist(cmd); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/** 6778c2ecf20Sopenharmony_ci * pmcraid_timeout_handler - Timeout handler for internally generated ops 6788c2ecf20Sopenharmony_ci * 6798c2ecf20Sopenharmony_ci * @cmd : pointer to command structure, that got timedout 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * This function blocks host requests and initiates an adapter reset. 6828c2ecf20Sopenharmony_ci * 6838c2ecf20Sopenharmony_ci * Return value: 6848c2ecf20Sopenharmony_ci * None 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_cistatic void pmcraid_timeout_handler(struct timer_list *t) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd = from_timer(cmd, t, timer); 6898c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 6908c2ecf20Sopenharmony_ci unsigned long lock_flags; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci dev_info(&pinstance->pdev->dev, 6938c2ecf20Sopenharmony_ci "Adapter being reset due to cmd(CDB[0] = %x) timeout\n", 6948c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0]); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Command timeouts result in hard reset sequence. The command that got 6978c2ecf20Sopenharmony_ci * timed out may be the one used as part of reset sequence. In this 6988c2ecf20Sopenharmony_ci * case restart reset sequence using the same command block even if 6998c2ecf20Sopenharmony_ci * reset is in progress. Otherwise fail this command and get a free 7008c2ecf20Sopenharmony_ci * command block to restart the reset sequence. 7018c2ecf20Sopenharmony_ci */ 7028c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 7038c2ecf20Sopenharmony_ci if (!pinstance->ioa_reset_in_progress) { 7048c2ecf20Sopenharmony_ci pinstance->ioa_reset_attempts = 0; 7058c2ecf20Sopenharmony_ci cmd = pmcraid_get_free_cmd(pinstance); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* If we are out of command blocks, just return here itself. 7088c2ecf20Sopenharmony_ci * Some other command's timeout handler can do the reset job 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci if (cmd == NULL) { 7118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, 7128c2ecf20Sopenharmony_ci lock_flags); 7138c2ecf20Sopenharmony_ci pmcraid_err("no free cmnd block for timeout handler\n"); 7148c2ecf20Sopenharmony_ci return; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci pinstance->reset_cmd = cmd; 7188c2ecf20Sopenharmony_ci pinstance->ioa_reset_in_progress = 1; 7198c2ecf20Sopenharmony_ci } else { 7208c2ecf20Sopenharmony_ci pmcraid_info("reset is already in progress\n"); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (pinstance->reset_cmd != cmd) { 7238c2ecf20Sopenharmony_ci /* This command should have been given to IOA, this 7248c2ecf20Sopenharmony_ci * command will be completed by fail_outstanding_cmds 7258c2ecf20Sopenharmony_ci * anyway 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci pmcraid_err("cmd is pending but reset in progress\n"); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* If this command was being used as part of the reset 7318c2ecf20Sopenharmony_ci * sequence, set cmd_done pointer to pmcraid_ioa_reset. This 7328c2ecf20Sopenharmony_ci * causes fail_outstanding_commands not to return the command 7338c2ecf20Sopenharmony_ci * block back to free pool 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci if (cmd == pinstance->reset_cmd) 7368c2ecf20Sopenharmony_ci cmd->cmd_done = pmcraid_ioa_reset; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Notify apps of important IOA bringup/bringdown sequences */ 7408c2ecf20Sopenharmony_ci if (pinstance->scn.ioa_state != PMC_DEVICE_EVENT_RESET_START && 7418c2ecf20Sopenharmony_ci pinstance->scn.ioa_state != PMC_DEVICE_EVENT_SHUTDOWN_START) 7428c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, 7438c2ecf20Sopenharmony_ci PMC_DEVICE_EVENT_RESET_START); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; 7468c2ecf20Sopenharmony_ci scsi_block_requests(pinstance->host); 7478c2ecf20Sopenharmony_ci pmcraid_reset_alert(cmd); 7488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/** 7528c2ecf20Sopenharmony_ci * pmcraid_internal_done - completion routine for internally generated cmds 7538c2ecf20Sopenharmony_ci * 7548c2ecf20Sopenharmony_ci * @cmd: command that got response from IOA 7558c2ecf20Sopenharmony_ci * 7568c2ecf20Sopenharmony_ci * Return Value: 7578c2ecf20Sopenharmony_ci * none 7588c2ecf20Sopenharmony_ci */ 7598c2ecf20Sopenharmony_cistatic void pmcraid_internal_done(struct pmcraid_cmd *cmd) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n", 7628c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 7638c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Some of the internal commands are sent with callers blocking for the 7668c2ecf20Sopenharmony_ci * response. Same will be indicated as part of cmd->completion_req 7678c2ecf20Sopenharmony_ci * field. Response path needs to wake up any waiters waiting for cmd 7688c2ecf20Sopenharmony_ci * completion if this flag is set. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci if (cmd->completion_req) { 7718c2ecf20Sopenharmony_ci cmd->completion_req = 0; 7728c2ecf20Sopenharmony_ci complete(&cmd->wait_for_completion); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* most of the internal commands are completed by caller itself, so 7768c2ecf20Sopenharmony_ci * no need to return the command block back to free pool until we are 7778c2ecf20Sopenharmony_ci * required to do so (e.g once done with initialization). 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_ci if (cmd->release) { 7808c2ecf20Sopenharmony_ci cmd->release = 0; 7818c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci/** 7868c2ecf20Sopenharmony_ci * pmcraid_reinit_cfgtable_done - done function for cfg table reinitialization 7878c2ecf20Sopenharmony_ci * 7888c2ecf20Sopenharmony_ci * @cmd: command that got response from IOA 7898c2ecf20Sopenharmony_ci * 7908c2ecf20Sopenharmony_ci * This routine is called after driver re-reads configuration table due to a 7918c2ecf20Sopenharmony_ci * lost CCN. It returns the command block back to free pool and schedules 7928c2ecf20Sopenharmony_ci * worker thread to add/delete devices into the system. 7938c2ecf20Sopenharmony_ci * 7948c2ecf20Sopenharmony_ci * Return Value: 7958c2ecf20Sopenharmony_ci * none 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic void pmcraid_reinit_cfgtable_done(struct pmcraid_cmd *cmd) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n", 8008c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 8018c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (cmd->release) { 8048c2ecf20Sopenharmony_ci cmd->release = 0; 8058c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci pmcraid_info("scheduling worker for config table reinitialization\n"); 8088c2ecf20Sopenharmony_ci schedule_work(&cmd->drv_inst->worker_q); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/** 8128c2ecf20Sopenharmony_ci * pmcraid_erp_done - Process completion of SCSI error response from device 8138c2ecf20Sopenharmony_ci * @cmd: pmcraid_command 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * This function copies the sense buffer into the scsi_cmd struct and completes 8168c2ecf20Sopenharmony_ci * scsi_cmd by calling scsi_done function. 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci * Return value: 8198c2ecf20Sopenharmony_ci * none 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_cistatic void pmcraid_erp_done(struct pmcraid_cmd *cmd) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; 8248c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 8258c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (PMCRAID_IOASC_SENSE_KEY(ioasc) > 0) { 8288c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_ERROR << 16); 8298c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, scsi_cmd, 8308c2ecf20Sopenharmony_ci "command CDB[0] = %x failed with IOASC: 0x%08X\n", 8318c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], ioasc); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (cmd->sense_buffer) { 8358c2ecf20Sopenharmony_ci dma_unmap_single(&pinstance->pdev->dev, cmd->sense_buffer_dma, 8368c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 8378c2ecf20Sopenharmony_ci cmd->sense_buffer = NULL; 8388c2ecf20Sopenharmony_ci cmd->sense_buffer_dma = 0; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci scsi_dma_unmap(scsi_cmd); 8428c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 8438c2ecf20Sopenharmony_ci scsi_cmd->scsi_done(scsi_cmd); 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/** 8478c2ecf20Sopenharmony_ci * pmcraid_fire_command - sends an IOA command to adapter 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * This function adds the given block into pending command list 8508c2ecf20Sopenharmony_ci * and returns without waiting 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * @cmd : command to be sent to the device 8538c2ecf20Sopenharmony_ci * 8548c2ecf20Sopenharmony_ci * Return Value 8558c2ecf20Sopenharmony_ci * None 8568c2ecf20Sopenharmony_ci */ 8578c2ecf20Sopenharmony_cistatic void _pmcraid_fire_command(struct pmcraid_cmd *cmd) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 8608c2ecf20Sopenharmony_ci unsigned long lock_flags; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* Add this command block to pending cmd pool. We do this prior to 8638c2ecf20Sopenharmony_ci * writting IOARCB to ioarrin because IOA might complete the command 8648c2ecf20Sopenharmony_ci * by the time we are about to add it to the list. Response handler 8658c2ecf20Sopenharmony_ci * (isr/tasklet) looks for cmd block in the pending pending list. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags); 8688c2ecf20Sopenharmony_ci list_add_tail(&cmd->free_list, &pinstance->pending_cmd_pool); 8698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags); 8708c2ecf20Sopenharmony_ci atomic_inc(&pinstance->outstanding_cmds); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* driver writes lower 32-bit value of IOARCB address only */ 8738c2ecf20Sopenharmony_ci mb(); 8748c2ecf20Sopenharmony_ci iowrite32(le64_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr), pinstance->ioarrin); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/** 8788c2ecf20Sopenharmony_ci * pmcraid_send_cmd - fires a command to IOA 8798c2ecf20Sopenharmony_ci * 8808c2ecf20Sopenharmony_ci * This function also sets up timeout function, and command completion 8818c2ecf20Sopenharmony_ci * function 8828c2ecf20Sopenharmony_ci * 8838c2ecf20Sopenharmony_ci * @cmd: pointer to the command block to be fired to IOA 8848c2ecf20Sopenharmony_ci * @cmd_done: command completion function, called once IOA responds 8858c2ecf20Sopenharmony_ci * @timeout: timeout to wait for this command completion 8868c2ecf20Sopenharmony_ci * @timeout_func: timeout handler 8878c2ecf20Sopenharmony_ci * 8888c2ecf20Sopenharmony_ci * Return value 8898c2ecf20Sopenharmony_ci * none 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_cistatic void pmcraid_send_cmd( 8928c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd, 8938c2ecf20Sopenharmony_ci void (*cmd_done) (struct pmcraid_cmd *), 8948c2ecf20Sopenharmony_ci unsigned long timeout, 8958c2ecf20Sopenharmony_ci void (*timeout_func) (struct timer_list *) 8968c2ecf20Sopenharmony_ci) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci /* initialize done function */ 8998c2ecf20Sopenharmony_ci cmd->cmd_done = cmd_done; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (timeout_func) { 9028c2ecf20Sopenharmony_ci /* setup timeout handler */ 9038c2ecf20Sopenharmony_ci cmd->timer.expires = jiffies + timeout; 9048c2ecf20Sopenharmony_ci cmd->timer.function = timeout_func; 9058c2ecf20Sopenharmony_ci add_timer(&cmd->timer); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* fire the command to IOA */ 9098c2ecf20Sopenharmony_ci _pmcraid_fire_command(cmd); 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci/** 9138c2ecf20Sopenharmony_ci * pmcraid_ioa_shutdown_done - completion function for IOA shutdown command 9148c2ecf20Sopenharmony_ci * @cmd: pointer to the command block used for sending IOA shutdown command 9158c2ecf20Sopenharmony_ci * 9168c2ecf20Sopenharmony_ci * Return value 9178c2ecf20Sopenharmony_ci * None 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_cistatic void pmcraid_ioa_shutdown_done(struct pmcraid_cmd *cmd) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 9228c2ecf20Sopenharmony_ci unsigned long lock_flags; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 9258c2ecf20Sopenharmony_ci pmcraid_ioa_reset(cmd); 9268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/** 9308c2ecf20Sopenharmony_ci * pmcraid_ioa_shutdown - sends SHUTDOWN command to ioa 9318c2ecf20Sopenharmony_ci * 9328c2ecf20Sopenharmony_ci * @cmd: pointer to the command block used as part of reset sequence 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * Return Value 9358c2ecf20Sopenharmony_ci * None 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_cistatic void pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci pmcraid_info("response for Cancel CCN CDB[0] = %x ioasc = %x\n", 9408c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 9418c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Note that commands sent during reset require next command to be sent 9448c2ecf20Sopenharmony_ci * to IOA. Hence reinit the done function as well as timeout function 9458c2ecf20Sopenharmony_ci */ 9468c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 9478c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.request_type = REQ_TYPE_IOACMD; 9488c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.resource_handle = 9498c2ecf20Sopenharmony_ci cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 9508c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0] = PMCRAID_IOA_SHUTDOWN; 9518c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[1] = PMCRAID_SHUTDOWN_NORMAL; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* fire shutdown command to hardware. */ 9548c2ecf20Sopenharmony_ci pmcraid_info("firing normal shutdown command (%d) to IOA\n", 9558c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle)); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(cmd->drv_inst, PMC_DEVICE_EVENT_SHUTDOWN_START); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, pmcraid_ioa_shutdown_done, 9608c2ecf20Sopenharmony_ci PMCRAID_SHUTDOWN_TIMEOUT, 9618c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/** 9658c2ecf20Sopenharmony_ci * pmcraid_get_fwversion_done - completion function for get_fwversion 9668c2ecf20Sopenharmony_ci * 9678c2ecf20Sopenharmony_ci * @cmd: pointer to command block used to send INQUIRY command 9688c2ecf20Sopenharmony_ci * 9698c2ecf20Sopenharmony_ci * Return Value 9708c2ecf20Sopenharmony_ci * none 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_cistatic void pmcraid_querycfg(struct pmcraid_cmd *); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 9778c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); 9788c2ecf20Sopenharmony_ci unsigned long lock_flags; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* configuration table entry size depends on firmware version. If fw 9818c2ecf20Sopenharmony_ci * version is not known, it is not possible to interpret IOA config 9828c2ecf20Sopenharmony_ci * table 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_ci if (ioasc) { 9858c2ecf20Sopenharmony_ci pmcraid_err("IOA Inquiry failed with %x\n", ioasc); 9868c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 9878c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; 9888c2ecf20Sopenharmony_ci pmcraid_reset_alert(cmd); 9898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 9908c2ecf20Sopenharmony_ci } else { 9918c2ecf20Sopenharmony_ci pmcraid_querycfg(cmd); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/** 9968c2ecf20Sopenharmony_ci * pmcraid_get_fwversion - reads firmware version information 9978c2ecf20Sopenharmony_ci * 9988c2ecf20Sopenharmony_ci * @cmd: pointer to command block used to send INQUIRY command 9998c2ecf20Sopenharmony_ci * 10008c2ecf20Sopenharmony_ci * Return Value 10018c2ecf20Sopenharmony_ci * none 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_cistatic void pmcraid_get_fwversion(struct pmcraid_cmd *cmd) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 10068c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 10078c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 10088c2ecf20Sopenharmony_ci u16 data_size = sizeof(struct pmcraid_inquiry_data); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 10118c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_SCSI; 10128c2ecf20Sopenharmony_ci ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 10138c2ecf20Sopenharmony_ci ioarcb->cdb[0] = INQUIRY; 10148c2ecf20Sopenharmony_ci ioarcb->cdb[1] = 1; 10158c2ecf20Sopenharmony_ci ioarcb->cdb[2] = 0xD0; 10168c2ecf20Sopenharmony_ci ioarcb->cdb[3] = (data_size >> 8) & 0xFF; 10178c2ecf20Sopenharmony_ci ioarcb->cdb[4] = data_size & 0xFF; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Since entire inquiry data it can be part of IOARCB itself 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + 10228c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 10238c2ecf20Sopenharmony_ci add_data.u.ioadl[0])); 10248c2ecf20Sopenharmony_ci ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); 10258c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL)); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= NO_LINK_DESCS; 10288c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = cpu_to_le32(data_size); 10298c2ecf20Sopenharmony_ci ioadl = &(ioarcb->add_data.u.ioadl[0]); 10308c2ecf20Sopenharmony_ci ioadl->flags = IOADL_FLAGS_LAST_DESC; 10318c2ecf20Sopenharmony_ci ioadl->address = cpu_to_le64(pinstance->inq_data_baddr); 10328c2ecf20Sopenharmony_ci ioadl->data_len = cpu_to_le32(data_size); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, pmcraid_get_fwversion_done, 10358c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/** 10398c2ecf20Sopenharmony_ci * pmcraid_identify_hrrq - registers host rrq buffers with IOA 10408c2ecf20Sopenharmony_ci * @cmd: pointer to command block to be used for identify hrrq 10418c2ecf20Sopenharmony_ci * 10428c2ecf20Sopenharmony_ci * Return Value 10438c2ecf20Sopenharmony_ci * none 10448c2ecf20Sopenharmony_ci */ 10458c2ecf20Sopenharmony_cistatic void pmcraid_identify_hrrq(struct pmcraid_cmd *cmd) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 10488c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 10498c2ecf20Sopenharmony_ci int index = cmd->hrrq_index; 10508c2ecf20Sopenharmony_ci __be64 hrrq_addr = cpu_to_be64(pinstance->hrrq_start_bus_addr[index]); 10518c2ecf20Sopenharmony_ci __be32 hrrq_size = cpu_to_be32(sizeof(u32) * PMCRAID_MAX_CMD); 10528c2ecf20Sopenharmony_ci void (*done_function)(struct pmcraid_cmd *); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 10558c2ecf20Sopenharmony_ci cmd->hrrq_index = index + 1; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (cmd->hrrq_index < pinstance->num_hrrq) { 10588c2ecf20Sopenharmony_ci done_function = pmcraid_identify_hrrq; 10598c2ecf20Sopenharmony_ci } else { 10608c2ecf20Sopenharmony_ci cmd->hrrq_index = 0; 10618c2ecf20Sopenharmony_ci done_function = pmcraid_get_fwversion; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Initialize ioarcb */ 10658c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_IOACMD; 10668c2ecf20Sopenharmony_ci ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* initialize the hrrq number where IOA will respond to this command */ 10698c2ecf20Sopenharmony_ci ioarcb->hrrq_id = index; 10708c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_IDENTIFY_HRRQ; 10718c2ecf20Sopenharmony_ci ioarcb->cdb[1] = index; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* IOA expects 64-bit pci address to be written in B.E format 10748c2ecf20Sopenharmony_ci * (i.e cdb[2]=MSByte..cdb[9]=LSB. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci pmcraid_info("HRRQ_IDENTIFY with hrrq:ioarcb:index => %llx:%llx:%x\n", 10778c2ecf20Sopenharmony_ci hrrq_addr, ioarcb->ioarcb_bus_addr, index); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci memcpy(&(ioarcb->cdb[2]), &hrrq_addr, sizeof(hrrq_addr)); 10808c2ecf20Sopenharmony_ci memcpy(&(ioarcb->cdb[10]), &hrrq_size, sizeof(hrrq_size)); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* Subsequent commands require HRRQ identification to be successful. 10838c2ecf20Sopenharmony_ci * Note that this gets called even during reset from SCSI mid-layer 10848c2ecf20Sopenharmony_ci * or tasklet 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, done_function, 10878c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, 10888c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic void pmcraid_process_ccn(struct pmcraid_cmd *cmd); 10928c2ecf20Sopenharmony_cistatic void pmcraid_process_ldn(struct pmcraid_cmd *cmd); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/** 10958c2ecf20Sopenharmony_ci * pmcraid_send_hcam_cmd - send an initialized command block(HCAM) to IOA 10968c2ecf20Sopenharmony_ci * 10978c2ecf20Sopenharmony_ci * @cmd: initialized command block pointer 10988c2ecf20Sopenharmony_ci * 10998c2ecf20Sopenharmony_ci * Return Value 11008c2ecf20Sopenharmony_ci * none 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_cistatic void pmcraid_send_hcam_cmd(struct pmcraid_cmd *cmd) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci if (cmd->ioa_cb->ioarcb.cdb[1] == PMCRAID_HCAM_CODE_CONFIG_CHANGE) 11058c2ecf20Sopenharmony_ci atomic_set(&(cmd->drv_inst->ccn.ignore), 0); 11068c2ecf20Sopenharmony_ci else 11078c2ecf20Sopenharmony_ci atomic_set(&(cmd->drv_inst->ldn.ignore), 0); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, cmd->cmd_done, 0, NULL); 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci/** 11138c2ecf20Sopenharmony_ci * pmcraid_init_hcam - send an initialized command block(HCAM) to IOA 11148c2ecf20Sopenharmony_ci * 11158c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 11168c2ecf20Sopenharmony_ci * @type: HCAM type 11178c2ecf20Sopenharmony_ci * 11188c2ecf20Sopenharmony_ci * Return Value 11198c2ecf20Sopenharmony_ci * pointer to initialized pmcraid_cmd structure or NULL 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_cistatic struct pmcraid_cmd *pmcraid_init_hcam 11228c2ecf20Sopenharmony_ci( 11238c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 11248c2ecf20Sopenharmony_ci u8 type 11258c2ecf20Sopenharmony_ci) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 11288c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb; 11298c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 11308c2ecf20Sopenharmony_ci struct pmcraid_hostrcb *hcam; 11318c2ecf20Sopenharmony_ci void (*cmd_done) (struct pmcraid_cmd *); 11328c2ecf20Sopenharmony_ci dma_addr_t dma; 11338c2ecf20Sopenharmony_ci int rcb_size; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci cmd = pmcraid_get_free_cmd(pinstance); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (!cmd) { 11388c2ecf20Sopenharmony_ci pmcraid_err("no free command blocks for hcam\n"); 11398c2ecf20Sopenharmony_ci return cmd; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (type == PMCRAID_HCAM_CODE_CONFIG_CHANGE) { 11438c2ecf20Sopenharmony_ci rcb_size = sizeof(struct pmcraid_hcam_ccn_ext); 11448c2ecf20Sopenharmony_ci cmd_done = pmcraid_process_ccn; 11458c2ecf20Sopenharmony_ci dma = pinstance->ccn.baddr + PMCRAID_AEN_HDR_SIZE; 11468c2ecf20Sopenharmony_ci hcam = &pinstance->ccn; 11478c2ecf20Sopenharmony_ci } else { 11488c2ecf20Sopenharmony_ci rcb_size = sizeof(struct pmcraid_hcam_ldn); 11498c2ecf20Sopenharmony_ci cmd_done = pmcraid_process_ldn; 11508c2ecf20Sopenharmony_ci dma = pinstance->ldn.baddr + PMCRAID_AEN_HDR_SIZE; 11518c2ecf20Sopenharmony_ci hcam = &pinstance->ldn; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* initialize command pointer used for HCAM registration */ 11558c2ecf20Sopenharmony_ci hcam->cmd = cmd; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci ioarcb = &cmd->ioa_cb->ioarcb; 11588c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + 11598c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 11608c2ecf20Sopenharmony_ci add_data.u.ioadl[0])); 11618c2ecf20Sopenharmony_ci ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); 11628c2ecf20Sopenharmony_ci ioadl = ioarcb->add_data.u.ioadl; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* Initialize ioarcb */ 11658c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_HCAM; 11668c2ecf20Sopenharmony_ci ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 11678c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_HOST_CONTROLLED_ASYNC; 11688c2ecf20Sopenharmony_ci ioarcb->cdb[1] = type; 11698c2ecf20Sopenharmony_ci ioarcb->cdb[7] = (rcb_size >> 8) & 0xFF; 11708c2ecf20Sopenharmony_ci ioarcb->cdb[8] = (rcb_size) & 0xFF; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = cpu_to_le32(rcb_size); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci ioadl[0].flags |= IOADL_FLAGS_READ_LAST; 11758c2ecf20Sopenharmony_ci ioadl[0].data_len = cpu_to_le32(rcb_size); 11768c2ecf20Sopenharmony_ci ioadl[0].address = cpu_to_le64(dma); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci cmd->cmd_done = cmd_done; 11798c2ecf20Sopenharmony_ci return cmd; 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci/** 11838c2ecf20Sopenharmony_ci * pmcraid_send_hcam - Send an HCAM to IOA 11848c2ecf20Sopenharmony_ci * @pinstance: ioa config struct 11858c2ecf20Sopenharmony_ci * @type: HCAM type 11868c2ecf20Sopenharmony_ci * 11878c2ecf20Sopenharmony_ci * This function will send a Host Controlled Async command to IOA. 11888c2ecf20Sopenharmony_ci * 11898c2ecf20Sopenharmony_ci * Return value: 11908c2ecf20Sopenharmony_ci * none 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_cistatic void pmcraid_send_hcam(struct pmcraid_instance *pinstance, u8 type) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd = pmcraid_init_hcam(pinstance, type); 11958c2ecf20Sopenharmony_ci pmcraid_send_hcam_cmd(cmd); 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/** 12008c2ecf20Sopenharmony_ci * pmcraid_prepare_cancel_cmd - prepares a command block to abort another 12018c2ecf20Sopenharmony_ci * 12028c2ecf20Sopenharmony_ci * @cmd: pointer to cmd that is used as cancelling command 12038c2ecf20Sopenharmony_ci * @cmd_to_cancel: pointer to the command that needs to be cancelled 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_cistatic void pmcraid_prepare_cancel_cmd( 12068c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd, 12078c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd_to_cancel 12088c2ecf20Sopenharmony_ci) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 12118c2ecf20Sopenharmony_ci __be64 ioarcb_addr; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* IOARCB address of the command to be cancelled is given in 12148c2ecf20Sopenharmony_ci * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in 12158c2ecf20Sopenharmony_ci * IOARCB address are not masked. 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci ioarcb_addr = cpu_to_be64(le64_to_cpu(cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr)); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* Get the resource handle to where the command to be aborted has been 12208c2ecf20Sopenharmony_ci * sent. 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_ci ioarcb->resource_handle = cmd_to_cancel->ioa_cb->ioarcb.resource_handle; 12238c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_IOACMD; 12248c2ecf20Sopenharmony_ci memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN); 12258c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_ABORT_CMD; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci memcpy(&(ioarcb->cdb[2]), &ioarcb_addr, sizeof(ioarcb_addr)); 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/** 12318c2ecf20Sopenharmony_ci * pmcraid_cancel_hcam - sends ABORT task to abort a given HCAM 12328c2ecf20Sopenharmony_ci * 12338c2ecf20Sopenharmony_ci * @cmd: command to be used as cancelling command 12348c2ecf20Sopenharmony_ci * @type: HCAM type 12358c2ecf20Sopenharmony_ci * @cmd_done: op done function for the cancelling command 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_cistatic void pmcraid_cancel_hcam( 12388c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd, 12398c2ecf20Sopenharmony_ci u8 type, 12408c2ecf20Sopenharmony_ci void (*cmd_done) (struct pmcraid_cmd *) 12418c2ecf20Sopenharmony_ci) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 12448c2ecf20Sopenharmony_ci struct pmcraid_hostrcb *hcam; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci pinstance = cmd->drv_inst; 12478c2ecf20Sopenharmony_ci hcam = (type == PMCRAID_HCAM_CODE_LOG_DATA) ? 12488c2ecf20Sopenharmony_ci &pinstance->ldn : &pinstance->ccn; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* prepare for cancelling previous hcam command. If the HCAM is 12518c2ecf20Sopenharmony_ci * currently not pending with IOA, we would have hcam->cmd as non-null 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_ci if (hcam->cmd == NULL) 12548c2ecf20Sopenharmony_ci return; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci pmcraid_prepare_cancel_cmd(cmd, hcam->cmd); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci /* writing to IOARRIN must be protected by host_lock, as mid-layer 12598c2ecf20Sopenharmony_ci * schedule queuecommand while we are doing this 12608c2ecf20Sopenharmony_ci */ 12618c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, cmd_done, 12628c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, 12638c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci/** 12678c2ecf20Sopenharmony_ci * pmcraid_cancel_ccn - cancel CCN HCAM already registered with IOA 12688c2ecf20Sopenharmony_ci * 12698c2ecf20Sopenharmony_ci * @cmd: command block to be used for cancelling the HCAM 12708c2ecf20Sopenharmony_ci */ 12718c2ecf20Sopenharmony_cistatic void pmcraid_cancel_ccn(struct pmcraid_cmd *cmd) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci pmcraid_info("response for Cancel LDN CDB[0] = %x ioasc = %x\n", 12748c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 12758c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci pmcraid_cancel_hcam(cmd, 12808c2ecf20Sopenharmony_ci PMCRAID_HCAM_CODE_CONFIG_CHANGE, 12818c2ecf20Sopenharmony_ci pmcraid_ioa_shutdown); 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci/** 12858c2ecf20Sopenharmony_ci * pmcraid_cancel_ldn - cancel LDN HCAM already registered with IOA 12868c2ecf20Sopenharmony_ci * 12878c2ecf20Sopenharmony_ci * @cmd: command block to be used for cancelling the HCAM 12888c2ecf20Sopenharmony_ci */ 12898c2ecf20Sopenharmony_cistatic void pmcraid_cancel_ldn(struct pmcraid_cmd *cmd) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci pmcraid_cancel_hcam(cmd, 12928c2ecf20Sopenharmony_ci PMCRAID_HCAM_CODE_LOG_DATA, 12938c2ecf20Sopenharmony_ci pmcraid_cancel_ccn); 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci/** 12978c2ecf20Sopenharmony_ci * pmcraid_expose_resource - check if the resource can be exposed to OS 12988c2ecf20Sopenharmony_ci * 12998c2ecf20Sopenharmony_ci * @fw_version: firmware version code 13008c2ecf20Sopenharmony_ci * @cfgte: pointer to configuration table entry of the resource 13018c2ecf20Sopenharmony_ci * 13028c2ecf20Sopenharmony_ci * Return value: 13038c2ecf20Sopenharmony_ci * true if resource can be added to midlayer, false(0) otherwise 13048c2ecf20Sopenharmony_ci */ 13058c2ecf20Sopenharmony_cistatic int pmcraid_expose_resource(u16 fw_version, 13068c2ecf20Sopenharmony_ci struct pmcraid_config_table_entry *cfgte) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci int retval = 0; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (cfgte->resource_type == RES_TYPE_VSET) { 13118c2ecf20Sopenharmony_ci if (fw_version <= PMCRAID_FW_VERSION_1) 13128c2ecf20Sopenharmony_ci retval = ((cfgte->unique_flags1 & 0x80) == 0); 13138c2ecf20Sopenharmony_ci else 13148c2ecf20Sopenharmony_ci retval = ((cfgte->unique_flags0 & 0x80) == 0 && 13158c2ecf20Sopenharmony_ci (cfgte->unique_flags1 & 0x80) == 0); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci } else if (cfgte->resource_type == RES_TYPE_GSCSI) 13188c2ecf20Sopenharmony_ci retval = (RES_BUS(cfgte->resource_address) != 13198c2ecf20Sopenharmony_ci PMCRAID_VIRTUAL_ENCL_BUS_ID); 13208c2ecf20Sopenharmony_ci return retval; 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci/* attributes supported by pmcraid_event_family */ 13248c2ecf20Sopenharmony_cienum { 13258c2ecf20Sopenharmony_ci PMCRAID_AEN_ATTR_UNSPEC, 13268c2ecf20Sopenharmony_ci PMCRAID_AEN_ATTR_EVENT, 13278c2ecf20Sopenharmony_ci __PMCRAID_AEN_ATTR_MAX, 13288c2ecf20Sopenharmony_ci}; 13298c2ecf20Sopenharmony_ci#define PMCRAID_AEN_ATTR_MAX (__PMCRAID_AEN_ATTR_MAX - 1) 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci/* commands supported by pmcraid_event_family */ 13328c2ecf20Sopenharmony_cienum { 13338c2ecf20Sopenharmony_ci PMCRAID_AEN_CMD_UNSPEC, 13348c2ecf20Sopenharmony_ci PMCRAID_AEN_CMD_EVENT, 13358c2ecf20Sopenharmony_ci __PMCRAID_AEN_CMD_MAX, 13368c2ecf20Sopenharmony_ci}; 13378c2ecf20Sopenharmony_ci#define PMCRAID_AEN_CMD_MAX (__PMCRAID_AEN_CMD_MAX - 1) 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic struct genl_multicast_group pmcraid_mcgrps[] = { 13408c2ecf20Sopenharmony_ci { .name = "events", /* not really used - see ID discussion below */ }, 13418c2ecf20Sopenharmony_ci}; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic struct genl_family pmcraid_event_family __ro_after_init = { 13448c2ecf20Sopenharmony_ci .module = THIS_MODULE, 13458c2ecf20Sopenharmony_ci .name = "pmcraid", 13468c2ecf20Sopenharmony_ci .version = 1, 13478c2ecf20Sopenharmony_ci .maxattr = PMCRAID_AEN_ATTR_MAX, 13488c2ecf20Sopenharmony_ci .mcgrps = pmcraid_mcgrps, 13498c2ecf20Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(pmcraid_mcgrps), 13508c2ecf20Sopenharmony_ci}; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci/** 13538c2ecf20Sopenharmony_ci * pmcraid_netlink_init - registers pmcraid_event_family 13548c2ecf20Sopenharmony_ci * 13558c2ecf20Sopenharmony_ci * Return value: 13568c2ecf20Sopenharmony_ci * 0 if the pmcraid_event_family is successfully registered 13578c2ecf20Sopenharmony_ci * with netlink generic, non-zero otherwise 13588c2ecf20Sopenharmony_ci */ 13598c2ecf20Sopenharmony_cistatic int __init pmcraid_netlink_init(void) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci int result; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci result = genl_register_family(&pmcraid_event_family); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (result) 13668c2ecf20Sopenharmony_ci return result; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci pmcraid_info("registered NETLINK GENERIC group: %d\n", 13698c2ecf20Sopenharmony_ci pmcraid_event_family.id); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci return result; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci/** 13758c2ecf20Sopenharmony_ci * pmcraid_netlink_release - unregisters pmcraid_event_family 13768c2ecf20Sopenharmony_ci * 13778c2ecf20Sopenharmony_ci * Return value: 13788c2ecf20Sopenharmony_ci * none 13798c2ecf20Sopenharmony_ci */ 13808c2ecf20Sopenharmony_cistatic void pmcraid_netlink_release(void) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci genl_unregister_family(&pmcraid_event_family); 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci/** 13868c2ecf20Sopenharmony_ci * pmcraid_notify_aen - sends event msg to user space application 13878c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 13888c2ecf20Sopenharmony_ci * @type: HCAM type 13898c2ecf20Sopenharmony_ci * 13908c2ecf20Sopenharmony_ci * Return value: 13918c2ecf20Sopenharmony_ci * 0 if success, error value in case of any failure. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_cistatic int pmcraid_notify_aen( 13948c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 13958c2ecf20Sopenharmony_ci struct pmcraid_aen_msg *aen_msg, 13968c2ecf20Sopenharmony_ci u32 data_size 13978c2ecf20Sopenharmony_ci) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci struct sk_buff *skb; 14008c2ecf20Sopenharmony_ci void *msg_header; 14018c2ecf20Sopenharmony_ci u32 total_size, nla_genl_hdr_total_size; 14028c2ecf20Sopenharmony_ci int result; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci aen_msg->hostno = (pinstance->host->unique_id << 16 | 14058c2ecf20Sopenharmony_ci MINOR(pinstance->cdev.dev)); 14068c2ecf20Sopenharmony_ci aen_msg->length = data_size; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci data_size += sizeof(*aen_msg); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci total_size = nla_total_size(data_size); 14118c2ecf20Sopenharmony_ci /* Add GENL_HDR to total_size */ 14128c2ecf20Sopenharmony_ci nla_genl_hdr_total_size = 14138c2ecf20Sopenharmony_ci (total_size + (GENL_HDRLEN + 14148c2ecf20Sopenharmony_ci ((struct genl_family *)&pmcraid_event_family)->hdrsize) 14158c2ecf20Sopenharmony_ci + NLMSG_HDRLEN); 14168c2ecf20Sopenharmony_ci skb = genlmsg_new(nla_genl_hdr_total_size, GFP_ATOMIC); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci if (!skb) { 14208c2ecf20Sopenharmony_ci pmcraid_err("Failed to allocate aen data SKB of size: %x\n", 14218c2ecf20Sopenharmony_ci total_size); 14228c2ecf20Sopenharmony_ci return -ENOMEM; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci /* add the genetlink message header */ 14268c2ecf20Sopenharmony_ci msg_header = genlmsg_put(skb, 0, 0, 14278c2ecf20Sopenharmony_ci &pmcraid_event_family, 0, 14288c2ecf20Sopenharmony_ci PMCRAID_AEN_CMD_EVENT); 14298c2ecf20Sopenharmony_ci if (!msg_header) { 14308c2ecf20Sopenharmony_ci pmcraid_err("failed to copy command details\n"); 14318c2ecf20Sopenharmony_ci nlmsg_free(skb); 14328c2ecf20Sopenharmony_ci return -ENOMEM; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci result = nla_put(skb, PMCRAID_AEN_ATTR_EVENT, data_size, aen_msg); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (result) { 14388c2ecf20Sopenharmony_ci pmcraid_err("failed to copy AEN attribute data\n"); 14398c2ecf20Sopenharmony_ci nlmsg_free(skb); 14408c2ecf20Sopenharmony_ci return -EINVAL; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci /* send genetlink multicast message to notify appplications */ 14448c2ecf20Sopenharmony_ci genlmsg_end(skb, msg_header); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci result = genlmsg_multicast(&pmcraid_event_family, skb, 14478c2ecf20Sopenharmony_ci 0, 0, GFP_ATOMIC); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* If there are no listeners, genlmsg_multicast may return non-zero 14508c2ecf20Sopenharmony_ci * value. 14518c2ecf20Sopenharmony_ci */ 14528c2ecf20Sopenharmony_ci if (result) 14538c2ecf20Sopenharmony_ci pmcraid_info("error (%x) sending aen event message\n", result); 14548c2ecf20Sopenharmony_ci return result; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci/** 14588c2ecf20Sopenharmony_ci * pmcraid_notify_ccn - notifies about CCN event msg to user space 14598c2ecf20Sopenharmony_ci * @pinstance: pointer adapter instance structure 14608c2ecf20Sopenharmony_ci * 14618c2ecf20Sopenharmony_ci * Return value: 14628c2ecf20Sopenharmony_ci * 0 if success, error value in case of any failure 14638c2ecf20Sopenharmony_ci */ 14648c2ecf20Sopenharmony_cistatic int pmcraid_notify_ccn(struct pmcraid_instance *pinstance) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci return pmcraid_notify_aen(pinstance, 14678c2ecf20Sopenharmony_ci pinstance->ccn.msg, 14688c2ecf20Sopenharmony_ci le32_to_cpu(pinstance->ccn.hcam->data_len) + 14698c2ecf20Sopenharmony_ci sizeof(struct pmcraid_hcam_hdr)); 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci/** 14738c2ecf20Sopenharmony_ci * pmcraid_notify_ldn - notifies about CCN event msg to user space 14748c2ecf20Sopenharmony_ci * @pinstance: pointer adapter instance structure 14758c2ecf20Sopenharmony_ci * 14768c2ecf20Sopenharmony_ci * Return value: 14778c2ecf20Sopenharmony_ci * 0 if success, error value in case of any failure 14788c2ecf20Sopenharmony_ci */ 14798c2ecf20Sopenharmony_cistatic int pmcraid_notify_ldn(struct pmcraid_instance *pinstance) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci return pmcraid_notify_aen(pinstance, 14828c2ecf20Sopenharmony_ci pinstance->ldn.msg, 14838c2ecf20Sopenharmony_ci le32_to_cpu(pinstance->ldn.hcam->data_len) + 14848c2ecf20Sopenharmony_ci sizeof(struct pmcraid_hcam_hdr)); 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci/** 14888c2ecf20Sopenharmony_ci * pmcraid_notify_ioastate - sends IOA state event msg to user space 14898c2ecf20Sopenharmony_ci * @pinstance: pointer adapter instance structure 14908c2ecf20Sopenharmony_ci * @evt: controller state event to be sent 14918c2ecf20Sopenharmony_ci * 14928c2ecf20Sopenharmony_ci * Return value: 14938c2ecf20Sopenharmony_ci * 0 if success, error value in case of any failure 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_cistatic void pmcraid_notify_ioastate(struct pmcraid_instance *pinstance, u32 evt) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci pinstance->scn.ioa_state = evt; 14988c2ecf20Sopenharmony_ci pmcraid_notify_aen(pinstance, 14998c2ecf20Sopenharmony_ci &pinstance->scn.msg, 15008c2ecf20Sopenharmony_ci sizeof(u32)); 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci/** 15048c2ecf20Sopenharmony_ci * pmcraid_handle_config_change - Handle a config change from the adapter 15058c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 15068c2ecf20Sopenharmony_ci * 15078c2ecf20Sopenharmony_ci * Return value: 15088c2ecf20Sopenharmony_ci * none 15098c2ecf20Sopenharmony_ci */ 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic void pmcraid_handle_config_change(struct pmcraid_instance *pinstance) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct pmcraid_config_table_entry *cfg_entry; 15148c2ecf20Sopenharmony_ci struct pmcraid_hcam_ccn *ccn_hcam; 15158c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 15168c2ecf20Sopenharmony_ci struct pmcraid_cmd *cfgcmd; 15178c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res = NULL; 15188c2ecf20Sopenharmony_ci unsigned long lock_flags; 15198c2ecf20Sopenharmony_ci unsigned long host_lock_flags; 15208c2ecf20Sopenharmony_ci u32 new_entry = 1; 15218c2ecf20Sopenharmony_ci u32 hidden_entry = 0; 15228c2ecf20Sopenharmony_ci u16 fw_version; 15238c2ecf20Sopenharmony_ci int rc; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci ccn_hcam = (struct pmcraid_hcam_ccn *)pinstance->ccn.hcam; 15268c2ecf20Sopenharmony_ci cfg_entry = &ccn_hcam->cfg_entry; 15278c2ecf20Sopenharmony_ci fw_version = be16_to_cpu(pinstance->inq_data->fw_version); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci pmcraid_info("CCN(%x): %x timestamp: %llx type: %x lost: %x flags: %x \ 15308c2ecf20Sopenharmony_ci res: %x:%x:%x:%x\n", 15318c2ecf20Sopenharmony_ci le32_to_cpu(pinstance->ccn.hcam->ilid), 15328c2ecf20Sopenharmony_ci pinstance->ccn.hcam->op_code, 15338c2ecf20Sopenharmony_ci (le32_to_cpu(pinstance->ccn.hcam->timestamp1) | 15348c2ecf20Sopenharmony_ci ((le32_to_cpu(pinstance->ccn.hcam->timestamp2) & 0xffffffffLL) << 32)), 15358c2ecf20Sopenharmony_ci pinstance->ccn.hcam->notification_type, 15368c2ecf20Sopenharmony_ci pinstance->ccn.hcam->notification_lost, 15378c2ecf20Sopenharmony_ci pinstance->ccn.hcam->flags, 15388c2ecf20Sopenharmony_ci pinstance->host->unique_id, 15398c2ecf20Sopenharmony_ci RES_IS_VSET(*cfg_entry) ? PMCRAID_VSET_BUS_ID : 15408c2ecf20Sopenharmony_ci (RES_IS_GSCSI(*cfg_entry) ? PMCRAID_PHYS_BUS_ID : 15418c2ecf20Sopenharmony_ci RES_BUS(cfg_entry->resource_address)), 15428c2ecf20Sopenharmony_ci RES_IS_VSET(*cfg_entry) ? 15438c2ecf20Sopenharmony_ci (fw_version <= PMCRAID_FW_VERSION_1 ? 15448c2ecf20Sopenharmony_ci cfg_entry->unique_flags1 : 15458c2ecf20Sopenharmony_ci le16_to_cpu(cfg_entry->array_id) & 0xFF) : 15468c2ecf20Sopenharmony_ci RES_TARGET(cfg_entry->resource_address), 15478c2ecf20Sopenharmony_ci RES_LUN(cfg_entry->resource_address)); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* If this HCAM indicates a lost notification, read the config table */ 15518c2ecf20Sopenharmony_ci if (pinstance->ccn.hcam->notification_lost) { 15528c2ecf20Sopenharmony_ci cfgcmd = pmcraid_get_free_cmd(pinstance); 15538c2ecf20Sopenharmony_ci if (cfgcmd) { 15548c2ecf20Sopenharmony_ci pmcraid_info("lost CCN, reading config table\b"); 15558c2ecf20Sopenharmony_ci pinstance->reinit_cfg_table = 1; 15568c2ecf20Sopenharmony_ci pmcraid_querycfg(cfgcmd); 15578c2ecf20Sopenharmony_ci } else { 15588c2ecf20Sopenharmony_ci pmcraid_err("lost CCN, no free cmd for querycfg\n"); 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci goto out_notify_apps; 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* If this resource is not going to be added to mid-layer, just notify 15648c2ecf20Sopenharmony_ci * applications and return. If this notification is about hiding a VSET 15658c2ecf20Sopenharmony_ci * resource, check if it was exposed already. 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci if (pinstance->ccn.hcam->notification_type == 15688c2ecf20Sopenharmony_ci NOTIFICATION_TYPE_ENTRY_CHANGED && 15698c2ecf20Sopenharmony_ci cfg_entry->resource_type == RES_TYPE_VSET) { 15708c2ecf20Sopenharmony_ci hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0; 15718c2ecf20Sopenharmony_ci } else if (!pmcraid_expose_resource(fw_version, cfg_entry)) { 15728c2ecf20Sopenharmony_ci goto out_notify_apps; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->resource_lock, lock_flags); 15768c2ecf20Sopenharmony_ci list_for_each_entry(res, &pinstance->used_res_q, queue) { 15778c2ecf20Sopenharmony_ci rc = memcmp(&res->cfg_entry.resource_address, 15788c2ecf20Sopenharmony_ci &cfg_entry->resource_address, 15798c2ecf20Sopenharmony_ci sizeof(cfg_entry->resource_address)); 15808c2ecf20Sopenharmony_ci if (!rc) { 15818c2ecf20Sopenharmony_ci new_entry = 0; 15828c2ecf20Sopenharmony_ci break; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (new_entry) { 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (hidden_entry) { 15898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, 15908c2ecf20Sopenharmony_ci lock_flags); 15918c2ecf20Sopenharmony_ci goto out_notify_apps; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci /* If there are more number of resources than what driver can 15958c2ecf20Sopenharmony_ci * manage, do not notify the applications about the CCN. Just 15968c2ecf20Sopenharmony_ci * ignore this notifications and re-register the same HCAM 15978c2ecf20Sopenharmony_ci */ 15988c2ecf20Sopenharmony_ci if (list_empty(&pinstance->free_res_q)) { 15998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, 16008c2ecf20Sopenharmony_ci lock_flags); 16018c2ecf20Sopenharmony_ci pmcraid_err("too many resources attached\n"); 16028c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, 16038c2ecf20Sopenharmony_ci host_lock_flags); 16048c2ecf20Sopenharmony_ci pmcraid_send_hcam(pinstance, 16058c2ecf20Sopenharmony_ci PMCRAID_HCAM_CODE_CONFIG_CHANGE); 16068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, 16078c2ecf20Sopenharmony_ci host_lock_flags); 16088c2ecf20Sopenharmony_ci return; 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci res = list_entry(pinstance->free_res_q.next, 16128c2ecf20Sopenharmony_ci struct pmcraid_resource_entry, queue); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci list_del(&res->queue); 16158c2ecf20Sopenharmony_ci res->scsi_dev = NULL; 16168c2ecf20Sopenharmony_ci res->reset_progress = 0; 16178c2ecf20Sopenharmony_ci list_add_tail(&res->queue, &pinstance->used_res_q); 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci memcpy(&res->cfg_entry, cfg_entry, pinstance->config_table_entry_size); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (pinstance->ccn.hcam->notification_type == 16238c2ecf20Sopenharmony_ci NOTIFICATION_TYPE_ENTRY_DELETED || hidden_entry) { 16248c2ecf20Sopenharmony_ci if (res->scsi_dev) { 16258c2ecf20Sopenharmony_ci if (fw_version <= PMCRAID_FW_VERSION_1) 16268c2ecf20Sopenharmony_ci res->cfg_entry.unique_flags1 &= 0x7F; 16278c2ecf20Sopenharmony_ci else 16288c2ecf20Sopenharmony_ci res->cfg_entry.array_id &= cpu_to_le16(0xFF); 16298c2ecf20Sopenharmony_ci res->change_detected = RES_CHANGE_DEL; 16308c2ecf20Sopenharmony_ci res->cfg_entry.resource_handle = 16318c2ecf20Sopenharmony_ci PMCRAID_INVALID_RES_HANDLE; 16328c2ecf20Sopenharmony_ci schedule_work(&pinstance->worker_q); 16338c2ecf20Sopenharmony_ci } else { 16348c2ecf20Sopenharmony_ci /* This may be one of the non-exposed resources */ 16358c2ecf20Sopenharmony_ci list_move_tail(&res->queue, &pinstance->free_res_q); 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci } else if (!res->scsi_dev) { 16388c2ecf20Sopenharmony_ci res->change_detected = RES_CHANGE_ADD; 16398c2ecf20Sopenharmony_ci schedule_work(&pinstance->worker_q); 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ciout_notify_apps: 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci /* Notify configuration changes to registered applications.*/ 16468c2ecf20Sopenharmony_ci if (!pmcraid_disable_aen) 16478c2ecf20Sopenharmony_ci pmcraid_notify_ccn(pinstance); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); 16508c2ecf20Sopenharmony_ci if (cmd) 16518c2ecf20Sopenharmony_ci pmcraid_send_hcam_cmd(cmd); 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci/** 16558c2ecf20Sopenharmony_ci * pmcraid_get_error_info - return error string for an ioasc 16568c2ecf20Sopenharmony_ci * @ioasc: ioasc code 16578c2ecf20Sopenharmony_ci * Return Value 16588c2ecf20Sopenharmony_ci * none 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_cistatic struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci int i; 16638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pmcraid_ioasc_error_table); i++) { 16648c2ecf20Sopenharmony_ci if (pmcraid_ioasc_error_table[i].ioasc_code == ioasc) 16658c2ecf20Sopenharmony_ci return &pmcraid_ioasc_error_table[i]; 16668c2ecf20Sopenharmony_ci } 16678c2ecf20Sopenharmony_ci return NULL; 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci/** 16718c2ecf20Sopenharmony_ci * pmcraid_ioasc_logger - log IOASC information based user-settings 16728c2ecf20Sopenharmony_ci * @ioasc: ioasc code 16738c2ecf20Sopenharmony_ci * @cmd: pointer to command that resulted in 'ioasc' 16748c2ecf20Sopenharmony_ci */ 16758c2ecf20Sopenharmony_cistatic void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci if (error_info == NULL || 16808c2ecf20Sopenharmony_ci cmd->drv_inst->current_log_level < error_info->log_level) 16818c2ecf20Sopenharmony_ci return; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* log the error string */ 16848c2ecf20Sopenharmony_ci pmcraid_err("cmd [%x] for resource %x failed with %x(%s)\n", 16858c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 16868c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle), 16878c2ecf20Sopenharmony_ci ioasc, error_info->error_string); 16888c2ecf20Sopenharmony_ci} 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci/** 16918c2ecf20Sopenharmony_ci * pmcraid_handle_error_log - Handle a config change (error log) from the IOA 16928c2ecf20Sopenharmony_ci * 16938c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 16948c2ecf20Sopenharmony_ci * 16958c2ecf20Sopenharmony_ci * Return value: 16968c2ecf20Sopenharmony_ci * none 16978c2ecf20Sopenharmony_ci */ 16988c2ecf20Sopenharmony_cistatic void pmcraid_handle_error_log(struct pmcraid_instance *pinstance) 16998c2ecf20Sopenharmony_ci{ 17008c2ecf20Sopenharmony_ci struct pmcraid_hcam_ldn *hcam_ldn; 17018c2ecf20Sopenharmony_ci u32 ioasc; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci hcam_ldn = (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci pmcraid_info 17068c2ecf20Sopenharmony_ci ("LDN(%x): %x type: %x lost: %x flags: %x overlay id: %x\n", 17078c2ecf20Sopenharmony_ci pinstance->ldn.hcam->ilid, 17088c2ecf20Sopenharmony_ci pinstance->ldn.hcam->op_code, 17098c2ecf20Sopenharmony_ci pinstance->ldn.hcam->notification_type, 17108c2ecf20Sopenharmony_ci pinstance->ldn.hcam->notification_lost, 17118c2ecf20Sopenharmony_ci pinstance->ldn.hcam->flags, 17128c2ecf20Sopenharmony_ci pinstance->ldn.hcam->overlay_id); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* log only the errors, no need to log informational log entries */ 17158c2ecf20Sopenharmony_ci if (pinstance->ldn.hcam->notification_type != 17168c2ecf20Sopenharmony_ci NOTIFICATION_TYPE_ERROR_LOG) 17178c2ecf20Sopenharmony_ci return; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (pinstance->ldn.hcam->notification_lost == 17208c2ecf20Sopenharmony_ci HOSTRCB_NOTIFICATIONS_LOST) 17218c2ecf20Sopenharmony_ci dev_info(&pinstance->pdev->dev, "Error notifications lost\n"); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci ioasc = le32_to_cpu(hcam_ldn->error_log.fd_ioasc); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET || 17268c2ecf20Sopenharmony_ci ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) { 17278c2ecf20Sopenharmony_ci dev_info(&pinstance->pdev->dev, 17288c2ecf20Sopenharmony_ci "UnitAttention due to IOA Bus Reset\n"); 17298c2ecf20Sopenharmony_ci scsi_report_bus_reset( 17308c2ecf20Sopenharmony_ci pinstance->host, 17318c2ecf20Sopenharmony_ci RES_BUS(hcam_ldn->error_log.fd_ra)); 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci return; 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci/** 17388c2ecf20Sopenharmony_ci * pmcraid_process_ccn - Op done function for a CCN. 17398c2ecf20Sopenharmony_ci * @cmd: pointer to command struct 17408c2ecf20Sopenharmony_ci * 17418c2ecf20Sopenharmony_ci * This function is the op done function for a configuration 17428c2ecf20Sopenharmony_ci * change notification 17438c2ecf20Sopenharmony_ci * 17448c2ecf20Sopenharmony_ci * Return value: 17458c2ecf20Sopenharmony_ci * none 17468c2ecf20Sopenharmony_ci */ 17478c2ecf20Sopenharmony_cistatic void pmcraid_process_ccn(struct pmcraid_cmd *cmd) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 17508c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); 17518c2ecf20Sopenharmony_ci unsigned long lock_flags; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci pinstance->ccn.cmd = NULL; 17548c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* If driver initiated IOA reset happened while this hcam was pending 17578c2ecf20Sopenharmony_ci * with IOA, or IOA bringdown sequence is in progress, no need to 17588c2ecf20Sopenharmony_ci * re-register the hcam 17598c2ecf20Sopenharmony_ci */ 17608c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET || 17618c2ecf20Sopenharmony_ci atomic_read(&pinstance->ccn.ignore) == 1) { 17628c2ecf20Sopenharmony_ci return; 17638c2ecf20Sopenharmony_ci } else if (ioasc) { 17648c2ecf20Sopenharmony_ci dev_info(&pinstance->pdev->dev, 17658c2ecf20Sopenharmony_ci "Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc); 17668c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 17678c2ecf20Sopenharmony_ci pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); 17688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 17698c2ecf20Sopenharmony_ci } else { 17708c2ecf20Sopenharmony_ci pmcraid_handle_config_change(pinstance); 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci/** 17758c2ecf20Sopenharmony_ci * pmcraid_process_ldn - op done function for an LDN 17768c2ecf20Sopenharmony_ci * @cmd: pointer to command block 17778c2ecf20Sopenharmony_ci * 17788c2ecf20Sopenharmony_ci * Return value 17798c2ecf20Sopenharmony_ci * none 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_cistatic void pmcraid_initiate_reset(struct pmcraid_instance *); 17828c2ecf20Sopenharmony_cistatic void pmcraid_set_timestamp(struct pmcraid_cmd *cmd); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_cistatic void pmcraid_process_ldn(struct pmcraid_cmd *cmd) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 17878c2ecf20Sopenharmony_ci struct pmcraid_hcam_ldn *ldn_hcam = 17888c2ecf20Sopenharmony_ci (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam; 17898c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); 17908c2ecf20Sopenharmony_ci u32 fd_ioasc = le32_to_cpu(ldn_hcam->error_log.fd_ioasc); 17918c2ecf20Sopenharmony_ci unsigned long lock_flags; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* return the command block back to freepool */ 17948c2ecf20Sopenharmony_ci pinstance->ldn.cmd = NULL; 17958c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci /* If driver initiated IOA reset happened while this hcam was pending 17988c2ecf20Sopenharmony_ci * with IOA, no need to re-register the hcam as reset engine will do it 17998c2ecf20Sopenharmony_ci * once reset sequence is complete 18008c2ecf20Sopenharmony_ci */ 18018c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET || 18028c2ecf20Sopenharmony_ci atomic_read(&pinstance->ccn.ignore) == 1) { 18038c2ecf20Sopenharmony_ci return; 18048c2ecf20Sopenharmony_ci } else if (!ioasc) { 18058c2ecf20Sopenharmony_ci pmcraid_handle_error_log(pinstance); 18068c2ecf20Sopenharmony_ci if (fd_ioasc == PMCRAID_IOASC_NR_IOA_RESET_REQUIRED) { 18078c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, 18088c2ecf20Sopenharmony_ci lock_flags); 18098c2ecf20Sopenharmony_ci pmcraid_initiate_reset(pinstance); 18108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, 18118c2ecf20Sopenharmony_ci lock_flags); 18128c2ecf20Sopenharmony_ci return; 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci if (fd_ioasc == PMCRAID_IOASC_TIME_STAMP_OUT_OF_SYNC) { 18158c2ecf20Sopenharmony_ci pinstance->timestamp_error = 1; 18168c2ecf20Sopenharmony_ci pmcraid_set_timestamp(cmd); 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci } else { 18198c2ecf20Sopenharmony_ci dev_info(&pinstance->pdev->dev, 18208c2ecf20Sopenharmony_ci "Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc); 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci /* send netlink message for HCAM notification if enabled */ 18238c2ecf20Sopenharmony_ci if (!pmcraid_disable_aen) 18248c2ecf20Sopenharmony_ci pmcraid_notify_ldn(pinstance); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA); 18278c2ecf20Sopenharmony_ci if (cmd) 18288c2ecf20Sopenharmony_ci pmcraid_send_hcam_cmd(cmd); 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci/** 18328c2ecf20Sopenharmony_ci * pmcraid_register_hcams - register HCAMs for CCN and LDN 18338c2ecf20Sopenharmony_ci * 18348c2ecf20Sopenharmony_ci * @pinstance: pointer per adapter instance structure 18358c2ecf20Sopenharmony_ci * 18368c2ecf20Sopenharmony_ci * Return Value 18378c2ecf20Sopenharmony_ci * none 18388c2ecf20Sopenharmony_ci */ 18398c2ecf20Sopenharmony_cistatic void pmcraid_register_hcams(struct pmcraid_instance *pinstance) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); 18428c2ecf20Sopenharmony_ci pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA); 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci/** 18468c2ecf20Sopenharmony_ci * pmcraid_unregister_hcams - cancel HCAMs registered already 18478c2ecf20Sopenharmony_ci * @cmd: pointer to command used as part of reset sequence 18488c2ecf20Sopenharmony_ci */ 18498c2ecf20Sopenharmony_cistatic void pmcraid_unregister_hcams(struct pmcraid_cmd *cmd) 18508c2ecf20Sopenharmony_ci{ 18518c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci /* During IOA bringdown, HCAM gets fired and tasklet proceeds with 18548c2ecf20Sopenharmony_ci * handling hcam response though it is not necessary. In order to 18558c2ecf20Sopenharmony_ci * prevent this, set 'ignore', so that bring-down sequence doesn't 18568c2ecf20Sopenharmony_ci * re-send any more hcams 18578c2ecf20Sopenharmony_ci */ 18588c2ecf20Sopenharmony_ci atomic_set(&pinstance->ccn.ignore, 1); 18598c2ecf20Sopenharmony_ci atomic_set(&pinstance->ldn.ignore, 1); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci /* If adapter reset was forced as part of runtime reset sequence, 18628c2ecf20Sopenharmony_ci * start the reset sequence. Reset will be triggered even in case 18638c2ecf20Sopenharmony_ci * IOA unit_check. 18648c2ecf20Sopenharmony_ci */ 18658c2ecf20Sopenharmony_ci if ((pinstance->force_ioa_reset && !pinstance->ioa_bringdown) || 18668c2ecf20Sopenharmony_ci pinstance->ioa_unit_check) { 18678c2ecf20Sopenharmony_ci pinstance->force_ioa_reset = 0; 18688c2ecf20Sopenharmony_ci pinstance->ioa_unit_check = 0; 18698c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; 18708c2ecf20Sopenharmony_ci pmcraid_reset_alert(cmd); 18718c2ecf20Sopenharmony_ci return; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* Driver tries to cancel HCAMs by sending ABORT TASK for each HCAM 18758c2ecf20Sopenharmony_ci * one after the other. So CCN cancellation will be triggered by 18768c2ecf20Sopenharmony_ci * pmcraid_cancel_ldn itself. 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_ci pmcraid_cancel_ldn(cmd); 18798c2ecf20Sopenharmony_ci} 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci/** 18828c2ecf20Sopenharmony_ci * pmcraid_reset_enable_ioa - re-enable IOA after a hard reset 18838c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 18848c2ecf20Sopenharmony_ci * Return Value 18858c2ecf20Sopenharmony_ci * 1 if TRANSITION_TO_OPERATIONAL is active, otherwise 0 18868c2ecf20Sopenharmony_ci */ 18878c2ecf20Sopenharmony_cistatic void pmcraid_reinit_buffers(struct pmcraid_instance *); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic int pmcraid_reset_enable_ioa(struct pmcraid_instance *pinstance) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci u32 intrs; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci pmcraid_reinit_buffers(pinstance); 18948c2ecf20Sopenharmony_ci intrs = pmcraid_read_interrupts(pinstance); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) { 18998c2ecf20Sopenharmony_ci if (!pinstance->interrupt_mode) { 19008c2ecf20Sopenharmony_ci iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, 19018c2ecf20Sopenharmony_ci pinstance->int_regs. 19028c2ecf20Sopenharmony_ci ioa_host_interrupt_mask_reg); 19038c2ecf20Sopenharmony_ci iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, 19048c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_clr_reg); 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci return 1; 19078c2ecf20Sopenharmony_ci } else { 19088c2ecf20Sopenharmony_ci return 0; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci/** 19138c2ecf20Sopenharmony_ci * pmcraid_soft_reset - performs a soft reset and makes IOA become ready 19148c2ecf20Sopenharmony_ci * @cmd : pointer to reset command block 19158c2ecf20Sopenharmony_ci * 19168c2ecf20Sopenharmony_ci * Return Value 19178c2ecf20Sopenharmony_ci * none 19188c2ecf20Sopenharmony_ci */ 19198c2ecf20Sopenharmony_cistatic void pmcraid_soft_reset(struct pmcraid_cmd *cmd) 19208c2ecf20Sopenharmony_ci{ 19218c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 19228c2ecf20Sopenharmony_ci u32 int_reg; 19238c2ecf20Sopenharmony_ci u32 doorbell; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci /* There will be an interrupt when Transition to Operational bit is 19268c2ecf20Sopenharmony_ci * set so tasklet would execute next reset task. The timeout handler 19278c2ecf20Sopenharmony_ci * would re-initiate a reset 19288c2ecf20Sopenharmony_ci */ 19298c2ecf20Sopenharmony_ci cmd->cmd_done = pmcraid_ioa_reset; 19308c2ecf20Sopenharmony_ci cmd->timer.expires = jiffies + 19318c2ecf20Sopenharmony_ci msecs_to_jiffies(PMCRAID_TRANSOP_TIMEOUT); 19328c2ecf20Sopenharmony_ci cmd->timer.function = pmcraid_timeout_handler; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (!timer_pending(&cmd->timer)) 19358c2ecf20Sopenharmony_ci add_timer(&cmd->timer); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci /* Enable destructive diagnostics on IOA if it is not yet in 19388c2ecf20Sopenharmony_ci * operational state 19398c2ecf20Sopenharmony_ci */ 19408c2ecf20Sopenharmony_ci doorbell = DOORBELL_RUNTIME_RESET | 19418c2ecf20Sopenharmony_ci DOORBELL_ENABLE_DESTRUCTIVE_DIAGS; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci /* Since we do RESET_ALERT and Start BIST we have to again write 19448c2ecf20Sopenharmony_ci * MSIX Doorbell to indicate the interrupt mode 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_ci if (pinstance->interrupt_mode) { 19478c2ecf20Sopenharmony_ci iowrite32(DOORBELL_INTR_MODE_MSIX, 19488c2ecf20Sopenharmony_ci pinstance->int_regs.host_ioa_interrupt_reg); 19498c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.host_ioa_interrupt_reg); 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci iowrite32(doorbell, pinstance->int_regs.host_ioa_interrupt_reg); 19538c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.host_ioa_interrupt_reg), 19548c2ecf20Sopenharmony_ci int_reg = ioread32(pinstance->int_regs.ioa_host_interrupt_reg); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci pmcraid_info("Waiting for IOA to become operational %x:%x\n", 19578c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.host_ioa_interrupt_reg), 19588c2ecf20Sopenharmony_ci int_reg); 19598c2ecf20Sopenharmony_ci} 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci/** 19628c2ecf20Sopenharmony_ci * pmcraid_get_dump - retrieves IOA dump in case of Unit Check interrupt 19638c2ecf20Sopenharmony_ci * 19648c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 19658c2ecf20Sopenharmony_ci * 19668c2ecf20Sopenharmony_ci * Return Value 19678c2ecf20Sopenharmony_ci * none 19688c2ecf20Sopenharmony_ci */ 19698c2ecf20Sopenharmony_cistatic void pmcraid_get_dump(struct pmcraid_instance *pinstance) 19708c2ecf20Sopenharmony_ci{ 19718c2ecf20Sopenharmony_ci pmcraid_info("%s is not yet implemented\n", __func__); 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci/** 19758c2ecf20Sopenharmony_ci * pmcraid_fail_outstanding_cmds - Fails all outstanding ops. 19768c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 19778c2ecf20Sopenharmony_ci * 19788c2ecf20Sopenharmony_ci * This function fails all outstanding ops. If they are submitted to IOA 19798c2ecf20Sopenharmony_ci * already, it sends cancel all messages if IOA is still accepting IOARCBs, 19808c2ecf20Sopenharmony_ci * otherwise just completes the commands and returns the cmd blocks to free 19818c2ecf20Sopenharmony_ci * pool. 19828c2ecf20Sopenharmony_ci * 19838c2ecf20Sopenharmony_ci * Return value: 19848c2ecf20Sopenharmony_ci * none 19858c2ecf20Sopenharmony_ci */ 19868c2ecf20Sopenharmony_cistatic void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance) 19878c2ecf20Sopenharmony_ci{ 19888c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd, *temp; 19898c2ecf20Sopenharmony_ci unsigned long lock_flags; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci /* pending command list is protected by pending_pool_lock. Its 19928c2ecf20Sopenharmony_ci * traversal must be done as within this lock 19938c2ecf20Sopenharmony_ci */ 19948c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags); 19958c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, temp, &pinstance->pending_cmd_pool, 19968c2ecf20Sopenharmony_ci free_list) { 19978c2ecf20Sopenharmony_ci list_del(&cmd->free_list); 19988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->pending_pool_lock, 19998c2ecf20Sopenharmony_ci lock_flags); 20008c2ecf20Sopenharmony_ci cmd->ioa_cb->ioasa.ioasc = 20018c2ecf20Sopenharmony_ci cpu_to_le32(PMCRAID_IOASC_IOA_WAS_RESET); 20028c2ecf20Sopenharmony_ci cmd->ioa_cb->ioasa.ilid = 20038c2ecf20Sopenharmony_ci cpu_to_le32(PMCRAID_DRIVER_ILID); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci /* In case the command timer is still running */ 20068c2ecf20Sopenharmony_ci del_timer(&cmd->timer); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* If this is an IO command, complete it by invoking scsi_done 20098c2ecf20Sopenharmony_ci * function. If this is one of the internal commands other 20108c2ecf20Sopenharmony_ci * than pmcraid_ioa_reset and HCAM commands invoke cmd_done to 20118c2ecf20Sopenharmony_ci * complete it 20128c2ecf20Sopenharmony_ci */ 20138c2ecf20Sopenharmony_ci if (cmd->scsi_cmd) { 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; 20168c2ecf20Sopenharmony_ci __le32 resp = cmd->ioa_cb->ioarcb.response_handle; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci scsi_cmd->result |= DID_ERROR << 16; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci scsi_dma_unmap(scsi_cmd); 20218c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci pmcraid_info("failing(%d) CDB[0] = %x result: %x\n", 20248c2ecf20Sopenharmony_ci le32_to_cpu(resp) >> 2, 20258c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 20268c2ecf20Sopenharmony_ci scsi_cmd->result); 20278c2ecf20Sopenharmony_ci scsi_cmd->scsi_done(scsi_cmd); 20288c2ecf20Sopenharmony_ci } else if (cmd->cmd_done == pmcraid_internal_done || 20298c2ecf20Sopenharmony_ci cmd->cmd_done == pmcraid_erp_done) { 20308c2ecf20Sopenharmony_ci cmd->cmd_done(cmd); 20318c2ecf20Sopenharmony_ci } else if (cmd->cmd_done != pmcraid_ioa_reset && 20328c2ecf20Sopenharmony_ci cmd->cmd_done != pmcraid_ioa_shutdown_done) { 20338c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 20348c2ecf20Sopenharmony_ci } 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci atomic_dec(&pinstance->outstanding_cmds); 20378c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags); 20388c2ecf20Sopenharmony_ci } 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags); 20418c2ecf20Sopenharmony_ci} 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci/** 20448c2ecf20Sopenharmony_ci * pmcraid_ioa_reset - Implementation of IOA reset logic 20458c2ecf20Sopenharmony_ci * 20468c2ecf20Sopenharmony_ci * @cmd: pointer to the cmd block to be used for entire reset process 20478c2ecf20Sopenharmony_ci * 20488c2ecf20Sopenharmony_ci * This function executes most of the steps required for IOA reset. This gets 20498c2ecf20Sopenharmony_ci * called by user threads (modprobe/insmod/rmmod) timer, tasklet and midlayer's 20508c2ecf20Sopenharmony_ci * 'eh_' thread. Access to variables used for controlling the reset sequence is 20518c2ecf20Sopenharmony_ci * synchronized using host lock. Various functions called during reset process 20528c2ecf20Sopenharmony_ci * would make use of a single command block, pointer to which is also stored in 20538c2ecf20Sopenharmony_ci * adapter instance structure. 20548c2ecf20Sopenharmony_ci * 20558c2ecf20Sopenharmony_ci * Return Value 20568c2ecf20Sopenharmony_ci * None 20578c2ecf20Sopenharmony_ci */ 20588c2ecf20Sopenharmony_cistatic void pmcraid_ioa_reset(struct pmcraid_cmd *cmd) 20598c2ecf20Sopenharmony_ci{ 20608c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 20618c2ecf20Sopenharmony_ci u8 reset_complete = 0; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci pinstance->ioa_reset_in_progress = 1; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci if (pinstance->reset_cmd != cmd) { 20668c2ecf20Sopenharmony_ci pmcraid_err("reset is called with different command block\n"); 20678c2ecf20Sopenharmony_ci pinstance->reset_cmd = cmd; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci pmcraid_info("reset_engine: state = %d, command = %p\n", 20718c2ecf20Sopenharmony_ci pinstance->ioa_state, cmd); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci switch (pinstance->ioa_state) { 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci case IOA_STATE_DEAD: 20768c2ecf20Sopenharmony_ci /* If IOA is offline, whatever may be the reset reason, just 20778c2ecf20Sopenharmony_ci * return. callers might be waiting on the reset wait_q, wake 20788c2ecf20Sopenharmony_ci * up them 20798c2ecf20Sopenharmony_ci */ 20808c2ecf20Sopenharmony_ci pmcraid_err("IOA is offline no reset is possible\n"); 20818c2ecf20Sopenharmony_ci reset_complete = 1; 20828c2ecf20Sopenharmony_ci break; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci case IOA_STATE_IN_BRINGDOWN: 20858c2ecf20Sopenharmony_ci /* we enter here, once ioa shutdown command is processed by IOA 20868c2ecf20Sopenharmony_ci * Alert IOA for a possible reset. If reset alert fails, IOA 20878c2ecf20Sopenharmony_ci * goes through hard-reset 20888c2ecf20Sopenharmony_ci */ 20898c2ecf20Sopenharmony_ci pmcraid_disable_interrupts(pinstance, ~0); 20908c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; 20918c2ecf20Sopenharmony_ci pmcraid_reset_alert(cmd); 20928c2ecf20Sopenharmony_ci break; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci case IOA_STATE_UNKNOWN: 20958c2ecf20Sopenharmony_ci /* We may be called during probe or resume. Some pre-processing 20968c2ecf20Sopenharmony_ci * is required for prior to reset 20978c2ecf20Sopenharmony_ci */ 20988c2ecf20Sopenharmony_ci scsi_block_requests(pinstance->host); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci /* If asked to reset while IOA was processing responses or 21018c2ecf20Sopenharmony_ci * there are any error responses then IOA may require 21028c2ecf20Sopenharmony_ci * hard-reset. 21038c2ecf20Sopenharmony_ci */ 21048c2ecf20Sopenharmony_ci if (pinstance->ioa_hard_reset == 0) { 21058c2ecf20Sopenharmony_ci if (ioread32(pinstance->ioa_status) & 21068c2ecf20Sopenharmony_ci INTRS_TRANSITION_TO_OPERATIONAL) { 21078c2ecf20Sopenharmony_ci pmcraid_info("sticky bit set, bring-up\n"); 21088c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_BRINGUP; 21098c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 21108c2ecf20Sopenharmony_ci pmcraid_identify_hrrq(cmd); 21118c2ecf20Sopenharmony_ci } else { 21128c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET; 21138c2ecf20Sopenharmony_ci pmcraid_soft_reset(cmd); 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci } else { 21168c2ecf20Sopenharmony_ci /* Alert IOA of a possible reset and wait for critical 21178c2ecf20Sopenharmony_ci * operation in progress bit to reset 21188c2ecf20Sopenharmony_ci */ 21198c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; 21208c2ecf20Sopenharmony_ci pmcraid_reset_alert(cmd); 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci break; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci case IOA_STATE_IN_RESET_ALERT: 21258c2ecf20Sopenharmony_ci /* If critical operation in progress bit is reset or wait gets 21268c2ecf20Sopenharmony_ci * timed out, reset proceeds with starting BIST on the IOA. 21278c2ecf20Sopenharmony_ci * pmcraid_ioa_hard_reset keeps a count of reset attempts. If 21288c2ecf20Sopenharmony_ci * they are 3 or more, reset engine marks IOA dead and returns 21298c2ecf20Sopenharmony_ci */ 21308c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_HARD_RESET; 21318c2ecf20Sopenharmony_ci pmcraid_start_bist(cmd); 21328c2ecf20Sopenharmony_ci break; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci case IOA_STATE_IN_HARD_RESET: 21358c2ecf20Sopenharmony_ci pinstance->ioa_reset_attempts++; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci /* retry reset if we haven't reached maximum allowed limit */ 21388c2ecf20Sopenharmony_ci if (pinstance->ioa_reset_attempts > PMCRAID_RESET_ATTEMPTS) { 21398c2ecf20Sopenharmony_ci pinstance->ioa_reset_attempts = 0; 21408c2ecf20Sopenharmony_ci pmcraid_err("IOA didn't respond marking it as dead\n"); 21418c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_DEAD; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if (pinstance->ioa_bringdown) 21448c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, 21458c2ecf20Sopenharmony_ci PMC_DEVICE_EVENT_SHUTDOWN_FAILED); 21468c2ecf20Sopenharmony_ci else 21478c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, 21488c2ecf20Sopenharmony_ci PMC_DEVICE_EVENT_RESET_FAILED); 21498c2ecf20Sopenharmony_ci reset_complete = 1; 21508c2ecf20Sopenharmony_ci break; 21518c2ecf20Sopenharmony_ci } 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci /* Once either bist or pci reset is done, restore PCI config 21548c2ecf20Sopenharmony_ci * space. If this fails, proceed with hard reset again 21558c2ecf20Sopenharmony_ci */ 21568c2ecf20Sopenharmony_ci pci_restore_state(pinstance->pdev); 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci /* fail all pending commands */ 21598c2ecf20Sopenharmony_ci pmcraid_fail_outstanding_cmds(pinstance); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci /* check if unit check is active, if so extract dump */ 21628c2ecf20Sopenharmony_ci if (pinstance->ioa_unit_check) { 21638c2ecf20Sopenharmony_ci pmcraid_info("unit check is active\n"); 21648c2ecf20Sopenharmony_ci pinstance->ioa_unit_check = 0; 21658c2ecf20Sopenharmony_ci pmcraid_get_dump(pinstance); 21668c2ecf20Sopenharmony_ci pinstance->ioa_reset_attempts--; 21678c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; 21688c2ecf20Sopenharmony_ci pmcraid_reset_alert(cmd); 21698c2ecf20Sopenharmony_ci break; 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* if the reset reason is to bring-down the ioa, we might be 21738c2ecf20Sopenharmony_ci * done with the reset restore pci_config_space and complete 21748c2ecf20Sopenharmony_ci * the reset 21758c2ecf20Sopenharmony_ci */ 21768c2ecf20Sopenharmony_ci if (pinstance->ioa_bringdown) { 21778c2ecf20Sopenharmony_ci pmcraid_info("bringing down the adapter\n"); 21788c2ecf20Sopenharmony_ci pinstance->ioa_shutdown_type = SHUTDOWN_NONE; 21798c2ecf20Sopenharmony_ci pinstance->ioa_bringdown = 0; 21808c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_UNKNOWN; 21818c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, 21828c2ecf20Sopenharmony_ci PMC_DEVICE_EVENT_SHUTDOWN_SUCCESS); 21838c2ecf20Sopenharmony_ci reset_complete = 1; 21848c2ecf20Sopenharmony_ci } else { 21858c2ecf20Sopenharmony_ci /* bring-up IOA, so proceed with soft reset 21868c2ecf20Sopenharmony_ci * Reinitialize hrrq_buffers and their indices also 21878c2ecf20Sopenharmony_ci * enable interrupts after a pci_restore_state 21888c2ecf20Sopenharmony_ci */ 21898c2ecf20Sopenharmony_ci if (pmcraid_reset_enable_ioa(pinstance)) { 21908c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_BRINGUP; 21918c2ecf20Sopenharmony_ci pmcraid_info("bringing up the adapter\n"); 21928c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 21938c2ecf20Sopenharmony_ci pmcraid_identify_hrrq(cmd); 21948c2ecf20Sopenharmony_ci } else { 21958c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET; 21968c2ecf20Sopenharmony_ci pmcraid_soft_reset(cmd); 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci } 21998c2ecf20Sopenharmony_ci break; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci case IOA_STATE_IN_SOFT_RESET: 22028c2ecf20Sopenharmony_ci /* TRANSITION TO OPERATIONAL is on so start initialization 22038c2ecf20Sopenharmony_ci * sequence 22048c2ecf20Sopenharmony_ci */ 22058c2ecf20Sopenharmony_ci pmcraid_info("In softreset proceeding with bring-up\n"); 22068c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_BRINGUP; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci /* Initialization commands start with HRRQ identification. From 22098c2ecf20Sopenharmony_ci * now on tasklet completes most of the commands as IOA is up 22108c2ecf20Sopenharmony_ci * and intrs are enabled 22118c2ecf20Sopenharmony_ci */ 22128c2ecf20Sopenharmony_ci pmcraid_identify_hrrq(cmd); 22138c2ecf20Sopenharmony_ci break; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci case IOA_STATE_IN_BRINGUP: 22168c2ecf20Sopenharmony_ci /* we are done with bringing up of IOA, change the ioa_state to 22178c2ecf20Sopenharmony_ci * operational and wake up any waiters 22188c2ecf20Sopenharmony_ci */ 22198c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_OPERATIONAL; 22208c2ecf20Sopenharmony_ci reset_complete = 1; 22218c2ecf20Sopenharmony_ci break; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci case IOA_STATE_OPERATIONAL: 22248c2ecf20Sopenharmony_ci default: 22258c2ecf20Sopenharmony_ci /* When IOA is operational and a reset is requested, check for 22268c2ecf20Sopenharmony_ci * the reset reason. If reset is to bring down IOA, unregister 22278c2ecf20Sopenharmony_ci * HCAMs and initiate shutdown; if adapter reset is forced then 22288c2ecf20Sopenharmony_ci * restart reset sequence again 22298c2ecf20Sopenharmony_ci */ 22308c2ecf20Sopenharmony_ci if (pinstance->ioa_shutdown_type == SHUTDOWN_NONE && 22318c2ecf20Sopenharmony_ci pinstance->force_ioa_reset == 0) { 22328c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, 22338c2ecf20Sopenharmony_ci PMC_DEVICE_EVENT_RESET_SUCCESS); 22348c2ecf20Sopenharmony_ci reset_complete = 1; 22358c2ecf20Sopenharmony_ci } else { 22368c2ecf20Sopenharmony_ci if (pinstance->ioa_shutdown_type != SHUTDOWN_NONE) 22378c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_IN_BRINGDOWN; 22388c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 22398c2ecf20Sopenharmony_ci pmcraid_unregister_hcams(cmd); 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci break; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci /* reset will be completed if ioa_state is either DEAD or UNKNOWN or 22458c2ecf20Sopenharmony_ci * OPERATIONAL. Reset all control variables used during reset, wake up 22468c2ecf20Sopenharmony_ci * any waiting threads and let the SCSI mid-layer send commands. Note 22478c2ecf20Sopenharmony_ci * that host_lock must be held before invoking scsi_report_bus_reset. 22488c2ecf20Sopenharmony_ci */ 22498c2ecf20Sopenharmony_ci if (reset_complete) { 22508c2ecf20Sopenharmony_ci pinstance->ioa_reset_in_progress = 0; 22518c2ecf20Sopenharmony_ci pinstance->ioa_reset_attempts = 0; 22528c2ecf20Sopenharmony_ci pinstance->reset_cmd = NULL; 22538c2ecf20Sopenharmony_ci pinstance->ioa_shutdown_type = SHUTDOWN_NONE; 22548c2ecf20Sopenharmony_ci pinstance->ioa_bringdown = 0; 22558c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci /* If target state is to bring up the adapter, proceed with 22588c2ecf20Sopenharmony_ci * hcam registration and resource exposure to mid-layer. 22598c2ecf20Sopenharmony_ci */ 22608c2ecf20Sopenharmony_ci if (pinstance->ioa_state == IOA_STATE_OPERATIONAL) 22618c2ecf20Sopenharmony_ci pmcraid_register_hcams(pinstance); 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci wake_up_all(&pinstance->reset_wait_q); 22648c2ecf20Sopenharmony_ci } 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci return; 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci/** 22708c2ecf20Sopenharmony_ci * pmcraid_initiate_reset - initiates reset sequence. This is called from 22718c2ecf20Sopenharmony_ci * ISR/tasklet during error interrupts including IOA unit check. If reset 22728c2ecf20Sopenharmony_ci * is already in progress, it just returns, otherwise initiates IOA reset 22738c2ecf20Sopenharmony_ci * to bring IOA up to operational state. 22748c2ecf20Sopenharmony_ci * 22758c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 22768c2ecf20Sopenharmony_ci * 22778c2ecf20Sopenharmony_ci * Return value 22788c2ecf20Sopenharmony_ci * none 22798c2ecf20Sopenharmony_ci */ 22808c2ecf20Sopenharmony_cistatic void pmcraid_initiate_reset(struct pmcraid_instance *pinstance) 22818c2ecf20Sopenharmony_ci{ 22828c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci /* If the reset is already in progress, just return, otherwise start 22858c2ecf20Sopenharmony_ci * reset sequence and return 22868c2ecf20Sopenharmony_ci */ 22878c2ecf20Sopenharmony_ci if (!pinstance->ioa_reset_in_progress) { 22888c2ecf20Sopenharmony_ci scsi_block_requests(pinstance->host); 22898c2ecf20Sopenharmony_ci cmd = pmcraid_get_free_cmd(pinstance); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci if (cmd == NULL) { 22928c2ecf20Sopenharmony_ci pmcraid_err("no cmnd blocks for initiate_reset\n"); 22938c2ecf20Sopenharmony_ci return; 22948c2ecf20Sopenharmony_ci } 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci pinstance->ioa_shutdown_type = SHUTDOWN_NONE; 22978c2ecf20Sopenharmony_ci pinstance->reset_cmd = cmd; 22988c2ecf20Sopenharmony_ci pinstance->force_ioa_reset = 1; 22998c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, 23008c2ecf20Sopenharmony_ci PMC_DEVICE_EVENT_RESET_START); 23018c2ecf20Sopenharmony_ci pmcraid_ioa_reset(cmd); 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ci} 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci/** 23068c2ecf20Sopenharmony_ci * pmcraid_reset_reload - utility routine for doing IOA reset either to bringup 23078c2ecf20Sopenharmony_ci * or bringdown IOA 23088c2ecf20Sopenharmony_ci * @pinstance: pointer adapter instance structure 23098c2ecf20Sopenharmony_ci * @shutdown_type: shutdown type to be used NONE, NORMAL or ABRREV 23108c2ecf20Sopenharmony_ci * @target_state: expected target state after reset 23118c2ecf20Sopenharmony_ci * 23128c2ecf20Sopenharmony_ci * Note: This command initiates reset and waits for its completion. Hence this 23138c2ecf20Sopenharmony_ci * should not be called from isr/timer/tasklet functions (timeout handlers, 23148c2ecf20Sopenharmony_ci * error response handlers and interrupt handlers). 23158c2ecf20Sopenharmony_ci * 23168c2ecf20Sopenharmony_ci * Return Value 23178c2ecf20Sopenharmony_ci * 1 in case ioa_state is not target_state, 0 otherwise. 23188c2ecf20Sopenharmony_ci */ 23198c2ecf20Sopenharmony_cistatic int pmcraid_reset_reload( 23208c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 23218c2ecf20Sopenharmony_ci u8 shutdown_type, 23228c2ecf20Sopenharmony_ci u8 target_state 23238c2ecf20Sopenharmony_ci) 23248c2ecf20Sopenharmony_ci{ 23258c2ecf20Sopenharmony_ci struct pmcraid_cmd *reset_cmd = NULL; 23268c2ecf20Sopenharmony_ci unsigned long lock_flags; 23278c2ecf20Sopenharmony_ci int reset = 1; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (pinstance->ioa_reset_in_progress) { 23328c2ecf20Sopenharmony_ci pmcraid_info("reset_reload: reset is already in progress\n"); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci wait_event(pinstance->reset_wait_q, 23378c2ecf20Sopenharmony_ci !pinstance->ioa_reset_in_progress); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci if (pinstance->ioa_state == IOA_STATE_DEAD) { 23428c2ecf20Sopenharmony_ci pmcraid_info("reset_reload: IOA is dead\n"); 23438c2ecf20Sopenharmony_ci goto out_unlock; 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (pinstance->ioa_state == target_state) { 23478c2ecf20Sopenharmony_ci reset = 0; 23488c2ecf20Sopenharmony_ci goto out_unlock; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci pmcraid_info("reset_reload: proceeding with reset\n"); 23538c2ecf20Sopenharmony_ci scsi_block_requests(pinstance->host); 23548c2ecf20Sopenharmony_ci reset_cmd = pmcraid_get_free_cmd(pinstance); 23558c2ecf20Sopenharmony_ci if (reset_cmd == NULL) { 23568c2ecf20Sopenharmony_ci pmcraid_err("no free cmnd for reset_reload\n"); 23578c2ecf20Sopenharmony_ci goto out_unlock; 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci if (shutdown_type == SHUTDOWN_NORMAL) 23618c2ecf20Sopenharmony_ci pinstance->ioa_bringdown = 1; 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci pinstance->ioa_shutdown_type = shutdown_type; 23648c2ecf20Sopenharmony_ci pinstance->reset_cmd = reset_cmd; 23658c2ecf20Sopenharmony_ci pinstance->force_ioa_reset = reset; 23668c2ecf20Sopenharmony_ci pmcraid_info("reset_reload: initiating reset\n"); 23678c2ecf20Sopenharmony_ci pmcraid_ioa_reset(reset_cmd); 23688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 23698c2ecf20Sopenharmony_ci pmcraid_info("reset_reload: waiting for reset to complete\n"); 23708c2ecf20Sopenharmony_ci wait_event(pinstance->reset_wait_q, 23718c2ecf20Sopenharmony_ci !pinstance->ioa_reset_in_progress); 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci pmcraid_info("reset_reload: reset is complete !!\n"); 23748c2ecf20Sopenharmony_ci scsi_unblock_requests(pinstance->host); 23758c2ecf20Sopenharmony_ci return pinstance->ioa_state != target_state; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ciout_unlock: 23788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 23798c2ecf20Sopenharmony_ci return reset; 23808c2ecf20Sopenharmony_ci} 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci/** 23838c2ecf20Sopenharmony_ci * pmcraid_reset_bringdown - wrapper over pmcraid_reset_reload to bringdown IOA 23848c2ecf20Sopenharmony_ci * 23858c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 23868c2ecf20Sopenharmony_ci * 23878c2ecf20Sopenharmony_ci * Return Value 23888c2ecf20Sopenharmony_ci * whatever is returned from pmcraid_reset_reload 23898c2ecf20Sopenharmony_ci */ 23908c2ecf20Sopenharmony_cistatic int pmcraid_reset_bringdown(struct pmcraid_instance *pinstance) 23918c2ecf20Sopenharmony_ci{ 23928c2ecf20Sopenharmony_ci return pmcraid_reset_reload(pinstance, 23938c2ecf20Sopenharmony_ci SHUTDOWN_NORMAL, 23948c2ecf20Sopenharmony_ci IOA_STATE_UNKNOWN); 23958c2ecf20Sopenharmony_ci} 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci/** 23988c2ecf20Sopenharmony_ci * pmcraid_reset_bringup - wrapper over pmcraid_reset_reload to bring up IOA 23998c2ecf20Sopenharmony_ci * 24008c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 24018c2ecf20Sopenharmony_ci * 24028c2ecf20Sopenharmony_ci * Return Value 24038c2ecf20Sopenharmony_ci * whatever is returned from pmcraid_reset_reload 24048c2ecf20Sopenharmony_ci */ 24058c2ecf20Sopenharmony_cistatic int pmcraid_reset_bringup(struct pmcraid_instance *pinstance) 24068c2ecf20Sopenharmony_ci{ 24078c2ecf20Sopenharmony_ci pmcraid_notify_ioastate(pinstance, PMC_DEVICE_EVENT_RESET_START); 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci return pmcraid_reset_reload(pinstance, 24108c2ecf20Sopenharmony_ci SHUTDOWN_NONE, 24118c2ecf20Sopenharmony_ci IOA_STATE_OPERATIONAL); 24128c2ecf20Sopenharmony_ci} 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci/** 24158c2ecf20Sopenharmony_ci * pmcraid_request_sense - Send request sense to a device 24168c2ecf20Sopenharmony_ci * @cmd: pmcraid command struct 24178c2ecf20Sopenharmony_ci * 24188c2ecf20Sopenharmony_ci * This function sends a request sense to a device as a result of a check 24198c2ecf20Sopenharmony_ci * condition. This method re-uses the same command block that failed earlier. 24208c2ecf20Sopenharmony_ci */ 24218c2ecf20Sopenharmony_cistatic void pmcraid_request_sense(struct pmcraid_cmd *cmd) 24228c2ecf20Sopenharmony_ci{ 24238c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 24248c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; 24258c2ecf20Sopenharmony_ci struct device *dev = &cmd->drv_inst->pdev->dev; 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_ci cmd->sense_buffer = cmd->scsi_cmd->sense_buffer; 24288c2ecf20Sopenharmony_ci cmd->sense_buffer_dma = dma_map_single(dev, cmd->sense_buffer, 24298c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 24308c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, cmd->sense_buffer_dma)) { 24318c2ecf20Sopenharmony_ci pmcraid_err 24328c2ecf20Sopenharmony_ci ("couldn't allocate sense buffer for request sense\n"); 24338c2ecf20Sopenharmony_ci pmcraid_erp_done(cmd); 24348c2ecf20Sopenharmony_ci return; 24358c2ecf20Sopenharmony_ci } 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci /* re-use the command block */ 24388c2ecf20Sopenharmony_ci memset(&cmd->ioa_cb->ioasa, 0, sizeof(struct pmcraid_ioasa)); 24398c2ecf20Sopenharmony_ci memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN); 24408c2ecf20Sopenharmony_ci ioarcb->request_flags0 = (SYNC_COMPLETE | 24418c2ecf20Sopenharmony_ci NO_LINK_DESCS | 24428c2ecf20Sopenharmony_ci INHIBIT_UL_CHECK); 24438c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_SCSI; 24448c2ecf20Sopenharmony_ci ioarcb->cdb[0] = REQUEST_SENSE; 24458c2ecf20Sopenharmony_ci ioarcb->cdb[4] = SCSI_SENSE_BUFFERSIZE; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + 24488c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 24498c2ecf20Sopenharmony_ci add_data.u.ioadl[0])); 24508c2ecf20Sopenharmony_ci ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = cpu_to_le32(SCSI_SENSE_BUFFERSIZE); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci ioadl->address = cpu_to_le64(cmd->sense_buffer_dma); 24558c2ecf20Sopenharmony_ci ioadl->data_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE); 24568c2ecf20Sopenharmony_ci ioadl->flags = IOADL_FLAGS_LAST_DESC; 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci /* request sense might be called as part of error response processing 24598c2ecf20Sopenharmony_ci * which runs in tasklets context. It is possible that mid-layer might 24608c2ecf20Sopenharmony_ci * schedule queuecommand during this time, hence, writting to IOARRIN 24618c2ecf20Sopenharmony_ci * must be protect by host_lock 24628c2ecf20Sopenharmony_ci */ 24638c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, pmcraid_erp_done, 24648c2ecf20Sopenharmony_ci PMCRAID_REQUEST_SENSE_TIMEOUT, 24658c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci/** 24698c2ecf20Sopenharmony_ci * pmcraid_cancel_all - cancel all outstanding IOARCBs as part of error recovery 24708c2ecf20Sopenharmony_ci * @cmd: command that failed 24718c2ecf20Sopenharmony_ci * @need_sense: true if request_sense is required after cancel all 24728c2ecf20Sopenharmony_ci * 24738c2ecf20Sopenharmony_ci * This function sends a cancel all to a device to clear the queue. 24748c2ecf20Sopenharmony_ci */ 24758c2ecf20Sopenharmony_cistatic void pmcraid_cancel_all(struct pmcraid_cmd *cmd, bool need_sense) 24768c2ecf20Sopenharmony_ci{ 24778c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; 24788c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 24798c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN); 24828c2ecf20Sopenharmony_ci ioarcb->request_flags0 = SYNC_OVERRIDE; 24838c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_IOACMD; 24848c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_CANCEL_ALL_REQUESTS; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci if (RES_IS_GSCSI(res->cfg_entry)) 24878c2ecf20Sopenharmony_ci ioarcb->cdb[1] = PMCRAID_SYNC_COMPLETE_AFTER_CANCEL; 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = 0; 24908c2ecf20Sopenharmony_ci ioarcb->ioadl_length = 0; 24918c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = 0; 24928c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr &= cpu_to_le64((~0x1FULL)); 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci /* writing to IOARRIN must be protected by host_lock, as mid-layer 24958c2ecf20Sopenharmony_ci * schedule queuecommand while we are doing this 24968c2ecf20Sopenharmony_ci */ 24978c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, need_sense ? 24988c2ecf20Sopenharmony_ci pmcraid_erp_done : pmcraid_request_sense, 24998c2ecf20Sopenharmony_ci PMCRAID_REQUEST_SENSE_TIMEOUT, 25008c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 25018c2ecf20Sopenharmony_ci} 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci/** 25048c2ecf20Sopenharmony_ci * pmcraid_frame_auto_sense: frame fixed format sense information 25058c2ecf20Sopenharmony_ci * 25068c2ecf20Sopenharmony_ci * @cmd: pointer to failing command block 25078c2ecf20Sopenharmony_ci * 25088c2ecf20Sopenharmony_ci * Return value 25098c2ecf20Sopenharmony_ci * none 25108c2ecf20Sopenharmony_ci */ 25118c2ecf20Sopenharmony_cistatic void pmcraid_frame_auto_sense(struct pmcraid_cmd *cmd) 25128c2ecf20Sopenharmony_ci{ 25138c2ecf20Sopenharmony_ci u8 *sense_buf = cmd->scsi_cmd->sense_buffer; 25148c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res = cmd->scsi_cmd->device->hostdata; 25158c2ecf20Sopenharmony_ci struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa; 25168c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(ioasa->ioasc); 25178c2ecf20Sopenharmony_ci u32 failing_lba = 0; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE); 25208c2ecf20Sopenharmony_ci cmd->scsi_cmd->result = SAM_STAT_CHECK_CONDITION; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci if (RES_IS_VSET(res->cfg_entry) && 25238c2ecf20Sopenharmony_ci ioasc == PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC && 25248c2ecf20Sopenharmony_ci ioasa->u.vset.failing_lba_hi != 0) { 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci sense_buf[0] = 0x72; 25278c2ecf20Sopenharmony_ci sense_buf[1] = PMCRAID_IOASC_SENSE_KEY(ioasc); 25288c2ecf20Sopenharmony_ci sense_buf[2] = PMCRAID_IOASC_SENSE_CODE(ioasc); 25298c2ecf20Sopenharmony_ci sense_buf[3] = PMCRAID_IOASC_SENSE_QUAL(ioasc); 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci sense_buf[7] = 12; 25328c2ecf20Sopenharmony_ci sense_buf[8] = 0; 25338c2ecf20Sopenharmony_ci sense_buf[9] = 0x0A; 25348c2ecf20Sopenharmony_ci sense_buf[10] = 0x80; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci failing_lba = le32_to_cpu(ioasa->u.vset.failing_lba_hi); 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci sense_buf[12] = (failing_lba & 0xff000000) >> 24; 25398c2ecf20Sopenharmony_ci sense_buf[13] = (failing_lba & 0x00ff0000) >> 16; 25408c2ecf20Sopenharmony_ci sense_buf[14] = (failing_lba & 0x0000ff00) >> 8; 25418c2ecf20Sopenharmony_ci sense_buf[15] = failing_lba & 0x000000ff; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci failing_lba = le32_to_cpu(ioasa->u.vset.failing_lba_lo); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci sense_buf[16] = (failing_lba & 0xff000000) >> 24; 25468c2ecf20Sopenharmony_ci sense_buf[17] = (failing_lba & 0x00ff0000) >> 16; 25478c2ecf20Sopenharmony_ci sense_buf[18] = (failing_lba & 0x0000ff00) >> 8; 25488c2ecf20Sopenharmony_ci sense_buf[19] = failing_lba & 0x000000ff; 25498c2ecf20Sopenharmony_ci } else { 25508c2ecf20Sopenharmony_ci sense_buf[0] = 0x70; 25518c2ecf20Sopenharmony_ci sense_buf[2] = PMCRAID_IOASC_SENSE_KEY(ioasc); 25528c2ecf20Sopenharmony_ci sense_buf[12] = PMCRAID_IOASC_SENSE_CODE(ioasc); 25538c2ecf20Sopenharmony_ci sense_buf[13] = PMCRAID_IOASC_SENSE_QUAL(ioasc); 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC) { 25568c2ecf20Sopenharmony_ci if (RES_IS_VSET(res->cfg_entry)) 25578c2ecf20Sopenharmony_ci failing_lba = 25588c2ecf20Sopenharmony_ci le32_to_cpu(ioasa->u. 25598c2ecf20Sopenharmony_ci vset.failing_lba_lo); 25608c2ecf20Sopenharmony_ci sense_buf[0] |= 0x80; 25618c2ecf20Sopenharmony_ci sense_buf[3] = (failing_lba >> 24) & 0xff; 25628c2ecf20Sopenharmony_ci sense_buf[4] = (failing_lba >> 16) & 0xff; 25638c2ecf20Sopenharmony_ci sense_buf[5] = (failing_lba >> 8) & 0xff; 25648c2ecf20Sopenharmony_ci sense_buf[6] = failing_lba & 0xff; 25658c2ecf20Sopenharmony_ci } 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci sense_buf[7] = 6; /* additional length */ 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci} 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci/** 25728c2ecf20Sopenharmony_ci * pmcraid_error_handler - Error response handlers for a SCSI op 25738c2ecf20Sopenharmony_ci * @cmd: pointer to pmcraid_cmd that has failed 25748c2ecf20Sopenharmony_ci * 25758c2ecf20Sopenharmony_ci * This function determines whether or not to initiate ERP on the affected 25768c2ecf20Sopenharmony_ci * device. This is called from a tasklet, which doesn't hold any locks. 25778c2ecf20Sopenharmony_ci * 25788c2ecf20Sopenharmony_ci * Return value: 25798c2ecf20Sopenharmony_ci * 0 it caller can complete the request, otherwise 1 where in error 25808c2ecf20Sopenharmony_ci * handler itself completes the request and returns the command block 25818c2ecf20Sopenharmony_ci * back to free-pool 25828c2ecf20Sopenharmony_ci */ 25838c2ecf20Sopenharmony_cistatic int pmcraid_error_handler(struct pmcraid_cmd *cmd) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; 25868c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata; 25878c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 25888c2ecf20Sopenharmony_ci struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa; 25898c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(ioasa->ioasc); 25908c2ecf20Sopenharmony_ci u32 masked_ioasc = ioasc & PMCRAID_IOASC_SENSE_MASK; 25918c2ecf20Sopenharmony_ci bool sense_copied = false; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci if (!res) { 25948c2ecf20Sopenharmony_ci pmcraid_info("resource pointer is NULL\n"); 25958c2ecf20Sopenharmony_ci return 0; 25968c2ecf20Sopenharmony_ci } 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci /* If this was a SCSI read/write command keep count of errors */ 25998c2ecf20Sopenharmony_ci if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_READ_CMD) 26008c2ecf20Sopenharmony_ci atomic_inc(&res->read_failures); 26018c2ecf20Sopenharmony_ci else if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_WRITE_CMD) 26028c2ecf20Sopenharmony_ci atomic_inc(&res->write_failures); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci if (!RES_IS_GSCSI(res->cfg_entry) && 26058c2ecf20Sopenharmony_ci masked_ioasc != PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR) { 26068c2ecf20Sopenharmony_ci pmcraid_frame_auto_sense(cmd); 26078c2ecf20Sopenharmony_ci } 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci /* Log IOASC/IOASA information based on user settings */ 26108c2ecf20Sopenharmony_ci pmcraid_ioasc_logger(ioasc, cmd); 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci switch (masked_ioasc) { 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci case PMCRAID_IOASC_AC_TERMINATED_BY_HOST: 26158c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_ABORT << 16); 26168c2ecf20Sopenharmony_ci break; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci case PMCRAID_IOASC_IR_INVALID_RESOURCE_HANDLE: 26198c2ecf20Sopenharmony_ci case PMCRAID_IOASC_HW_CANNOT_COMMUNICATE: 26208c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_NO_CONNECT << 16); 26218c2ecf20Sopenharmony_ci break; 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci case PMCRAID_IOASC_NR_SYNC_REQUIRED: 26248c2ecf20Sopenharmony_ci res->sync_reqd = 1; 26258c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_IMM_RETRY << 16); 26268c2ecf20Sopenharmony_ci break; 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci case PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC: 26298c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_PASSTHROUGH << 16); 26308c2ecf20Sopenharmony_ci break; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci case PMCRAID_IOASC_UA_BUS_WAS_RESET: 26338c2ecf20Sopenharmony_ci case PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER: 26348c2ecf20Sopenharmony_ci if (!res->reset_progress) 26358c2ecf20Sopenharmony_ci scsi_report_bus_reset(pinstance->host, 26368c2ecf20Sopenharmony_ci scsi_cmd->device->channel); 26378c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_ERROR << 16); 26388c2ecf20Sopenharmony_ci break; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci case PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR: 26418c2ecf20Sopenharmony_ci scsi_cmd->result |= PMCRAID_IOASC_SENSE_STATUS(ioasc); 26428c2ecf20Sopenharmony_ci res->sync_reqd = 1; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci /* if check_condition is not active return with error otherwise 26458c2ecf20Sopenharmony_ci * get/frame the sense buffer 26468c2ecf20Sopenharmony_ci */ 26478c2ecf20Sopenharmony_ci if (PMCRAID_IOASC_SENSE_STATUS(ioasc) != 26488c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION && 26498c2ecf20Sopenharmony_ci PMCRAID_IOASC_SENSE_STATUS(ioasc) != SAM_STAT_ACA_ACTIVE) 26508c2ecf20Sopenharmony_ci return 0; 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci /* If we have auto sense data as part of IOASA pass it to 26538c2ecf20Sopenharmony_ci * mid-layer 26548c2ecf20Sopenharmony_ci */ 26558c2ecf20Sopenharmony_ci if (ioasa->auto_sense_length != 0) { 26568c2ecf20Sopenharmony_ci short sense_len = le16_to_cpu(ioasa->auto_sense_length); 26578c2ecf20Sopenharmony_ci int data_size = min_t(u16, sense_len, 26588c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE); 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci memcpy(scsi_cmd->sense_buffer, 26618c2ecf20Sopenharmony_ci ioasa->sense_data, 26628c2ecf20Sopenharmony_ci data_size); 26638c2ecf20Sopenharmony_ci sense_copied = true; 26648c2ecf20Sopenharmony_ci } 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci if (RES_IS_GSCSI(res->cfg_entry)) 26678c2ecf20Sopenharmony_ci pmcraid_cancel_all(cmd, sense_copied); 26688c2ecf20Sopenharmony_ci else if (sense_copied) 26698c2ecf20Sopenharmony_ci pmcraid_erp_done(cmd); 26708c2ecf20Sopenharmony_ci else 26718c2ecf20Sopenharmony_ci pmcraid_request_sense(cmd); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci return 1; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci case PMCRAID_IOASC_NR_INIT_CMD_REQUIRED: 26768c2ecf20Sopenharmony_ci break; 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci default: 26798c2ecf20Sopenharmony_ci if (PMCRAID_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR) 26808c2ecf20Sopenharmony_ci scsi_cmd->result |= (DID_ERROR << 16); 26818c2ecf20Sopenharmony_ci break; 26828c2ecf20Sopenharmony_ci } 26838c2ecf20Sopenharmony_ci return 0; 26848c2ecf20Sopenharmony_ci} 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci/** 26878c2ecf20Sopenharmony_ci * pmcraid_reset_device - device reset handler functions 26888c2ecf20Sopenharmony_ci * 26898c2ecf20Sopenharmony_ci * @scsi_cmd: scsi command struct 26908c2ecf20Sopenharmony_ci * @modifier: reset modifier indicating the reset sequence to be performed 26918c2ecf20Sopenharmony_ci * 26928c2ecf20Sopenharmony_ci * This function issues a device reset to the affected device. 26938c2ecf20Sopenharmony_ci * A LUN reset will be sent to the device first. If that does 26948c2ecf20Sopenharmony_ci * not work, a target reset will be sent. 26958c2ecf20Sopenharmony_ci * 26968c2ecf20Sopenharmony_ci * Return value: 26978c2ecf20Sopenharmony_ci * SUCCESS / FAILED 26988c2ecf20Sopenharmony_ci */ 26998c2ecf20Sopenharmony_cistatic int pmcraid_reset_device( 27008c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd, 27018c2ecf20Sopenharmony_ci unsigned long timeout, 27028c2ecf20Sopenharmony_ci u8 modifier 27038c2ecf20Sopenharmony_ci) 27048c2ecf20Sopenharmony_ci{ 27058c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 27068c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 27078c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res; 27088c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb; 27098c2ecf20Sopenharmony_ci unsigned long lock_flags; 27108c2ecf20Sopenharmony_ci u32 ioasc; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci pinstance = 27138c2ecf20Sopenharmony_ci (struct pmcraid_instance *)scsi_cmd->device->host->hostdata; 27148c2ecf20Sopenharmony_ci res = scsi_cmd->device->hostdata; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci if (!res) { 27178c2ecf20Sopenharmony_ci sdev_printk(KERN_ERR, scsi_cmd->device, 27188c2ecf20Sopenharmony_ci "reset_device: NULL resource pointer\n"); 27198c2ecf20Sopenharmony_ci return FAILED; 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci /* If adapter is currently going through reset/reload, return failed. 27238c2ecf20Sopenharmony_ci * This will force the mid-layer to call _eh_bus/host reset, which 27248c2ecf20Sopenharmony_ci * will then go to sleep and wait for the reset to complete 27258c2ecf20Sopenharmony_ci */ 27268c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 27278c2ecf20Sopenharmony_ci if (pinstance->ioa_reset_in_progress || 27288c2ecf20Sopenharmony_ci pinstance->ioa_state == IOA_STATE_DEAD) { 27298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 27308c2ecf20Sopenharmony_ci return FAILED; 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci res->reset_progress = 1; 27348c2ecf20Sopenharmony_ci pmcraid_info("Resetting %s resource with addr %x\n", 27358c2ecf20Sopenharmony_ci ((modifier & RESET_DEVICE_LUN) ? "LUN" : 27368c2ecf20Sopenharmony_ci ((modifier & RESET_DEVICE_TARGET) ? "TARGET" : "BUS")), 27378c2ecf20Sopenharmony_ci le32_to_cpu(res->cfg_entry.resource_address)); 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci /* get a free cmd block */ 27408c2ecf20Sopenharmony_ci cmd = pmcraid_get_free_cmd(pinstance); 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci if (cmd == NULL) { 27438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 27448c2ecf20Sopenharmony_ci pmcraid_err("%s: no cmd blocks are available\n", __func__); 27458c2ecf20Sopenharmony_ci return FAILED; 27468c2ecf20Sopenharmony_ci } 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci ioarcb = &cmd->ioa_cb->ioarcb; 27498c2ecf20Sopenharmony_ci ioarcb->resource_handle = res->cfg_entry.resource_handle; 27508c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_IOACMD; 27518c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_RESET_DEVICE; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci /* Initialize reset modifier bits */ 27548c2ecf20Sopenharmony_ci if (modifier) 27558c2ecf20Sopenharmony_ci modifier = ENABLE_RESET_MODIFIER | modifier; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci ioarcb->cdb[1] = modifier; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci init_completion(&cmd->wait_for_completion); 27608c2ecf20Sopenharmony_ci cmd->completion_req = 1; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci pmcraid_info("cmd(CDB[0] = %x) for %x with index = %d\n", 27638c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 27648c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle), 27658c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2); 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, 27688c2ecf20Sopenharmony_ci pmcraid_internal_done, 27698c2ecf20Sopenharmony_ci timeout, 27708c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci /* RESET_DEVICE command completes after all pending IOARCBs are 27758c2ecf20Sopenharmony_ci * completed. Once this command is completed, pmcraind_internal_done 27768c2ecf20Sopenharmony_ci * will wake up the 'completion' queue. 27778c2ecf20Sopenharmony_ci */ 27788c2ecf20Sopenharmony_ci wait_for_completion(&cmd->wait_for_completion); 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci /* complete the command here itself and return the command block 27818c2ecf20Sopenharmony_ci * to free list 27828c2ecf20Sopenharmony_ci */ 27838c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 27848c2ecf20Sopenharmony_ci res->reset_progress = 0; 27858c2ecf20Sopenharmony_ci ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci /* set the return value based on the returned ioasc */ 27888c2ecf20Sopenharmony_ci return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS; 27898c2ecf20Sopenharmony_ci} 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci/** 27928c2ecf20Sopenharmony_ci * _pmcraid_io_done - helper for pmcraid_io_done function 27938c2ecf20Sopenharmony_ci * 27948c2ecf20Sopenharmony_ci * @cmd: pointer to pmcraid command struct 27958c2ecf20Sopenharmony_ci * @reslen: residual data length to be set in the ioasa 27968c2ecf20Sopenharmony_ci * @ioasc: ioasc either returned by IOA or set by driver itself. 27978c2ecf20Sopenharmony_ci * 27988c2ecf20Sopenharmony_ci * This function is invoked by pmcraid_io_done to complete mid-layer 27998c2ecf20Sopenharmony_ci * scsi ops. 28008c2ecf20Sopenharmony_ci * 28018c2ecf20Sopenharmony_ci * Return value: 28028c2ecf20Sopenharmony_ci * 0 if caller is required to return it to free_pool. Returns 1 if 28038c2ecf20Sopenharmony_ci * caller need not worry about freeing command block as error handler 28048c2ecf20Sopenharmony_ci * will take care of that. 28058c2ecf20Sopenharmony_ci */ 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_cistatic int _pmcraid_io_done(struct pmcraid_cmd *cmd, int reslen, int ioasc) 28088c2ecf20Sopenharmony_ci{ 28098c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; 28108c2ecf20Sopenharmony_ci int rc = 0; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci scsi_set_resid(scsi_cmd, reslen); 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci pmcraid_info("response(%d) CDB[0] = %x ioasc:result: %x:%x\n", 28158c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2, 28168c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 28178c2ecf20Sopenharmony_ci ioasc, scsi_cmd->result); 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci if (PMCRAID_IOASC_SENSE_KEY(ioasc) != 0) 28208c2ecf20Sopenharmony_ci rc = pmcraid_error_handler(cmd); 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci if (rc == 0) { 28238c2ecf20Sopenharmony_ci scsi_dma_unmap(scsi_cmd); 28248c2ecf20Sopenharmony_ci scsi_cmd->scsi_done(scsi_cmd); 28258c2ecf20Sopenharmony_ci } 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci return rc; 28288c2ecf20Sopenharmony_ci} 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci/** 28318c2ecf20Sopenharmony_ci * pmcraid_io_done - SCSI completion function 28328c2ecf20Sopenharmony_ci * 28338c2ecf20Sopenharmony_ci * @cmd: pointer to pmcraid command struct 28348c2ecf20Sopenharmony_ci * 28358c2ecf20Sopenharmony_ci * This function is invoked by tasklet/mid-layer error handler to completing 28368c2ecf20Sopenharmony_ci * the SCSI ops sent from mid-layer. 28378c2ecf20Sopenharmony_ci * 28388c2ecf20Sopenharmony_ci * Return value 28398c2ecf20Sopenharmony_ci * none 28408c2ecf20Sopenharmony_ci */ 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_cistatic void pmcraid_io_done(struct pmcraid_cmd *cmd) 28438c2ecf20Sopenharmony_ci{ 28448c2ecf20Sopenharmony_ci u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); 28458c2ecf20Sopenharmony_ci u32 reslen = le32_to_cpu(cmd->ioa_cb->ioasa.residual_data_length); 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci if (_pmcraid_io_done(cmd, reslen, ioasc) == 0) 28488c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 28498c2ecf20Sopenharmony_ci} 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci/** 28528c2ecf20Sopenharmony_ci * pmcraid_abort_cmd - Aborts a single IOARCB already submitted to IOA 28538c2ecf20Sopenharmony_ci * 28548c2ecf20Sopenharmony_ci * @cmd: command block of the command to be aborted 28558c2ecf20Sopenharmony_ci * 28568c2ecf20Sopenharmony_ci * Return Value: 28578c2ecf20Sopenharmony_ci * returns pointer to command structure used as cancelling cmd 28588c2ecf20Sopenharmony_ci */ 28598c2ecf20Sopenharmony_cistatic struct pmcraid_cmd *pmcraid_abort_cmd(struct pmcraid_cmd *cmd) 28608c2ecf20Sopenharmony_ci{ 28618c2ecf20Sopenharmony_ci struct pmcraid_cmd *cancel_cmd; 28628c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci pinstance = (struct pmcraid_instance *)cmd->drv_inst; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci cancel_cmd = pmcraid_get_free_cmd(pinstance); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci if (cancel_cmd == NULL) { 28698c2ecf20Sopenharmony_ci pmcraid_err("%s: no cmd blocks are available\n", __func__); 28708c2ecf20Sopenharmony_ci return NULL; 28718c2ecf20Sopenharmony_ci } 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci pmcraid_prepare_cancel_cmd(cancel_cmd, cmd); 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci pmcraid_info("aborting command CDB[0]= %x with index = %d\n", 28768c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 28778c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2); 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci init_completion(&cancel_cmd->wait_for_completion); 28808c2ecf20Sopenharmony_ci cancel_cmd->completion_req = 1; 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci pmcraid_info("command (%d) CDB[0] = %x for %x\n", 28838c2ecf20Sopenharmony_ci le32_to_cpu(cancel_cmd->ioa_cb->ioarcb.response_handle) >> 2, 28848c2ecf20Sopenharmony_ci cancel_cmd->ioa_cb->ioarcb.cdb[0], 28858c2ecf20Sopenharmony_ci le32_to_cpu(cancel_cmd->ioa_cb->ioarcb.resource_handle)); 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci pmcraid_send_cmd(cancel_cmd, 28888c2ecf20Sopenharmony_ci pmcraid_internal_done, 28898c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, 28908c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 28918c2ecf20Sopenharmony_ci return cancel_cmd; 28928c2ecf20Sopenharmony_ci} 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci/** 28958c2ecf20Sopenharmony_ci * pmcraid_abort_complete - Waits for ABORT TASK completion 28968c2ecf20Sopenharmony_ci * 28978c2ecf20Sopenharmony_ci * @cancel_cmd: command block use as cancelling command 28988c2ecf20Sopenharmony_ci * 28998c2ecf20Sopenharmony_ci * Return Value: 29008c2ecf20Sopenharmony_ci * returns SUCCESS if ABORT TASK has good completion 29018c2ecf20Sopenharmony_ci * otherwise FAILED 29028c2ecf20Sopenharmony_ci */ 29038c2ecf20Sopenharmony_cistatic int pmcraid_abort_complete(struct pmcraid_cmd *cancel_cmd) 29048c2ecf20Sopenharmony_ci{ 29058c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res; 29068c2ecf20Sopenharmony_ci u32 ioasc; 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci wait_for_completion(&cancel_cmd->wait_for_completion); 29098c2ecf20Sopenharmony_ci res = cancel_cmd->res; 29108c2ecf20Sopenharmony_ci cancel_cmd->res = NULL; 29118c2ecf20Sopenharmony_ci ioasc = le32_to_cpu(cancel_cmd->ioa_cb->ioasa.ioasc); 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci /* If the abort task is not timed out we will get a Good completion 29148c2ecf20Sopenharmony_ci * as sense_key, otherwise we may get one the following responses 29158c2ecf20Sopenharmony_ci * due to subsequent bus reset or device reset. In case IOASC is 29168c2ecf20Sopenharmony_ci * NR_SYNC_REQUIRED, set sync_reqd flag for the corresponding resource 29178c2ecf20Sopenharmony_ci */ 29188c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET || 29198c2ecf20Sopenharmony_ci ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED) { 29208c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED) 29218c2ecf20Sopenharmony_ci res->sync_reqd = 1; 29228c2ecf20Sopenharmony_ci ioasc = 0; 29238c2ecf20Sopenharmony_ci } 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci /* complete the command here itself */ 29268c2ecf20Sopenharmony_ci pmcraid_return_cmd(cancel_cmd); 29278c2ecf20Sopenharmony_ci return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS; 29288c2ecf20Sopenharmony_ci} 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci/** 29318c2ecf20Sopenharmony_ci * pmcraid_eh_abort_handler - entry point for aborting a single task on errors 29328c2ecf20Sopenharmony_ci * 29338c2ecf20Sopenharmony_ci * @scsi_cmd: scsi command struct given by mid-layer. When this is called 29348c2ecf20Sopenharmony_ci * mid-layer ensures that no other commands are queued. This 29358c2ecf20Sopenharmony_ci * never gets called under interrupt, but a separate eh thread. 29368c2ecf20Sopenharmony_ci * 29378c2ecf20Sopenharmony_ci * Return value: 29388c2ecf20Sopenharmony_ci * SUCCESS / FAILED 29398c2ecf20Sopenharmony_ci */ 29408c2ecf20Sopenharmony_cistatic int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd) 29418c2ecf20Sopenharmony_ci{ 29428c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 29438c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 29448c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res; 29458c2ecf20Sopenharmony_ci unsigned long host_lock_flags; 29468c2ecf20Sopenharmony_ci unsigned long pending_lock_flags; 29478c2ecf20Sopenharmony_ci struct pmcraid_cmd *cancel_cmd = NULL; 29488c2ecf20Sopenharmony_ci int cmd_found = 0; 29498c2ecf20Sopenharmony_ci int rc = FAILED; 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci pinstance = 29528c2ecf20Sopenharmony_ci (struct pmcraid_instance *)scsi_cmd->device->host->hostdata; 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, scsi_cmd, 29558c2ecf20Sopenharmony_ci "I/O command timed out, aborting it.\n"); 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci res = scsi_cmd->device->hostdata; 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci if (res == NULL) 29608c2ecf20Sopenharmony_ci return rc; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci /* If we are currently going through reset/reload, return failed. 29638c2ecf20Sopenharmony_ci * This will force the mid-layer to eventually call 29648c2ecf20Sopenharmony_ci * pmcraid_eh_host_reset which will then go to sleep and wait for the 29658c2ecf20Sopenharmony_ci * reset to complete 29668c2ecf20Sopenharmony_ci */ 29678c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, host_lock_flags); 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci if (pinstance->ioa_reset_in_progress || 29708c2ecf20Sopenharmony_ci pinstance->ioa_state == IOA_STATE_DEAD) { 29718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, 29728c2ecf20Sopenharmony_ci host_lock_flags); 29738c2ecf20Sopenharmony_ci return rc; 29748c2ecf20Sopenharmony_ci } 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci /* loop over pending cmd list to find cmd corresponding to this 29778c2ecf20Sopenharmony_ci * scsi_cmd. Note that this command might not have been completed 29788c2ecf20Sopenharmony_ci * already. locking: all pending commands are protected with 29798c2ecf20Sopenharmony_ci * pending_pool_lock. 29808c2ecf20Sopenharmony_ci */ 29818c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->pending_pool_lock, pending_lock_flags); 29828c2ecf20Sopenharmony_ci list_for_each_entry(cmd, &pinstance->pending_cmd_pool, free_list) { 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci if (cmd->scsi_cmd == scsi_cmd) { 29858c2ecf20Sopenharmony_ci cmd_found = 1; 29868c2ecf20Sopenharmony_ci break; 29878c2ecf20Sopenharmony_ci } 29888c2ecf20Sopenharmony_ci } 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->pending_pool_lock, 29918c2ecf20Sopenharmony_ci pending_lock_flags); 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_ci /* If the command to be aborted was given to IOA and still pending with 29948c2ecf20Sopenharmony_ci * it, send ABORT_TASK to abort this and wait for its completion 29958c2ecf20Sopenharmony_ci */ 29968c2ecf20Sopenharmony_ci if (cmd_found) 29978c2ecf20Sopenharmony_ci cancel_cmd = pmcraid_abort_cmd(cmd); 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, 30008c2ecf20Sopenharmony_ci host_lock_flags); 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci if (cancel_cmd) { 30038c2ecf20Sopenharmony_ci cancel_cmd->res = cmd->scsi_cmd->device->hostdata; 30048c2ecf20Sopenharmony_ci rc = pmcraid_abort_complete(cancel_cmd); 30058c2ecf20Sopenharmony_ci } 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci return cmd_found ? rc : SUCCESS; 30088c2ecf20Sopenharmony_ci} 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci/** 30118c2ecf20Sopenharmony_ci * pmcraid_eh_xxxx_reset_handler - bus/target/device reset handler callbacks 30128c2ecf20Sopenharmony_ci * 30138c2ecf20Sopenharmony_ci * @scmd: pointer to scsi_cmd that was sent to the resource to be reset. 30148c2ecf20Sopenharmony_ci * 30158c2ecf20Sopenharmony_ci * All these routines invokve pmcraid_reset_device with appropriate parameters. 30168c2ecf20Sopenharmony_ci * Since these are called from mid-layer EH thread, no other IO will be queued 30178c2ecf20Sopenharmony_ci * to the resource being reset. However, control path (IOCTL) may be active so 30188c2ecf20Sopenharmony_ci * it is necessary to synchronize IOARRIN writes which pmcraid_reset_device 30198c2ecf20Sopenharmony_ci * takes care by locking/unlocking host_lock. 30208c2ecf20Sopenharmony_ci * 30218c2ecf20Sopenharmony_ci * Return value 30228c2ecf20Sopenharmony_ci * SUCCESS or FAILED 30238c2ecf20Sopenharmony_ci */ 30248c2ecf20Sopenharmony_cistatic int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd) 30258c2ecf20Sopenharmony_ci{ 30268c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 30278c2ecf20Sopenharmony_ci "resetting device due to an I/O command timeout.\n"); 30288c2ecf20Sopenharmony_ci return pmcraid_reset_device(scmd, 30298c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, 30308c2ecf20Sopenharmony_ci RESET_DEVICE_LUN); 30318c2ecf20Sopenharmony_ci} 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_cistatic int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd) 30348c2ecf20Sopenharmony_ci{ 30358c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 30368c2ecf20Sopenharmony_ci "Doing bus reset due to an I/O command timeout.\n"); 30378c2ecf20Sopenharmony_ci return pmcraid_reset_device(scmd, 30388c2ecf20Sopenharmony_ci PMCRAID_RESET_BUS_TIMEOUT, 30398c2ecf20Sopenharmony_ci RESET_DEVICE_BUS); 30408c2ecf20Sopenharmony_ci} 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_cistatic int pmcraid_eh_target_reset_handler(struct scsi_cmnd *scmd) 30438c2ecf20Sopenharmony_ci{ 30448c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 30458c2ecf20Sopenharmony_ci "Doing target reset due to an I/O command timeout.\n"); 30468c2ecf20Sopenharmony_ci return pmcraid_reset_device(scmd, 30478c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, 30488c2ecf20Sopenharmony_ci RESET_DEVICE_TARGET); 30498c2ecf20Sopenharmony_ci} 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci/** 30528c2ecf20Sopenharmony_ci * pmcraid_eh_host_reset_handler - adapter reset handler callback 30538c2ecf20Sopenharmony_ci * 30548c2ecf20Sopenharmony_ci * @scmd: pointer to scsi_cmd that was sent to a resource of adapter 30558c2ecf20Sopenharmony_ci * 30568c2ecf20Sopenharmony_ci * Initiates adapter reset to bring it up to operational state 30578c2ecf20Sopenharmony_ci * 30588c2ecf20Sopenharmony_ci * Return value 30598c2ecf20Sopenharmony_ci * SUCCESS or FAILED 30608c2ecf20Sopenharmony_ci */ 30618c2ecf20Sopenharmony_cistatic int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd) 30628c2ecf20Sopenharmony_ci{ 30638c2ecf20Sopenharmony_ci unsigned long interval = 10000; /* 10 seconds interval */ 30648c2ecf20Sopenharmony_ci int waits = jiffies_to_msecs(PMCRAID_RESET_HOST_TIMEOUT) / interval; 30658c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = 30668c2ecf20Sopenharmony_ci (struct pmcraid_instance *)(scmd->device->host->hostdata); 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci /* wait for an additional 150 seconds just in case firmware could come 30708c2ecf20Sopenharmony_ci * up and if it could complete all the pending commands excluding the 30718c2ecf20Sopenharmony_ci * two HCAM (CCN and LDN). 30728c2ecf20Sopenharmony_ci */ 30738c2ecf20Sopenharmony_ci while (waits--) { 30748c2ecf20Sopenharmony_ci if (atomic_read(&pinstance->outstanding_cmds) <= 30758c2ecf20Sopenharmony_ci PMCRAID_MAX_HCAM_CMD) 30768c2ecf20Sopenharmony_ci return SUCCESS; 30778c2ecf20Sopenharmony_ci msleep(interval); 30788c2ecf20Sopenharmony_ci } 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci dev_err(&pinstance->pdev->dev, 30818c2ecf20Sopenharmony_ci "Adapter being reset due to an I/O command timeout.\n"); 30828c2ecf20Sopenharmony_ci return pmcraid_reset_bringup(pinstance) == 0 ? SUCCESS : FAILED; 30838c2ecf20Sopenharmony_ci} 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci/** 30868c2ecf20Sopenharmony_ci * pmcraid_init_ioadls - initializes IOADL related fields in IOARCB 30878c2ecf20Sopenharmony_ci * @cmd: pmcraid command struct 30888c2ecf20Sopenharmony_ci * @sgcount: count of scatter-gather elements 30898c2ecf20Sopenharmony_ci * 30908c2ecf20Sopenharmony_ci * Return value 30918c2ecf20Sopenharmony_ci * returns pointer pmcraid_ioadl_desc, initialized to point to internal 30928c2ecf20Sopenharmony_ci * or external IOADLs 30938c2ecf20Sopenharmony_ci */ 30948c2ecf20Sopenharmony_cistatic struct pmcraid_ioadl_desc * 30958c2ecf20Sopenharmony_cipmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount) 30968c2ecf20Sopenharmony_ci{ 30978c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 30988c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 30998c2ecf20Sopenharmony_ci int ioadl_count = 0; 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci if (ioarcb->add_cmd_param_length) 31028c2ecf20Sopenharmony_ci ioadl_count = DIV_ROUND_UP(le16_to_cpu(ioarcb->add_cmd_param_length), 16); 31038c2ecf20Sopenharmony_ci ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc) * sgcount); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci if ((sgcount + ioadl_count) > (ARRAY_SIZE(ioarcb->add_data.u.ioadl))) { 31068c2ecf20Sopenharmony_ci /* external ioadls start at offset 0x80 from control_block 31078c2ecf20Sopenharmony_ci * structure, re-using 24 out of 27 ioadls part of IOARCB. 31088c2ecf20Sopenharmony_ci * It is necessary to indicate to firmware that driver is 31098c2ecf20Sopenharmony_ci * using ioadls to be treated as external to IOARCB. 31108c2ecf20Sopenharmony_ci */ 31118c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL)); 31128c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = 31138c2ecf20Sopenharmony_ci cpu_to_le64((cmd->ioa_cb_bus_addr) + 31148c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 31158c2ecf20Sopenharmony_ci add_data.u.ioadl[3])); 31168c2ecf20Sopenharmony_ci ioadl = &ioarcb->add_data.u.ioadl[3]; 31178c2ecf20Sopenharmony_ci } else { 31188c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = 31198c2ecf20Sopenharmony_ci cpu_to_le64((cmd->ioa_cb_bus_addr) + 31208c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 31218c2ecf20Sopenharmony_ci add_data.u.ioadl[ioadl_count])); 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ci ioadl = &ioarcb->add_data.u.ioadl[ioadl_count]; 31248c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr |= 31258c2ecf20Sopenharmony_ci cpu_to_le64(DIV_ROUND_CLOSEST(sgcount + ioadl_count, 8)); 31268c2ecf20Sopenharmony_ci } 31278c2ecf20Sopenharmony_ci 31288c2ecf20Sopenharmony_ci return ioadl; 31298c2ecf20Sopenharmony_ci} 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci/** 31328c2ecf20Sopenharmony_ci * pmcraid_build_ioadl - Build a scatter/gather list and map the buffer 31338c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 31348c2ecf20Sopenharmony_ci * @cmd: pmcraid command struct 31358c2ecf20Sopenharmony_ci * 31368c2ecf20Sopenharmony_ci * This function is invoked by queuecommand entry point while sending a command 31378c2ecf20Sopenharmony_ci * to firmware. This builds ioadl descriptors and sets up ioarcb fields. 31388c2ecf20Sopenharmony_ci * 31398c2ecf20Sopenharmony_ci * Return value: 31408c2ecf20Sopenharmony_ci * 0 on success or -1 on failure 31418c2ecf20Sopenharmony_ci */ 31428c2ecf20Sopenharmony_cistatic int pmcraid_build_ioadl( 31438c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 31448c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd 31458c2ecf20Sopenharmony_ci) 31468c2ecf20Sopenharmony_ci{ 31478c2ecf20Sopenharmony_ci int i, nseg; 31488c2ecf20Sopenharmony_ci struct scatterlist *sglist; 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; 31518c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); 31528c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci u32 length = scsi_bufflen(scsi_cmd); 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ci if (!length) 31578c2ecf20Sopenharmony_ci return 0; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci nseg = scsi_dma_map(scsi_cmd); 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci if (nseg < 0) { 31628c2ecf20Sopenharmony_ci scmd_printk(KERN_ERR, scsi_cmd, "scsi_map_dma failed!\n"); 31638c2ecf20Sopenharmony_ci return -1; 31648c2ecf20Sopenharmony_ci } else if (nseg > PMCRAID_MAX_IOADLS) { 31658c2ecf20Sopenharmony_ci scsi_dma_unmap(scsi_cmd); 31668c2ecf20Sopenharmony_ci scmd_printk(KERN_ERR, scsi_cmd, 31678c2ecf20Sopenharmony_ci "sg count is (%d) more than allowed!\n", nseg); 31688c2ecf20Sopenharmony_ci return -1; 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci /* Initialize IOARCB data transfer length fields */ 31728c2ecf20Sopenharmony_ci if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) 31738c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= TRANSFER_DIR_WRITE; 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= NO_LINK_DESCS; 31768c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = cpu_to_le32(length); 31778c2ecf20Sopenharmony_ci ioadl = pmcraid_init_ioadls(cmd, nseg); 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci /* Initialize IOADL descriptor addresses */ 31808c2ecf20Sopenharmony_ci scsi_for_each_sg(scsi_cmd, sglist, nseg, i) { 31818c2ecf20Sopenharmony_ci ioadl[i].data_len = cpu_to_le32(sg_dma_len(sglist)); 31828c2ecf20Sopenharmony_ci ioadl[i].address = cpu_to_le64(sg_dma_address(sglist)); 31838c2ecf20Sopenharmony_ci ioadl[i].flags = 0; 31848c2ecf20Sopenharmony_ci } 31858c2ecf20Sopenharmony_ci /* setup last descriptor */ 31868c2ecf20Sopenharmony_ci ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC; 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci return 0; 31898c2ecf20Sopenharmony_ci} 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci/** 31928c2ecf20Sopenharmony_ci * pmcraid_free_sglist - Frees an allocated SG buffer list 31938c2ecf20Sopenharmony_ci * @sglist: scatter/gather list pointer 31948c2ecf20Sopenharmony_ci * 31958c2ecf20Sopenharmony_ci * Free a DMA'able memory previously allocated with pmcraid_alloc_sglist 31968c2ecf20Sopenharmony_ci * 31978c2ecf20Sopenharmony_ci * Return value: 31988c2ecf20Sopenharmony_ci * none 31998c2ecf20Sopenharmony_ci */ 32008c2ecf20Sopenharmony_cistatic void pmcraid_free_sglist(struct pmcraid_sglist *sglist) 32018c2ecf20Sopenharmony_ci{ 32028c2ecf20Sopenharmony_ci sgl_free_order(sglist->scatterlist, sglist->order); 32038c2ecf20Sopenharmony_ci kfree(sglist); 32048c2ecf20Sopenharmony_ci} 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci/** 32078c2ecf20Sopenharmony_ci * pmcraid_alloc_sglist - Allocates memory for a SG list 32088c2ecf20Sopenharmony_ci * @buflen: buffer length 32098c2ecf20Sopenharmony_ci * 32108c2ecf20Sopenharmony_ci * Allocates a DMA'able buffer in chunks and assembles a scatter/gather 32118c2ecf20Sopenharmony_ci * list. 32128c2ecf20Sopenharmony_ci * 32138c2ecf20Sopenharmony_ci * Return value 32148c2ecf20Sopenharmony_ci * pointer to sglist / NULL on failure 32158c2ecf20Sopenharmony_ci */ 32168c2ecf20Sopenharmony_cistatic struct pmcraid_sglist *pmcraid_alloc_sglist(int buflen) 32178c2ecf20Sopenharmony_ci{ 32188c2ecf20Sopenharmony_ci struct pmcraid_sglist *sglist; 32198c2ecf20Sopenharmony_ci int sg_size; 32208c2ecf20Sopenharmony_ci int order; 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci sg_size = buflen / (PMCRAID_MAX_IOADLS - 1); 32238c2ecf20Sopenharmony_ci order = (sg_size > 0) ? get_order(sg_size) : 0; 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci /* Allocate a scatter/gather list for the DMA */ 32268c2ecf20Sopenharmony_ci sglist = kzalloc(sizeof(struct pmcraid_sglist), GFP_KERNEL); 32278c2ecf20Sopenharmony_ci if (sglist == NULL) 32288c2ecf20Sopenharmony_ci return NULL; 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci sglist->order = order; 32318c2ecf20Sopenharmony_ci sgl_alloc_order(buflen, order, false, 32328c2ecf20Sopenharmony_ci GFP_KERNEL | GFP_DMA | __GFP_ZERO, &sglist->num_sg); 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci return sglist; 32358c2ecf20Sopenharmony_ci} 32368c2ecf20Sopenharmony_ci 32378c2ecf20Sopenharmony_ci/** 32388c2ecf20Sopenharmony_ci * pmcraid_copy_sglist - Copy user buffer to kernel buffer's SG list 32398c2ecf20Sopenharmony_ci * @sglist: scatter/gather list pointer 32408c2ecf20Sopenharmony_ci * @buffer: buffer pointer 32418c2ecf20Sopenharmony_ci * @len: buffer length 32428c2ecf20Sopenharmony_ci * @direction: data transfer direction 32438c2ecf20Sopenharmony_ci * 32448c2ecf20Sopenharmony_ci * Copy a user buffer into a buffer allocated by pmcraid_alloc_sglist 32458c2ecf20Sopenharmony_ci * 32468c2ecf20Sopenharmony_ci * Return value: 32478c2ecf20Sopenharmony_ci * 0 on success / other on failure 32488c2ecf20Sopenharmony_ci */ 32498c2ecf20Sopenharmony_cistatic int pmcraid_copy_sglist( 32508c2ecf20Sopenharmony_ci struct pmcraid_sglist *sglist, 32518c2ecf20Sopenharmony_ci void __user *buffer, 32528c2ecf20Sopenharmony_ci u32 len, 32538c2ecf20Sopenharmony_ci int direction 32548c2ecf20Sopenharmony_ci) 32558c2ecf20Sopenharmony_ci{ 32568c2ecf20Sopenharmony_ci struct scatterlist *sg; 32578c2ecf20Sopenharmony_ci void *kaddr; 32588c2ecf20Sopenharmony_ci int bsize_elem; 32598c2ecf20Sopenharmony_ci int i; 32608c2ecf20Sopenharmony_ci int rc = 0; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci /* Determine the actual number of bytes per element */ 32638c2ecf20Sopenharmony_ci bsize_elem = PAGE_SIZE * (1 << sglist->order); 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_ci sg = sglist->scatterlist; 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci for (i = 0; i < (len / bsize_elem); i++, sg = sg_next(sg), buffer += bsize_elem) { 32688c2ecf20Sopenharmony_ci struct page *page = sg_page(sg); 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci kaddr = kmap(page); 32718c2ecf20Sopenharmony_ci if (direction == DMA_TO_DEVICE) 32728c2ecf20Sopenharmony_ci rc = copy_from_user(kaddr, buffer, bsize_elem); 32738c2ecf20Sopenharmony_ci else 32748c2ecf20Sopenharmony_ci rc = copy_to_user(buffer, kaddr, bsize_elem); 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci kunmap(page); 32778c2ecf20Sopenharmony_ci 32788c2ecf20Sopenharmony_ci if (rc) { 32798c2ecf20Sopenharmony_ci pmcraid_err("failed to copy user data into sg list\n"); 32808c2ecf20Sopenharmony_ci return -EFAULT; 32818c2ecf20Sopenharmony_ci } 32828c2ecf20Sopenharmony_ci 32838c2ecf20Sopenharmony_ci sg->length = bsize_elem; 32848c2ecf20Sopenharmony_ci } 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci if (len % bsize_elem) { 32878c2ecf20Sopenharmony_ci struct page *page = sg_page(sg); 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci kaddr = kmap(page); 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci if (direction == DMA_TO_DEVICE) 32928c2ecf20Sopenharmony_ci rc = copy_from_user(kaddr, buffer, len % bsize_elem); 32938c2ecf20Sopenharmony_ci else 32948c2ecf20Sopenharmony_ci rc = copy_to_user(buffer, kaddr, len % bsize_elem); 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci kunmap(page); 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_ci sg->length = len % bsize_elem; 32998c2ecf20Sopenharmony_ci } 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci if (rc) { 33028c2ecf20Sopenharmony_ci pmcraid_err("failed to copy user data into sg list\n"); 33038c2ecf20Sopenharmony_ci rc = -EFAULT; 33048c2ecf20Sopenharmony_ci } 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci return rc; 33078c2ecf20Sopenharmony_ci} 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci/** 33108c2ecf20Sopenharmony_ci * pmcraid_queuecommand - Queue a mid-layer request 33118c2ecf20Sopenharmony_ci * @scsi_cmd: scsi command struct 33128c2ecf20Sopenharmony_ci * @done: done function 33138c2ecf20Sopenharmony_ci * 33148c2ecf20Sopenharmony_ci * This function queues a request generated by the mid-layer. Midlayer calls 33158c2ecf20Sopenharmony_ci * this routine within host->lock. Some of the functions called by queuecommand 33168c2ecf20Sopenharmony_ci * would use cmd block queue locks (free_pool_lock and pending_pool_lock) 33178c2ecf20Sopenharmony_ci * 33188c2ecf20Sopenharmony_ci * Return value: 33198c2ecf20Sopenharmony_ci * 0 on success 33208c2ecf20Sopenharmony_ci * SCSI_MLQUEUE_DEVICE_BUSY if device is busy 33218c2ecf20Sopenharmony_ci * SCSI_MLQUEUE_HOST_BUSY if host is busy 33228c2ecf20Sopenharmony_ci */ 33238c2ecf20Sopenharmony_cistatic int pmcraid_queuecommand_lck( 33248c2ecf20Sopenharmony_ci struct scsi_cmnd *scsi_cmd, 33258c2ecf20Sopenharmony_ci void (*done) (struct scsi_cmnd *) 33268c2ecf20Sopenharmony_ci) 33278c2ecf20Sopenharmony_ci{ 33288c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 33298c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res; 33308c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb; 33318c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 33328c2ecf20Sopenharmony_ci u32 fw_version; 33338c2ecf20Sopenharmony_ci int rc = 0; 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci pinstance = 33368c2ecf20Sopenharmony_ci (struct pmcraid_instance *)scsi_cmd->device->host->hostdata; 33378c2ecf20Sopenharmony_ci fw_version = be16_to_cpu(pinstance->inq_data->fw_version); 33388c2ecf20Sopenharmony_ci scsi_cmd->scsi_done = done; 33398c2ecf20Sopenharmony_ci res = scsi_cmd->device->hostdata; 33408c2ecf20Sopenharmony_ci scsi_cmd->result = (DID_OK << 16); 33418c2ecf20Sopenharmony_ci 33428c2ecf20Sopenharmony_ci /* if adapter is marked as dead, set result to DID_NO_CONNECT complete 33438c2ecf20Sopenharmony_ci * the command 33448c2ecf20Sopenharmony_ci */ 33458c2ecf20Sopenharmony_ci if (pinstance->ioa_state == IOA_STATE_DEAD) { 33468c2ecf20Sopenharmony_ci pmcraid_info("IOA is dead, but queuecommand is scheduled\n"); 33478c2ecf20Sopenharmony_ci scsi_cmd->result = (DID_NO_CONNECT << 16); 33488c2ecf20Sopenharmony_ci scsi_cmd->scsi_done(scsi_cmd); 33498c2ecf20Sopenharmony_ci return 0; 33508c2ecf20Sopenharmony_ci } 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci /* If IOA reset is in progress, can't queue the commands */ 33538c2ecf20Sopenharmony_ci if (pinstance->ioa_reset_in_progress) 33548c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci /* Firmware doesn't support SYNCHRONIZE_CACHE command (0x35), complete 33578c2ecf20Sopenharmony_ci * the command here itself with success return 33588c2ecf20Sopenharmony_ci */ 33598c2ecf20Sopenharmony_ci if (scsi_cmd->cmnd[0] == SYNCHRONIZE_CACHE) { 33608c2ecf20Sopenharmony_ci pmcraid_info("SYNC_CACHE(0x35), completing in driver itself\n"); 33618c2ecf20Sopenharmony_ci scsi_cmd->scsi_done(scsi_cmd); 33628c2ecf20Sopenharmony_ci return 0; 33638c2ecf20Sopenharmony_ci } 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci /* initialize the command and IOARCB to be sent to IOA */ 33668c2ecf20Sopenharmony_ci cmd = pmcraid_get_free_cmd(pinstance); 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci if (cmd == NULL) { 33698c2ecf20Sopenharmony_ci pmcraid_err("free command block is not available\n"); 33708c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 33718c2ecf20Sopenharmony_ci } 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci cmd->scsi_cmd = scsi_cmd; 33748c2ecf20Sopenharmony_ci ioarcb = &(cmd->ioa_cb->ioarcb); 33758c2ecf20Sopenharmony_ci memcpy(ioarcb->cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len); 33768c2ecf20Sopenharmony_ci ioarcb->resource_handle = res->cfg_entry.resource_handle; 33778c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_SCSI; 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci /* set hrrq number where the IOA should respond to. Note that all cmds 33808c2ecf20Sopenharmony_ci * generated internally uses hrrq_id 0, exception to this is the cmd 33818c2ecf20Sopenharmony_ci * block of scsi_cmd which is re-used (e.g. cancel/abort), which uses 33828c2ecf20Sopenharmony_ci * hrrq_id assigned here in queuecommand 33838c2ecf20Sopenharmony_ci */ 33848c2ecf20Sopenharmony_ci ioarcb->hrrq_id = atomic_add_return(1, &(pinstance->last_message_id)) % 33858c2ecf20Sopenharmony_ci pinstance->num_hrrq; 33868c2ecf20Sopenharmony_ci cmd->cmd_done = pmcraid_io_done; 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci if (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry)) { 33898c2ecf20Sopenharmony_ci if (scsi_cmd->underflow == 0) 33908c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= INHIBIT_UL_CHECK; 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci if (res->sync_reqd) { 33938c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= SYNC_COMPLETE; 33948c2ecf20Sopenharmony_ci res->sync_reqd = 0; 33958c2ecf20Sopenharmony_ci } 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= NO_LINK_DESCS; 33988c2ecf20Sopenharmony_ci 33998c2ecf20Sopenharmony_ci if (scsi_cmd->flags & SCMD_TAGGED) 34008c2ecf20Sopenharmony_ci ioarcb->request_flags1 |= TASK_TAG_SIMPLE; 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci if (RES_IS_GSCSI(res->cfg_entry)) 34038c2ecf20Sopenharmony_ci ioarcb->request_flags1 |= DELAY_AFTER_RESET; 34048c2ecf20Sopenharmony_ci } 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ci rc = pmcraid_build_ioadl(pinstance, cmd); 34078c2ecf20Sopenharmony_ci 34088c2ecf20Sopenharmony_ci pmcraid_info("command (%d) CDB[0] = %x for %x:%x:%x:%x\n", 34098c2ecf20Sopenharmony_ci le32_to_cpu(ioarcb->response_handle) >> 2, 34108c2ecf20Sopenharmony_ci scsi_cmd->cmnd[0], pinstance->host->unique_id, 34118c2ecf20Sopenharmony_ci RES_IS_VSET(res->cfg_entry) ? PMCRAID_VSET_BUS_ID : 34128c2ecf20Sopenharmony_ci PMCRAID_PHYS_BUS_ID, 34138c2ecf20Sopenharmony_ci RES_IS_VSET(res->cfg_entry) ? 34148c2ecf20Sopenharmony_ci (fw_version <= PMCRAID_FW_VERSION_1 ? 34158c2ecf20Sopenharmony_ci res->cfg_entry.unique_flags1 : 34168c2ecf20Sopenharmony_ci le16_to_cpu(res->cfg_entry.array_id) & 0xFF) : 34178c2ecf20Sopenharmony_ci RES_TARGET(res->cfg_entry.resource_address), 34188c2ecf20Sopenharmony_ci RES_LUN(res->cfg_entry.resource_address)); 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci if (likely(rc == 0)) { 34218c2ecf20Sopenharmony_ci _pmcraid_fire_command(cmd); 34228c2ecf20Sopenharmony_ci } else { 34238c2ecf20Sopenharmony_ci pmcraid_err("queuecommand could not build ioadl\n"); 34248c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 34258c2ecf20Sopenharmony_ci rc = SCSI_MLQUEUE_HOST_BUSY; 34268c2ecf20Sopenharmony_ci } 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci return rc; 34298c2ecf20Sopenharmony_ci} 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(pmcraid_queuecommand) 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci/** 34348c2ecf20Sopenharmony_ci * pmcraid_open -char node "open" entry, allowed only users with admin access 34358c2ecf20Sopenharmony_ci */ 34368c2ecf20Sopenharmony_cistatic int pmcraid_chr_open(struct inode *inode, struct file *filep) 34378c2ecf20Sopenharmony_ci{ 34388c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 34418c2ecf20Sopenharmony_ci return -EACCES; 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci /* Populate adapter instance * pointer for use by ioctl */ 34448c2ecf20Sopenharmony_ci pinstance = container_of(inode->i_cdev, struct pmcraid_instance, cdev); 34458c2ecf20Sopenharmony_ci filep->private_data = pinstance; 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci return 0; 34488c2ecf20Sopenharmony_ci} 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci/** 34518c2ecf20Sopenharmony_ci * pmcraid_fasync - Async notifier registration from applications 34528c2ecf20Sopenharmony_ci * 34538c2ecf20Sopenharmony_ci * This function adds the calling process to a driver global queue. When an 34548c2ecf20Sopenharmony_ci * event occurs, SIGIO will be sent to all processes in this queue. 34558c2ecf20Sopenharmony_ci */ 34568c2ecf20Sopenharmony_cistatic int pmcraid_chr_fasync(int fd, struct file *filep, int mode) 34578c2ecf20Sopenharmony_ci{ 34588c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 34598c2ecf20Sopenharmony_ci int rc; 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci pinstance = filep->private_data; 34628c2ecf20Sopenharmony_ci mutex_lock(&pinstance->aen_queue_lock); 34638c2ecf20Sopenharmony_ci rc = fasync_helper(fd, filep, mode, &pinstance->aen_queue); 34648c2ecf20Sopenharmony_ci mutex_unlock(&pinstance->aen_queue_lock); 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci return rc; 34678c2ecf20Sopenharmony_ci} 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci/** 34718c2ecf20Sopenharmony_ci * pmcraid_build_passthrough_ioadls - builds SG elements for passthrough 34728c2ecf20Sopenharmony_ci * commands sent over IOCTL interface 34738c2ecf20Sopenharmony_ci * 34748c2ecf20Sopenharmony_ci * @cmd : pointer to struct pmcraid_cmd 34758c2ecf20Sopenharmony_ci * @buflen : length of the request buffer 34768c2ecf20Sopenharmony_ci * @direction : data transfer direction 34778c2ecf20Sopenharmony_ci * 34788c2ecf20Sopenharmony_ci * Return value 34798c2ecf20Sopenharmony_ci * 0 on success, non-zero error code on failure 34808c2ecf20Sopenharmony_ci */ 34818c2ecf20Sopenharmony_cistatic int pmcraid_build_passthrough_ioadls( 34828c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd, 34838c2ecf20Sopenharmony_ci int buflen, 34848c2ecf20Sopenharmony_ci int direction 34858c2ecf20Sopenharmony_ci) 34868c2ecf20Sopenharmony_ci{ 34878c2ecf20Sopenharmony_ci struct pmcraid_sglist *sglist = NULL; 34888c2ecf20Sopenharmony_ci struct scatterlist *sg = NULL; 34898c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 34908c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 34918c2ecf20Sopenharmony_ci int i; 34928c2ecf20Sopenharmony_ci 34938c2ecf20Sopenharmony_ci sglist = pmcraid_alloc_sglist(buflen); 34948c2ecf20Sopenharmony_ci 34958c2ecf20Sopenharmony_ci if (!sglist) { 34968c2ecf20Sopenharmony_ci pmcraid_err("can't allocate memory for passthrough SGls\n"); 34978c2ecf20Sopenharmony_ci return -ENOMEM; 34988c2ecf20Sopenharmony_ci } 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci sglist->num_dma_sg = dma_map_sg(&cmd->drv_inst->pdev->dev, 35018c2ecf20Sopenharmony_ci sglist->scatterlist, 35028c2ecf20Sopenharmony_ci sglist->num_sg, direction); 35038c2ecf20Sopenharmony_ci 35048c2ecf20Sopenharmony_ci if (!sglist->num_dma_sg || sglist->num_dma_sg > PMCRAID_MAX_IOADLS) { 35058c2ecf20Sopenharmony_ci dev_err(&cmd->drv_inst->pdev->dev, 35068c2ecf20Sopenharmony_ci "Failed to map passthrough buffer!\n"); 35078c2ecf20Sopenharmony_ci pmcraid_free_sglist(sglist); 35088c2ecf20Sopenharmony_ci return -EIO; 35098c2ecf20Sopenharmony_ci } 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci cmd->sglist = sglist; 35128c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= NO_LINK_DESCS; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci ioadl = pmcraid_init_ioadls(cmd, sglist->num_dma_sg); 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci /* Initialize IOADL descriptor addresses */ 35178c2ecf20Sopenharmony_ci for_each_sg(sglist->scatterlist, sg, sglist->num_dma_sg, i) { 35188c2ecf20Sopenharmony_ci ioadl[i].data_len = cpu_to_le32(sg_dma_len(sg)); 35198c2ecf20Sopenharmony_ci ioadl[i].address = cpu_to_le64(sg_dma_address(sg)); 35208c2ecf20Sopenharmony_ci ioadl[i].flags = 0; 35218c2ecf20Sopenharmony_ci } 35228c2ecf20Sopenharmony_ci 35238c2ecf20Sopenharmony_ci /* setup the last descriptor */ 35248c2ecf20Sopenharmony_ci ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC; 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci return 0; 35278c2ecf20Sopenharmony_ci} 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci/** 35318c2ecf20Sopenharmony_ci * pmcraid_release_passthrough_ioadls - release passthrough ioadls 35328c2ecf20Sopenharmony_ci * 35338c2ecf20Sopenharmony_ci * @cmd: pointer to struct pmcraid_cmd for which ioadls were allocated 35348c2ecf20Sopenharmony_ci * @buflen: size of the request buffer 35358c2ecf20Sopenharmony_ci * @direction: data transfer direction 35368c2ecf20Sopenharmony_ci * 35378c2ecf20Sopenharmony_ci * Return value 35388c2ecf20Sopenharmony_ci * 0 on success, non-zero error code on failure 35398c2ecf20Sopenharmony_ci */ 35408c2ecf20Sopenharmony_cistatic void pmcraid_release_passthrough_ioadls( 35418c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd, 35428c2ecf20Sopenharmony_ci int buflen, 35438c2ecf20Sopenharmony_ci int direction 35448c2ecf20Sopenharmony_ci) 35458c2ecf20Sopenharmony_ci{ 35468c2ecf20Sopenharmony_ci struct pmcraid_sglist *sglist = cmd->sglist; 35478c2ecf20Sopenharmony_ci 35488c2ecf20Sopenharmony_ci if (buflen > 0) { 35498c2ecf20Sopenharmony_ci dma_unmap_sg(&cmd->drv_inst->pdev->dev, 35508c2ecf20Sopenharmony_ci sglist->scatterlist, 35518c2ecf20Sopenharmony_ci sglist->num_sg, 35528c2ecf20Sopenharmony_ci direction); 35538c2ecf20Sopenharmony_ci pmcraid_free_sglist(sglist); 35548c2ecf20Sopenharmony_ci cmd->sglist = NULL; 35558c2ecf20Sopenharmony_ci } 35568c2ecf20Sopenharmony_ci} 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci/** 35598c2ecf20Sopenharmony_ci * pmcraid_ioctl_passthrough - handling passthrough IOCTL commands 35608c2ecf20Sopenharmony_ci * 35618c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 35628c2ecf20Sopenharmony_ci * @cmd: ioctl code 35638c2ecf20Sopenharmony_ci * @arg: pointer to pmcraid_passthrough_buffer user buffer 35648c2ecf20Sopenharmony_ci * 35658c2ecf20Sopenharmony_ci * Return value 35668c2ecf20Sopenharmony_ci * 0 on success, non-zero error code on failure 35678c2ecf20Sopenharmony_ci */ 35688c2ecf20Sopenharmony_cistatic long pmcraid_ioctl_passthrough( 35698c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 35708c2ecf20Sopenharmony_ci unsigned int ioctl_cmd, 35718c2ecf20Sopenharmony_ci unsigned int buflen, 35728c2ecf20Sopenharmony_ci void __user *arg 35738c2ecf20Sopenharmony_ci) 35748c2ecf20Sopenharmony_ci{ 35758c2ecf20Sopenharmony_ci struct pmcraid_passthrough_ioctl_buffer *buffer; 35768c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb; 35778c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd; 35788c2ecf20Sopenharmony_ci struct pmcraid_cmd *cancel_cmd; 35798c2ecf20Sopenharmony_ci void __user *request_buffer; 35808c2ecf20Sopenharmony_ci unsigned long request_offset; 35818c2ecf20Sopenharmony_ci unsigned long lock_flags; 35828c2ecf20Sopenharmony_ci void __user *ioasa; 35838c2ecf20Sopenharmony_ci u32 ioasc; 35848c2ecf20Sopenharmony_ci int request_size; 35858c2ecf20Sopenharmony_ci int buffer_size; 35868c2ecf20Sopenharmony_ci u8 direction; 35878c2ecf20Sopenharmony_ci int rc = 0; 35888c2ecf20Sopenharmony_ci 35898c2ecf20Sopenharmony_ci /* If IOA reset is in progress, wait 10 secs for reset to complete */ 35908c2ecf20Sopenharmony_ci if (pinstance->ioa_reset_in_progress) { 35918c2ecf20Sopenharmony_ci rc = wait_event_interruptible_timeout( 35928c2ecf20Sopenharmony_ci pinstance->reset_wait_q, 35938c2ecf20Sopenharmony_ci !pinstance->ioa_reset_in_progress, 35948c2ecf20Sopenharmony_ci msecs_to_jiffies(10000)); 35958c2ecf20Sopenharmony_ci 35968c2ecf20Sopenharmony_ci if (!rc) 35978c2ecf20Sopenharmony_ci return -ETIMEDOUT; 35988c2ecf20Sopenharmony_ci else if (rc < 0) 35998c2ecf20Sopenharmony_ci return -ERESTARTSYS; 36008c2ecf20Sopenharmony_ci } 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci /* If adapter is not in operational state, return error */ 36038c2ecf20Sopenharmony_ci if (pinstance->ioa_state != IOA_STATE_OPERATIONAL) { 36048c2ecf20Sopenharmony_ci pmcraid_err("IOA is not operational\n"); 36058c2ecf20Sopenharmony_ci return -ENOTTY; 36068c2ecf20Sopenharmony_ci } 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci buffer_size = sizeof(struct pmcraid_passthrough_ioctl_buffer); 36098c2ecf20Sopenharmony_ci buffer = kmalloc(buffer_size, GFP_KERNEL); 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci if (!buffer) { 36128c2ecf20Sopenharmony_ci pmcraid_err("no memory for passthrough buffer\n"); 36138c2ecf20Sopenharmony_ci return -ENOMEM; 36148c2ecf20Sopenharmony_ci } 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci request_offset = 36178c2ecf20Sopenharmony_ci offsetof(struct pmcraid_passthrough_ioctl_buffer, request_buffer); 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci request_buffer = arg + request_offset; 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci rc = copy_from_user(buffer, arg, 36228c2ecf20Sopenharmony_ci sizeof(struct pmcraid_passthrough_ioctl_buffer)); 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_ci ioasa = arg + offsetof(struct pmcraid_passthrough_ioctl_buffer, ioasa); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci if (rc) { 36278c2ecf20Sopenharmony_ci pmcraid_err("ioctl: can't copy passthrough buffer\n"); 36288c2ecf20Sopenharmony_ci rc = -EFAULT; 36298c2ecf20Sopenharmony_ci goto out_free_buffer; 36308c2ecf20Sopenharmony_ci } 36318c2ecf20Sopenharmony_ci 36328c2ecf20Sopenharmony_ci request_size = le32_to_cpu(buffer->ioarcb.data_transfer_length); 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci if (buffer->ioarcb.request_flags0 & TRANSFER_DIR_WRITE) { 36358c2ecf20Sopenharmony_ci direction = DMA_TO_DEVICE; 36368c2ecf20Sopenharmony_ci } else { 36378c2ecf20Sopenharmony_ci direction = DMA_FROM_DEVICE; 36388c2ecf20Sopenharmony_ci } 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_ci if (request_size < 0) { 36418c2ecf20Sopenharmony_ci rc = -EINVAL; 36428c2ecf20Sopenharmony_ci goto out_free_buffer; 36438c2ecf20Sopenharmony_ci } 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci /* check if we have any additional command parameters */ 36468c2ecf20Sopenharmony_ci if (le16_to_cpu(buffer->ioarcb.add_cmd_param_length) 36478c2ecf20Sopenharmony_ci > PMCRAID_ADD_CMD_PARAM_LEN) { 36488c2ecf20Sopenharmony_ci rc = -EINVAL; 36498c2ecf20Sopenharmony_ci goto out_free_buffer; 36508c2ecf20Sopenharmony_ci } 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci cmd = pmcraid_get_free_cmd(pinstance); 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_ci if (!cmd) { 36558c2ecf20Sopenharmony_ci pmcraid_err("free command block is not available\n"); 36568c2ecf20Sopenharmony_ci rc = -ENOMEM; 36578c2ecf20Sopenharmony_ci goto out_free_buffer; 36588c2ecf20Sopenharmony_ci } 36598c2ecf20Sopenharmony_ci 36608c2ecf20Sopenharmony_ci cmd->scsi_cmd = NULL; 36618c2ecf20Sopenharmony_ci ioarcb = &(cmd->ioa_cb->ioarcb); 36628c2ecf20Sopenharmony_ci 36638c2ecf20Sopenharmony_ci /* Copy the user-provided IOARCB stuff field by field */ 36648c2ecf20Sopenharmony_ci ioarcb->resource_handle = buffer->ioarcb.resource_handle; 36658c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = buffer->ioarcb.data_transfer_length; 36668c2ecf20Sopenharmony_ci ioarcb->cmd_timeout = buffer->ioarcb.cmd_timeout; 36678c2ecf20Sopenharmony_ci ioarcb->request_type = buffer->ioarcb.request_type; 36688c2ecf20Sopenharmony_ci ioarcb->request_flags0 = buffer->ioarcb.request_flags0; 36698c2ecf20Sopenharmony_ci ioarcb->request_flags1 = buffer->ioarcb.request_flags1; 36708c2ecf20Sopenharmony_ci memcpy(ioarcb->cdb, buffer->ioarcb.cdb, PMCRAID_MAX_CDB_LEN); 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci if (buffer->ioarcb.add_cmd_param_length) { 36738c2ecf20Sopenharmony_ci ioarcb->add_cmd_param_length = 36748c2ecf20Sopenharmony_ci buffer->ioarcb.add_cmd_param_length; 36758c2ecf20Sopenharmony_ci ioarcb->add_cmd_param_offset = 36768c2ecf20Sopenharmony_ci buffer->ioarcb.add_cmd_param_offset; 36778c2ecf20Sopenharmony_ci memcpy(ioarcb->add_data.u.add_cmd_params, 36788c2ecf20Sopenharmony_ci buffer->ioarcb.add_data.u.add_cmd_params, 36798c2ecf20Sopenharmony_ci le16_to_cpu(buffer->ioarcb.add_cmd_param_length)); 36808c2ecf20Sopenharmony_ci } 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci /* set hrrq number where the IOA should respond to. Note that all cmds 36838c2ecf20Sopenharmony_ci * generated internally uses hrrq_id 0, exception to this is the cmd 36848c2ecf20Sopenharmony_ci * block of scsi_cmd which is re-used (e.g. cancel/abort), which uses 36858c2ecf20Sopenharmony_ci * hrrq_id assigned here in queuecommand 36868c2ecf20Sopenharmony_ci */ 36878c2ecf20Sopenharmony_ci ioarcb->hrrq_id = atomic_add_return(1, &(pinstance->last_message_id)) % 36888c2ecf20Sopenharmony_ci pinstance->num_hrrq; 36898c2ecf20Sopenharmony_ci 36908c2ecf20Sopenharmony_ci if (request_size) { 36918c2ecf20Sopenharmony_ci rc = pmcraid_build_passthrough_ioadls(cmd, 36928c2ecf20Sopenharmony_ci request_size, 36938c2ecf20Sopenharmony_ci direction); 36948c2ecf20Sopenharmony_ci if (rc) { 36958c2ecf20Sopenharmony_ci pmcraid_err("couldn't build passthrough ioadls\n"); 36968c2ecf20Sopenharmony_ci goto out_free_cmd; 36978c2ecf20Sopenharmony_ci } 36988c2ecf20Sopenharmony_ci } 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_ci /* If data is being written into the device, copy the data from user 37018c2ecf20Sopenharmony_ci * buffers 37028c2ecf20Sopenharmony_ci */ 37038c2ecf20Sopenharmony_ci if (direction == DMA_TO_DEVICE && request_size > 0) { 37048c2ecf20Sopenharmony_ci rc = pmcraid_copy_sglist(cmd->sglist, 37058c2ecf20Sopenharmony_ci request_buffer, 37068c2ecf20Sopenharmony_ci request_size, 37078c2ecf20Sopenharmony_ci direction); 37088c2ecf20Sopenharmony_ci if (rc) { 37098c2ecf20Sopenharmony_ci pmcraid_err("failed to copy user buffer\n"); 37108c2ecf20Sopenharmony_ci goto out_free_sglist; 37118c2ecf20Sopenharmony_ci } 37128c2ecf20Sopenharmony_ci } 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci /* passthrough ioctl is a blocking command so, put the user to sleep 37158c2ecf20Sopenharmony_ci * until timeout. Note that a timeout value of 0 means, do timeout. 37168c2ecf20Sopenharmony_ci */ 37178c2ecf20Sopenharmony_ci cmd->cmd_done = pmcraid_internal_done; 37188c2ecf20Sopenharmony_ci init_completion(&cmd->wait_for_completion); 37198c2ecf20Sopenharmony_ci cmd->completion_req = 1; 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci pmcraid_info("command(%d) (CDB[0] = %x) for %x\n", 37228c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2, 37238c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0], 37248c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle)); 37258c2ecf20Sopenharmony_ci 37268c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 37278c2ecf20Sopenharmony_ci _pmcraid_fire_command(cmd); 37288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci /* NOTE ! Remove the below line once abort_task is implemented 37318c2ecf20Sopenharmony_ci * in firmware. This line disables ioctl command timeout handling logic 37328c2ecf20Sopenharmony_ci * similar to IO command timeout handling, making ioctl commands to wait 37338c2ecf20Sopenharmony_ci * until the command completion regardless of timeout value specified in 37348c2ecf20Sopenharmony_ci * ioarcb 37358c2ecf20Sopenharmony_ci */ 37368c2ecf20Sopenharmony_ci buffer->ioarcb.cmd_timeout = 0; 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci /* If command timeout is specified put caller to wait till that time, 37398c2ecf20Sopenharmony_ci * otherwise it would be blocking wait. If command gets timed out, it 37408c2ecf20Sopenharmony_ci * will be aborted. 37418c2ecf20Sopenharmony_ci */ 37428c2ecf20Sopenharmony_ci if (buffer->ioarcb.cmd_timeout == 0) { 37438c2ecf20Sopenharmony_ci wait_for_completion(&cmd->wait_for_completion); 37448c2ecf20Sopenharmony_ci } else if (!wait_for_completion_timeout( 37458c2ecf20Sopenharmony_ci &cmd->wait_for_completion, 37468c2ecf20Sopenharmony_ci msecs_to_jiffies(le16_to_cpu(buffer->ioarcb.cmd_timeout) * 1000))) { 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci pmcraid_info("aborting cmd %d (CDB[0] = %x) due to timeout\n", 37498c2ecf20Sopenharmony_ci le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2, 37508c2ecf20Sopenharmony_ci cmd->ioa_cb->ioarcb.cdb[0]); 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 37538c2ecf20Sopenharmony_ci cancel_cmd = pmcraid_abort_cmd(cmd); 37548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci if (cancel_cmd) { 37578c2ecf20Sopenharmony_ci wait_for_completion(&cancel_cmd->wait_for_completion); 37588c2ecf20Sopenharmony_ci ioasc = le32_to_cpu(cancel_cmd->ioa_cb->ioasa.ioasc); 37598c2ecf20Sopenharmony_ci pmcraid_return_cmd(cancel_cmd); 37608c2ecf20Sopenharmony_ci 37618c2ecf20Sopenharmony_ci /* if abort task couldn't find the command i.e it got 37628c2ecf20Sopenharmony_ci * completed prior to aborting, return good completion. 37638c2ecf20Sopenharmony_ci * if command got aborted successfully or there was IOA 37648c2ecf20Sopenharmony_ci * reset due to abort task itself getting timedout then 37658c2ecf20Sopenharmony_ci * return -ETIMEDOUT 37668c2ecf20Sopenharmony_ci */ 37678c2ecf20Sopenharmony_ci if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET || 37688c2ecf20Sopenharmony_ci PMCRAID_IOASC_SENSE_KEY(ioasc) == 0x00) { 37698c2ecf20Sopenharmony_ci if (ioasc != PMCRAID_IOASC_GC_IOARCB_NOTFOUND) 37708c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 37718c2ecf20Sopenharmony_ci goto out_handle_response; 37728c2ecf20Sopenharmony_ci } 37738c2ecf20Sopenharmony_ci } 37748c2ecf20Sopenharmony_ci 37758c2ecf20Sopenharmony_ci /* no command block for abort task or abort task failed to abort 37768c2ecf20Sopenharmony_ci * the IOARCB, then wait for 150 more seconds and initiate reset 37778c2ecf20Sopenharmony_ci * sequence after timeout 37788c2ecf20Sopenharmony_ci */ 37798c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout( 37808c2ecf20Sopenharmony_ci &cmd->wait_for_completion, 37818c2ecf20Sopenharmony_ci msecs_to_jiffies(150 * 1000))) { 37828c2ecf20Sopenharmony_ci pmcraid_reset_bringup(cmd->drv_inst); 37838c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 37848c2ecf20Sopenharmony_ci } 37858c2ecf20Sopenharmony_ci } 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ciout_handle_response: 37888c2ecf20Sopenharmony_ci /* copy entire IOASA buffer and return IOCTL success. 37898c2ecf20Sopenharmony_ci * If copying IOASA to user-buffer fails, return 37908c2ecf20Sopenharmony_ci * EFAULT 37918c2ecf20Sopenharmony_ci */ 37928c2ecf20Sopenharmony_ci if (copy_to_user(ioasa, &cmd->ioa_cb->ioasa, 37938c2ecf20Sopenharmony_ci sizeof(struct pmcraid_ioasa))) { 37948c2ecf20Sopenharmony_ci pmcraid_err("failed to copy ioasa buffer to user\n"); 37958c2ecf20Sopenharmony_ci rc = -EFAULT; 37968c2ecf20Sopenharmony_ci } 37978c2ecf20Sopenharmony_ci 37988c2ecf20Sopenharmony_ci /* If the data transfer was from device, copy the data onto user 37998c2ecf20Sopenharmony_ci * buffers 38008c2ecf20Sopenharmony_ci */ 38018c2ecf20Sopenharmony_ci else if (direction == DMA_FROM_DEVICE && request_size > 0) { 38028c2ecf20Sopenharmony_ci rc = pmcraid_copy_sglist(cmd->sglist, 38038c2ecf20Sopenharmony_ci request_buffer, 38048c2ecf20Sopenharmony_ci request_size, 38058c2ecf20Sopenharmony_ci direction); 38068c2ecf20Sopenharmony_ci if (rc) { 38078c2ecf20Sopenharmony_ci pmcraid_err("failed to copy user buffer\n"); 38088c2ecf20Sopenharmony_ci rc = -EFAULT; 38098c2ecf20Sopenharmony_ci } 38108c2ecf20Sopenharmony_ci } 38118c2ecf20Sopenharmony_ci 38128c2ecf20Sopenharmony_ciout_free_sglist: 38138c2ecf20Sopenharmony_ci pmcraid_release_passthrough_ioadls(cmd, request_size, direction); 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ciout_free_cmd: 38168c2ecf20Sopenharmony_ci pmcraid_return_cmd(cmd); 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ciout_free_buffer: 38198c2ecf20Sopenharmony_ci kfree(buffer); 38208c2ecf20Sopenharmony_ci 38218c2ecf20Sopenharmony_ci return rc; 38228c2ecf20Sopenharmony_ci} 38238c2ecf20Sopenharmony_ci 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci 38278c2ecf20Sopenharmony_ci/** 38288c2ecf20Sopenharmony_ci * pmcraid_ioctl_driver - ioctl handler for commands handled by driver itself 38298c2ecf20Sopenharmony_ci * 38308c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 38318c2ecf20Sopenharmony_ci * @cmd: ioctl command passed in 38328c2ecf20Sopenharmony_ci * @buflen: length of user_buffer 38338c2ecf20Sopenharmony_ci * @user_buffer: user buffer pointer 38348c2ecf20Sopenharmony_ci * 38358c2ecf20Sopenharmony_ci * Return Value 38368c2ecf20Sopenharmony_ci * 0 in case of success, otherwise appropriate error code 38378c2ecf20Sopenharmony_ci */ 38388c2ecf20Sopenharmony_cistatic long pmcraid_ioctl_driver( 38398c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 38408c2ecf20Sopenharmony_ci unsigned int cmd, 38418c2ecf20Sopenharmony_ci unsigned int buflen, 38428c2ecf20Sopenharmony_ci void __user *user_buffer 38438c2ecf20Sopenharmony_ci) 38448c2ecf20Sopenharmony_ci{ 38458c2ecf20Sopenharmony_ci int rc = -ENOSYS; 38468c2ecf20Sopenharmony_ci 38478c2ecf20Sopenharmony_ci switch (cmd) { 38488c2ecf20Sopenharmony_ci case PMCRAID_IOCTL_RESET_ADAPTER: 38498c2ecf20Sopenharmony_ci pmcraid_reset_bringup(pinstance); 38508c2ecf20Sopenharmony_ci rc = 0; 38518c2ecf20Sopenharmony_ci break; 38528c2ecf20Sopenharmony_ci 38538c2ecf20Sopenharmony_ci default: 38548c2ecf20Sopenharmony_ci break; 38558c2ecf20Sopenharmony_ci } 38568c2ecf20Sopenharmony_ci 38578c2ecf20Sopenharmony_ci return rc; 38588c2ecf20Sopenharmony_ci} 38598c2ecf20Sopenharmony_ci 38608c2ecf20Sopenharmony_ci/** 38618c2ecf20Sopenharmony_ci * pmcraid_check_ioctl_buffer - check for proper access to user buffer 38628c2ecf20Sopenharmony_ci * 38638c2ecf20Sopenharmony_ci * @cmd: ioctl command 38648c2ecf20Sopenharmony_ci * @arg: user buffer 38658c2ecf20Sopenharmony_ci * @hdr: pointer to kernel memory for pmcraid_ioctl_header 38668c2ecf20Sopenharmony_ci * 38678c2ecf20Sopenharmony_ci * Return Value 38688c2ecf20Sopenharmony_ci * negetive error code if there are access issues, otherwise zero. 38698c2ecf20Sopenharmony_ci * Upon success, returns ioctl header copied out of user buffer. 38708c2ecf20Sopenharmony_ci */ 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_cistatic int pmcraid_check_ioctl_buffer( 38738c2ecf20Sopenharmony_ci int cmd, 38748c2ecf20Sopenharmony_ci void __user *arg, 38758c2ecf20Sopenharmony_ci struct pmcraid_ioctl_header *hdr 38768c2ecf20Sopenharmony_ci) 38778c2ecf20Sopenharmony_ci{ 38788c2ecf20Sopenharmony_ci int rc; 38798c2ecf20Sopenharmony_ci 38808c2ecf20Sopenharmony_ci if (copy_from_user(hdr, arg, sizeof(struct pmcraid_ioctl_header))) { 38818c2ecf20Sopenharmony_ci pmcraid_err("couldn't copy ioctl header from user buffer\n"); 38828c2ecf20Sopenharmony_ci return -EFAULT; 38838c2ecf20Sopenharmony_ci } 38848c2ecf20Sopenharmony_ci 38858c2ecf20Sopenharmony_ci /* check for valid driver signature */ 38868c2ecf20Sopenharmony_ci rc = memcmp(hdr->signature, 38878c2ecf20Sopenharmony_ci PMCRAID_IOCTL_SIGNATURE, 38888c2ecf20Sopenharmony_ci sizeof(hdr->signature)); 38898c2ecf20Sopenharmony_ci if (rc) { 38908c2ecf20Sopenharmony_ci pmcraid_err("signature verification failed\n"); 38918c2ecf20Sopenharmony_ci return -EINVAL; 38928c2ecf20Sopenharmony_ci } 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_ci return 0; 38958c2ecf20Sopenharmony_ci} 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci/** 38988c2ecf20Sopenharmony_ci * pmcraid_ioctl - char node ioctl entry point 38998c2ecf20Sopenharmony_ci */ 39008c2ecf20Sopenharmony_cistatic long pmcraid_chr_ioctl( 39018c2ecf20Sopenharmony_ci struct file *filep, 39028c2ecf20Sopenharmony_ci unsigned int cmd, 39038c2ecf20Sopenharmony_ci unsigned long arg 39048c2ecf20Sopenharmony_ci) 39058c2ecf20Sopenharmony_ci{ 39068c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = NULL; 39078c2ecf20Sopenharmony_ci struct pmcraid_ioctl_header *hdr = NULL; 39088c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 39098c2ecf20Sopenharmony_ci int retval = -ENOTTY; 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_ci hdr = kmalloc(sizeof(struct pmcraid_ioctl_header), GFP_KERNEL); 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci if (!hdr) { 39148c2ecf20Sopenharmony_ci pmcraid_err("failed to allocate memory for ioctl header\n"); 39158c2ecf20Sopenharmony_ci return -ENOMEM; 39168c2ecf20Sopenharmony_ci } 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci retval = pmcraid_check_ioctl_buffer(cmd, argp, hdr); 39198c2ecf20Sopenharmony_ci 39208c2ecf20Sopenharmony_ci if (retval) { 39218c2ecf20Sopenharmony_ci pmcraid_info("chr_ioctl: header check failed\n"); 39228c2ecf20Sopenharmony_ci kfree(hdr); 39238c2ecf20Sopenharmony_ci return retval; 39248c2ecf20Sopenharmony_ci } 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci pinstance = filep->private_data; 39278c2ecf20Sopenharmony_ci 39288c2ecf20Sopenharmony_ci if (!pinstance) { 39298c2ecf20Sopenharmony_ci pmcraid_info("adapter instance is not found\n"); 39308c2ecf20Sopenharmony_ci kfree(hdr); 39318c2ecf20Sopenharmony_ci return -ENOTTY; 39328c2ecf20Sopenharmony_ci } 39338c2ecf20Sopenharmony_ci 39348c2ecf20Sopenharmony_ci switch (_IOC_TYPE(cmd)) { 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci case PMCRAID_PASSTHROUGH_IOCTL: 39378c2ecf20Sopenharmony_ci /* If ioctl code is to download microcode, we need to block 39388c2ecf20Sopenharmony_ci * mid-layer requests. 39398c2ecf20Sopenharmony_ci */ 39408c2ecf20Sopenharmony_ci if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE) 39418c2ecf20Sopenharmony_ci scsi_block_requests(pinstance->host); 39428c2ecf20Sopenharmony_ci 39438c2ecf20Sopenharmony_ci retval = pmcraid_ioctl_passthrough(pinstance, cmd, 39448c2ecf20Sopenharmony_ci hdr->buffer_length, argp); 39458c2ecf20Sopenharmony_ci 39468c2ecf20Sopenharmony_ci if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE) 39478c2ecf20Sopenharmony_ci scsi_unblock_requests(pinstance->host); 39488c2ecf20Sopenharmony_ci break; 39498c2ecf20Sopenharmony_ci 39508c2ecf20Sopenharmony_ci case PMCRAID_DRIVER_IOCTL: 39518c2ecf20Sopenharmony_ci arg += sizeof(struct pmcraid_ioctl_header); 39528c2ecf20Sopenharmony_ci retval = pmcraid_ioctl_driver(pinstance, cmd, 39538c2ecf20Sopenharmony_ci hdr->buffer_length, argp); 39548c2ecf20Sopenharmony_ci break; 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci default: 39578c2ecf20Sopenharmony_ci retval = -ENOTTY; 39588c2ecf20Sopenharmony_ci break; 39598c2ecf20Sopenharmony_ci } 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci kfree(hdr); 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci return retval; 39648c2ecf20Sopenharmony_ci} 39658c2ecf20Sopenharmony_ci 39668c2ecf20Sopenharmony_ci/** 39678c2ecf20Sopenharmony_ci * File operations structure for management interface 39688c2ecf20Sopenharmony_ci */ 39698c2ecf20Sopenharmony_cistatic const struct file_operations pmcraid_fops = { 39708c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 39718c2ecf20Sopenharmony_ci .open = pmcraid_chr_open, 39728c2ecf20Sopenharmony_ci .fasync = pmcraid_chr_fasync, 39738c2ecf20Sopenharmony_ci .unlocked_ioctl = pmcraid_chr_ioctl, 39748c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 39758c2ecf20Sopenharmony_ci .llseek = noop_llseek, 39768c2ecf20Sopenharmony_ci}; 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci/** 39828c2ecf20Sopenharmony_ci * pmcraid_show_log_level - Display adapter's error logging level 39838c2ecf20Sopenharmony_ci * @dev: class device struct 39848c2ecf20Sopenharmony_ci * @buf: buffer 39858c2ecf20Sopenharmony_ci * 39868c2ecf20Sopenharmony_ci * Return value: 39878c2ecf20Sopenharmony_ci * number of bytes printed to buffer 39888c2ecf20Sopenharmony_ci */ 39898c2ecf20Sopenharmony_cistatic ssize_t pmcraid_show_log_level( 39908c2ecf20Sopenharmony_ci struct device *dev, 39918c2ecf20Sopenharmony_ci struct device_attribute *attr, 39928c2ecf20Sopenharmony_ci char *buf) 39938c2ecf20Sopenharmony_ci{ 39948c2ecf20Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 39958c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = 39968c2ecf20Sopenharmony_ci (struct pmcraid_instance *)shost->hostdata; 39978c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", pinstance->current_log_level); 39988c2ecf20Sopenharmony_ci} 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci/** 40018c2ecf20Sopenharmony_ci * pmcraid_store_log_level - Change the adapter's error logging level 40028c2ecf20Sopenharmony_ci * @dev: class device struct 40038c2ecf20Sopenharmony_ci * @buf: buffer 40048c2ecf20Sopenharmony_ci * @count: not used 40058c2ecf20Sopenharmony_ci * 40068c2ecf20Sopenharmony_ci * Return value: 40078c2ecf20Sopenharmony_ci * number of bytes printed to buffer 40088c2ecf20Sopenharmony_ci */ 40098c2ecf20Sopenharmony_cistatic ssize_t pmcraid_store_log_level( 40108c2ecf20Sopenharmony_ci struct device *dev, 40118c2ecf20Sopenharmony_ci struct device_attribute *attr, 40128c2ecf20Sopenharmony_ci const char *buf, 40138c2ecf20Sopenharmony_ci size_t count 40148c2ecf20Sopenharmony_ci) 40158c2ecf20Sopenharmony_ci{ 40168c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 40178c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 40188c2ecf20Sopenharmony_ci u8 val; 40198c2ecf20Sopenharmony_ci 40208c2ecf20Sopenharmony_ci if (kstrtou8(buf, 10, &val)) 40218c2ecf20Sopenharmony_ci return -EINVAL; 40228c2ecf20Sopenharmony_ci /* log-level should be from 0 to 2 */ 40238c2ecf20Sopenharmony_ci if (val > 2) 40248c2ecf20Sopenharmony_ci return -EINVAL; 40258c2ecf20Sopenharmony_ci 40268c2ecf20Sopenharmony_ci shost = class_to_shost(dev); 40278c2ecf20Sopenharmony_ci pinstance = (struct pmcraid_instance *)shost->hostdata; 40288c2ecf20Sopenharmony_ci pinstance->current_log_level = val; 40298c2ecf20Sopenharmony_ci 40308c2ecf20Sopenharmony_ci return strlen(buf); 40318c2ecf20Sopenharmony_ci} 40328c2ecf20Sopenharmony_ci 40338c2ecf20Sopenharmony_cistatic struct device_attribute pmcraid_log_level_attr = { 40348c2ecf20Sopenharmony_ci .attr = { 40358c2ecf20Sopenharmony_ci .name = "log_level", 40368c2ecf20Sopenharmony_ci .mode = S_IRUGO | S_IWUSR, 40378c2ecf20Sopenharmony_ci }, 40388c2ecf20Sopenharmony_ci .show = pmcraid_show_log_level, 40398c2ecf20Sopenharmony_ci .store = pmcraid_store_log_level, 40408c2ecf20Sopenharmony_ci}; 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_ci/** 40438c2ecf20Sopenharmony_ci * pmcraid_show_drv_version - Display driver version 40448c2ecf20Sopenharmony_ci * @dev: class device struct 40458c2ecf20Sopenharmony_ci * @buf: buffer 40468c2ecf20Sopenharmony_ci * 40478c2ecf20Sopenharmony_ci * Return value: 40488c2ecf20Sopenharmony_ci * number of bytes printed to buffer 40498c2ecf20Sopenharmony_ci */ 40508c2ecf20Sopenharmony_cistatic ssize_t pmcraid_show_drv_version( 40518c2ecf20Sopenharmony_ci struct device *dev, 40528c2ecf20Sopenharmony_ci struct device_attribute *attr, 40538c2ecf20Sopenharmony_ci char *buf 40548c2ecf20Sopenharmony_ci) 40558c2ecf20Sopenharmony_ci{ 40568c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "version: %s\n", 40578c2ecf20Sopenharmony_ci PMCRAID_DRIVER_VERSION); 40588c2ecf20Sopenharmony_ci} 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_cistatic struct device_attribute pmcraid_driver_version_attr = { 40618c2ecf20Sopenharmony_ci .attr = { 40628c2ecf20Sopenharmony_ci .name = "drv_version", 40638c2ecf20Sopenharmony_ci .mode = S_IRUGO, 40648c2ecf20Sopenharmony_ci }, 40658c2ecf20Sopenharmony_ci .show = pmcraid_show_drv_version, 40668c2ecf20Sopenharmony_ci}; 40678c2ecf20Sopenharmony_ci 40688c2ecf20Sopenharmony_ci/** 40698c2ecf20Sopenharmony_ci * pmcraid_show_io_adapter_id - Display driver assigned adapter id 40708c2ecf20Sopenharmony_ci * @dev: class device struct 40718c2ecf20Sopenharmony_ci * @buf: buffer 40728c2ecf20Sopenharmony_ci * 40738c2ecf20Sopenharmony_ci * Return value: 40748c2ecf20Sopenharmony_ci * number of bytes printed to buffer 40758c2ecf20Sopenharmony_ci */ 40768c2ecf20Sopenharmony_cistatic ssize_t pmcraid_show_adapter_id( 40778c2ecf20Sopenharmony_ci struct device *dev, 40788c2ecf20Sopenharmony_ci struct device_attribute *attr, 40798c2ecf20Sopenharmony_ci char *buf 40808c2ecf20Sopenharmony_ci) 40818c2ecf20Sopenharmony_ci{ 40828c2ecf20Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 40838c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = 40848c2ecf20Sopenharmony_ci (struct pmcraid_instance *)shost->hostdata; 40858c2ecf20Sopenharmony_ci u32 adapter_id = (pinstance->pdev->bus->number << 8) | 40868c2ecf20Sopenharmony_ci pinstance->pdev->devfn; 40878c2ecf20Sopenharmony_ci u32 aen_group = pmcraid_event_family.id; 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, 40908c2ecf20Sopenharmony_ci "adapter id: %d\nminor: %d\naen group: %d\n", 40918c2ecf20Sopenharmony_ci adapter_id, MINOR(pinstance->cdev.dev), aen_group); 40928c2ecf20Sopenharmony_ci} 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_cistatic struct device_attribute pmcraid_adapter_id_attr = { 40958c2ecf20Sopenharmony_ci .attr = { 40968c2ecf20Sopenharmony_ci .name = "adapter_id", 40978c2ecf20Sopenharmony_ci .mode = S_IRUGO, 40988c2ecf20Sopenharmony_ci }, 40998c2ecf20Sopenharmony_ci .show = pmcraid_show_adapter_id, 41008c2ecf20Sopenharmony_ci}; 41018c2ecf20Sopenharmony_ci 41028c2ecf20Sopenharmony_cistatic struct device_attribute *pmcraid_host_attrs[] = { 41038c2ecf20Sopenharmony_ci &pmcraid_log_level_attr, 41048c2ecf20Sopenharmony_ci &pmcraid_driver_version_attr, 41058c2ecf20Sopenharmony_ci &pmcraid_adapter_id_attr, 41068c2ecf20Sopenharmony_ci NULL, 41078c2ecf20Sopenharmony_ci}; 41088c2ecf20Sopenharmony_ci 41098c2ecf20Sopenharmony_ci 41108c2ecf20Sopenharmony_ci/* host template structure for pmcraid driver */ 41118c2ecf20Sopenharmony_cistatic struct scsi_host_template pmcraid_host_template = { 41128c2ecf20Sopenharmony_ci .module = THIS_MODULE, 41138c2ecf20Sopenharmony_ci .name = PMCRAID_DRIVER_NAME, 41148c2ecf20Sopenharmony_ci .queuecommand = pmcraid_queuecommand, 41158c2ecf20Sopenharmony_ci .eh_abort_handler = pmcraid_eh_abort_handler, 41168c2ecf20Sopenharmony_ci .eh_bus_reset_handler = pmcraid_eh_bus_reset_handler, 41178c2ecf20Sopenharmony_ci .eh_target_reset_handler = pmcraid_eh_target_reset_handler, 41188c2ecf20Sopenharmony_ci .eh_device_reset_handler = pmcraid_eh_device_reset_handler, 41198c2ecf20Sopenharmony_ci .eh_host_reset_handler = pmcraid_eh_host_reset_handler, 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci .slave_alloc = pmcraid_slave_alloc, 41228c2ecf20Sopenharmony_ci .slave_configure = pmcraid_slave_configure, 41238c2ecf20Sopenharmony_ci .slave_destroy = pmcraid_slave_destroy, 41248c2ecf20Sopenharmony_ci .change_queue_depth = pmcraid_change_queue_depth, 41258c2ecf20Sopenharmony_ci .can_queue = PMCRAID_MAX_IO_CMD, 41268c2ecf20Sopenharmony_ci .this_id = -1, 41278c2ecf20Sopenharmony_ci .sg_tablesize = PMCRAID_MAX_IOADLS, 41288c2ecf20Sopenharmony_ci .max_sectors = PMCRAID_IOA_MAX_SECTORS, 41298c2ecf20Sopenharmony_ci .no_write_same = 1, 41308c2ecf20Sopenharmony_ci .cmd_per_lun = PMCRAID_MAX_CMD_PER_LUN, 41318c2ecf20Sopenharmony_ci .shost_attrs = pmcraid_host_attrs, 41328c2ecf20Sopenharmony_ci .proc_name = PMCRAID_DRIVER_NAME, 41338c2ecf20Sopenharmony_ci}; 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci/* 41368c2ecf20Sopenharmony_ci * pmcraid_isr_msix - implements MSI-X interrupt handling routine 41378c2ecf20Sopenharmony_ci * @irq: interrupt vector number 41388c2ecf20Sopenharmony_ci * @dev_id: pointer hrrq_vector 41398c2ecf20Sopenharmony_ci * 41408c2ecf20Sopenharmony_ci * Return Value 41418c2ecf20Sopenharmony_ci * IRQ_HANDLED if interrupt is handled or IRQ_NONE if ignored 41428c2ecf20Sopenharmony_ci */ 41438c2ecf20Sopenharmony_ci 41448c2ecf20Sopenharmony_cistatic irqreturn_t pmcraid_isr_msix(int irq, void *dev_id) 41458c2ecf20Sopenharmony_ci{ 41468c2ecf20Sopenharmony_ci struct pmcraid_isr_param *hrrq_vector; 41478c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 41488c2ecf20Sopenharmony_ci unsigned long lock_flags; 41498c2ecf20Sopenharmony_ci u32 intrs_val; 41508c2ecf20Sopenharmony_ci int hrrq_id; 41518c2ecf20Sopenharmony_ci 41528c2ecf20Sopenharmony_ci hrrq_vector = (struct pmcraid_isr_param *)dev_id; 41538c2ecf20Sopenharmony_ci hrrq_id = hrrq_vector->hrrq_id; 41548c2ecf20Sopenharmony_ci pinstance = hrrq_vector->drv_inst; 41558c2ecf20Sopenharmony_ci 41568c2ecf20Sopenharmony_ci if (!hrrq_id) { 41578c2ecf20Sopenharmony_ci /* Read the interrupt */ 41588c2ecf20Sopenharmony_ci intrs_val = pmcraid_read_interrupts(pinstance); 41598c2ecf20Sopenharmony_ci if (intrs_val && 41608c2ecf20Sopenharmony_ci ((ioread32(pinstance->int_regs.host_ioa_interrupt_reg) 41618c2ecf20Sopenharmony_ci & DOORBELL_INTR_MSIX_CLR) == 0)) { 41628c2ecf20Sopenharmony_ci /* Any error interrupts including unit_check, 41638c2ecf20Sopenharmony_ci * initiate IOA reset.In case of unit check indicate 41648c2ecf20Sopenharmony_ci * to reset_sequence that IOA unit checked and prepare 41658c2ecf20Sopenharmony_ci * for a dump during reset sequence 41668c2ecf20Sopenharmony_ci */ 41678c2ecf20Sopenharmony_ci if (intrs_val & PMCRAID_ERROR_INTERRUPTS) { 41688c2ecf20Sopenharmony_ci if (intrs_val & INTRS_IOA_UNIT_CHECK) 41698c2ecf20Sopenharmony_ci pinstance->ioa_unit_check = 1; 41708c2ecf20Sopenharmony_ci 41718c2ecf20Sopenharmony_ci pmcraid_err("ISR: error interrupts: %x \ 41728c2ecf20Sopenharmony_ci initiating reset\n", intrs_val); 41738c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, 41748c2ecf20Sopenharmony_ci lock_flags); 41758c2ecf20Sopenharmony_ci pmcraid_initiate_reset(pinstance); 41768c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 41778c2ecf20Sopenharmony_ci pinstance->host->host_lock, 41788c2ecf20Sopenharmony_ci lock_flags); 41798c2ecf20Sopenharmony_ci } 41808c2ecf20Sopenharmony_ci /* If interrupt was as part of the ioa initialization, 41818c2ecf20Sopenharmony_ci * clear it. Delete the timer and wakeup the 41828c2ecf20Sopenharmony_ci * reset engine to proceed with reset sequence 41838c2ecf20Sopenharmony_ci */ 41848c2ecf20Sopenharmony_ci if (intrs_val & INTRS_TRANSITION_TO_OPERATIONAL) 41858c2ecf20Sopenharmony_ci pmcraid_clr_trans_op(pinstance); 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci /* Clear the interrupt register by writing 41888c2ecf20Sopenharmony_ci * to host to ioa doorbell. Once done 41898c2ecf20Sopenharmony_ci * FW will clear the interrupt. 41908c2ecf20Sopenharmony_ci */ 41918c2ecf20Sopenharmony_ci iowrite32(DOORBELL_INTR_MSIX_CLR, 41928c2ecf20Sopenharmony_ci pinstance->int_regs.host_ioa_interrupt_reg); 41938c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.host_ioa_interrupt_reg); 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci 41968c2ecf20Sopenharmony_ci } 41978c2ecf20Sopenharmony_ci } 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci tasklet_schedule(&(pinstance->isr_tasklet[hrrq_id])); 42008c2ecf20Sopenharmony_ci 42018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 42028c2ecf20Sopenharmony_ci} 42038c2ecf20Sopenharmony_ci 42048c2ecf20Sopenharmony_ci/** 42058c2ecf20Sopenharmony_ci * pmcraid_isr - implements legacy interrupt handling routine 42068c2ecf20Sopenharmony_ci * 42078c2ecf20Sopenharmony_ci * @irq: interrupt vector number 42088c2ecf20Sopenharmony_ci * @dev_id: pointer hrrq_vector 42098c2ecf20Sopenharmony_ci * 42108c2ecf20Sopenharmony_ci * Return Value 42118c2ecf20Sopenharmony_ci * IRQ_HANDLED if interrupt is handled or IRQ_NONE if ignored 42128c2ecf20Sopenharmony_ci */ 42138c2ecf20Sopenharmony_cistatic irqreturn_t pmcraid_isr(int irq, void *dev_id) 42148c2ecf20Sopenharmony_ci{ 42158c2ecf20Sopenharmony_ci struct pmcraid_isr_param *hrrq_vector; 42168c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 42178c2ecf20Sopenharmony_ci u32 intrs; 42188c2ecf20Sopenharmony_ci unsigned long lock_flags; 42198c2ecf20Sopenharmony_ci int hrrq_id = 0; 42208c2ecf20Sopenharmony_ci 42218c2ecf20Sopenharmony_ci /* In case of legacy interrupt mode where interrupts are shared across 42228c2ecf20Sopenharmony_ci * isrs, it may be possible that the current interrupt is not from IOA 42238c2ecf20Sopenharmony_ci */ 42248c2ecf20Sopenharmony_ci if (!dev_id) { 42258c2ecf20Sopenharmony_ci printk(KERN_INFO "%s(): NULL host pointer\n", __func__); 42268c2ecf20Sopenharmony_ci return IRQ_NONE; 42278c2ecf20Sopenharmony_ci } 42288c2ecf20Sopenharmony_ci hrrq_vector = (struct pmcraid_isr_param *)dev_id; 42298c2ecf20Sopenharmony_ci pinstance = hrrq_vector->drv_inst; 42308c2ecf20Sopenharmony_ci 42318c2ecf20Sopenharmony_ci intrs = pmcraid_read_interrupts(pinstance); 42328c2ecf20Sopenharmony_ci 42338c2ecf20Sopenharmony_ci if (unlikely((intrs & PMCRAID_PCI_INTERRUPTS) == 0)) 42348c2ecf20Sopenharmony_ci return IRQ_NONE; 42358c2ecf20Sopenharmony_ci 42368c2ecf20Sopenharmony_ci /* Any error interrupts including unit_check, initiate IOA reset. 42378c2ecf20Sopenharmony_ci * In case of unit check indicate to reset_sequence that IOA unit 42388c2ecf20Sopenharmony_ci * checked and prepare for a dump during reset sequence 42398c2ecf20Sopenharmony_ci */ 42408c2ecf20Sopenharmony_ci if (intrs & PMCRAID_ERROR_INTERRUPTS) { 42418c2ecf20Sopenharmony_ci 42428c2ecf20Sopenharmony_ci if (intrs & INTRS_IOA_UNIT_CHECK) 42438c2ecf20Sopenharmony_ci pinstance->ioa_unit_check = 1; 42448c2ecf20Sopenharmony_ci 42458c2ecf20Sopenharmony_ci iowrite32(intrs, 42468c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_clr_reg); 42478c2ecf20Sopenharmony_ci pmcraid_err("ISR: error interrupts: %x initiating reset\n", 42488c2ecf20Sopenharmony_ci intrs); 42498c2ecf20Sopenharmony_ci intrs = ioread32( 42508c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_clr_reg); 42518c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, lock_flags); 42528c2ecf20Sopenharmony_ci pmcraid_initiate_reset(pinstance); 42538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); 42548c2ecf20Sopenharmony_ci } else { 42558c2ecf20Sopenharmony_ci /* If interrupt was as part of the ioa initialization, 42568c2ecf20Sopenharmony_ci * clear. Delete the timer and wakeup the 42578c2ecf20Sopenharmony_ci * reset engine to proceed with reset sequence 42588c2ecf20Sopenharmony_ci */ 42598c2ecf20Sopenharmony_ci if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) { 42608c2ecf20Sopenharmony_ci pmcraid_clr_trans_op(pinstance); 42618c2ecf20Sopenharmony_ci } else { 42628c2ecf20Sopenharmony_ci iowrite32(intrs, 42638c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_clr_reg); 42648c2ecf20Sopenharmony_ci ioread32( 42658c2ecf20Sopenharmony_ci pinstance->int_regs.ioa_host_interrupt_clr_reg); 42668c2ecf20Sopenharmony_ci 42678c2ecf20Sopenharmony_ci tasklet_schedule( 42688c2ecf20Sopenharmony_ci &(pinstance->isr_tasklet[hrrq_id])); 42698c2ecf20Sopenharmony_ci } 42708c2ecf20Sopenharmony_ci } 42718c2ecf20Sopenharmony_ci 42728c2ecf20Sopenharmony_ci return IRQ_HANDLED; 42738c2ecf20Sopenharmony_ci} 42748c2ecf20Sopenharmony_ci 42758c2ecf20Sopenharmony_ci 42768c2ecf20Sopenharmony_ci/** 42778c2ecf20Sopenharmony_ci * pmcraid_worker_function - worker thread function 42788c2ecf20Sopenharmony_ci * 42798c2ecf20Sopenharmony_ci * @workp: pointer to struct work queue 42808c2ecf20Sopenharmony_ci * 42818c2ecf20Sopenharmony_ci * Return Value 42828c2ecf20Sopenharmony_ci * None 42838c2ecf20Sopenharmony_ci */ 42848c2ecf20Sopenharmony_ci 42858c2ecf20Sopenharmony_cistatic void pmcraid_worker_function(struct work_struct *workp) 42868c2ecf20Sopenharmony_ci{ 42878c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 42888c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res; 42898c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *temp; 42908c2ecf20Sopenharmony_ci struct scsi_device *sdev; 42918c2ecf20Sopenharmony_ci unsigned long lock_flags; 42928c2ecf20Sopenharmony_ci unsigned long host_lock_flags; 42938c2ecf20Sopenharmony_ci u16 fw_version; 42948c2ecf20Sopenharmony_ci u8 bus, target, lun; 42958c2ecf20Sopenharmony_ci 42968c2ecf20Sopenharmony_ci pinstance = container_of(workp, struct pmcraid_instance, worker_q); 42978c2ecf20Sopenharmony_ci /* add resources only after host is added into system */ 42988c2ecf20Sopenharmony_ci if (!atomic_read(&pinstance->expose_resources)) 42998c2ecf20Sopenharmony_ci return; 43008c2ecf20Sopenharmony_ci 43018c2ecf20Sopenharmony_ci fw_version = be16_to_cpu(pinstance->inq_data->fw_version); 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->resource_lock, lock_flags); 43048c2ecf20Sopenharmony_ci list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue) { 43058c2ecf20Sopenharmony_ci 43068c2ecf20Sopenharmony_ci if (res->change_detected == RES_CHANGE_DEL && res->scsi_dev) { 43078c2ecf20Sopenharmony_ci sdev = res->scsi_dev; 43088c2ecf20Sopenharmony_ci 43098c2ecf20Sopenharmony_ci /* host_lock must be held before calling 43108c2ecf20Sopenharmony_ci * scsi_device_get 43118c2ecf20Sopenharmony_ci */ 43128c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, 43138c2ecf20Sopenharmony_ci host_lock_flags); 43148c2ecf20Sopenharmony_ci if (!scsi_device_get(sdev)) { 43158c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 43168c2ecf20Sopenharmony_ci pinstance->host->host_lock, 43178c2ecf20Sopenharmony_ci host_lock_flags); 43188c2ecf20Sopenharmony_ci pmcraid_info("deleting %x from midlayer\n", 43198c2ecf20Sopenharmony_ci res->cfg_entry.resource_address); 43208c2ecf20Sopenharmony_ci list_move_tail(&res->queue, 43218c2ecf20Sopenharmony_ci &pinstance->free_res_q); 43228c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 43238c2ecf20Sopenharmony_ci &pinstance->resource_lock, 43248c2ecf20Sopenharmony_ci lock_flags); 43258c2ecf20Sopenharmony_ci scsi_remove_device(sdev); 43268c2ecf20Sopenharmony_ci scsi_device_put(sdev); 43278c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->resource_lock, 43288c2ecf20Sopenharmony_ci lock_flags); 43298c2ecf20Sopenharmony_ci res->change_detected = 0; 43308c2ecf20Sopenharmony_ci } else { 43318c2ecf20Sopenharmony_ci spin_unlock_irqrestore( 43328c2ecf20Sopenharmony_ci pinstance->host->host_lock, 43338c2ecf20Sopenharmony_ci host_lock_flags); 43348c2ecf20Sopenharmony_ci } 43358c2ecf20Sopenharmony_ci } 43368c2ecf20Sopenharmony_ci } 43378c2ecf20Sopenharmony_ci 43388c2ecf20Sopenharmony_ci list_for_each_entry(res, &pinstance->used_res_q, queue) { 43398c2ecf20Sopenharmony_ci 43408c2ecf20Sopenharmony_ci if (res->change_detected == RES_CHANGE_ADD) { 43418c2ecf20Sopenharmony_ci 43428c2ecf20Sopenharmony_ci if (!pmcraid_expose_resource(fw_version, 43438c2ecf20Sopenharmony_ci &res->cfg_entry)) 43448c2ecf20Sopenharmony_ci continue; 43458c2ecf20Sopenharmony_ci 43468c2ecf20Sopenharmony_ci if (RES_IS_VSET(res->cfg_entry)) { 43478c2ecf20Sopenharmony_ci bus = PMCRAID_VSET_BUS_ID; 43488c2ecf20Sopenharmony_ci if (fw_version <= PMCRAID_FW_VERSION_1) 43498c2ecf20Sopenharmony_ci target = res->cfg_entry.unique_flags1; 43508c2ecf20Sopenharmony_ci else 43518c2ecf20Sopenharmony_ci target = le16_to_cpu(res->cfg_entry.array_id) & 0xFF; 43528c2ecf20Sopenharmony_ci lun = PMCRAID_VSET_LUN_ID; 43538c2ecf20Sopenharmony_ci } else { 43548c2ecf20Sopenharmony_ci bus = PMCRAID_PHYS_BUS_ID; 43558c2ecf20Sopenharmony_ci target = 43568c2ecf20Sopenharmony_ci RES_TARGET( 43578c2ecf20Sopenharmony_ci res->cfg_entry.resource_address); 43588c2ecf20Sopenharmony_ci lun = RES_LUN(res->cfg_entry.resource_address); 43598c2ecf20Sopenharmony_ci } 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_ci res->change_detected = 0; 43628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, 43638c2ecf20Sopenharmony_ci lock_flags); 43648c2ecf20Sopenharmony_ci scsi_add_device(pinstance->host, bus, target, lun); 43658c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->resource_lock, 43668c2ecf20Sopenharmony_ci lock_flags); 43678c2ecf20Sopenharmony_ci } 43688c2ecf20Sopenharmony_ci } 43698c2ecf20Sopenharmony_ci 43708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); 43718c2ecf20Sopenharmony_ci} 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci/** 43748c2ecf20Sopenharmony_ci * pmcraid_tasklet_function - Tasklet function 43758c2ecf20Sopenharmony_ci * 43768c2ecf20Sopenharmony_ci * @instance: pointer to msix param structure 43778c2ecf20Sopenharmony_ci * 43788c2ecf20Sopenharmony_ci * Return Value 43798c2ecf20Sopenharmony_ci * None 43808c2ecf20Sopenharmony_ci */ 43818c2ecf20Sopenharmony_cistatic void pmcraid_tasklet_function(unsigned long instance) 43828c2ecf20Sopenharmony_ci{ 43838c2ecf20Sopenharmony_ci struct pmcraid_isr_param *hrrq_vector; 43848c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 43858c2ecf20Sopenharmony_ci unsigned long hrrq_lock_flags; 43868c2ecf20Sopenharmony_ci unsigned long pending_lock_flags; 43878c2ecf20Sopenharmony_ci unsigned long host_lock_flags; 43888c2ecf20Sopenharmony_ci spinlock_t *lockp; /* hrrq buffer lock */ 43898c2ecf20Sopenharmony_ci int id; 43908c2ecf20Sopenharmony_ci u32 resp; 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci hrrq_vector = (struct pmcraid_isr_param *)instance; 43938c2ecf20Sopenharmony_ci pinstance = hrrq_vector->drv_inst; 43948c2ecf20Sopenharmony_ci id = hrrq_vector->hrrq_id; 43958c2ecf20Sopenharmony_ci lockp = &(pinstance->hrrq_lock[id]); 43968c2ecf20Sopenharmony_ci 43978c2ecf20Sopenharmony_ci /* loop through each of the commands responded by IOA. Each HRRQ buf is 43988c2ecf20Sopenharmony_ci * protected by its own lock. Traversals must be done within this lock 43998c2ecf20Sopenharmony_ci * as there may be multiple tasklets running on multiple CPUs. Note 44008c2ecf20Sopenharmony_ci * that the lock is held just for picking up the response handle and 44018c2ecf20Sopenharmony_ci * manipulating hrrq_curr/toggle_bit values. 44028c2ecf20Sopenharmony_ci */ 44038c2ecf20Sopenharmony_ci spin_lock_irqsave(lockp, hrrq_lock_flags); 44048c2ecf20Sopenharmony_ci 44058c2ecf20Sopenharmony_ci resp = le32_to_cpu(*(pinstance->hrrq_curr[id])); 44068c2ecf20Sopenharmony_ci 44078c2ecf20Sopenharmony_ci while ((resp & HRRQ_TOGGLE_BIT) == 44088c2ecf20Sopenharmony_ci pinstance->host_toggle_bit[id]) { 44098c2ecf20Sopenharmony_ci 44108c2ecf20Sopenharmony_ci int cmd_index = resp >> 2; 44118c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmd = NULL; 44128c2ecf20Sopenharmony_ci 44138c2ecf20Sopenharmony_ci if (pinstance->hrrq_curr[id] < pinstance->hrrq_end[id]) { 44148c2ecf20Sopenharmony_ci pinstance->hrrq_curr[id]++; 44158c2ecf20Sopenharmony_ci } else { 44168c2ecf20Sopenharmony_ci pinstance->hrrq_curr[id] = pinstance->hrrq_start[id]; 44178c2ecf20Sopenharmony_ci pinstance->host_toggle_bit[id] ^= 1u; 44188c2ecf20Sopenharmony_ci } 44198c2ecf20Sopenharmony_ci 44208c2ecf20Sopenharmony_ci if (cmd_index >= PMCRAID_MAX_CMD) { 44218c2ecf20Sopenharmony_ci /* In case of invalid response handle, log message */ 44228c2ecf20Sopenharmony_ci pmcraid_err("Invalid response handle %d\n", cmd_index); 44238c2ecf20Sopenharmony_ci resp = le32_to_cpu(*(pinstance->hrrq_curr[id])); 44248c2ecf20Sopenharmony_ci continue; 44258c2ecf20Sopenharmony_ci } 44268c2ecf20Sopenharmony_ci 44278c2ecf20Sopenharmony_ci cmd = pinstance->cmd_list[cmd_index]; 44288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lockp, hrrq_lock_flags); 44298c2ecf20Sopenharmony_ci 44308c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->pending_pool_lock, 44318c2ecf20Sopenharmony_ci pending_lock_flags); 44328c2ecf20Sopenharmony_ci list_del(&cmd->free_list); 44338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->pending_pool_lock, 44348c2ecf20Sopenharmony_ci pending_lock_flags); 44358c2ecf20Sopenharmony_ci del_timer(&cmd->timer); 44368c2ecf20Sopenharmony_ci atomic_dec(&pinstance->outstanding_cmds); 44378c2ecf20Sopenharmony_ci 44388c2ecf20Sopenharmony_ci if (cmd->cmd_done == pmcraid_ioa_reset) { 44398c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, 44408c2ecf20Sopenharmony_ci host_lock_flags); 44418c2ecf20Sopenharmony_ci cmd->cmd_done(cmd); 44428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, 44438c2ecf20Sopenharmony_ci host_lock_flags); 44448c2ecf20Sopenharmony_ci } else if (cmd->cmd_done != NULL) { 44458c2ecf20Sopenharmony_ci cmd->cmd_done(cmd); 44468c2ecf20Sopenharmony_ci } 44478c2ecf20Sopenharmony_ci /* loop over until we are done with all responses */ 44488c2ecf20Sopenharmony_ci spin_lock_irqsave(lockp, hrrq_lock_flags); 44498c2ecf20Sopenharmony_ci resp = le32_to_cpu(*(pinstance->hrrq_curr[id])); 44508c2ecf20Sopenharmony_ci } 44518c2ecf20Sopenharmony_ci 44528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lockp, hrrq_lock_flags); 44538c2ecf20Sopenharmony_ci} 44548c2ecf20Sopenharmony_ci 44558c2ecf20Sopenharmony_ci/** 44568c2ecf20Sopenharmony_ci * pmcraid_unregister_interrupt_handler - de-register interrupts handlers 44578c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 44588c2ecf20Sopenharmony_ci * 44598c2ecf20Sopenharmony_ci * This routine un-registers registered interrupt handler and 44608c2ecf20Sopenharmony_ci * also frees irqs/vectors. 44618c2ecf20Sopenharmony_ci * 44628c2ecf20Sopenharmony_ci * Retun Value 44638c2ecf20Sopenharmony_ci * None 44648c2ecf20Sopenharmony_ci */ 44658c2ecf20Sopenharmony_cistatic 44668c2ecf20Sopenharmony_civoid pmcraid_unregister_interrupt_handler(struct pmcraid_instance *pinstance) 44678c2ecf20Sopenharmony_ci{ 44688c2ecf20Sopenharmony_ci struct pci_dev *pdev = pinstance->pdev; 44698c2ecf20Sopenharmony_ci int i; 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_ci for (i = 0; i < pinstance->num_hrrq; i++) 44728c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]); 44738c2ecf20Sopenharmony_ci 44748c2ecf20Sopenharmony_ci pinstance->interrupt_mode = 0; 44758c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 44768c2ecf20Sopenharmony_ci} 44778c2ecf20Sopenharmony_ci 44788c2ecf20Sopenharmony_ci/** 44798c2ecf20Sopenharmony_ci * pmcraid_register_interrupt_handler - registers interrupt handler 44808c2ecf20Sopenharmony_ci * @pinstance: pointer to per-adapter instance structure 44818c2ecf20Sopenharmony_ci * 44828c2ecf20Sopenharmony_ci * Return Value 44838c2ecf20Sopenharmony_ci * 0 on success, non-zero error code otherwise. 44848c2ecf20Sopenharmony_ci */ 44858c2ecf20Sopenharmony_cistatic int 44868c2ecf20Sopenharmony_cipmcraid_register_interrupt_handler(struct pmcraid_instance *pinstance) 44878c2ecf20Sopenharmony_ci{ 44888c2ecf20Sopenharmony_ci struct pci_dev *pdev = pinstance->pdev; 44898c2ecf20Sopenharmony_ci unsigned int irq_flag = PCI_IRQ_LEGACY, flag; 44908c2ecf20Sopenharmony_ci int num_hrrq, rc, i; 44918c2ecf20Sopenharmony_ci irq_handler_t isr; 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_ci if (pmcraid_enable_msix) 44948c2ecf20Sopenharmony_ci irq_flag |= PCI_IRQ_MSIX; 44958c2ecf20Sopenharmony_ci 44968c2ecf20Sopenharmony_ci num_hrrq = pci_alloc_irq_vectors(pdev, 1, PMCRAID_NUM_MSIX_VECTORS, 44978c2ecf20Sopenharmony_ci irq_flag); 44988c2ecf20Sopenharmony_ci if (num_hrrq < 0) 44998c2ecf20Sopenharmony_ci return num_hrrq; 45008c2ecf20Sopenharmony_ci 45018c2ecf20Sopenharmony_ci if (pdev->msix_enabled) { 45028c2ecf20Sopenharmony_ci flag = 0; 45038c2ecf20Sopenharmony_ci isr = pmcraid_isr_msix; 45048c2ecf20Sopenharmony_ci } else { 45058c2ecf20Sopenharmony_ci flag = IRQF_SHARED; 45068c2ecf20Sopenharmony_ci isr = pmcraid_isr; 45078c2ecf20Sopenharmony_ci } 45088c2ecf20Sopenharmony_ci 45098c2ecf20Sopenharmony_ci for (i = 0; i < num_hrrq; i++) { 45108c2ecf20Sopenharmony_ci struct pmcraid_isr_param *vec = &pinstance->hrrq_vector[i]; 45118c2ecf20Sopenharmony_ci 45128c2ecf20Sopenharmony_ci vec->hrrq_id = i; 45138c2ecf20Sopenharmony_ci vec->drv_inst = pinstance; 45148c2ecf20Sopenharmony_ci rc = request_irq(pci_irq_vector(pdev, i), isr, flag, 45158c2ecf20Sopenharmony_ci PMCRAID_DRIVER_NAME, vec); 45168c2ecf20Sopenharmony_ci if (rc) 45178c2ecf20Sopenharmony_ci goto out_unwind; 45188c2ecf20Sopenharmony_ci } 45198c2ecf20Sopenharmony_ci 45208c2ecf20Sopenharmony_ci pinstance->num_hrrq = num_hrrq; 45218c2ecf20Sopenharmony_ci if (pdev->msix_enabled) { 45228c2ecf20Sopenharmony_ci pinstance->interrupt_mode = 1; 45238c2ecf20Sopenharmony_ci iowrite32(DOORBELL_INTR_MODE_MSIX, 45248c2ecf20Sopenharmony_ci pinstance->int_regs.host_ioa_interrupt_reg); 45258c2ecf20Sopenharmony_ci ioread32(pinstance->int_regs.host_ioa_interrupt_reg); 45268c2ecf20Sopenharmony_ci } 45278c2ecf20Sopenharmony_ci 45288c2ecf20Sopenharmony_ci return 0; 45298c2ecf20Sopenharmony_ci 45308c2ecf20Sopenharmony_ciout_unwind: 45318c2ecf20Sopenharmony_ci while (--i >= 0) 45328c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]); 45338c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 45348c2ecf20Sopenharmony_ci return rc; 45358c2ecf20Sopenharmony_ci} 45368c2ecf20Sopenharmony_ci 45378c2ecf20Sopenharmony_ci/** 45388c2ecf20Sopenharmony_ci * pmcraid_release_cmd_blocks - release buufers allocated for command blocks 45398c2ecf20Sopenharmony_ci * @pinstance: per adapter instance structure pointer 45408c2ecf20Sopenharmony_ci * @max_index: number of buffer blocks to release 45418c2ecf20Sopenharmony_ci * 45428c2ecf20Sopenharmony_ci * Return Value 45438c2ecf20Sopenharmony_ci * None 45448c2ecf20Sopenharmony_ci */ 45458c2ecf20Sopenharmony_cistatic void 45468c2ecf20Sopenharmony_cipmcraid_release_cmd_blocks(struct pmcraid_instance *pinstance, int max_index) 45478c2ecf20Sopenharmony_ci{ 45488c2ecf20Sopenharmony_ci int i; 45498c2ecf20Sopenharmony_ci for (i = 0; i < max_index; i++) { 45508c2ecf20Sopenharmony_ci kmem_cache_free(pinstance->cmd_cachep, pinstance->cmd_list[i]); 45518c2ecf20Sopenharmony_ci pinstance->cmd_list[i] = NULL; 45528c2ecf20Sopenharmony_ci } 45538c2ecf20Sopenharmony_ci kmem_cache_destroy(pinstance->cmd_cachep); 45548c2ecf20Sopenharmony_ci pinstance->cmd_cachep = NULL; 45558c2ecf20Sopenharmony_ci} 45568c2ecf20Sopenharmony_ci 45578c2ecf20Sopenharmony_ci/** 45588c2ecf20Sopenharmony_ci * pmcraid_release_control_blocks - releases buffers alloced for control blocks 45598c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 45608c2ecf20Sopenharmony_ci * @max_index: number of buffers (from 0 onwards) to release 45618c2ecf20Sopenharmony_ci * 45628c2ecf20Sopenharmony_ci * This function assumes that the command blocks for which control blocks are 45638c2ecf20Sopenharmony_ci * linked are not released. 45648c2ecf20Sopenharmony_ci * 45658c2ecf20Sopenharmony_ci * Return Value 45668c2ecf20Sopenharmony_ci * None 45678c2ecf20Sopenharmony_ci */ 45688c2ecf20Sopenharmony_cistatic void 45698c2ecf20Sopenharmony_cipmcraid_release_control_blocks( 45708c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance, 45718c2ecf20Sopenharmony_ci int max_index 45728c2ecf20Sopenharmony_ci) 45738c2ecf20Sopenharmony_ci{ 45748c2ecf20Sopenharmony_ci int i; 45758c2ecf20Sopenharmony_ci 45768c2ecf20Sopenharmony_ci if (pinstance->control_pool == NULL) 45778c2ecf20Sopenharmony_ci return; 45788c2ecf20Sopenharmony_ci 45798c2ecf20Sopenharmony_ci for (i = 0; i < max_index; i++) { 45808c2ecf20Sopenharmony_ci dma_pool_free(pinstance->control_pool, 45818c2ecf20Sopenharmony_ci pinstance->cmd_list[i]->ioa_cb, 45828c2ecf20Sopenharmony_ci pinstance->cmd_list[i]->ioa_cb_bus_addr); 45838c2ecf20Sopenharmony_ci pinstance->cmd_list[i]->ioa_cb = NULL; 45848c2ecf20Sopenharmony_ci pinstance->cmd_list[i]->ioa_cb_bus_addr = 0; 45858c2ecf20Sopenharmony_ci } 45868c2ecf20Sopenharmony_ci dma_pool_destroy(pinstance->control_pool); 45878c2ecf20Sopenharmony_ci pinstance->control_pool = NULL; 45888c2ecf20Sopenharmony_ci} 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci/** 45918c2ecf20Sopenharmony_ci * pmcraid_allocate_cmd_blocks - allocate memory for cmd block structures 45928c2ecf20Sopenharmony_ci * @pinstance - pointer to per adapter instance structure 45938c2ecf20Sopenharmony_ci * 45948c2ecf20Sopenharmony_ci * Allocates memory for command blocks using kernel slab allocator. 45958c2ecf20Sopenharmony_ci * 45968c2ecf20Sopenharmony_ci * Return Value 45978c2ecf20Sopenharmony_ci * 0 in case of success; -ENOMEM in case of failure 45988c2ecf20Sopenharmony_ci */ 45998c2ecf20Sopenharmony_cistatic int pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance) 46008c2ecf20Sopenharmony_ci{ 46018c2ecf20Sopenharmony_ci int i; 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci sprintf(pinstance->cmd_pool_name, "pmcraid_cmd_pool_%d", 46048c2ecf20Sopenharmony_ci pinstance->host->unique_id); 46058c2ecf20Sopenharmony_ci 46068c2ecf20Sopenharmony_ci 46078c2ecf20Sopenharmony_ci pinstance->cmd_cachep = kmem_cache_create( 46088c2ecf20Sopenharmony_ci pinstance->cmd_pool_name, 46098c2ecf20Sopenharmony_ci sizeof(struct pmcraid_cmd), 0, 46108c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 46118c2ecf20Sopenharmony_ci if (!pinstance->cmd_cachep) 46128c2ecf20Sopenharmony_ci return -ENOMEM; 46138c2ecf20Sopenharmony_ci 46148c2ecf20Sopenharmony_ci for (i = 0; i < PMCRAID_MAX_CMD; i++) { 46158c2ecf20Sopenharmony_ci pinstance->cmd_list[i] = 46168c2ecf20Sopenharmony_ci kmem_cache_alloc(pinstance->cmd_cachep, GFP_KERNEL); 46178c2ecf20Sopenharmony_ci if (!pinstance->cmd_list[i]) { 46188c2ecf20Sopenharmony_ci pmcraid_release_cmd_blocks(pinstance, i); 46198c2ecf20Sopenharmony_ci return -ENOMEM; 46208c2ecf20Sopenharmony_ci } 46218c2ecf20Sopenharmony_ci } 46228c2ecf20Sopenharmony_ci return 0; 46238c2ecf20Sopenharmony_ci} 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci/** 46268c2ecf20Sopenharmony_ci * pmcraid_allocate_control_blocks - allocates memory control blocks 46278c2ecf20Sopenharmony_ci * @pinstance : pointer to per adapter instance structure 46288c2ecf20Sopenharmony_ci * 46298c2ecf20Sopenharmony_ci * This function allocates PCI memory for DMAable buffers like IOARCB, IOADLs 46308c2ecf20Sopenharmony_ci * and IOASAs. This is called after command blocks are already allocated. 46318c2ecf20Sopenharmony_ci * 46328c2ecf20Sopenharmony_ci * Return Value 46338c2ecf20Sopenharmony_ci * 0 in case it can allocate all control blocks, otherwise -ENOMEM 46348c2ecf20Sopenharmony_ci */ 46358c2ecf20Sopenharmony_cistatic int pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance) 46368c2ecf20Sopenharmony_ci{ 46378c2ecf20Sopenharmony_ci int i; 46388c2ecf20Sopenharmony_ci 46398c2ecf20Sopenharmony_ci sprintf(pinstance->ctl_pool_name, "pmcraid_control_pool_%d", 46408c2ecf20Sopenharmony_ci pinstance->host->unique_id); 46418c2ecf20Sopenharmony_ci 46428c2ecf20Sopenharmony_ci pinstance->control_pool = 46438c2ecf20Sopenharmony_ci dma_pool_create(pinstance->ctl_pool_name, 46448c2ecf20Sopenharmony_ci &pinstance->pdev->dev, 46458c2ecf20Sopenharmony_ci sizeof(struct pmcraid_control_block), 46468c2ecf20Sopenharmony_ci PMCRAID_IOARCB_ALIGNMENT, 0); 46478c2ecf20Sopenharmony_ci 46488c2ecf20Sopenharmony_ci if (!pinstance->control_pool) 46498c2ecf20Sopenharmony_ci return -ENOMEM; 46508c2ecf20Sopenharmony_ci 46518c2ecf20Sopenharmony_ci for (i = 0; i < PMCRAID_MAX_CMD; i++) { 46528c2ecf20Sopenharmony_ci pinstance->cmd_list[i]->ioa_cb = 46538c2ecf20Sopenharmony_ci dma_pool_zalloc( 46548c2ecf20Sopenharmony_ci pinstance->control_pool, 46558c2ecf20Sopenharmony_ci GFP_KERNEL, 46568c2ecf20Sopenharmony_ci &(pinstance->cmd_list[i]->ioa_cb_bus_addr)); 46578c2ecf20Sopenharmony_ci 46588c2ecf20Sopenharmony_ci if (!pinstance->cmd_list[i]->ioa_cb) { 46598c2ecf20Sopenharmony_ci pmcraid_release_control_blocks(pinstance, i); 46608c2ecf20Sopenharmony_ci return -ENOMEM; 46618c2ecf20Sopenharmony_ci } 46628c2ecf20Sopenharmony_ci } 46638c2ecf20Sopenharmony_ci return 0; 46648c2ecf20Sopenharmony_ci} 46658c2ecf20Sopenharmony_ci 46668c2ecf20Sopenharmony_ci/** 46678c2ecf20Sopenharmony_ci * pmcraid_release_host_rrqs - release memory allocated for hrrq buffer(s) 46688c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 46698c2ecf20Sopenharmony_ci * @maxindex: size of hrrq buffer pointer array 46708c2ecf20Sopenharmony_ci * 46718c2ecf20Sopenharmony_ci * Return Value 46728c2ecf20Sopenharmony_ci * None 46738c2ecf20Sopenharmony_ci */ 46748c2ecf20Sopenharmony_cistatic void 46758c2ecf20Sopenharmony_cipmcraid_release_host_rrqs(struct pmcraid_instance *pinstance, int maxindex) 46768c2ecf20Sopenharmony_ci{ 46778c2ecf20Sopenharmony_ci int i; 46788c2ecf20Sopenharmony_ci 46798c2ecf20Sopenharmony_ci for (i = 0; i < maxindex; i++) { 46808c2ecf20Sopenharmony_ci dma_free_coherent(&pinstance->pdev->dev, 46818c2ecf20Sopenharmony_ci HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD, 46828c2ecf20Sopenharmony_ci pinstance->hrrq_start[i], 46838c2ecf20Sopenharmony_ci pinstance->hrrq_start_bus_addr[i]); 46848c2ecf20Sopenharmony_ci 46858c2ecf20Sopenharmony_ci /* reset pointers and toggle bit to zeros */ 46868c2ecf20Sopenharmony_ci pinstance->hrrq_start[i] = NULL; 46878c2ecf20Sopenharmony_ci pinstance->hrrq_start_bus_addr[i] = 0; 46888c2ecf20Sopenharmony_ci pinstance->host_toggle_bit[i] = 0; 46898c2ecf20Sopenharmony_ci } 46908c2ecf20Sopenharmony_ci} 46918c2ecf20Sopenharmony_ci 46928c2ecf20Sopenharmony_ci/** 46938c2ecf20Sopenharmony_ci * pmcraid_allocate_host_rrqs - Allocate and initialize host RRQ buffers 46948c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 46958c2ecf20Sopenharmony_ci * 46968c2ecf20Sopenharmony_ci * Return value 46978c2ecf20Sopenharmony_ci * 0 hrrq buffers are allocated, -ENOMEM otherwise. 46988c2ecf20Sopenharmony_ci */ 46998c2ecf20Sopenharmony_cistatic int pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance) 47008c2ecf20Sopenharmony_ci{ 47018c2ecf20Sopenharmony_ci int i, buffer_size; 47028c2ecf20Sopenharmony_ci 47038c2ecf20Sopenharmony_ci buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD; 47048c2ecf20Sopenharmony_ci 47058c2ecf20Sopenharmony_ci for (i = 0; i < pinstance->num_hrrq; i++) { 47068c2ecf20Sopenharmony_ci pinstance->hrrq_start[i] = 47078c2ecf20Sopenharmony_ci dma_alloc_coherent(&pinstance->pdev->dev, buffer_size, 47088c2ecf20Sopenharmony_ci &pinstance->hrrq_start_bus_addr[i], 47098c2ecf20Sopenharmony_ci GFP_KERNEL); 47108c2ecf20Sopenharmony_ci if (!pinstance->hrrq_start[i]) { 47118c2ecf20Sopenharmony_ci pmcraid_err("pci_alloc failed for hrrq vector : %d\n", 47128c2ecf20Sopenharmony_ci i); 47138c2ecf20Sopenharmony_ci pmcraid_release_host_rrqs(pinstance, i); 47148c2ecf20Sopenharmony_ci return -ENOMEM; 47158c2ecf20Sopenharmony_ci } 47168c2ecf20Sopenharmony_ci 47178c2ecf20Sopenharmony_ci pinstance->hrrq_curr[i] = pinstance->hrrq_start[i]; 47188c2ecf20Sopenharmony_ci pinstance->hrrq_end[i] = 47198c2ecf20Sopenharmony_ci pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1; 47208c2ecf20Sopenharmony_ci pinstance->host_toggle_bit[i] = 1; 47218c2ecf20Sopenharmony_ci spin_lock_init(&pinstance->hrrq_lock[i]); 47228c2ecf20Sopenharmony_ci } 47238c2ecf20Sopenharmony_ci return 0; 47248c2ecf20Sopenharmony_ci} 47258c2ecf20Sopenharmony_ci 47268c2ecf20Sopenharmony_ci/** 47278c2ecf20Sopenharmony_ci * pmcraid_release_hcams - release HCAM buffers 47288c2ecf20Sopenharmony_ci * 47298c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 47308c2ecf20Sopenharmony_ci * 47318c2ecf20Sopenharmony_ci * Return value 47328c2ecf20Sopenharmony_ci * none 47338c2ecf20Sopenharmony_ci */ 47348c2ecf20Sopenharmony_cistatic void pmcraid_release_hcams(struct pmcraid_instance *pinstance) 47358c2ecf20Sopenharmony_ci{ 47368c2ecf20Sopenharmony_ci if (pinstance->ccn.msg != NULL) { 47378c2ecf20Sopenharmony_ci dma_free_coherent(&pinstance->pdev->dev, 47388c2ecf20Sopenharmony_ci PMCRAID_AEN_HDR_SIZE + 47398c2ecf20Sopenharmony_ci sizeof(struct pmcraid_hcam_ccn_ext), 47408c2ecf20Sopenharmony_ci pinstance->ccn.msg, 47418c2ecf20Sopenharmony_ci pinstance->ccn.baddr); 47428c2ecf20Sopenharmony_ci 47438c2ecf20Sopenharmony_ci pinstance->ccn.msg = NULL; 47448c2ecf20Sopenharmony_ci pinstance->ccn.hcam = NULL; 47458c2ecf20Sopenharmony_ci pinstance->ccn.baddr = 0; 47468c2ecf20Sopenharmony_ci } 47478c2ecf20Sopenharmony_ci 47488c2ecf20Sopenharmony_ci if (pinstance->ldn.msg != NULL) { 47498c2ecf20Sopenharmony_ci dma_free_coherent(&pinstance->pdev->dev, 47508c2ecf20Sopenharmony_ci PMCRAID_AEN_HDR_SIZE + 47518c2ecf20Sopenharmony_ci sizeof(struct pmcraid_hcam_ldn), 47528c2ecf20Sopenharmony_ci pinstance->ldn.msg, 47538c2ecf20Sopenharmony_ci pinstance->ldn.baddr); 47548c2ecf20Sopenharmony_ci 47558c2ecf20Sopenharmony_ci pinstance->ldn.msg = NULL; 47568c2ecf20Sopenharmony_ci pinstance->ldn.hcam = NULL; 47578c2ecf20Sopenharmony_ci pinstance->ldn.baddr = 0; 47588c2ecf20Sopenharmony_ci } 47598c2ecf20Sopenharmony_ci} 47608c2ecf20Sopenharmony_ci 47618c2ecf20Sopenharmony_ci/** 47628c2ecf20Sopenharmony_ci * pmcraid_allocate_hcams - allocates HCAM buffers 47638c2ecf20Sopenharmony_ci * @pinstance : pointer to per adapter instance structure 47648c2ecf20Sopenharmony_ci * 47658c2ecf20Sopenharmony_ci * Return Value: 47668c2ecf20Sopenharmony_ci * 0 in case of successful allocation, non-zero otherwise 47678c2ecf20Sopenharmony_ci */ 47688c2ecf20Sopenharmony_cistatic int pmcraid_allocate_hcams(struct pmcraid_instance *pinstance) 47698c2ecf20Sopenharmony_ci{ 47708c2ecf20Sopenharmony_ci pinstance->ccn.msg = dma_alloc_coherent(&pinstance->pdev->dev, 47718c2ecf20Sopenharmony_ci PMCRAID_AEN_HDR_SIZE + 47728c2ecf20Sopenharmony_ci sizeof(struct pmcraid_hcam_ccn_ext), 47738c2ecf20Sopenharmony_ci &pinstance->ccn.baddr, GFP_KERNEL); 47748c2ecf20Sopenharmony_ci 47758c2ecf20Sopenharmony_ci pinstance->ldn.msg = dma_alloc_coherent(&pinstance->pdev->dev, 47768c2ecf20Sopenharmony_ci PMCRAID_AEN_HDR_SIZE + 47778c2ecf20Sopenharmony_ci sizeof(struct pmcraid_hcam_ldn), 47788c2ecf20Sopenharmony_ci &pinstance->ldn.baddr, GFP_KERNEL); 47798c2ecf20Sopenharmony_ci 47808c2ecf20Sopenharmony_ci if (pinstance->ldn.msg == NULL || pinstance->ccn.msg == NULL) { 47818c2ecf20Sopenharmony_ci pmcraid_release_hcams(pinstance); 47828c2ecf20Sopenharmony_ci } else { 47838c2ecf20Sopenharmony_ci pinstance->ccn.hcam = 47848c2ecf20Sopenharmony_ci (void *)pinstance->ccn.msg + PMCRAID_AEN_HDR_SIZE; 47858c2ecf20Sopenharmony_ci pinstance->ldn.hcam = 47868c2ecf20Sopenharmony_ci (void *)pinstance->ldn.msg + PMCRAID_AEN_HDR_SIZE; 47878c2ecf20Sopenharmony_ci 47888c2ecf20Sopenharmony_ci atomic_set(&pinstance->ccn.ignore, 0); 47898c2ecf20Sopenharmony_ci atomic_set(&pinstance->ldn.ignore, 0); 47908c2ecf20Sopenharmony_ci } 47918c2ecf20Sopenharmony_ci 47928c2ecf20Sopenharmony_ci return (pinstance->ldn.msg == NULL) ? -ENOMEM : 0; 47938c2ecf20Sopenharmony_ci} 47948c2ecf20Sopenharmony_ci 47958c2ecf20Sopenharmony_ci/** 47968c2ecf20Sopenharmony_ci * pmcraid_release_config_buffers - release config.table buffers 47978c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 47988c2ecf20Sopenharmony_ci * 47998c2ecf20Sopenharmony_ci * Return Value 48008c2ecf20Sopenharmony_ci * none 48018c2ecf20Sopenharmony_ci */ 48028c2ecf20Sopenharmony_cistatic void pmcraid_release_config_buffers(struct pmcraid_instance *pinstance) 48038c2ecf20Sopenharmony_ci{ 48048c2ecf20Sopenharmony_ci if (pinstance->cfg_table != NULL && 48058c2ecf20Sopenharmony_ci pinstance->cfg_table_bus_addr != 0) { 48068c2ecf20Sopenharmony_ci dma_free_coherent(&pinstance->pdev->dev, 48078c2ecf20Sopenharmony_ci sizeof(struct pmcraid_config_table), 48088c2ecf20Sopenharmony_ci pinstance->cfg_table, 48098c2ecf20Sopenharmony_ci pinstance->cfg_table_bus_addr); 48108c2ecf20Sopenharmony_ci pinstance->cfg_table = NULL; 48118c2ecf20Sopenharmony_ci pinstance->cfg_table_bus_addr = 0; 48128c2ecf20Sopenharmony_ci } 48138c2ecf20Sopenharmony_ci 48148c2ecf20Sopenharmony_ci if (pinstance->res_entries != NULL) { 48158c2ecf20Sopenharmony_ci int i; 48168c2ecf20Sopenharmony_ci 48178c2ecf20Sopenharmony_ci for (i = 0; i < PMCRAID_MAX_RESOURCES; i++) 48188c2ecf20Sopenharmony_ci list_del(&pinstance->res_entries[i].queue); 48198c2ecf20Sopenharmony_ci kfree(pinstance->res_entries); 48208c2ecf20Sopenharmony_ci pinstance->res_entries = NULL; 48218c2ecf20Sopenharmony_ci } 48228c2ecf20Sopenharmony_ci 48238c2ecf20Sopenharmony_ci pmcraid_release_hcams(pinstance); 48248c2ecf20Sopenharmony_ci} 48258c2ecf20Sopenharmony_ci 48268c2ecf20Sopenharmony_ci/** 48278c2ecf20Sopenharmony_ci * pmcraid_allocate_config_buffers - allocates DMAable memory for config table 48288c2ecf20Sopenharmony_ci * @pinstance : pointer to per adapter instance structure 48298c2ecf20Sopenharmony_ci * 48308c2ecf20Sopenharmony_ci * Return Value 48318c2ecf20Sopenharmony_ci * 0 for successful allocation, -ENOMEM for any failure 48328c2ecf20Sopenharmony_ci */ 48338c2ecf20Sopenharmony_cistatic int pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance) 48348c2ecf20Sopenharmony_ci{ 48358c2ecf20Sopenharmony_ci int i; 48368c2ecf20Sopenharmony_ci 48378c2ecf20Sopenharmony_ci pinstance->res_entries = 48388c2ecf20Sopenharmony_ci kcalloc(PMCRAID_MAX_RESOURCES, 48398c2ecf20Sopenharmony_ci sizeof(struct pmcraid_resource_entry), 48408c2ecf20Sopenharmony_ci GFP_KERNEL); 48418c2ecf20Sopenharmony_ci 48428c2ecf20Sopenharmony_ci if (NULL == pinstance->res_entries) { 48438c2ecf20Sopenharmony_ci pmcraid_err("failed to allocate memory for resource table\n"); 48448c2ecf20Sopenharmony_ci return -ENOMEM; 48458c2ecf20Sopenharmony_ci } 48468c2ecf20Sopenharmony_ci 48478c2ecf20Sopenharmony_ci for (i = 0; i < PMCRAID_MAX_RESOURCES; i++) 48488c2ecf20Sopenharmony_ci list_add_tail(&pinstance->res_entries[i].queue, 48498c2ecf20Sopenharmony_ci &pinstance->free_res_q); 48508c2ecf20Sopenharmony_ci 48518c2ecf20Sopenharmony_ci pinstance->cfg_table = dma_alloc_coherent(&pinstance->pdev->dev, 48528c2ecf20Sopenharmony_ci sizeof(struct pmcraid_config_table), 48538c2ecf20Sopenharmony_ci &pinstance->cfg_table_bus_addr, 48548c2ecf20Sopenharmony_ci GFP_KERNEL); 48558c2ecf20Sopenharmony_ci 48568c2ecf20Sopenharmony_ci if (NULL == pinstance->cfg_table) { 48578c2ecf20Sopenharmony_ci pmcraid_err("couldn't alloc DMA memory for config table\n"); 48588c2ecf20Sopenharmony_ci pmcraid_release_config_buffers(pinstance); 48598c2ecf20Sopenharmony_ci return -ENOMEM; 48608c2ecf20Sopenharmony_ci } 48618c2ecf20Sopenharmony_ci 48628c2ecf20Sopenharmony_ci if (pmcraid_allocate_hcams(pinstance)) { 48638c2ecf20Sopenharmony_ci pmcraid_err("could not alloc DMA memory for HCAMS\n"); 48648c2ecf20Sopenharmony_ci pmcraid_release_config_buffers(pinstance); 48658c2ecf20Sopenharmony_ci return -ENOMEM; 48668c2ecf20Sopenharmony_ci } 48678c2ecf20Sopenharmony_ci 48688c2ecf20Sopenharmony_ci return 0; 48698c2ecf20Sopenharmony_ci} 48708c2ecf20Sopenharmony_ci 48718c2ecf20Sopenharmony_ci/** 48728c2ecf20Sopenharmony_ci * pmcraid_init_tasklets - registers tasklets for response handling 48738c2ecf20Sopenharmony_ci * 48748c2ecf20Sopenharmony_ci * @pinstance: pointer adapter instance structure 48758c2ecf20Sopenharmony_ci * 48768c2ecf20Sopenharmony_ci * Return value 48778c2ecf20Sopenharmony_ci * none 48788c2ecf20Sopenharmony_ci */ 48798c2ecf20Sopenharmony_cistatic void pmcraid_init_tasklets(struct pmcraid_instance *pinstance) 48808c2ecf20Sopenharmony_ci{ 48818c2ecf20Sopenharmony_ci int i; 48828c2ecf20Sopenharmony_ci for (i = 0; i < pinstance->num_hrrq; i++) 48838c2ecf20Sopenharmony_ci tasklet_init(&pinstance->isr_tasklet[i], 48848c2ecf20Sopenharmony_ci pmcraid_tasklet_function, 48858c2ecf20Sopenharmony_ci (unsigned long)&pinstance->hrrq_vector[i]); 48868c2ecf20Sopenharmony_ci} 48878c2ecf20Sopenharmony_ci 48888c2ecf20Sopenharmony_ci/** 48898c2ecf20Sopenharmony_ci * pmcraid_kill_tasklets - destroys tasklets registered for response handling 48908c2ecf20Sopenharmony_ci * 48918c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 48928c2ecf20Sopenharmony_ci * 48938c2ecf20Sopenharmony_ci * Return value 48948c2ecf20Sopenharmony_ci * none 48958c2ecf20Sopenharmony_ci */ 48968c2ecf20Sopenharmony_cistatic void pmcraid_kill_tasklets(struct pmcraid_instance *pinstance) 48978c2ecf20Sopenharmony_ci{ 48988c2ecf20Sopenharmony_ci int i; 48998c2ecf20Sopenharmony_ci for (i = 0; i < pinstance->num_hrrq; i++) 49008c2ecf20Sopenharmony_ci tasklet_kill(&pinstance->isr_tasklet[i]); 49018c2ecf20Sopenharmony_ci} 49028c2ecf20Sopenharmony_ci 49038c2ecf20Sopenharmony_ci/** 49048c2ecf20Sopenharmony_ci * pmcraid_release_buffers - release per-adapter buffers allocated 49058c2ecf20Sopenharmony_ci * 49068c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter soft state 49078c2ecf20Sopenharmony_ci * 49088c2ecf20Sopenharmony_ci * Return Value 49098c2ecf20Sopenharmony_ci * none 49108c2ecf20Sopenharmony_ci */ 49118c2ecf20Sopenharmony_cistatic void pmcraid_release_buffers(struct pmcraid_instance *pinstance) 49128c2ecf20Sopenharmony_ci{ 49138c2ecf20Sopenharmony_ci pmcraid_release_config_buffers(pinstance); 49148c2ecf20Sopenharmony_ci pmcraid_release_control_blocks(pinstance, PMCRAID_MAX_CMD); 49158c2ecf20Sopenharmony_ci pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD); 49168c2ecf20Sopenharmony_ci pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); 49178c2ecf20Sopenharmony_ci 49188c2ecf20Sopenharmony_ci if (pinstance->inq_data != NULL) { 49198c2ecf20Sopenharmony_ci dma_free_coherent(&pinstance->pdev->dev, 49208c2ecf20Sopenharmony_ci sizeof(struct pmcraid_inquiry_data), 49218c2ecf20Sopenharmony_ci pinstance->inq_data, 49228c2ecf20Sopenharmony_ci pinstance->inq_data_baddr); 49238c2ecf20Sopenharmony_ci 49248c2ecf20Sopenharmony_ci pinstance->inq_data = NULL; 49258c2ecf20Sopenharmony_ci pinstance->inq_data_baddr = 0; 49268c2ecf20Sopenharmony_ci } 49278c2ecf20Sopenharmony_ci 49288c2ecf20Sopenharmony_ci if (pinstance->timestamp_data != NULL) { 49298c2ecf20Sopenharmony_ci dma_free_coherent(&pinstance->pdev->dev, 49308c2ecf20Sopenharmony_ci sizeof(struct pmcraid_timestamp_data), 49318c2ecf20Sopenharmony_ci pinstance->timestamp_data, 49328c2ecf20Sopenharmony_ci pinstance->timestamp_data_baddr); 49338c2ecf20Sopenharmony_ci 49348c2ecf20Sopenharmony_ci pinstance->timestamp_data = NULL; 49358c2ecf20Sopenharmony_ci pinstance->timestamp_data_baddr = 0; 49368c2ecf20Sopenharmony_ci } 49378c2ecf20Sopenharmony_ci} 49388c2ecf20Sopenharmony_ci 49398c2ecf20Sopenharmony_ci/** 49408c2ecf20Sopenharmony_ci * pmcraid_init_buffers - allocates memory and initializes various structures 49418c2ecf20Sopenharmony_ci * @pinstance: pointer to per adapter instance structure 49428c2ecf20Sopenharmony_ci * 49438c2ecf20Sopenharmony_ci * This routine pre-allocates memory based on the type of block as below: 49448c2ecf20Sopenharmony_ci * cmdblocks(PMCRAID_MAX_CMD): kernel memory using kernel's slab_allocator, 49458c2ecf20Sopenharmony_ci * IOARCBs(PMCRAID_MAX_CMD) : DMAable memory, using pci pool allocator 49468c2ecf20Sopenharmony_ci * config-table entries : DMAable memory using dma_alloc_coherent 49478c2ecf20Sopenharmony_ci * HostRRQs : DMAable memory, using dma_alloc_coherent 49488c2ecf20Sopenharmony_ci * 49498c2ecf20Sopenharmony_ci * Return Value 49508c2ecf20Sopenharmony_ci * 0 in case all of the blocks are allocated, -ENOMEM otherwise. 49518c2ecf20Sopenharmony_ci */ 49528c2ecf20Sopenharmony_cistatic int pmcraid_init_buffers(struct pmcraid_instance *pinstance) 49538c2ecf20Sopenharmony_ci{ 49548c2ecf20Sopenharmony_ci int i; 49558c2ecf20Sopenharmony_ci 49568c2ecf20Sopenharmony_ci if (pmcraid_allocate_host_rrqs(pinstance)) { 49578c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate memory for %d host rrqs\n", 49588c2ecf20Sopenharmony_ci pinstance->num_hrrq); 49598c2ecf20Sopenharmony_ci return -ENOMEM; 49608c2ecf20Sopenharmony_ci } 49618c2ecf20Sopenharmony_ci 49628c2ecf20Sopenharmony_ci if (pmcraid_allocate_config_buffers(pinstance)) { 49638c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate memory for config buffers\n"); 49648c2ecf20Sopenharmony_ci pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); 49658c2ecf20Sopenharmony_ci return -ENOMEM; 49668c2ecf20Sopenharmony_ci } 49678c2ecf20Sopenharmony_ci 49688c2ecf20Sopenharmony_ci if (pmcraid_allocate_cmd_blocks(pinstance)) { 49698c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate memory for cmd blocks\n"); 49708c2ecf20Sopenharmony_ci pmcraid_release_config_buffers(pinstance); 49718c2ecf20Sopenharmony_ci pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); 49728c2ecf20Sopenharmony_ci return -ENOMEM; 49738c2ecf20Sopenharmony_ci } 49748c2ecf20Sopenharmony_ci 49758c2ecf20Sopenharmony_ci if (pmcraid_allocate_control_blocks(pinstance)) { 49768c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate memory control blocks\n"); 49778c2ecf20Sopenharmony_ci pmcraid_release_config_buffers(pinstance); 49788c2ecf20Sopenharmony_ci pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD); 49798c2ecf20Sopenharmony_ci pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); 49808c2ecf20Sopenharmony_ci return -ENOMEM; 49818c2ecf20Sopenharmony_ci } 49828c2ecf20Sopenharmony_ci 49838c2ecf20Sopenharmony_ci /* allocate DMAable memory for page D0 INQUIRY buffer */ 49848c2ecf20Sopenharmony_ci pinstance->inq_data = dma_alloc_coherent(&pinstance->pdev->dev, 49858c2ecf20Sopenharmony_ci sizeof(struct pmcraid_inquiry_data), 49868c2ecf20Sopenharmony_ci &pinstance->inq_data_baddr, GFP_KERNEL); 49878c2ecf20Sopenharmony_ci if (pinstance->inq_data == NULL) { 49888c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate DMA memory for INQUIRY\n"); 49898c2ecf20Sopenharmony_ci pmcraid_release_buffers(pinstance); 49908c2ecf20Sopenharmony_ci return -ENOMEM; 49918c2ecf20Sopenharmony_ci } 49928c2ecf20Sopenharmony_ci 49938c2ecf20Sopenharmony_ci /* allocate DMAable memory for set timestamp data buffer */ 49948c2ecf20Sopenharmony_ci pinstance->timestamp_data = dma_alloc_coherent(&pinstance->pdev->dev, 49958c2ecf20Sopenharmony_ci sizeof(struct pmcraid_timestamp_data), 49968c2ecf20Sopenharmony_ci &pinstance->timestamp_data_baddr, 49978c2ecf20Sopenharmony_ci GFP_KERNEL); 49988c2ecf20Sopenharmony_ci if (pinstance->timestamp_data == NULL) { 49998c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate DMA memory for \ 50008c2ecf20Sopenharmony_ci set time_stamp \n"); 50018c2ecf20Sopenharmony_ci pmcraid_release_buffers(pinstance); 50028c2ecf20Sopenharmony_ci return -ENOMEM; 50038c2ecf20Sopenharmony_ci } 50048c2ecf20Sopenharmony_ci 50058c2ecf20Sopenharmony_ci 50068c2ecf20Sopenharmony_ci /* Initialize all the command blocks and add them to free pool. No 50078c2ecf20Sopenharmony_ci * need to lock (free_pool_lock) as this is done in initialization 50088c2ecf20Sopenharmony_ci * itself 50098c2ecf20Sopenharmony_ci */ 50108c2ecf20Sopenharmony_ci for (i = 0; i < PMCRAID_MAX_CMD; i++) { 50118c2ecf20Sopenharmony_ci struct pmcraid_cmd *cmdp = pinstance->cmd_list[i]; 50128c2ecf20Sopenharmony_ci pmcraid_init_cmdblk(cmdp, i); 50138c2ecf20Sopenharmony_ci cmdp->drv_inst = pinstance; 50148c2ecf20Sopenharmony_ci list_add_tail(&cmdp->free_list, &pinstance->free_cmd_pool); 50158c2ecf20Sopenharmony_ci } 50168c2ecf20Sopenharmony_ci 50178c2ecf20Sopenharmony_ci return 0; 50188c2ecf20Sopenharmony_ci} 50198c2ecf20Sopenharmony_ci 50208c2ecf20Sopenharmony_ci/** 50218c2ecf20Sopenharmony_ci * pmcraid_reinit_buffers - resets various buffer pointers 50228c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance 50238c2ecf20Sopenharmony_ci * Return value 50248c2ecf20Sopenharmony_ci * none 50258c2ecf20Sopenharmony_ci */ 50268c2ecf20Sopenharmony_cistatic void pmcraid_reinit_buffers(struct pmcraid_instance *pinstance) 50278c2ecf20Sopenharmony_ci{ 50288c2ecf20Sopenharmony_ci int i; 50298c2ecf20Sopenharmony_ci int buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD; 50308c2ecf20Sopenharmony_ci 50318c2ecf20Sopenharmony_ci for (i = 0; i < pinstance->num_hrrq; i++) { 50328c2ecf20Sopenharmony_ci memset(pinstance->hrrq_start[i], 0, buffer_size); 50338c2ecf20Sopenharmony_ci pinstance->hrrq_curr[i] = pinstance->hrrq_start[i]; 50348c2ecf20Sopenharmony_ci pinstance->hrrq_end[i] = 50358c2ecf20Sopenharmony_ci pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1; 50368c2ecf20Sopenharmony_ci pinstance->host_toggle_bit[i] = 1; 50378c2ecf20Sopenharmony_ci } 50388c2ecf20Sopenharmony_ci} 50398c2ecf20Sopenharmony_ci 50408c2ecf20Sopenharmony_ci/** 50418c2ecf20Sopenharmony_ci * pmcraid_init_instance - initialize per instance data structure 50428c2ecf20Sopenharmony_ci * @pdev: pointer to pci device structure 50438c2ecf20Sopenharmony_ci * @host: pointer to Scsi_Host structure 50448c2ecf20Sopenharmony_ci * @mapped_pci_addr: memory mapped IOA configuration registers 50458c2ecf20Sopenharmony_ci * 50468c2ecf20Sopenharmony_ci * Return Value 50478c2ecf20Sopenharmony_ci * 0 on success, non-zero in case of any failure 50488c2ecf20Sopenharmony_ci */ 50498c2ecf20Sopenharmony_cistatic int pmcraid_init_instance(struct pci_dev *pdev, struct Scsi_Host *host, 50508c2ecf20Sopenharmony_ci void __iomem *mapped_pci_addr) 50518c2ecf20Sopenharmony_ci{ 50528c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = 50538c2ecf20Sopenharmony_ci (struct pmcraid_instance *)host->hostdata; 50548c2ecf20Sopenharmony_ci 50558c2ecf20Sopenharmony_ci pinstance->host = host; 50568c2ecf20Sopenharmony_ci pinstance->pdev = pdev; 50578c2ecf20Sopenharmony_ci 50588c2ecf20Sopenharmony_ci /* Initialize register addresses */ 50598c2ecf20Sopenharmony_ci pinstance->mapped_dma_addr = mapped_pci_addr; 50608c2ecf20Sopenharmony_ci 50618c2ecf20Sopenharmony_ci /* Initialize chip-specific details */ 50628c2ecf20Sopenharmony_ci { 50638c2ecf20Sopenharmony_ci struct pmcraid_chip_details *chip_cfg = pinstance->chip_cfg; 50648c2ecf20Sopenharmony_ci struct pmcraid_interrupts *pint_regs = &pinstance->int_regs; 50658c2ecf20Sopenharmony_ci 50668c2ecf20Sopenharmony_ci pinstance->ioarrin = mapped_pci_addr + chip_cfg->ioarrin; 50678c2ecf20Sopenharmony_ci 50688c2ecf20Sopenharmony_ci pint_regs->ioa_host_interrupt_reg = 50698c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->ioa_host_intr; 50708c2ecf20Sopenharmony_ci pint_regs->ioa_host_interrupt_clr_reg = 50718c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->ioa_host_intr_clr; 50728c2ecf20Sopenharmony_ci pint_regs->ioa_host_msix_interrupt_reg = 50738c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->ioa_host_msix_intr; 50748c2ecf20Sopenharmony_ci pint_regs->host_ioa_interrupt_reg = 50758c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->host_ioa_intr; 50768c2ecf20Sopenharmony_ci pint_regs->host_ioa_interrupt_clr_reg = 50778c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->host_ioa_intr_clr; 50788c2ecf20Sopenharmony_ci 50798c2ecf20Sopenharmony_ci /* Current version of firmware exposes interrupt mask set 50808c2ecf20Sopenharmony_ci * and mask clr registers through memory mapped bar0. 50818c2ecf20Sopenharmony_ci */ 50828c2ecf20Sopenharmony_ci pinstance->mailbox = mapped_pci_addr + chip_cfg->mailbox; 50838c2ecf20Sopenharmony_ci pinstance->ioa_status = mapped_pci_addr + chip_cfg->ioastatus; 50848c2ecf20Sopenharmony_ci pint_regs->ioa_host_interrupt_mask_reg = 50858c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->ioa_host_mask; 50868c2ecf20Sopenharmony_ci pint_regs->ioa_host_interrupt_mask_clr_reg = 50878c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->ioa_host_mask_clr; 50888c2ecf20Sopenharmony_ci pint_regs->global_interrupt_mask_reg = 50898c2ecf20Sopenharmony_ci mapped_pci_addr + chip_cfg->global_intr_mask; 50908c2ecf20Sopenharmony_ci }; 50918c2ecf20Sopenharmony_ci 50928c2ecf20Sopenharmony_ci pinstance->ioa_reset_attempts = 0; 50938c2ecf20Sopenharmony_ci init_waitqueue_head(&pinstance->reset_wait_q); 50948c2ecf20Sopenharmony_ci 50958c2ecf20Sopenharmony_ci atomic_set(&pinstance->outstanding_cmds, 0); 50968c2ecf20Sopenharmony_ci atomic_set(&pinstance->last_message_id, 0); 50978c2ecf20Sopenharmony_ci atomic_set(&pinstance->expose_resources, 0); 50988c2ecf20Sopenharmony_ci 50998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pinstance->free_res_q); 51008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pinstance->used_res_q); 51018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pinstance->free_cmd_pool); 51028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pinstance->pending_cmd_pool); 51038c2ecf20Sopenharmony_ci 51048c2ecf20Sopenharmony_ci spin_lock_init(&pinstance->free_pool_lock); 51058c2ecf20Sopenharmony_ci spin_lock_init(&pinstance->pending_pool_lock); 51068c2ecf20Sopenharmony_ci spin_lock_init(&pinstance->resource_lock); 51078c2ecf20Sopenharmony_ci mutex_init(&pinstance->aen_queue_lock); 51088c2ecf20Sopenharmony_ci 51098c2ecf20Sopenharmony_ci /* Work-queue (Shared) for deferred processing error handling */ 51108c2ecf20Sopenharmony_ci INIT_WORK(&pinstance->worker_q, pmcraid_worker_function); 51118c2ecf20Sopenharmony_ci 51128c2ecf20Sopenharmony_ci /* Initialize the default log_level */ 51138c2ecf20Sopenharmony_ci pinstance->current_log_level = pmcraid_log_level; 51148c2ecf20Sopenharmony_ci 51158c2ecf20Sopenharmony_ci /* Setup variables required for reset engine */ 51168c2ecf20Sopenharmony_ci pinstance->ioa_state = IOA_STATE_UNKNOWN; 51178c2ecf20Sopenharmony_ci pinstance->reset_cmd = NULL; 51188c2ecf20Sopenharmony_ci return 0; 51198c2ecf20Sopenharmony_ci} 51208c2ecf20Sopenharmony_ci 51218c2ecf20Sopenharmony_ci/** 51228c2ecf20Sopenharmony_ci * pmcraid_shutdown - shutdown adapter controller. 51238c2ecf20Sopenharmony_ci * @pdev: pci device struct 51248c2ecf20Sopenharmony_ci * 51258c2ecf20Sopenharmony_ci * Issues an adapter shutdown to the card waits for its completion 51268c2ecf20Sopenharmony_ci * 51278c2ecf20Sopenharmony_ci * Return value 51288c2ecf20Sopenharmony_ci * none 51298c2ecf20Sopenharmony_ci */ 51308c2ecf20Sopenharmony_cistatic void pmcraid_shutdown(struct pci_dev *pdev) 51318c2ecf20Sopenharmony_ci{ 51328c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = pci_get_drvdata(pdev); 51338c2ecf20Sopenharmony_ci pmcraid_reset_bringdown(pinstance); 51348c2ecf20Sopenharmony_ci} 51358c2ecf20Sopenharmony_ci 51368c2ecf20Sopenharmony_ci 51378c2ecf20Sopenharmony_ci/** 51388c2ecf20Sopenharmony_ci * pmcraid_get_minor - returns unused minor number from minor number bitmap 51398c2ecf20Sopenharmony_ci */ 51408c2ecf20Sopenharmony_cistatic unsigned short pmcraid_get_minor(void) 51418c2ecf20Sopenharmony_ci{ 51428c2ecf20Sopenharmony_ci int minor; 51438c2ecf20Sopenharmony_ci 51448c2ecf20Sopenharmony_ci minor = find_first_zero_bit(pmcraid_minor, PMCRAID_MAX_ADAPTERS); 51458c2ecf20Sopenharmony_ci __set_bit(minor, pmcraid_minor); 51468c2ecf20Sopenharmony_ci return minor; 51478c2ecf20Sopenharmony_ci} 51488c2ecf20Sopenharmony_ci 51498c2ecf20Sopenharmony_ci/** 51508c2ecf20Sopenharmony_ci * pmcraid_release_minor - releases given minor back to minor number bitmap 51518c2ecf20Sopenharmony_ci */ 51528c2ecf20Sopenharmony_cistatic void pmcraid_release_minor(unsigned short minor) 51538c2ecf20Sopenharmony_ci{ 51548c2ecf20Sopenharmony_ci __clear_bit(minor, pmcraid_minor); 51558c2ecf20Sopenharmony_ci} 51568c2ecf20Sopenharmony_ci 51578c2ecf20Sopenharmony_ci/** 51588c2ecf20Sopenharmony_ci * pmcraid_setup_chrdev - allocates a minor number and registers a char device 51598c2ecf20Sopenharmony_ci * 51608c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance for which to register device 51618c2ecf20Sopenharmony_ci * 51628c2ecf20Sopenharmony_ci * Return value 51638c2ecf20Sopenharmony_ci * 0 in case of success, otherwise non-zero 51648c2ecf20Sopenharmony_ci */ 51658c2ecf20Sopenharmony_cistatic int pmcraid_setup_chrdev(struct pmcraid_instance *pinstance) 51668c2ecf20Sopenharmony_ci{ 51678c2ecf20Sopenharmony_ci int minor; 51688c2ecf20Sopenharmony_ci int error; 51698c2ecf20Sopenharmony_ci 51708c2ecf20Sopenharmony_ci minor = pmcraid_get_minor(); 51718c2ecf20Sopenharmony_ci cdev_init(&pinstance->cdev, &pmcraid_fops); 51728c2ecf20Sopenharmony_ci pinstance->cdev.owner = THIS_MODULE; 51738c2ecf20Sopenharmony_ci 51748c2ecf20Sopenharmony_ci error = cdev_add(&pinstance->cdev, MKDEV(pmcraid_major, minor), 1); 51758c2ecf20Sopenharmony_ci 51768c2ecf20Sopenharmony_ci if (error) 51778c2ecf20Sopenharmony_ci pmcraid_release_minor(minor); 51788c2ecf20Sopenharmony_ci else 51798c2ecf20Sopenharmony_ci device_create(pmcraid_class, NULL, MKDEV(pmcraid_major, minor), 51808c2ecf20Sopenharmony_ci NULL, "%s%u", PMCRAID_DEVFILE, minor); 51818c2ecf20Sopenharmony_ci return error; 51828c2ecf20Sopenharmony_ci} 51838c2ecf20Sopenharmony_ci 51848c2ecf20Sopenharmony_ci/** 51858c2ecf20Sopenharmony_ci * pmcraid_release_chrdev - unregisters per-adapter management interface 51868c2ecf20Sopenharmony_ci * 51878c2ecf20Sopenharmony_ci * @pinstance: pointer to adapter instance structure 51888c2ecf20Sopenharmony_ci * 51898c2ecf20Sopenharmony_ci * Return value 51908c2ecf20Sopenharmony_ci * none 51918c2ecf20Sopenharmony_ci */ 51928c2ecf20Sopenharmony_cistatic void pmcraid_release_chrdev(struct pmcraid_instance *pinstance) 51938c2ecf20Sopenharmony_ci{ 51948c2ecf20Sopenharmony_ci pmcraid_release_minor(MINOR(pinstance->cdev.dev)); 51958c2ecf20Sopenharmony_ci device_destroy(pmcraid_class, 51968c2ecf20Sopenharmony_ci MKDEV(pmcraid_major, MINOR(pinstance->cdev.dev))); 51978c2ecf20Sopenharmony_ci cdev_del(&pinstance->cdev); 51988c2ecf20Sopenharmony_ci} 51998c2ecf20Sopenharmony_ci 52008c2ecf20Sopenharmony_ci/** 52018c2ecf20Sopenharmony_ci * pmcraid_remove - IOA hot plug remove entry point 52028c2ecf20Sopenharmony_ci * @pdev: pci device struct 52038c2ecf20Sopenharmony_ci * 52048c2ecf20Sopenharmony_ci * Return value 52058c2ecf20Sopenharmony_ci * none 52068c2ecf20Sopenharmony_ci */ 52078c2ecf20Sopenharmony_cistatic void pmcraid_remove(struct pci_dev *pdev) 52088c2ecf20Sopenharmony_ci{ 52098c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = pci_get_drvdata(pdev); 52108c2ecf20Sopenharmony_ci 52118c2ecf20Sopenharmony_ci /* remove the management interface (/dev file) for this device */ 52128c2ecf20Sopenharmony_ci pmcraid_release_chrdev(pinstance); 52138c2ecf20Sopenharmony_ci 52148c2ecf20Sopenharmony_ci /* remove host template from scsi midlayer */ 52158c2ecf20Sopenharmony_ci scsi_remove_host(pinstance->host); 52168c2ecf20Sopenharmony_ci 52178c2ecf20Sopenharmony_ci /* block requests from mid-layer */ 52188c2ecf20Sopenharmony_ci scsi_block_requests(pinstance->host); 52198c2ecf20Sopenharmony_ci 52208c2ecf20Sopenharmony_ci /* initiate shutdown adapter */ 52218c2ecf20Sopenharmony_ci pmcraid_shutdown(pdev); 52228c2ecf20Sopenharmony_ci 52238c2ecf20Sopenharmony_ci pmcraid_disable_interrupts(pinstance, ~0); 52248c2ecf20Sopenharmony_ci flush_work(&pinstance->worker_q); 52258c2ecf20Sopenharmony_ci 52268c2ecf20Sopenharmony_ci pmcraid_kill_tasklets(pinstance); 52278c2ecf20Sopenharmony_ci pmcraid_unregister_interrupt_handler(pinstance); 52288c2ecf20Sopenharmony_ci pmcraid_release_buffers(pinstance); 52298c2ecf20Sopenharmony_ci iounmap(pinstance->mapped_dma_addr); 52308c2ecf20Sopenharmony_ci pci_release_regions(pdev); 52318c2ecf20Sopenharmony_ci scsi_host_put(pinstance->host); 52328c2ecf20Sopenharmony_ci pci_disable_device(pdev); 52338c2ecf20Sopenharmony_ci 52348c2ecf20Sopenharmony_ci return; 52358c2ecf20Sopenharmony_ci} 52368c2ecf20Sopenharmony_ci 52378c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 52388c2ecf20Sopenharmony_ci/** 52398c2ecf20Sopenharmony_ci * pmcraid_suspend - driver suspend entry point for power management 52408c2ecf20Sopenharmony_ci * @pdev: PCI device structure 52418c2ecf20Sopenharmony_ci * @state: PCI power state to suspend routine 52428c2ecf20Sopenharmony_ci * 52438c2ecf20Sopenharmony_ci * Return Value - 0 always 52448c2ecf20Sopenharmony_ci */ 52458c2ecf20Sopenharmony_cistatic int pmcraid_suspend(struct pci_dev *pdev, pm_message_t state) 52468c2ecf20Sopenharmony_ci{ 52478c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = pci_get_drvdata(pdev); 52488c2ecf20Sopenharmony_ci 52498c2ecf20Sopenharmony_ci pmcraid_shutdown(pdev); 52508c2ecf20Sopenharmony_ci pmcraid_disable_interrupts(pinstance, ~0); 52518c2ecf20Sopenharmony_ci pmcraid_kill_tasklets(pinstance); 52528c2ecf20Sopenharmony_ci pci_set_drvdata(pinstance->pdev, pinstance); 52538c2ecf20Sopenharmony_ci pmcraid_unregister_interrupt_handler(pinstance); 52548c2ecf20Sopenharmony_ci pci_save_state(pdev); 52558c2ecf20Sopenharmony_ci pci_disable_device(pdev); 52568c2ecf20Sopenharmony_ci pci_set_power_state(pdev, pci_choose_state(pdev, state)); 52578c2ecf20Sopenharmony_ci 52588c2ecf20Sopenharmony_ci return 0; 52598c2ecf20Sopenharmony_ci} 52608c2ecf20Sopenharmony_ci 52618c2ecf20Sopenharmony_ci/** 52628c2ecf20Sopenharmony_ci * pmcraid_resume - driver resume entry point PCI power management 52638c2ecf20Sopenharmony_ci * @pdev: PCI device structure 52648c2ecf20Sopenharmony_ci * 52658c2ecf20Sopenharmony_ci * Return Value - 0 in case of success. Error code in case of any failure 52668c2ecf20Sopenharmony_ci */ 52678c2ecf20Sopenharmony_cistatic int pmcraid_resume(struct pci_dev *pdev) 52688c2ecf20Sopenharmony_ci{ 52698c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = pci_get_drvdata(pdev); 52708c2ecf20Sopenharmony_ci struct Scsi_Host *host = pinstance->host; 52718c2ecf20Sopenharmony_ci int rc; 52728c2ecf20Sopenharmony_ci 52738c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 52748c2ecf20Sopenharmony_ci pci_enable_wake(pdev, PCI_D0, 0); 52758c2ecf20Sopenharmony_ci pci_restore_state(pdev); 52768c2ecf20Sopenharmony_ci 52778c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 52788c2ecf20Sopenharmony_ci 52798c2ecf20Sopenharmony_ci if (rc) { 52808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "resume: Enable device failed\n"); 52818c2ecf20Sopenharmony_ci return rc; 52828c2ecf20Sopenharmony_ci } 52838c2ecf20Sopenharmony_ci 52848c2ecf20Sopenharmony_ci pci_set_master(pdev); 52858c2ecf20Sopenharmony_ci 52868c2ecf20Sopenharmony_ci if (sizeof(dma_addr_t) == 4 || 52878c2ecf20Sopenharmony_ci dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) 52888c2ecf20Sopenharmony_ci rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 52898c2ecf20Sopenharmony_ci 52908c2ecf20Sopenharmony_ci if (rc == 0) 52918c2ecf20Sopenharmony_ci rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 52928c2ecf20Sopenharmony_ci 52938c2ecf20Sopenharmony_ci if (rc != 0) { 52948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "resume: Failed to set PCI DMA mask\n"); 52958c2ecf20Sopenharmony_ci goto disable_device; 52968c2ecf20Sopenharmony_ci } 52978c2ecf20Sopenharmony_ci 52988c2ecf20Sopenharmony_ci pmcraid_disable_interrupts(pinstance, ~0); 52998c2ecf20Sopenharmony_ci atomic_set(&pinstance->outstanding_cmds, 0); 53008c2ecf20Sopenharmony_ci rc = pmcraid_register_interrupt_handler(pinstance); 53018c2ecf20Sopenharmony_ci 53028c2ecf20Sopenharmony_ci if (rc) { 53038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 53048c2ecf20Sopenharmony_ci "resume: couldn't register interrupt handlers\n"); 53058c2ecf20Sopenharmony_ci rc = -ENODEV; 53068c2ecf20Sopenharmony_ci goto release_host; 53078c2ecf20Sopenharmony_ci } 53088c2ecf20Sopenharmony_ci 53098c2ecf20Sopenharmony_ci pmcraid_init_tasklets(pinstance); 53108c2ecf20Sopenharmony_ci pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS); 53118c2ecf20Sopenharmony_ci 53128c2ecf20Sopenharmony_ci /* Start with hard reset sequence which brings up IOA to operational 53138c2ecf20Sopenharmony_ci * state as well as completes the reset sequence. 53148c2ecf20Sopenharmony_ci */ 53158c2ecf20Sopenharmony_ci pinstance->ioa_hard_reset = 1; 53168c2ecf20Sopenharmony_ci 53178c2ecf20Sopenharmony_ci /* Start IOA firmware initialization and bring card to Operational 53188c2ecf20Sopenharmony_ci * state. 53198c2ecf20Sopenharmony_ci */ 53208c2ecf20Sopenharmony_ci if (pmcraid_reset_bringup(pinstance)) { 53218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't initialize IOA\n"); 53228c2ecf20Sopenharmony_ci rc = -ENODEV; 53238c2ecf20Sopenharmony_ci goto release_tasklets; 53248c2ecf20Sopenharmony_ci } 53258c2ecf20Sopenharmony_ci 53268c2ecf20Sopenharmony_ci return 0; 53278c2ecf20Sopenharmony_ci 53288c2ecf20Sopenharmony_cirelease_tasklets: 53298c2ecf20Sopenharmony_ci pmcraid_disable_interrupts(pinstance, ~0); 53308c2ecf20Sopenharmony_ci pmcraid_kill_tasklets(pinstance); 53318c2ecf20Sopenharmony_ci pmcraid_unregister_interrupt_handler(pinstance); 53328c2ecf20Sopenharmony_ci 53338c2ecf20Sopenharmony_cirelease_host: 53348c2ecf20Sopenharmony_ci scsi_host_put(host); 53358c2ecf20Sopenharmony_ci 53368c2ecf20Sopenharmony_cidisable_device: 53378c2ecf20Sopenharmony_ci pci_disable_device(pdev); 53388c2ecf20Sopenharmony_ci 53398c2ecf20Sopenharmony_ci return rc; 53408c2ecf20Sopenharmony_ci} 53418c2ecf20Sopenharmony_ci 53428c2ecf20Sopenharmony_ci#else 53438c2ecf20Sopenharmony_ci 53448c2ecf20Sopenharmony_ci#define pmcraid_suspend NULL 53458c2ecf20Sopenharmony_ci#define pmcraid_resume NULL 53468c2ecf20Sopenharmony_ci 53478c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 53488c2ecf20Sopenharmony_ci 53498c2ecf20Sopenharmony_ci/** 53508c2ecf20Sopenharmony_ci * pmcraid_complete_ioa_reset - Called by either timer or tasklet during 53518c2ecf20Sopenharmony_ci * completion of the ioa reset 53528c2ecf20Sopenharmony_ci * @cmd: pointer to reset command block 53538c2ecf20Sopenharmony_ci */ 53548c2ecf20Sopenharmony_cistatic void pmcraid_complete_ioa_reset(struct pmcraid_cmd *cmd) 53558c2ecf20Sopenharmony_ci{ 53568c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 53578c2ecf20Sopenharmony_ci unsigned long flags; 53588c2ecf20Sopenharmony_ci 53598c2ecf20Sopenharmony_ci spin_lock_irqsave(pinstance->host->host_lock, flags); 53608c2ecf20Sopenharmony_ci pmcraid_ioa_reset(cmd); 53618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(pinstance->host->host_lock, flags); 53628c2ecf20Sopenharmony_ci scsi_unblock_requests(pinstance->host); 53638c2ecf20Sopenharmony_ci schedule_work(&pinstance->worker_q); 53648c2ecf20Sopenharmony_ci} 53658c2ecf20Sopenharmony_ci 53668c2ecf20Sopenharmony_ci/** 53678c2ecf20Sopenharmony_ci * pmcraid_set_supported_devs - sends SET SUPPORTED DEVICES to IOAFP 53688c2ecf20Sopenharmony_ci * 53698c2ecf20Sopenharmony_ci * @cmd: pointer to pmcraid_cmd structure 53708c2ecf20Sopenharmony_ci * 53718c2ecf20Sopenharmony_ci * Return Value 53728c2ecf20Sopenharmony_ci * 0 for success or non-zero for failure cases 53738c2ecf20Sopenharmony_ci */ 53748c2ecf20Sopenharmony_cistatic void pmcraid_set_supported_devs(struct pmcraid_cmd *cmd) 53758c2ecf20Sopenharmony_ci{ 53768c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 53778c2ecf20Sopenharmony_ci void (*cmd_done) (struct pmcraid_cmd *) = pmcraid_complete_ioa_reset; 53788c2ecf20Sopenharmony_ci 53798c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 53808c2ecf20Sopenharmony_ci 53818c2ecf20Sopenharmony_ci ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 53828c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_IOACMD; 53838c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_SET_SUPPORTED_DEVICES; 53848c2ecf20Sopenharmony_ci ioarcb->cdb[1] = ALL_DEVICES_SUPPORTED; 53858c2ecf20Sopenharmony_ci 53868c2ecf20Sopenharmony_ci /* If this was called as part of resource table reinitialization due to 53878c2ecf20Sopenharmony_ci * lost CCN, it is enough to return the command block back to free pool 53888c2ecf20Sopenharmony_ci * as part of set_supported_devs completion function. 53898c2ecf20Sopenharmony_ci */ 53908c2ecf20Sopenharmony_ci if (cmd->drv_inst->reinit_cfg_table) { 53918c2ecf20Sopenharmony_ci cmd->drv_inst->reinit_cfg_table = 0; 53928c2ecf20Sopenharmony_ci cmd->release = 1; 53938c2ecf20Sopenharmony_ci cmd_done = pmcraid_reinit_cfgtable_done; 53948c2ecf20Sopenharmony_ci } 53958c2ecf20Sopenharmony_ci 53968c2ecf20Sopenharmony_ci /* we will be done with the reset sequence after set supported devices, 53978c2ecf20Sopenharmony_ci * setup the done function to return the command block back to free 53988c2ecf20Sopenharmony_ci * pool 53998c2ecf20Sopenharmony_ci */ 54008c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, 54018c2ecf20Sopenharmony_ci cmd_done, 54028c2ecf20Sopenharmony_ci PMCRAID_SET_SUP_DEV_TIMEOUT, 54038c2ecf20Sopenharmony_ci pmcraid_timeout_handler); 54048c2ecf20Sopenharmony_ci return; 54058c2ecf20Sopenharmony_ci} 54068c2ecf20Sopenharmony_ci 54078c2ecf20Sopenharmony_ci/** 54088c2ecf20Sopenharmony_ci * pmcraid_set_timestamp - set the timestamp to IOAFP 54098c2ecf20Sopenharmony_ci * 54108c2ecf20Sopenharmony_ci * @cmd: pointer to pmcraid_cmd structure 54118c2ecf20Sopenharmony_ci * 54128c2ecf20Sopenharmony_ci * Return Value 54138c2ecf20Sopenharmony_ci * 0 for success or non-zero for failure cases 54148c2ecf20Sopenharmony_ci */ 54158c2ecf20Sopenharmony_cistatic void pmcraid_set_timestamp(struct pmcraid_cmd *cmd) 54168c2ecf20Sopenharmony_ci{ 54178c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 54188c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 54198c2ecf20Sopenharmony_ci __be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN); 54208c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 54218c2ecf20Sopenharmony_ci u64 timestamp; 54228c2ecf20Sopenharmony_ci 54238c2ecf20Sopenharmony_ci timestamp = ktime_get_real_seconds() * 1000; 54248c2ecf20Sopenharmony_ci 54258c2ecf20Sopenharmony_ci pinstance->timestamp_data->timestamp[0] = (__u8)(timestamp); 54268c2ecf20Sopenharmony_ci pinstance->timestamp_data->timestamp[1] = (__u8)((timestamp) >> 8); 54278c2ecf20Sopenharmony_ci pinstance->timestamp_data->timestamp[2] = (__u8)((timestamp) >> 16); 54288c2ecf20Sopenharmony_ci pinstance->timestamp_data->timestamp[3] = (__u8)((timestamp) >> 24); 54298c2ecf20Sopenharmony_ci pinstance->timestamp_data->timestamp[4] = (__u8)((timestamp) >> 32); 54308c2ecf20Sopenharmony_ci pinstance->timestamp_data->timestamp[5] = (__u8)((timestamp) >> 40); 54318c2ecf20Sopenharmony_ci 54328c2ecf20Sopenharmony_ci pmcraid_reinit_cmdblk(cmd); 54338c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_SCSI; 54348c2ecf20Sopenharmony_ci ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 54358c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_SCSI_SET_TIMESTAMP; 54368c2ecf20Sopenharmony_ci ioarcb->cdb[1] = PMCRAID_SCSI_SERVICE_ACTION; 54378c2ecf20Sopenharmony_ci memcpy(&(ioarcb->cdb[6]), &time_stamp_len, sizeof(time_stamp_len)); 54388c2ecf20Sopenharmony_ci 54398c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + 54408c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 54418c2ecf20Sopenharmony_ci add_data.u.ioadl[0])); 54428c2ecf20Sopenharmony_ci ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); 54438c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL)); 54448c2ecf20Sopenharmony_ci 54458c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= NO_LINK_DESCS; 54468c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= TRANSFER_DIR_WRITE; 54478c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = 54488c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct pmcraid_timestamp_data)); 54498c2ecf20Sopenharmony_ci ioadl = &(ioarcb->add_data.u.ioadl[0]); 54508c2ecf20Sopenharmony_ci ioadl->flags = IOADL_FLAGS_LAST_DESC; 54518c2ecf20Sopenharmony_ci ioadl->address = cpu_to_le64(pinstance->timestamp_data_baddr); 54528c2ecf20Sopenharmony_ci ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_timestamp_data)); 54538c2ecf20Sopenharmony_ci 54548c2ecf20Sopenharmony_ci if (!pinstance->timestamp_error) { 54558c2ecf20Sopenharmony_ci pinstance->timestamp_error = 0; 54568c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, pmcraid_set_supported_devs, 54578c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); 54588c2ecf20Sopenharmony_ci } else { 54598c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, pmcraid_return_cmd, 54608c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); 54618c2ecf20Sopenharmony_ci return; 54628c2ecf20Sopenharmony_ci } 54638c2ecf20Sopenharmony_ci} 54648c2ecf20Sopenharmony_ci 54658c2ecf20Sopenharmony_ci 54668c2ecf20Sopenharmony_ci/** 54678c2ecf20Sopenharmony_ci * pmcraid_init_res_table - Initialize the resource table 54688c2ecf20Sopenharmony_ci * @cmd: pointer to pmcraid command struct 54698c2ecf20Sopenharmony_ci * 54708c2ecf20Sopenharmony_ci * This function looks through the existing resource table, comparing 54718c2ecf20Sopenharmony_ci * it with the config table. This function will take care of old/new 54728c2ecf20Sopenharmony_ci * devices and schedule adding/removing them from the mid-layer 54738c2ecf20Sopenharmony_ci * as appropriate. 54748c2ecf20Sopenharmony_ci * 54758c2ecf20Sopenharmony_ci * Return value 54768c2ecf20Sopenharmony_ci * None 54778c2ecf20Sopenharmony_ci */ 54788c2ecf20Sopenharmony_cistatic void pmcraid_init_res_table(struct pmcraid_cmd *cmd) 54798c2ecf20Sopenharmony_ci{ 54808c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 54818c2ecf20Sopenharmony_ci struct pmcraid_resource_entry *res, *temp; 54828c2ecf20Sopenharmony_ci struct pmcraid_config_table_entry *cfgte; 54838c2ecf20Sopenharmony_ci unsigned long lock_flags; 54848c2ecf20Sopenharmony_ci int found, rc, i; 54858c2ecf20Sopenharmony_ci u16 fw_version; 54868c2ecf20Sopenharmony_ci LIST_HEAD(old_res); 54878c2ecf20Sopenharmony_ci 54888c2ecf20Sopenharmony_ci if (pinstance->cfg_table->flags & MICROCODE_UPDATE_REQUIRED) 54898c2ecf20Sopenharmony_ci pmcraid_err("IOA requires microcode download\n"); 54908c2ecf20Sopenharmony_ci 54918c2ecf20Sopenharmony_ci fw_version = be16_to_cpu(pinstance->inq_data->fw_version); 54928c2ecf20Sopenharmony_ci 54938c2ecf20Sopenharmony_ci /* resource list is protected by pinstance->resource_lock. 54948c2ecf20Sopenharmony_ci * init_res_table can be called from probe (user-thread) or runtime 54958c2ecf20Sopenharmony_ci * reset (timer/tasklet) 54968c2ecf20Sopenharmony_ci */ 54978c2ecf20Sopenharmony_ci spin_lock_irqsave(&pinstance->resource_lock, lock_flags); 54988c2ecf20Sopenharmony_ci 54998c2ecf20Sopenharmony_ci list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue) 55008c2ecf20Sopenharmony_ci list_move_tail(&res->queue, &old_res); 55018c2ecf20Sopenharmony_ci 55028c2ecf20Sopenharmony_ci for (i = 0; i < le16_to_cpu(pinstance->cfg_table->num_entries); i++) { 55038c2ecf20Sopenharmony_ci if (be16_to_cpu(pinstance->inq_data->fw_version) <= 55048c2ecf20Sopenharmony_ci PMCRAID_FW_VERSION_1) 55058c2ecf20Sopenharmony_ci cfgte = &pinstance->cfg_table->entries[i]; 55068c2ecf20Sopenharmony_ci else 55078c2ecf20Sopenharmony_ci cfgte = (struct pmcraid_config_table_entry *) 55088c2ecf20Sopenharmony_ci &pinstance->cfg_table->entries_ext[i]; 55098c2ecf20Sopenharmony_ci 55108c2ecf20Sopenharmony_ci if (!pmcraid_expose_resource(fw_version, cfgte)) 55118c2ecf20Sopenharmony_ci continue; 55128c2ecf20Sopenharmony_ci 55138c2ecf20Sopenharmony_ci found = 0; 55148c2ecf20Sopenharmony_ci 55158c2ecf20Sopenharmony_ci /* If this entry was already detected and initialized */ 55168c2ecf20Sopenharmony_ci list_for_each_entry_safe(res, temp, &old_res, queue) { 55178c2ecf20Sopenharmony_ci 55188c2ecf20Sopenharmony_ci rc = memcmp(&res->cfg_entry.resource_address, 55198c2ecf20Sopenharmony_ci &cfgte->resource_address, 55208c2ecf20Sopenharmony_ci sizeof(cfgte->resource_address)); 55218c2ecf20Sopenharmony_ci if (!rc) { 55228c2ecf20Sopenharmony_ci list_move_tail(&res->queue, 55238c2ecf20Sopenharmony_ci &pinstance->used_res_q); 55248c2ecf20Sopenharmony_ci found = 1; 55258c2ecf20Sopenharmony_ci break; 55268c2ecf20Sopenharmony_ci } 55278c2ecf20Sopenharmony_ci } 55288c2ecf20Sopenharmony_ci 55298c2ecf20Sopenharmony_ci /* If this is new entry, initialize it and add it the queue */ 55308c2ecf20Sopenharmony_ci if (!found) { 55318c2ecf20Sopenharmony_ci 55328c2ecf20Sopenharmony_ci if (list_empty(&pinstance->free_res_q)) { 55338c2ecf20Sopenharmony_ci pmcraid_err("Too many devices attached\n"); 55348c2ecf20Sopenharmony_ci break; 55358c2ecf20Sopenharmony_ci } 55368c2ecf20Sopenharmony_ci 55378c2ecf20Sopenharmony_ci found = 1; 55388c2ecf20Sopenharmony_ci res = list_entry(pinstance->free_res_q.next, 55398c2ecf20Sopenharmony_ci struct pmcraid_resource_entry, queue); 55408c2ecf20Sopenharmony_ci 55418c2ecf20Sopenharmony_ci res->scsi_dev = NULL; 55428c2ecf20Sopenharmony_ci res->change_detected = RES_CHANGE_ADD; 55438c2ecf20Sopenharmony_ci res->reset_progress = 0; 55448c2ecf20Sopenharmony_ci list_move_tail(&res->queue, &pinstance->used_res_q); 55458c2ecf20Sopenharmony_ci } 55468c2ecf20Sopenharmony_ci 55478c2ecf20Sopenharmony_ci /* copy new configuration table entry details into driver 55488c2ecf20Sopenharmony_ci * maintained resource entry 55498c2ecf20Sopenharmony_ci */ 55508c2ecf20Sopenharmony_ci if (found) { 55518c2ecf20Sopenharmony_ci memcpy(&res->cfg_entry, cfgte, 55528c2ecf20Sopenharmony_ci pinstance->config_table_entry_size); 55538c2ecf20Sopenharmony_ci pmcraid_info("New res type:%x, vset:%x, addr:%x:\n", 55548c2ecf20Sopenharmony_ci res->cfg_entry.resource_type, 55558c2ecf20Sopenharmony_ci (fw_version <= PMCRAID_FW_VERSION_1 ? 55568c2ecf20Sopenharmony_ci res->cfg_entry.unique_flags1 : 55578c2ecf20Sopenharmony_ci le16_to_cpu(res->cfg_entry.array_id) & 0xFF), 55588c2ecf20Sopenharmony_ci le32_to_cpu(res->cfg_entry.resource_address)); 55598c2ecf20Sopenharmony_ci } 55608c2ecf20Sopenharmony_ci } 55618c2ecf20Sopenharmony_ci 55628c2ecf20Sopenharmony_ci /* Detect any deleted entries, mark them for deletion from mid-layer */ 55638c2ecf20Sopenharmony_ci list_for_each_entry_safe(res, temp, &old_res, queue) { 55648c2ecf20Sopenharmony_ci 55658c2ecf20Sopenharmony_ci if (res->scsi_dev) { 55668c2ecf20Sopenharmony_ci res->change_detected = RES_CHANGE_DEL; 55678c2ecf20Sopenharmony_ci res->cfg_entry.resource_handle = 55688c2ecf20Sopenharmony_ci PMCRAID_INVALID_RES_HANDLE; 55698c2ecf20Sopenharmony_ci list_move_tail(&res->queue, &pinstance->used_res_q); 55708c2ecf20Sopenharmony_ci } else { 55718c2ecf20Sopenharmony_ci list_move_tail(&res->queue, &pinstance->free_res_q); 55728c2ecf20Sopenharmony_ci } 55738c2ecf20Sopenharmony_ci } 55748c2ecf20Sopenharmony_ci 55758c2ecf20Sopenharmony_ci /* release the resource list lock */ 55768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); 55778c2ecf20Sopenharmony_ci pmcraid_set_timestamp(cmd); 55788c2ecf20Sopenharmony_ci} 55798c2ecf20Sopenharmony_ci 55808c2ecf20Sopenharmony_ci/** 55818c2ecf20Sopenharmony_ci * pmcraid_querycfg - Send a Query IOA Config to the adapter. 55828c2ecf20Sopenharmony_ci * @cmd: pointer pmcraid_cmd struct 55838c2ecf20Sopenharmony_ci * 55848c2ecf20Sopenharmony_ci * This function sends a Query IOA Configuration command to the adapter to 55858c2ecf20Sopenharmony_ci * retrieve the IOA configuration table. 55868c2ecf20Sopenharmony_ci * 55878c2ecf20Sopenharmony_ci * Return value: 55888c2ecf20Sopenharmony_ci * none 55898c2ecf20Sopenharmony_ci */ 55908c2ecf20Sopenharmony_cistatic void pmcraid_querycfg(struct pmcraid_cmd *cmd) 55918c2ecf20Sopenharmony_ci{ 55928c2ecf20Sopenharmony_ci struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; 55938c2ecf20Sopenharmony_ci struct pmcraid_ioadl_desc *ioadl; 55948c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance = cmd->drv_inst; 55958c2ecf20Sopenharmony_ci __be32 cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table)); 55968c2ecf20Sopenharmony_ci 55978c2ecf20Sopenharmony_ci if (be16_to_cpu(pinstance->inq_data->fw_version) <= 55988c2ecf20Sopenharmony_ci PMCRAID_FW_VERSION_1) 55998c2ecf20Sopenharmony_ci pinstance->config_table_entry_size = 56008c2ecf20Sopenharmony_ci sizeof(struct pmcraid_config_table_entry); 56018c2ecf20Sopenharmony_ci else 56028c2ecf20Sopenharmony_ci pinstance->config_table_entry_size = 56038c2ecf20Sopenharmony_ci sizeof(struct pmcraid_config_table_entry_ext); 56048c2ecf20Sopenharmony_ci 56058c2ecf20Sopenharmony_ci ioarcb->request_type = REQ_TYPE_IOACMD; 56068c2ecf20Sopenharmony_ci ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); 56078c2ecf20Sopenharmony_ci 56088c2ecf20Sopenharmony_ci ioarcb->cdb[0] = PMCRAID_QUERY_IOA_CONFIG; 56098c2ecf20Sopenharmony_ci 56108c2ecf20Sopenharmony_ci /* firmware requires 4-byte length field, specified in B.E format */ 56118c2ecf20Sopenharmony_ci memcpy(&(ioarcb->cdb[10]), &cfg_table_size, sizeof(cfg_table_size)); 56128c2ecf20Sopenharmony_ci 56138c2ecf20Sopenharmony_ci /* Since entire config table can be described by single IOADL, it can 56148c2ecf20Sopenharmony_ci * be part of IOARCB itself 56158c2ecf20Sopenharmony_ci */ 56168c2ecf20Sopenharmony_ci ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + 56178c2ecf20Sopenharmony_ci offsetof(struct pmcraid_ioarcb, 56188c2ecf20Sopenharmony_ci add_data.u.ioadl[0])); 56198c2ecf20Sopenharmony_ci ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); 56208c2ecf20Sopenharmony_ci ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL); 56218c2ecf20Sopenharmony_ci 56228c2ecf20Sopenharmony_ci ioarcb->request_flags0 |= NO_LINK_DESCS; 56238c2ecf20Sopenharmony_ci ioarcb->data_transfer_length = 56248c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct pmcraid_config_table)); 56258c2ecf20Sopenharmony_ci 56268c2ecf20Sopenharmony_ci ioadl = &(ioarcb->add_data.u.ioadl[0]); 56278c2ecf20Sopenharmony_ci ioadl->flags = IOADL_FLAGS_LAST_DESC; 56288c2ecf20Sopenharmony_ci ioadl->address = cpu_to_le64(pinstance->cfg_table_bus_addr); 56298c2ecf20Sopenharmony_ci ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_config_table)); 56308c2ecf20Sopenharmony_ci 56318c2ecf20Sopenharmony_ci pmcraid_send_cmd(cmd, pmcraid_init_res_table, 56328c2ecf20Sopenharmony_ci PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); 56338c2ecf20Sopenharmony_ci} 56348c2ecf20Sopenharmony_ci 56358c2ecf20Sopenharmony_ci 56368c2ecf20Sopenharmony_ci/** 56378c2ecf20Sopenharmony_ci * pmcraid_probe - PCI probe entry pointer for PMC MaxRAID controller driver 56388c2ecf20Sopenharmony_ci * @pdev: pointer to pci device structure 56398c2ecf20Sopenharmony_ci * @dev_id: pointer to device ids structure 56408c2ecf20Sopenharmony_ci * 56418c2ecf20Sopenharmony_ci * Return Value 56428c2ecf20Sopenharmony_ci * returns 0 if the device is claimed and successfully configured. 56438c2ecf20Sopenharmony_ci * returns non-zero error code in case of any failure 56448c2ecf20Sopenharmony_ci */ 56458c2ecf20Sopenharmony_cistatic int pmcraid_probe(struct pci_dev *pdev, 56468c2ecf20Sopenharmony_ci const struct pci_device_id *dev_id) 56478c2ecf20Sopenharmony_ci{ 56488c2ecf20Sopenharmony_ci struct pmcraid_instance *pinstance; 56498c2ecf20Sopenharmony_ci struct Scsi_Host *host; 56508c2ecf20Sopenharmony_ci void __iomem *mapped_pci_addr; 56518c2ecf20Sopenharmony_ci int rc = PCIBIOS_SUCCESSFUL; 56528c2ecf20Sopenharmony_ci 56538c2ecf20Sopenharmony_ci if (atomic_read(&pmcraid_adapter_count) >= PMCRAID_MAX_ADAPTERS) { 56548c2ecf20Sopenharmony_ci pmcraid_err 56558c2ecf20Sopenharmony_ci ("maximum number(%d) of supported adapters reached\n", 56568c2ecf20Sopenharmony_ci atomic_read(&pmcraid_adapter_count)); 56578c2ecf20Sopenharmony_ci return -ENOMEM; 56588c2ecf20Sopenharmony_ci } 56598c2ecf20Sopenharmony_ci 56608c2ecf20Sopenharmony_ci atomic_inc(&pmcraid_adapter_count); 56618c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 56628c2ecf20Sopenharmony_ci 56638c2ecf20Sopenharmony_ci if (rc) { 56648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable adapter\n"); 56658c2ecf20Sopenharmony_ci atomic_dec(&pmcraid_adapter_count); 56668c2ecf20Sopenharmony_ci return rc; 56678c2ecf20Sopenharmony_ci } 56688c2ecf20Sopenharmony_ci 56698c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 56708c2ecf20Sopenharmony_ci "Found new IOA(%x:%x), Total IOA count: %d\n", 56718c2ecf20Sopenharmony_ci pdev->vendor, pdev->device, 56728c2ecf20Sopenharmony_ci atomic_read(&pmcraid_adapter_count)); 56738c2ecf20Sopenharmony_ci 56748c2ecf20Sopenharmony_ci rc = pci_request_regions(pdev, PMCRAID_DRIVER_NAME); 56758c2ecf20Sopenharmony_ci 56768c2ecf20Sopenharmony_ci if (rc < 0) { 56778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 56788c2ecf20Sopenharmony_ci "Couldn't register memory range of registers\n"); 56798c2ecf20Sopenharmony_ci goto out_disable_device; 56808c2ecf20Sopenharmony_ci } 56818c2ecf20Sopenharmony_ci 56828c2ecf20Sopenharmony_ci mapped_pci_addr = pci_iomap(pdev, 0, 0); 56838c2ecf20Sopenharmony_ci 56848c2ecf20Sopenharmony_ci if (!mapped_pci_addr) { 56858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't map PCI registers memory\n"); 56868c2ecf20Sopenharmony_ci rc = -ENOMEM; 56878c2ecf20Sopenharmony_ci goto out_release_regions; 56888c2ecf20Sopenharmony_ci } 56898c2ecf20Sopenharmony_ci 56908c2ecf20Sopenharmony_ci pci_set_master(pdev); 56918c2ecf20Sopenharmony_ci 56928c2ecf20Sopenharmony_ci /* Firmware requires the system bus address of IOARCB to be within 56938c2ecf20Sopenharmony_ci * 32-bit addressable range though it has 64-bit IOARRIN register. 56948c2ecf20Sopenharmony_ci * However, firmware supports 64-bit streaming DMA buffers, whereas 56958c2ecf20Sopenharmony_ci * coherent buffers are to be 32-bit. Since dma_alloc_coherent always 56968c2ecf20Sopenharmony_ci * returns memory within 4GB (if not, change this logic), coherent 56978c2ecf20Sopenharmony_ci * buffers are within firmware acceptable address ranges. 56988c2ecf20Sopenharmony_ci */ 56998c2ecf20Sopenharmony_ci if (sizeof(dma_addr_t) == 4 || 57008c2ecf20Sopenharmony_ci dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) 57018c2ecf20Sopenharmony_ci rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 57028c2ecf20Sopenharmony_ci 57038c2ecf20Sopenharmony_ci /* firmware expects 32-bit DMA addresses for IOARRIN register; set 32 57048c2ecf20Sopenharmony_ci * bit mask for dma_alloc_coherent to return addresses within 4GB 57058c2ecf20Sopenharmony_ci */ 57068c2ecf20Sopenharmony_ci if (rc == 0) 57078c2ecf20Sopenharmony_ci rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 57088c2ecf20Sopenharmony_ci 57098c2ecf20Sopenharmony_ci if (rc != 0) { 57108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); 57118c2ecf20Sopenharmony_ci goto cleanup_nomem; 57128c2ecf20Sopenharmony_ci } 57138c2ecf20Sopenharmony_ci 57148c2ecf20Sopenharmony_ci host = scsi_host_alloc(&pmcraid_host_template, 57158c2ecf20Sopenharmony_ci sizeof(struct pmcraid_instance)); 57168c2ecf20Sopenharmony_ci 57178c2ecf20Sopenharmony_ci if (!host) { 57188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "scsi_host_alloc failed!\n"); 57198c2ecf20Sopenharmony_ci rc = -ENOMEM; 57208c2ecf20Sopenharmony_ci goto cleanup_nomem; 57218c2ecf20Sopenharmony_ci } 57228c2ecf20Sopenharmony_ci 57238c2ecf20Sopenharmony_ci host->max_id = PMCRAID_MAX_NUM_TARGETS_PER_BUS; 57248c2ecf20Sopenharmony_ci host->max_lun = PMCRAID_MAX_NUM_LUNS_PER_TARGET; 57258c2ecf20Sopenharmony_ci host->unique_id = host->host_no; 57268c2ecf20Sopenharmony_ci host->max_channel = PMCRAID_MAX_BUS_TO_SCAN; 57278c2ecf20Sopenharmony_ci host->max_cmd_len = PMCRAID_MAX_CDB_LEN; 57288c2ecf20Sopenharmony_ci 57298c2ecf20Sopenharmony_ci /* zero out entire instance structure */ 57308c2ecf20Sopenharmony_ci pinstance = (struct pmcraid_instance *)host->hostdata; 57318c2ecf20Sopenharmony_ci memset(pinstance, 0, sizeof(*pinstance)); 57328c2ecf20Sopenharmony_ci 57338c2ecf20Sopenharmony_ci pinstance->chip_cfg = 57348c2ecf20Sopenharmony_ci (struct pmcraid_chip_details *)(dev_id->driver_data); 57358c2ecf20Sopenharmony_ci 57368c2ecf20Sopenharmony_ci rc = pmcraid_init_instance(pdev, host, mapped_pci_addr); 57378c2ecf20Sopenharmony_ci 57388c2ecf20Sopenharmony_ci if (rc < 0) { 57398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize adapter instance\n"); 57408c2ecf20Sopenharmony_ci goto out_scsi_host_put; 57418c2ecf20Sopenharmony_ci } 57428c2ecf20Sopenharmony_ci 57438c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, pinstance); 57448c2ecf20Sopenharmony_ci 57458c2ecf20Sopenharmony_ci /* Save PCI config-space for use following the reset */ 57468c2ecf20Sopenharmony_ci rc = pci_save_state(pinstance->pdev); 57478c2ecf20Sopenharmony_ci 57488c2ecf20Sopenharmony_ci if (rc != 0) { 57498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to save PCI config space\n"); 57508c2ecf20Sopenharmony_ci goto out_scsi_host_put; 57518c2ecf20Sopenharmony_ci } 57528c2ecf20Sopenharmony_ci 57538c2ecf20Sopenharmony_ci pmcraid_disable_interrupts(pinstance, ~0); 57548c2ecf20Sopenharmony_ci 57558c2ecf20Sopenharmony_ci rc = pmcraid_register_interrupt_handler(pinstance); 57568c2ecf20Sopenharmony_ci 57578c2ecf20Sopenharmony_ci if (rc) { 57588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't register interrupt handler\n"); 57598c2ecf20Sopenharmony_ci goto out_scsi_host_put; 57608c2ecf20Sopenharmony_ci } 57618c2ecf20Sopenharmony_ci 57628c2ecf20Sopenharmony_ci pmcraid_init_tasklets(pinstance); 57638c2ecf20Sopenharmony_ci 57648c2ecf20Sopenharmony_ci /* allocate verious buffers used by LLD.*/ 57658c2ecf20Sopenharmony_ci rc = pmcraid_init_buffers(pinstance); 57668c2ecf20Sopenharmony_ci 57678c2ecf20Sopenharmony_ci if (rc) { 57688c2ecf20Sopenharmony_ci pmcraid_err("couldn't allocate memory blocks\n"); 57698c2ecf20Sopenharmony_ci goto out_unregister_isr; 57708c2ecf20Sopenharmony_ci } 57718c2ecf20Sopenharmony_ci 57728c2ecf20Sopenharmony_ci /* check the reset type required */ 57738c2ecf20Sopenharmony_ci pmcraid_reset_type(pinstance); 57748c2ecf20Sopenharmony_ci 57758c2ecf20Sopenharmony_ci pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS); 57768c2ecf20Sopenharmony_ci 57778c2ecf20Sopenharmony_ci /* Start IOA firmware initialization and bring card to Operational 57788c2ecf20Sopenharmony_ci * state. 57798c2ecf20Sopenharmony_ci */ 57808c2ecf20Sopenharmony_ci pmcraid_info("starting IOA initialization sequence\n"); 57818c2ecf20Sopenharmony_ci if (pmcraid_reset_bringup(pinstance)) { 57828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't initialize IOA\n"); 57838c2ecf20Sopenharmony_ci rc = 1; 57848c2ecf20Sopenharmony_ci goto out_release_bufs; 57858c2ecf20Sopenharmony_ci } 57868c2ecf20Sopenharmony_ci 57878c2ecf20Sopenharmony_ci /* Add adapter instance into mid-layer list */ 57888c2ecf20Sopenharmony_ci rc = scsi_add_host(pinstance->host, &pdev->dev); 57898c2ecf20Sopenharmony_ci if (rc != 0) { 57908c2ecf20Sopenharmony_ci pmcraid_err("couldn't add host into mid-layer: %d\n", rc); 57918c2ecf20Sopenharmony_ci goto out_release_bufs; 57928c2ecf20Sopenharmony_ci } 57938c2ecf20Sopenharmony_ci 57948c2ecf20Sopenharmony_ci scsi_scan_host(pinstance->host); 57958c2ecf20Sopenharmony_ci 57968c2ecf20Sopenharmony_ci rc = pmcraid_setup_chrdev(pinstance); 57978c2ecf20Sopenharmony_ci 57988c2ecf20Sopenharmony_ci if (rc != 0) { 57998c2ecf20Sopenharmony_ci pmcraid_err("couldn't create mgmt interface, error: %x\n", 58008c2ecf20Sopenharmony_ci rc); 58018c2ecf20Sopenharmony_ci goto out_remove_host; 58028c2ecf20Sopenharmony_ci } 58038c2ecf20Sopenharmony_ci 58048c2ecf20Sopenharmony_ci /* Schedule worker thread to handle CCN and take care of adding and 58058c2ecf20Sopenharmony_ci * removing devices to OS 58068c2ecf20Sopenharmony_ci */ 58078c2ecf20Sopenharmony_ci atomic_set(&pinstance->expose_resources, 1); 58088c2ecf20Sopenharmony_ci schedule_work(&pinstance->worker_q); 58098c2ecf20Sopenharmony_ci return rc; 58108c2ecf20Sopenharmony_ci 58118c2ecf20Sopenharmony_ciout_remove_host: 58128c2ecf20Sopenharmony_ci scsi_remove_host(host); 58138c2ecf20Sopenharmony_ci 58148c2ecf20Sopenharmony_ciout_release_bufs: 58158c2ecf20Sopenharmony_ci pmcraid_release_buffers(pinstance); 58168c2ecf20Sopenharmony_ci 58178c2ecf20Sopenharmony_ciout_unregister_isr: 58188c2ecf20Sopenharmony_ci pmcraid_kill_tasklets(pinstance); 58198c2ecf20Sopenharmony_ci pmcraid_unregister_interrupt_handler(pinstance); 58208c2ecf20Sopenharmony_ci 58218c2ecf20Sopenharmony_ciout_scsi_host_put: 58228c2ecf20Sopenharmony_ci scsi_host_put(host); 58238c2ecf20Sopenharmony_ci 58248c2ecf20Sopenharmony_cicleanup_nomem: 58258c2ecf20Sopenharmony_ci iounmap(mapped_pci_addr); 58268c2ecf20Sopenharmony_ci 58278c2ecf20Sopenharmony_ciout_release_regions: 58288c2ecf20Sopenharmony_ci pci_release_regions(pdev); 58298c2ecf20Sopenharmony_ci 58308c2ecf20Sopenharmony_ciout_disable_device: 58318c2ecf20Sopenharmony_ci atomic_dec(&pmcraid_adapter_count); 58328c2ecf20Sopenharmony_ci pci_disable_device(pdev); 58338c2ecf20Sopenharmony_ci return -ENODEV; 58348c2ecf20Sopenharmony_ci} 58358c2ecf20Sopenharmony_ci 58368c2ecf20Sopenharmony_ci/* 58378c2ecf20Sopenharmony_ci * PCI driver structure of pmcraid driver 58388c2ecf20Sopenharmony_ci */ 58398c2ecf20Sopenharmony_cistatic struct pci_driver pmcraid_driver = { 58408c2ecf20Sopenharmony_ci .name = PMCRAID_DRIVER_NAME, 58418c2ecf20Sopenharmony_ci .id_table = pmcraid_pci_table, 58428c2ecf20Sopenharmony_ci .probe = pmcraid_probe, 58438c2ecf20Sopenharmony_ci .remove = pmcraid_remove, 58448c2ecf20Sopenharmony_ci .suspend = pmcraid_suspend, 58458c2ecf20Sopenharmony_ci .resume = pmcraid_resume, 58468c2ecf20Sopenharmony_ci .shutdown = pmcraid_shutdown 58478c2ecf20Sopenharmony_ci}; 58488c2ecf20Sopenharmony_ci 58498c2ecf20Sopenharmony_ci/** 58508c2ecf20Sopenharmony_ci * pmcraid_init - module load entry point 58518c2ecf20Sopenharmony_ci */ 58528c2ecf20Sopenharmony_cistatic int __init pmcraid_init(void) 58538c2ecf20Sopenharmony_ci{ 58548c2ecf20Sopenharmony_ci dev_t dev; 58558c2ecf20Sopenharmony_ci int error; 58568c2ecf20Sopenharmony_ci 58578c2ecf20Sopenharmony_ci pmcraid_info("%s Device Driver version: %s\n", 58588c2ecf20Sopenharmony_ci PMCRAID_DRIVER_NAME, PMCRAID_DRIVER_VERSION); 58598c2ecf20Sopenharmony_ci 58608c2ecf20Sopenharmony_ci error = alloc_chrdev_region(&dev, 0, 58618c2ecf20Sopenharmony_ci PMCRAID_MAX_ADAPTERS, 58628c2ecf20Sopenharmony_ci PMCRAID_DEVFILE); 58638c2ecf20Sopenharmony_ci 58648c2ecf20Sopenharmony_ci if (error) { 58658c2ecf20Sopenharmony_ci pmcraid_err("failed to get a major number for adapters\n"); 58668c2ecf20Sopenharmony_ci goto out_init; 58678c2ecf20Sopenharmony_ci } 58688c2ecf20Sopenharmony_ci 58698c2ecf20Sopenharmony_ci pmcraid_major = MAJOR(dev); 58708c2ecf20Sopenharmony_ci pmcraid_class = class_create(THIS_MODULE, PMCRAID_DEVFILE); 58718c2ecf20Sopenharmony_ci 58728c2ecf20Sopenharmony_ci if (IS_ERR(pmcraid_class)) { 58738c2ecf20Sopenharmony_ci error = PTR_ERR(pmcraid_class); 58748c2ecf20Sopenharmony_ci pmcraid_err("failed to register with sysfs, error = %x\n", 58758c2ecf20Sopenharmony_ci error); 58768c2ecf20Sopenharmony_ci goto out_unreg_chrdev; 58778c2ecf20Sopenharmony_ci } 58788c2ecf20Sopenharmony_ci 58798c2ecf20Sopenharmony_ci error = pmcraid_netlink_init(); 58808c2ecf20Sopenharmony_ci 58818c2ecf20Sopenharmony_ci if (error) { 58828c2ecf20Sopenharmony_ci class_destroy(pmcraid_class); 58838c2ecf20Sopenharmony_ci goto out_unreg_chrdev; 58848c2ecf20Sopenharmony_ci } 58858c2ecf20Sopenharmony_ci 58868c2ecf20Sopenharmony_ci error = pci_register_driver(&pmcraid_driver); 58878c2ecf20Sopenharmony_ci 58888c2ecf20Sopenharmony_ci if (error == 0) 58898c2ecf20Sopenharmony_ci goto out_init; 58908c2ecf20Sopenharmony_ci 58918c2ecf20Sopenharmony_ci pmcraid_err("failed to register pmcraid driver, error = %x\n", 58928c2ecf20Sopenharmony_ci error); 58938c2ecf20Sopenharmony_ci class_destroy(pmcraid_class); 58948c2ecf20Sopenharmony_ci pmcraid_netlink_release(); 58958c2ecf20Sopenharmony_ci 58968c2ecf20Sopenharmony_ciout_unreg_chrdev: 58978c2ecf20Sopenharmony_ci unregister_chrdev_region(MKDEV(pmcraid_major, 0), PMCRAID_MAX_ADAPTERS); 58988c2ecf20Sopenharmony_ci 58998c2ecf20Sopenharmony_ciout_init: 59008c2ecf20Sopenharmony_ci return error; 59018c2ecf20Sopenharmony_ci} 59028c2ecf20Sopenharmony_ci 59038c2ecf20Sopenharmony_ci/** 59048c2ecf20Sopenharmony_ci * pmcraid_exit - module unload entry point 59058c2ecf20Sopenharmony_ci */ 59068c2ecf20Sopenharmony_cistatic void __exit pmcraid_exit(void) 59078c2ecf20Sopenharmony_ci{ 59088c2ecf20Sopenharmony_ci pmcraid_netlink_release(); 59098c2ecf20Sopenharmony_ci unregister_chrdev_region(MKDEV(pmcraid_major, 0), 59108c2ecf20Sopenharmony_ci PMCRAID_MAX_ADAPTERS); 59118c2ecf20Sopenharmony_ci pci_unregister_driver(&pmcraid_driver); 59128c2ecf20Sopenharmony_ci class_destroy(pmcraid_class); 59138c2ecf20Sopenharmony_ci} 59148c2ecf20Sopenharmony_ci 59158c2ecf20Sopenharmony_cimodule_init(pmcraid_init); 59168c2ecf20Sopenharmony_cimodule_exit(pmcraid_exit); 5917