// SPDX-License-Identifier: GPL-2.0-or-later/* * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters * * Written By: Anil Ravindranath<anil_ravindranath@pmc-sierra.com> * PMC-Sierra Inc * * Copyright (C) 2008, 2009 PMC Sierra Inc */#include <linux/fs.h>#include <linux/init.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/wait.h>#include <linux/spinlock.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/blkdev.h>#include <linux/firmware.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/hdreg.h>#include <linux/io.h>#include <linux/slab.h>#include <asm/irq.h>#include <asm/processor.h>#include <linux/libata.h>#include <linux/mutex.h>#include <linux/ktime.h>#include <scsi/scsi.h>#include <scsi/scsi_host.h>#include <scsi/scsi_device.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsicam.h> #include "pmcraid.h" /* * Module configuration parameters */static unsigned int pmcraid_debug_log;static unsigned int pmcraid_disable_aen;static unsigned int pmcraid_log_level = IOASC_LOG_LEVEL_MUST;static unsigned int pmcraid_enable_msix; /* * Data structures to support multiple adapters by the LLD. * pmcraid_adapter_count - count of configured adapters */static atomic_t pmcraid_adapter_count = ATOMIC_INIT(0); /* * Supporting user-level control interface through IOCTL commands. * pmcraid_major - major number to use * pmcraid_minor - minor number(s) to use */static unsigned int pmcraid_major;static const struct class pmcraid_class = { .name = PMCRAID_DEVFILE,};static DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS); /* * Module parameters */MODULE_AUTHOR("Anil Ravindranath<anil_ravindranath@pmc-sierra.com>");MODULE_DESCRIPTION("PMC Sierra MaxRAID Controller Driver");MODULE_LICENSE("GPL");MODULE_VERSION(PMCRAID_DRIVER_VERSION); module_param_named(log_level, pmcraid_log_level, uint, (S_IRUGO | S_IWUSR));MODULE_PARM_DESC(log_level, "Enables firmware error code logging, default :1 high-severity" " errors, 2: all errors including high-severity errors," " 0: disables logging"); module_param_named(debug, pmcraid_debug_log, uint, (S_IRUGO | S_IWUSR));MODULE_PARM_DESC(debug, "Enable driver verbose message logging. Set 1 to enable." "(default: 0)"); module_param_named(disable_aen, pmcraid_disable_aen, uint, (S_IRUGO | S_IWUSR));MODULE_PARM_DESC(disable_aen, "Disable driver aen notifications to apps. Set 1 to disable." "(default: 0)"); /* chip specific constants for PMC MaxRAID controllers (same for * 0x5220 and 0x8010 */static struct pmcraid_chip_details pmcraid_chip_cfg[] = { { .ioastatus = 0x0, .ioarrin = 0x00040, .mailbox = 0x7FC30, .global_intr_mask = 0x00034, .ioa_host_intr = 0x0009C, .ioa_host_intr_clr = 0x000A0, .ioa_host_msix_intr = 0x7FC40, .ioa_host_mask = 0x7FC28, .ioa_host_mask_clr = 0x7FC28, .host_ioa_intr = 0x00020, .host_ioa_intr_clr = 0x00020, .transop_timeout = 300 }}; /* * PCI device ids supported by pmcraid driver */static const struct pci_device_id pmcraid_pci_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID), 0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0] }, {}}; MODULE_DEVICE_TABLE(pci, pmcraid_pci_table); /** * pmcraid_sdev_init - Prepare for commands to a device * @scsi_dev: scsi device struct * * This function is called by mid-layer prior to sending any command to the new * device. Stores resource entry details of the device in scsi_device struct. * Queuecommand uses the resource handle and other details to fill up IOARCB * while sending commands to the device. * * Return value: * 0 on success / -ENXIO if device does not exist */static int pmcraid_sdev_init(struct scsi_device *scsi_dev){ struct pmcraid_resource_entry *temp, *res = NULL; struct pmcraid_instance *pinstance; u8 target, bus, lun; unsigned long lock_flags; int rc = -ENXIO; u16 fw_version; pinstance = shost_priv(scsi_dev->host); fw_version = be16_to_cpu(pinstance->inq_data->fw_version); /* Driver exposes VSET and GSCSI resources only; all other device types * are not exposed. Resource list is synchronized using resource lock * so any traversal or modifications to the list should be done inside * this lock */ spin_lock_irqsave(&pinstance->resource_lock, lock_flags); list_for_each_entry(temp, &pinstance->used_res_q, queue) { /* do not expose VSETs with order-ids > MAX_VSET_TARGETS */ if (RES_IS_VSET(temp->cfg_entry)) { if (fw_version <= PMCRAID_FW_VERSION_1) target = temp->cfg_entry.unique_flags1; else target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF; if (target > PMCRAID_MAX_VSET_TARGETS) continue; bus = PMCRAID_VSET_BUS_ID; lun = 0; } else if (RES_IS_GSCSI(temp->cfg_entry)) { target = RES_TARGET(temp->cfg_entry.resource_address); bus = PMCRAID_PHYS_BUS_ID; lun = RES_LUN(temp->cfg_entry.resource_address); } else { continue; } if (bus == scsi_dev->channel && target == scsi_dev->id && lun == scsi_dev->lun) { res = temp; break; } } if (res) { res->scsi_dev = scsi_dev; scsi_dev->hostdata = res; res->change_detected = 0; atomic_set(&res->read_failures, 0); atomic_set(&res->write_failures, 0); rc = 0; } spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); return rc;} /** * pmcraid_sdev_configure - Configures a SCSI device * @scsi_dev: scsi device struct * @lim: queue limits * * This function is executed by SCSI mid layer just after a device is first * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the * timeout value (default 30s) will be over-written to a higher value (60s) * and max_sectors value will be over-written to 512. It also sets queue depth * to host->cmd_per_lun value * * Return value: * 0 on success */static int pmcraid_sdev_configure(struct scsi_device *scsi_dev, struct queue_limits *lim){ struct pmcraid_resource_entry *res = scsi_dev->hostdata; if (!res) return 0; /* LLD exposes VSETs and Enclosure devices only */ if (RES_IS_GSCSI(res->cfg_entry) && scsi_dev->type != TYPE_ENCLOSURE) return -ENXIO; pmcraid_info("configuring %x:%x:%x:%x\n", scsi_dev->host->unique_id, scsi_dev->channel, scsi_dev->id, (u8)scsi_dev->lun); if (RES_IS_GSCSI(res->cfg_entry)) { scsi_dev->allow_restart = 1; } else if (RES_IS_VSET(res->cfg_entry)) { scsi_dev->allow_restart = 1; blk_queue_rq_timeout(scsi_dev->request_queue, PMCRAID_VSET_IO_TIMEOUT); lim->max_hw_sectors = PMCRAID_VSET_MAX_SECTORS; } /* * We never want to report TCQ support for these types of devices. */ if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry)) scsi_dev->tagged_supported = 0; return 0;} /** * pmcraid_sdev_destroy - Unconfigure a SCSI device before removing it * * @scsi_dev: scsi device struct * * This is called by mid-layer before removing a device. Pointer assignments * done in pmcraid_sdev_init will be reset to NULL here. * * Return value * none */static void pmcraid_sdev_destroy(struct scsi_device *scsi_dev){ struct pmcraid_resource_entry *res; res = (struct pmcraid_resource_entry *)scsi_dev->hostdata; if (res) res->scsi_dev = NULL; scsi_dev->hostdata = NULL;} /** * pmcraid_change_queue_depth - Change the device's queue depth * @scsi_dev: scsi device struct * @depth: depth to set * * Return value * actual depth set */static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth){ if (depth > PMCRAID_MAX_CMD_PER_LUN) depth = PMCRAID_MAX_CMD_PER_LUN; return scsi_change_queue_depth(scsi_dev, depth);} /** * pmcraid_init_cmdblk - initializes a command block * * @cmd: pointer to struct pmcraid_cmd to be initialized * @index: if >=0 first time initialization; otherwise reinitialization * * Return Value * None */static void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index){ struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); dma_addr_t dma_addr = cmd->ioa_cb_bus_addr; if (index >= 0) { /* first time initialization (called from probe) */ u32 ioasa_offset = offsetof(struct pmcraid_control_block, ioasa); cmd->index = index; ioarcb->response_handle = cpu_to_le32(index << 2); ioarcb->ioarcb_bus_addr = cpu_to_le64(dma_addr); ioarcb->ioasa_bus_addr = cpu_to_le64(dma_addr + ioasa_offset); ioarcb->ioasa_len = cpu_to_le16(sizeof(struct pmcraid_ioasa)); } else { /* re-initialization of various lengths, called once command is * processed by IOA */ memset(&cmd->ioa_cb->ioarcb.cdb, 0, PMCRAID_MAX_CDB_LEN); ioarcb->hrrq_id = 0; ioarcb->request_flags0 = 0; ioarcb->request_flags1 = 0; ioarcb->cmd_timeout = 0; ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL); ioarcb->ioadl_bus_addr = 0; ioarcb->ioadl_length = 0; ioarcb->data_transfer_length = 0; ioarcb->add_cmd_param_length = 0; ioarcb->add_cmd_param_offset = 0; cmd->ioa_cb->ioasa.ioasc = 0; cmd->ioa_cb->ioasa.residual_data_length = 0; cmd->time_left = 0; } cmd->cmd_done = NULL; cmd->scsi_cmd = NULL; cmd->release = 0; cmd->completion_req = 0; cmd->sense_buffer = NULL; cmd->sense_buffer_dma = 0; cmd->dma_handle = 0; timer_setup(&cmd->timer, NULL, 0);} /** * pmcraid_reinit_cmdblk - reinitialize a command block * * @cmd: pointer to struct pmcraid_cmd to be reinitialized * * Return Value * None */static void pmcraid_reinit_cmdblk(struct pmcraid_cmd *cmd){ pmcraid_init_cmdblk(cmd, -1);} /** * pmcraid_get_free_cmd - get a free cmd block from command block pool * @pinstance: adapter instance structure * * Return Value: * returns pointer to cmd block or NULL if no blocks are available */static struct pmcraid_cmd *pmcraid_get_free_cmd( struct pmcraid_instance *pinstance){ struct pmcraid_cmd *cmd = NULL; unsigned long lock_flags; /* free cmd block list is protected by free_pool_lock */ spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); if (!list_empty(&pinstance->free_cmd_pool)) { cmd = list_entry(pinstance->free_cmd_pool.next, struct pmcraid_cmd, free_list); list_del(&cmd->free_list); } spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags); /* Initialize the command block before giving it the caller */ if (cmd != NULL) pmcraid_reinit_cmdblk(cmd); return cmd;} /** * pmcraid_return_cmd - return a completed command block back into free pool * @cmd: pointer to the command block * * Return Value: * nothing */static void pmcraid_return_cmd(struct pmcraid_cmd *cmd){ struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); list_add_tail(&cmd->free_list, &pinstance->free_cmd_pool); spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags);} /** * pmcraid_read_interrupts - reads IOA interrupts * * @pinstance: pointer to adapter instance structure * * Return value * interrupts read from IOA */static u32 pmcraid_read_interrupts(struct pmcraid_instance *pinstance){ return (pinstance->interrupt_mode) ? ioread32(pinstance->int_regs.ioa_host_msix_interrupt_reg) : ioread32(pinstance->int_regs.ioa_host_interrupt_reg);} /** * pmcraid_disable_interrupts - Masks and clears all specified interrupts * * @pinstance: pointer to per adapter instance structure * @intrs: interrupts to disable * * Return Value * None */static void pmcraid_disable_interrupts( struct pmcraid_instance *pinstance, u32 intrs){ u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg); u32 nmask = gmask | GLOBAL_INTERRUPT_MASK; iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_clr_reg); iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); ioread32(pinstance->int_regs.global_interrupt_mask_reg); if (!pinstance->interrupt_mode) { iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_mask_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); }} /** * pmcraid_enable_interrupts - Enables specified interrupts * * @pinstance: pointer to per adapter instance structure * @intrs: interrupts to enable * * Return Value * None */static void pmcraid_enable_interrupts( struct pmcraid_instance *pinstance, u32 intrs){ u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg); u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK); iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); if (!pinstance->interrupt_mode) { iowrite32(~intrs, pinstance->int_regs.ioa_host_interrupt_mask_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); } pmcraid_info("enabled interrupts global mask = %x intr_mask = %x\n", ioread32(pinstance->int_regs.global_interrupt_mask_reg), ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg));} /** * pmcraid_clr_trans_op - clear trans to op interrupt * * @pinstance: pointer to per adapter instance structure * * Return Value * None */static void pmcraid_clr_trans_op( struct pmcraid_instance *pinstance){ unsigned long lock_flags; if (!pinstance->interrupt_mode) { iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, pinstance->int_regs.ioa_host_interrupt_mask_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, pinstance->int_regs.ioa_host_interrupt_clr_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_clr_reg); } if (pinstance->reset_cmd != NULL) { timer_delete(&pinstance->reset_cmd->timer); spin_lock_irqsave( pinstance->host->host_lock, lock_flags);Lines 1–500 of 5441. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters * * Written By: Anil Ravindranath<anil_ravindranath@pmc-sierra.com> * PMC-Sierra Inc * * Copyright (C) 2008, 2009 PMC Sierra Inc */ #include <linux/fs.h> #include <linux/init.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/wait.h> #include <linux/spinlock.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/blkdev.h> #include <linux/firmware.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/hdreg.h> #include <linux/io.h> #include <linux/slab.h> #include <asm/irq.h> #include <asm/processor.h> #include <linux/libata.h> #include <linux/mutex.h> #include <linux/ktime.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsicam.h> #include "pmcraid.h" /* * Module configuration parameters */ static unsigned int pmcraid_debug_log; static unsigned int pmcraid_disable_aen; static unsigned int pmcraid_log_level = IOASC_LOG_LEVEL_MUST; static unsigned int pmcraid_enable_msix; /* * Data structures to support multiple adapters by the LLD. * pmcraid_adapter_count - count of configured adapters */ static atomic_t pmcraid_adapter_count = ATOMIC_INIT(0); /* * Supporting user-level control interface through IOCTL commands. * pmcraid_major - major number to use * pmcraid_minor - minor number(s) to use */ static unsigned int pmcraid_major; static const struct class pmcraid_class = { .name = PMCRAID_DEVFILE, }; static DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS); /* * Module parameters */ MODULE_AUTHOR("Anil Ravindranath<anil_ravindranath@pmc-sierra.com>"); MODULE_DESCRIPTION("PMC Sierra MaxRAID Controller Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(PMCRAID_DRIVER_VERSION); module_param_named(log_level, pmcraid_log_level, uint, (S_IRUGO | S_IWUSR)); MODULE_PARM_DESC(log_level, "Enables firmware error code logging, default :1 high-severity" " errors, 2: all errors including high-severity errors," " 0: disables logging"); module_param_named(debug, pmcraid_debug_log, uint, (S_IRUGO | S_IWUSR)); MODULE_PARM_DESC(debug, "Enable driver verbose message logging. Set 1 to enable." "(default: 0)"); module_param_named(disable_aen, pmcraid_disable_aen, uint, (S_IRUGO | S_IWUSR)); MODULE_PARM_DESC(disable_aen, "Disable driver aen notifications to apps. Set 1 to disable." "(default: 0)"); /* chip specific constants for PMC MaxRAID controllers (same for * 0x5220 and 0x8010 */ static struct pmcraid_chip_details pmcraid_chip_cfg[] = { { .ioastatus = 0x0, .ioarrin = 0x00040, .mailbox = 0x7FC30, .global_intr_mask = 0x00034, .ioa_host_intr = 0x0009C, .ioa_host_intr_clr = 0x000A0, .ioa_host_msix_intr = 0x7FC40, .ioa_host_mask = 0x7FC28, .ioa_host_mask_clr = 0x7FC28, .host_ioa_intr = 0x00020, .host_ioa_intr_clr = 0x00020, .transop_timeout = 300 } }; /* * PCI device ids supported by pmcraid driver */ static const struct pci_device_id pmcraid_pci_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID), 0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0] }, {} }; MODULE_DEVICE_TABLE(pci, pmcraid_pci_table); /** * pmcraid_sdev_init - Prepare for commands to a device * @scsi_dev: scsi device struct * * This function is called by mid-layer prior to sending any command to the new * device. Stores resource entry details of the device in scsi_device struct. * Queuecommand uses the resource handle and other details to fill up IOARCB * while sending commands to the device. * * Return value: * 0 on success / -ENXIO if device does not exist */ static int pmcraid_sdev_init(struct scsi_device *scsi_dev) { struct pmcraid_resource_entry *temp, *res = NULL; struct pmcraid_instance *pinstance; u8 target, bus, lun; unsigned long lock_flags; int rc = -ENXIO; u16 fw_version; pinstance = shost_priv(scsi_dev->host); fw_version = be16_to_cpu(pinstance->inq_data->fw_version); /* Driver exposes VSET and GSCSI resources only; all other device types * are not exposed. Resource list is synchronized using resource lock * so any traversal or modifications to the list should be done inside * this lock */ spin_lock_irqsave(&pinstance->resource_lock, lock_flags); list_for_each_entry(temp, &pinstance->used_res_q, queue) { /* do not expose VSETs with order-ids > MAX_VSET_TARGETS */ if (RES_IS_VSET(temp->cfg_entry)) { if (fw_version <= PMCRAID_FW_VERSION_1) target = temp->cfg_entry.unique_flags1; else target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF; if (target > PMCRAID_MAX_VSET_TARGETS) continue; bus = PMCRAID_VSET_BUS_ID; lun = 0; } else if (RES_IS_GSCSI(temp->cfg_entry)) { target = RES_TARGET(temp->cfg_entry.resource_address); bus = PMCRAID_PHYS_BUS_ID; lun = RES_LUN(temp->cfg_entry.resource_address); } else { continue; } if (bus == scsi_dev->channel && target == scsi_dev->id && lun == scsi_dev->lun) { res = temp; break; } } if (res) { res->scsi_dev = scsi_dev; scsi_dev->hostdata = res; res->change_detected = 0; atomic_set(&res->read_failures, 0); atomic_set(&res->write_failures, 0); rc = 0; } spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); return rc; } /** * pmcraid_sdev_configure - Configures a SCSI device * @scsi_dev: scsi device struct * @lim: queue limits * * This function is executed by SCSI mid layer just after a device is first * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the * timeout value (default 30s) will be over-written to a higher value (60s) * and max_sectors value will be over-written to 512. It also sets queue depth * to host->cmd_per_lun value * * Return value: * 0 on success */ static int pmcraid_sdev_configure(struct scsi_device *scsi_dev, struct queue_limits *lim) { struct pmcraid_resource_entry *res = scsi_dev->hostdata; if (!res) return 0; /* LLD exposes VSETs and Enclosure devices only */ if (RES_IS_GSCSI(res->cfg_entry) && scsi_dev->type != TYPE_ENCLOSURE) return -ENXIO; pmcraid_info("configuring %x:%x:%x:%x\n", scsi_dev->host->unique_id, scsi_dev->channel, scsi_dev->id, (u8)scsi_dev->lun); if (RES_IS_GSCSI(res->cfg_entry)) { scsi_dev->allow_restart = 1; } else if (RES_IS_VSET(res->cfg_entry)) { scsi_dev->allow_restart = 1; blk_queue_rq_timeout(scsi_dev->request_queue, PMCRAID_VSET_IO_TIMEOUT); lim->max_hw_sectors = PMCRAID_VSET_MAX_SECTORS; } /* * We never want to report TCQ support for these types of devices. */ if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry)) scsi_dev->tagged_supported = 0; return 0; } /** * pmcraid_sdev_destroy - Unconfigure a SCSI device before removing it * * @scsi_dev: scsi device struct * * This is called by mid-layer before removing a device. Pointer assignments * done in pmcraid_sdev_init will be reset to NULL here. * * Return value * none */ static void pmcraid_sdev_destroy(struct scsi_device *scsi_dev) { struct pmcraid_resource_entry *res; res = (struct pmcraid_resource_entry *)scsi_dev->hostdata; if (res) res->scsi_dev = NULL; scsi_dev->hostdata = NULL; } /** * pmcraid_change_queue_depth - Change the device's queue depth * @scsi_dev: scsi device struct * @depth: depth to set * * Return value * actual depth set */ static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth) { if (depth > PMCRAID_MAX_CMD_PER_LUN) depth = PMCRAID_MAX_CMD_PER_LUN; return scsi_change_queue_depth(scsi_dev, depth); } /** * pmcraid_init_cmdblk - initializes a command block * * @cmd: pointer to struct pmcraid_cmd to be initialized * @index: if >=0 first time initialization; otherwise reinitialization * * Return Value * None */ static void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index) { struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); dma_addr_t dma_addr = cmd->ioa_cb_bus_addr; if (index >= 0) { /* first time initialization (called from probe) */ u32 ioasa_offset = offsetof(struct pmcraid_control_block, ioasa); cmd->index = index; ioarcb->response_handle = cpu_to_le32(index << 2); ioarcb->ioarcb_bus_addr = cpu_to_le64(dma_addr); ioarcb->ioasa_bus_addr = cpu_to_le64(dma_addr + ioasa_offset); ioarcb->ioasa_len = cpu_to_le16(sizeof(struct pmcraid_ioasa)); } else { /* re-initialization of various lengths, called once command is * processed by IOA */ memset(&cmd->ioa_cb->ioarcb.cdb, 0, PMCRAID_MAX_CDB_LEN); ioarcb->hrrq_id = 0; ioarcb->request_flags0 = 0; ioarcb->request_flags1 = 0; ioarcb->cmd_timeout = 0; ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL); ioarcb->ioadl_bus_addr = 0; ioarcb->ioadl_length = 0; ioarcb->data_transfer_length = 0; ioarcb->add_cmd_param_length = 0; ioarcb->add_cmd_param_offset = 0; cmd->ioa_cb->ioasa.ioasc = 0; cmd->ioa_cb->ioasa.residual_data_length = 0; cmd->time_left = 0; } cmd->cmd_done = NULL; cmd->scsi_cmd = NULL; cmd->release = 0; cmd->completion_req = 0; cmd->sense_buffer = NULL; cmd->sense_buffer_dma = 0; cmd->dma_handle = 0; timer_setup(&cmd->timer, NULL, 0); } /** * pmcraid_reinit_cmdblk - reinitialize a command block * * @cmd: pointer to struct pmcraid_cmd to be reinitialized * * Return Value * None */ static void pmcraid_reinit_cmdblk(struct pmcraid_cmd *cmd) { pmcraid_init_cmdblk(cmd, -1); } /** * pmcraid_get_free_cmd - get a free cmd block from command block pool * @pinstance: adapter instance structure * * Return Value: * returns pointer to cmd block or NULL if no blocks are available */ static struct pmcraid_cmd *pmcraid_get_free_cmd( struct pmcraid_instance *pinstance ) { struct pmcraid_cmd *cmd = NULL; unsigned long lock_flags; /* free cmd block list is protected by free_pool_lock */ spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); if (!list_empty(&pinstance->free_cmd_pool)) { cmd = list_entry(pinstance->free_cmd_pool.next, struct pmcraid_cmd, free_list); list_del(&cmd->free_list); } spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags); /* Initialize the command block before giving it the caller */ if (cmd != NULL) pmcraid_reinit_cmdblk(cmd); return cmd; } /** * pmcraid_return_cmd - return a completed command block back into free pool * @cmd: pointer to the command block * * Return Value: * nothing */ static void pmcraid_return_cmd(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags); list_add_tail(&cmd->free_list, &pinstance->free_cmd_pool); spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags); } /** * pmcraid_read_interrupts - reads IOA interrupts * * @pinstance: pointer to adapter instance structure * * Return value * interrupts read from IOA */ static u32 pmcraid_read_interrupts(struct pmcraid_instance *pinstance) { return (pinstance->interrupt_mode) ? ioread32(pinstance->int_regs.ioa_host_msix_interrupt_reg) : ioread32(pinstance->int_regs.ioa_host_interrupt_reg); } /** * pmcraid_disable_interrupts - Masks and clears all specified interrupts * * @pinstance: pointer to per adapter instance structure * @intrs: interrupts to disable * * Return Value * None */ static void pmcraid_disable_interrupts( struct pmcraid_instance *pinstance, u32 intrs ) { u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg); u32 nmask = gmask | GLOBAL_INTERRUPT_MASK; iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_clr_reg); iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); ioread32(pinstance->int_regs.global_interrupt_mask_reg); if (!pinstance->interrupt_mode) { iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_mask_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); } } /** * pmcraid_enable_interrupts - Enables specified interrupts * * @pinstance: pointer to per adapter instance structure * @intrs: interrupts to enable * * Return Value * None */ static void pmcraid_enable_interrupts( struct pmcraid_instance *pinstance, u32 intrs) { u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg); u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK); iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg); if (!pinstance->interrupt_mode) { iowrite32(~intrs, pinstance->int_regs.ioa_host_interrupt_mask_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); } pmcraid_info("enabled interrupts global mask = %x intr_mask = %x\n", ioread32(pinstance->int_regs.global_interrupt_mask_reg), ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg)); } /** * pmcraid_clr_trans_op - clear trans to op interrupt * * @pinstance: pointer to per adapter instance structure * * Return Value * None */ static void pmcraid_clr_trans_op( struct pmcraid_instance *pinstance ) { unsigned long lock_flags; if (!pinstance->interrupt_mode) { iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, pinstance->int_regs.ioa_host_interrupt_mask_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, pinstance->int_regs.ioa_host_interrupt_clr_reg); ioread32(pinstance->int_regs.ioa_host_interrupt_clr_reg); } if (pinstance->reset_cmd != NULL) { timer_delete(&pinstance->reset_cmd->timer); spin_lock_irqsave( pinstance->host->host_lock, lock_flags);