/* * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters * * Written By: Anil Ravindranath * PMC-Sierra Inc * * Copyright (C) 2008, 2009 PMC Sierra Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 struct class *pmcraid_class; DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS); /* * Module parameters */ MODULE_AUTHOR("Anil Ravindranath"); 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 struct pci_device_id pmcraid_pci_table[] __devinitdata = { { 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_slave_alloc - 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_slave_alloc(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 = 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_slave_configure - Configures a SCSI device * @scsi_dev: scsi device struct * * 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_slave_configure(struct scsi_device *scsi_dev) { 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, 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); blk_queue_max_hw_sectors(scsi_dev->request_queue, PMCRAID_VSET_MAX_SECTORS); } if (scsi_dev->tagged_supported && (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) { scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); scsi_adjust_queue_depth(scsi_dev, MSG_SIMPLE_TAG, scsi_dev->host->cmd_per_lun); } else { scsi_adjust_queue_depth(scsi_dev, 0, scsi_dev->host->cmd_per_lun); } return 0; } /** * pmcraid_slave_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_slave_alloc will be reset to NULL here. * * Return value * none */ static void pmcraid_slave_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 * @reason: calling context * * Return value * actual depth set */ static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth, int reason) { if (reason != SCSI_QDEPTH_DEFAULT) return -EOPNOTSUPP; if (depth > PMCRAID_MAX_CMD_PER_LUN) depth = PMCRAID_MAX_CMD_PER_LUN; scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), depth); return scsi_dev->queue_depth; } /** * pmcraid_change_queue_type - Change the device's queue type * @scsi_dev: scsi device struct * @tag: type of tags to use * * Return value: * actual queue type set */ static int pmcraid_change_queue_type(struct scsi_device *scsi_dev, int tag) { struct pmcraid_resource_entry *res; res = (struct pmcraid_resource_entry *)scsi_dev->hostdata; if ((res) && scsi_dev->tagged_supported && (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) { scsi_set_tag_type(scsi_dev, tag); if (tag) scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); else scsi_deactivate_tcq(scsi_dev, scsi_dev->queue_depth); } else tag = 0; return tag; } /** * 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 */ 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 &= (~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 = 0; cmd->sense_buffer_dma = 0; cmd->dma_handle = 0; init_timer(&cmd->timer); } /** * 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 */ 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 * @intr: 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) { del_timer(&pinstance->reset_cmd->timer); spin_lock_irqsave( pinstance->host->host_lock, lock_flags); pinstance->reset_cmd->cmd_done(pinstance->reset_cmd); spin_unlock_irqrestore( pinstance->host->host_lock, lock_flags); } } /** * pmcraid_reset_type - Determine the required reset type * @pinstance: pointer to adapter instance structure * * IOA requires hard reset if any of the following conditions is true. * 1. If HRRQ valid interrupt is not masked * 2. IOA reset alert doorbell is set * 3. If there are any error interrupts */ static void pmcraid_reset_type(struct pmcraid_instance *pinstance) { u32 mask; u32 intrs; u32 alerts; mask = ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg); intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg); alerts = ioread32(pinstance->int_regs.host_ioa_interrupt_reg); if ((mask & INTRS_HRRQ_VALID) == 0 || (alerts & DOORBELL_IOA_RESET_ALERT) || (intrs & PMCRAID_ERROR_INTERRUPTS)) { pmcraid_info("IOA requires hard reset\n"); pinstance->ioa_hard_reset = 1; } /* If unit check is active, trigger the dump */ if (intrs & INTRS_IOA_UNIT_CHECK) pinstance->ioa_unit_check = 1; } /** * pmcraid_bist_done - completion function for PCI BIST * @cmd: pointer to reset command * Return Value * none */ static void pmcraid_ioa_reset(struct pmcraid_cmd *); static void pmcraid_bist_done(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; int rc; u16 pci_reg; rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg); /* If PCI config space can't be accessed wait for another two secs */ if ((rc != PCIBIOS_SUCCESSFUL || (!(pci_reg & PCI_COMMAND_MEMORY))) && cmd->time_left > 0) { pmcraid_info("BIST not complete, waiting another 2 secs\n"); cmd->timer.expires = jiffies + cmd->time_left; cmd->time_left = 0; cmd->timer.data = (unsigned long)cmd; cmd->timer.function = (void (*)(unsigned long))pmcraid_bist_done; add_timer(&cmd->timer); } else { cmd->time_left = 0; pmcraid_info("BIST is complete, proceeding with reset\n"); spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_ioa_reset(cmd); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); } } /** * pmcraid_start_bist - starts BIST * @cmd: pointer to reset cmd * Return Value * none */ static void pmcraid_start_bist(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u32 doorbells, intrs; /* proceed with bist and wait for 2 seconds */ iowrite32(DOORBELL_IOA_START_BIST, pinstance->int_regs.host_ioa_interrupt_reg); doorbells = ioread32(pinstance->int_regs.host_ioa_interrupt_reg); intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg); pmcraid_info("doorbells after start bist: %x intrs: %x\n", doorbells, intrs); cmd->time_left = msecs_to_jiffies(PMCRAID_BIST_TIMEOUT); cmd->timer.data = (unsigned long)cmd; cmd->timer.expires = jiffies + msecs_to_jiffies(PMCRAID_BIST_TIMEOUT); cmd->timer.function = (void (*)(unsigned long))pmcraid_bist_done; add_timer(&cmd->timer); } /** * pmcraid_reset_alert_done - completion routine for reset_alert * @cmd: pointer to command block used in reset sequence * Return value * None */ static void pmcraid_reset_alert_done(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u32 status = ioread32(pinstance->ioa_status); unsigned long lock_flags; /* if the critical operation in progress bit is set or the wait times * out, invoke reset engine to proceed with hard reset. If there is * some more time to wait, restart the timer */ if (((status & INTRS_CRITICAL_OP_IN_PROGRESS) == 0) || cmd->time_left <= 0) { pmcraid_info("critical op is reset proceeding with reset\n"); spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_ioa_reset(cmd); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); } else { pmcraid_info("critical op is not yet reset waiting again\n"); /* restart timer if some more time is available to wait */ cmd->time_left -= PMCRAID_CHECK_FOR_RESET_TIMEOUT; cmd->timer.data = (unsigned long)cmd; cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT; cmd->timer.function = (void (*)(unsigned long))pmcraid_reset_alert_done; add_timer(&cmd->timer); } } /** * pmcraid_reset_alert - alerts IOA for a possible reset * @cmd : command block to be used for reset sequence. * * Return Value * returns 0 if pci config-space is accessible and RESET_DOORBELL is * successfully written to IOA. Returns non-zero in case pci_config_space * is not accessible */ static void pmcraid_notify_ioastate(struct pmcraid_instance *, u32); static void pmcraid_reset_alert(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u32 doorbells; int rc; u16 pci_reg; /* If we are able to access IOA PCI config space, alert IOA that we are * going to reset it soon. This enables IOA to preserv persistent error * data if any. In case memory space is not accessible, proceed with * BIST or slot_reset */ rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg); if ((rc == PCIBIOS_SUCCESSFUL) && (pci_reg & PCI_COMMAND_MEMORY)) { /* wait for IOA permission i.e until CRITICAL_OPERATION bit is * reset IOA doesn't generate any interrupts when CRITICAL * OPERATION bit is reset. A timer is started to wait for this * bit to be reset. */ cmd->time_left = PMCRAID_RESET_TIMEOUT; cmd->timer.data = (unsigned long)cmd; cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT; cmd->timer.function = (void (*)(unsigned long))pmcraid_reset_alert_done; add_timer(&cmd->timer); iowrite32(DOORBELL_IOA_RESET_ALERT, pinstance->int_regs.host_ioa_interrupt_reg); doorbells = ioread32(pinstance->int_regs.host_ioa_interrupt_reg); pmcraid_info("doorbells after reset alert: %x\n", doorbells); } else { pmcraid_info("PCI config is not accessible starting BIST\n"); pinstance->ioa_state = IOA_STATE_IN_HARD_RESET; pmcraid_start_bist(cmd); } } /** * pmcraid_timeout_handler - Timeout handler for internally generated ops * * @cmd : pointer to command structure, that got timedout * * This function blocks host requests and initiates an adapter reset. * * Return value: * None */ static void pmcraid_timeout_handler(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; dev_info(&pinstance->pdev->dev, "Adapter being reset due to cmd(CDB[0] = %x) timeout\n", cmd->ioa_cb->ioarcb.cdb[0]); /* Command timeouts result in hard reset sequence. The command that got * timed out may be the one used as part of reset sequence. In this * case restart reset sequence using the same command block even if * reset is in progress. Otherwise fail this command and get a free * command block to restart the reset sequence. */ spin_lock_irqsave(pinstance->host->host_lock, lock_flags); if (!pinstance->ioa_reset_in_progress) { pinstance->ioa_reset_attempts = 0; cmd = pmcraid_get_free_cmd(pinstance); /* If we are out of command blocks, just return here itself. * Some other command's timeout handler can do the reset job */ if (cmd == NULL) { spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); pmcraid_err("no free cmnd block for timeout handler\n"); return; } pinstance->reset_cmd = cmd; pinstance->ioa_reset_in_progress = 1; } else { pmcraid_info("reset is already in progress\n"); if (pinstance->reset_cmd != cmd) { /* This command should have been given to IOA, this * command will be completed by fail_outstanding_cmds * anyway */ pmcraid_err("cmd is pending but reset in progress\n"); } /* If this command was being used as part of the reset * sequence, set cmd_done pointer to pmcraid_ioa_reset. This * causes fail_outstanding_commands not to return the command * block back to free pool */ if (cmd == pinstance->reset_cmd) cmd->cmd_done = pmcraid_ioa_reset; } /* Notify apps of important IOA bringup/bringdown sequences */ if (pinstance->scn.ioa_state != PMC_DEVICE_EVENT_RESET_START && pinstance->scn.ioa_state != PMC_DEVICE_EVENT_SHUTDOWN_START) pmcraid_notify_ioastate(pinstance, PMC_DEVICE_EVENT_RESET_START); pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; scsi_block_requests(pinstance->host); pmcraid_reset_alert(cmd); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); } /** * pmcraid_internal_done - completion routine for internally generated cmds * * @cmd: command that got response from IOA * * Return Value: * none */ static void pmcraid_internal_done(struct pmcraid_cmd *cmd) { pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n", cmd->ioa_cb->ioarcb.cdb[0], le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); /* Some of the internal commands are sent with callers blocking for the * response. Same will be indicated as part of cmd->completion_req * field. Response path needs to wake up any waiters waiting for cmd * completion if this flag is set. */ if (cmd->completion_req) { cmd->completion_req = 0; complete(&cmd->wait_for_completion); } /* most of the internal commands are completed by caller itself, so * no need to return the command block back to free pool until we are * required to do so (e.g once done with initialization). */ if (cmd->release) { cmd->release = 0; pmcraid_return_cmd(cmd); } } /** * pmcraid_reinit_cfgtable_done - done function for cfg table reinitialization * * @cmd: command that got response from IOA * * This routine is called after driver re-reads configuration table due to a * lost CCN. It returns the command block back to free pool and schedules * worker thread to add/delete devices into the system. * * Return Value: * none */ static void pmcraid_reinit_cfgtable_done(struct pmcraid_cmd *cmd) { pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n", cmd->ioa_cb->ioarcb.cdb[0], le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); if (cmd->release) { cmd->release = 0; pmcraid_return_cmd(cmd); } pmcraid_info("scheduling worker for config table reinitialization\n"); schedule_work(&cmd->drv_inst->worker_q); } /** * pmcraid_erp_done - Process completion of SCSI error response from device * @cmd: pmcraid_command * * This function copies the sense buffer into the scsi_cmd struct and completes * scsi_cmd by calling scsi_done function. * * Return value: * none */ static void pmcraid_erp_done(struct pmcraid_cmd *cmd) { struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; struct pmcraid_instance *pinstance = cmd->drv_inst; u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); if (PMCRAID_IOASC_SENSE_KEY(ioasc) > 0) { scsi_cmd->result |= (DID_ERROR << 16); scmd_printk(KERN_INFO, scsi_cmd, "command CDB[0] = %x failed with IOASC: 0x%08X\n", cmd->ioa_cb->ioarcb.cdb[0], ioasc); } /* if we had allocated sense buffers for request sense, copy the sense * release the buffers */ if (cmd->sense_buffer != NULL) { memcpy(scsi_cmd->sense_buffer, cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); pci_free_consistent(pinstance->pdev, SCSI_SENSE_BUFFERSIZE, cmd->sense_buffer, cmd->sense_buffer_dma); cmd->sense_buffer = NULL; cmd->sense_buffer_dma = 0; } scsi_dma_unmap(scsi_cmd); pmcraid_return_cmd(cmd); scsi_cmd->scsi_done(scsi_cmd); } /** * pmcraid_fire_command - sends an IOA command to adapter * * This function adds the given block into pending command list * and returns without waiting * * @cmd : command to be sent to the device * * Return Value * None */ static void _pmcraid_fire_command(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; /* Add this command block to pending cmd pool. We do this prior to * writting IOARCB to ioarrin because IOA might complete the command * by the time we are about to add it to the list. Response handler * (isr/tasklet) looks for cmd block in the pending pending list. */ spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags); list_add_tail(&cmd->free_list, &pinstance->pending_cmd_pool); spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags); atomic_inc(&pinstance->outstanding_cmds); /* driver writes lower 32-bit value of IOARCB address only */ mb(); iowrite32(le32_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr), pinstance->ioarrin); } /** * pmcraid_send_cmd - fires a command to IOA * * This function also sets up timeout function, and command completion * function * * @cmd: pointer to the command block to be fired to IOA * @cmd_done: command completion function, called once IOA responds * @timeout: timeout to wait for this command completion * @timeout_func: timeout handler * * Return value * none */ static void pmcraid_send_cmd( struct pmcraid_cmd *cmd, void (*cmd_done) (struct pmcraid_cmd *), unsigned long timeout, void (*timeout_func) (struct pmcraid_cmd *) ) { /* initialize done function */ cmd->cmd_done = cmd_done; if (timeout_func) { /* setup timeout handler */ cmd->timer.data = (unsigned long)cmd; cmd->timer.expires = jiffies + timeout; cmd->timer.function = (void (*)(unsigned long))timeout_func; add_timer(&cmd->timer); } /* fire the command to IOA */ _pmcraid_fire_command(cmd); } /** * pmcraid_ioa_shutdown_done - completion function for IOA shutdown command * @cmd: pointer to the command block used for sending IOA shutdown command * * Return value * None */ static void pmcraid_ioa_shutdown_done(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_ioa_reset(cmd); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); } /** * pmcraid_ioa_shutdown - sends SHUTDOWN command to ioa * * @cmd: pointer to the command block used as part of reset sequence * * Return Value * None */ static void pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd) { pmcraid_info("response for Cancel CCN CDB[0] = %x ioasc = %x\n", cmd->ioa_cb->ioarcb.cdb[0], le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); /* Note that commands sent during reset require next command to be sent * to IOA. Hence reinit the done function as well as timeout function */ pmcraid_reinit_cmdblk(cmd); cmd->ioa_cb->ioarcb.request_type = REQ_TYPE_IOACMD; cmd->ioa_cb->ioarcb.resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); cmd->ioa_cb->ioarcb.cdb[0] = PMCRAID_IOA_SHUTDOWN; cmd->ioa_cb->ioarcb.cdb[1] = PMCRAID_SHUTDOWN_NORMAL; /* fire shutdown command to hardware. */ pmcraid_info("firing normal shutdown command (%d) to IOA\n", le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle)); pmcraid_notify_ioastate(cmd->drv_inst, PMC_DEVICE_EVENT_SHUTDOWN_START); pmcraid_send_cmd(cmd, pmcraid_ioa_shutdown_done, PMCRAID_SHUTDOWN_TIMEOUT, pmcraid_timeout_handler); } /** * pmcraid_get_fwversion_done - completion function for get_fwversion * * @cmd: pointer to command block used to send INQUIRY command * * Return Value * none */ static void pmcraid_querycfg(struct pmcraid_cmd *); static void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); unsigned long lock_flags; /* configuration table entry size depends on firmware version. If fw * version is not known, it is not possible to interpret IOA config * table */ if (ioasc) { pmcraid_err("IOA Inquiry failed with %x\n", ioasc); spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; pmcraid_reset_alert(cmd); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); } else { pmcraid_querycfg(cmd); } } /** * pmcraid_get_fwversion - reads firmware version information * * @cmd: pointer to command block used to send INQUIRY command * * Return Value * none */ static void pmcraid_get_fwversion(struct pmcraid_cmd *cmd) { struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; struct pmcraid_instance *pinstance = cmd->drv_inst; u16 data_size = sizeof(struct pmcraid_inquiry_data); pmcraid_reinit_cmdblk(cmd); ioarcb->request_type = REQ_TYPE_SCSI; ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); ioarcb->cdb[0] = INQUIRY; ioarcb->cdb[1] = 1; ioarcb->cdb[2] = 0xD0; ioarcb->cdb[3] = (data_size >> 8) & 0xFF; ioarcb->cdb[4] = data_size & 0xFF; /* Since entire inquiry data it can be part of IOARCB itself */ ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + offsetof(struct pmcraid_ioarcb, add_data.u.ioadl[0])); ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); ioarcb->ioarcb_bus_addr &= ~(0x1FULL); ioarcb->request_flags0 |= NO_LINK_DESCS; ioarcb->data_transfer_length = cpu_to_le32(data_size); ioadl = &(ioarcb->add_data.u.ioadl[0]); ioadl->flags = IOADL_FLAGS_LAST_DESC; ioadl->address = cpu_to_le64(pinstance->inq_data_baddr); ioadl->data_len = cpu_to_le32(data_size); pmcraid_send_cmd(cmd, pmcraid_get_fwversion_done, PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); } /** * pmcraid_identify_hrrq - registers host rrq buffers with IOA * @cmd: pointer to command block to be used for identify hrrq * * Return Value * none */ static void pmcraid_identify_hrrq(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; int index = cmd->hrrq_index; __be64 hrrq_addr = cpu_to_be64(pinstance->hrrq_start_bus_addr[index]); u32 hrrq_size = cpu_to_be32(sizeof(u32) * PMCRAID_MAX_CMD); void (*done_function)(struct pmcraid_cmd *); pmcraid_reinit_cmdblk(cmd); cmd->hrrq_index = index + 1; if (cmd->hrrq_index < pinstance->num_hrrq) { done_function = pmcraid_identify_hrrq; } else { cmd->hrrq_index = 0; done_function = pmcraid_get_fwversion; } /* Initialize ioarcb */ ioarcb->request_type = REQ_TYPE_IOACMD; ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); /* initialize the hrrq number where IOA will respond to this command */ ioarcb->hrrq_id = index; ioarcb->cdb[0] = PMCRAID_IDENTIFY_HRRQ; ioarcb->cdb[1] = index; /* IOA expects 64-bit pci address to be written in B.E format * (i.e cdb[2]=MSByte..cdb[9]=LSB. */ pmcraid_info("HRRQ_IDENTIFY with hrrq:ioarcb:index => %llx:%llx:%x\n", hrrq_addr, ioarcb->ioarcb_bus_addr, index); memcpy(&(ioarcb->cdb[2]), &hrrq_addr, sizeof(hrrq_addr)); memcpy(&(ioarcb->cdb[10]), &hrrq_size, sizeof(hrrq_size)); /* Subsequent commands require HRRQ identification to be successful. * Note that this gets called even during reset from SCSI mid-layer * or tasklet */ pmcraid_send_cmd(cmd, done_function, PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); } static void pmcraid_process_ccn(struct pmcraid_cmd *cmd); static void pmcraid_process_ldn(struct pmcraid_cmd *cmd); /** * pmcraid_send_hcam_cmd - send an initialized command block(HCAM) to IOA * * @cmd: initialized command block pointer * * Return Value * none */ static void pmcraid_send_hcam_cmd(struct pmcraid_cmd *cmd) { if (cmd->ioa_cb->ioarcb.cdb[1] == PMCRAID_HCAM_CODE_CONFIG_CHANGE) atomic_set(&(cmd->drv_inst->ccn.ignore), 0); else atomic_set(&(cmd->drv_inst->ldn.ignore), 0); pmcraid_send_cmd(cmd, cmd->cmd_done, 0, NULL); } /** * pmcraid_init_hcam - send an initialized command block(HCAM) to IOA * * @pinstance: pointer to adapter instance structure * @type: HCAM type * * Return Value * pointer to initialized pmcraid_cmd structure or NULL */ static struct pmcraid_cmd *pmcraid_init_hcam ( struct pmcraid_instance *pinstance, u8 type ) { struct pmcraid_cmd *cmd; struct pmcraid_ioarcb *ioarcb; struct pmcraid_ioadl_desc *ioadl; struct pmcraid_hostrcb *hcam; void (*cmd_done) (struct pmcraid_cmd *); dma_addr_t dma; int rcb_size; cmd = pmcraid_get_free_cmd(pinstance); if (!cmd) { pmcraid_err("no free command blocks for hcam\n"); return cmd; } if (type == PMCRAID_HCAM_CODE_CONFIG_CHANGE) { rcb_size = sizeof(struct pmcraid_hcam_ccn_ext); cmd_done = pmcraid_process_ccn; dma = pinstance->ccn.baddr + PMCRAID_AEN_HDR_SIZE; hcam = &pinstance->ccn; } else { rcb_size = sizeof(struct pmcraid_hcam_ldn); cmd_done = pmcraid_process_ldn; dma = pinstance->ldn.baddr + PMCRAID_AEN_HDR_SIZE; hcam = &pinstance->ldn; } /* initialize command pointer used for HCAM registration */ hcam->cmd = cmd; ioarcb = &cmd->ioa_cb->ioarcb; ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) + offsetof(struct pmcraid_ioarcb, add_data.u.ioadl[0])); ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc)); ioadl = ioarcb->add_data.u.ioadl; /* Initialize ioarcb */ ioarcb->request_type = REQ_TYPE_HCAM; ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE); ioarcb->cdb[0] = PMCRAID_HOST_CONTROLLED_ASYNC; ioarcb->cdb[1] = type; ioarcb->cdb[7] = (rcb_size >> 8) & 0xFF; ioarcb->cdb[8] = (rcb_size) & 0xFF; ioarcb->data_transfer_length = cpu_to_le32(rcb_size); ioadl[0].flags |= IOADL_FLAGS_READ_LAST; ioadl[0].data_len = cpu_to_le32(rcb_size); ioadl[0].address = cpu_to_le32(dma); cmd->cmd_done = cmd_done; return cmd; } /** * pmcraid_send_hcam - Send an HCAM to IOA * @pinstance: ioa config struct * @type: HCAM type * * This function will send a Host Controlled Async command to IOA. * * Return value: * none */ static void pmcraid_send_hcam(struct pmcraid_instance *pinstance, u8 type) { struct pmcraid_cmd *cmd = pmcraid_init_hcam(pinstance, type); pmcraid_send_hcam_cmd(cmd); } /** * pmcraid_prepare_cancel_cmd - prepares a command block to abort another * * @cmd: pointer to cmd that is used as cancelling command * @cmd_to_cancel: pointer to the command that needs to be cancelled */ static void pmcraid_prepare_cancel_cmd( struct pmcraid_cmd *cmd, struct pmcraid_cmd *cmd_to_cancel ) { struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; __be64 ioarcb_addr = cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr; /* Get the resource handle to where the command to be aborted has been * sent. */ ioarcb->resource_handle = cmd_to_cancel->ioa_cb->ioarcb.resource_handle; ioarcb->request_type = REQ_TYPE_IOACMD; memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN); ioarcb->cdb[0] = PMCRAID_ABORT_CMD; /* IOARCB address of the command to be cancelled is given in * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in * IOARCB address are not masked. */ ioarcb_addr = cpu_to_be64(ioarcb_addr); memcpy(&(ioarcb->cdb[2]), &ioarcb_addr, sizeof(ioarcb_addr)); } /** * pmcraid_cancel_hcam - sends ABORT task to abort a given HCAM * * @cmd: command to be used as cancelling command * @type: HCAM type * @cmd_done: op done function for the cancelling command */ static void pmcraid_cancel_hcam( struct pmcraid_cmd *cmd, u8 type, void (*cmd_done) (struct pmcraid_cmd *) ) { struct pmcraid_instance *pinstance; struct pmcraid_hostrcb *hcam; pinstance = cmd->drv_inst; hcam = (type == PMCRAID_HCAM_CODE_LOG_DATA) ? &pinstance->ldn : &pinstance->ccn; /* prepare for cancelling previous hcam command. If the HCAM is * currently not pending with IOA, we would have hcam->cmd as non-null */ if (hcam->cmd == NULL) return; pmcraid_prepare_cancel_cmd(cmd, hcam->cmd); /* writing to IOARRIN must be protected by host_lock, as mid-layer * schedule queuecommand while we are doing this */ pmcraid_send_cmd(cmd, cmd_done, PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler); } /** * pmcraid_cancel_ccn - cancel CCN HCAM already registered with IOA * * @cmd: command block to be used for cancelling the HCAM */ static void pmcraid_cancel_ccn(struct pmcraid_cmd *cmd) { pmcraid_info("response for Cancel LDN CDB[0] = %x ioasc = %x\n", cmd->ioa_cb->ioarcb.cdb[0], le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)); pmcraid_reinit_cmdblk(cmd); pmcraid_cancel_hcam(cmd, PMCRAID_HCAM_CODE_CONFIG_CHANGE, pmcraid_ioa_shutdown); } /** * pmcraid_cancel_ldn - cancel LDN HCAM already registered with IOA * * @cmd: command block to be used for cancelling the HCAM */ static void pmcraid_cancel_ldn(struct pmcraid_cmd *cmd) { pmcraid_cancel_hcam(cmd, PMCRAID_HCAM_CODE_LOG_DATA, pmcraid_cancel_ccn); } /** * pmcraid_expose_resource - check if the resource can be exposed to OS * * @fw_version: firmware version code * @cfgte: pointer to configuration table entry of the resource * * Return value: * true if resource can be added to midlayer, false(0) otherwise */ static int pmcraid_expose_resource(u16 fw_version, struct pmcraid_config_table_entry *cfgte) { int retval = 0; if (cfgte->resource_type == RES_TYPE_VSET) { if (fw_version <= PMCRAID_FW_VERSION_1) retval = ((cfgte->unique_flags1 & 0x80) == 0); else retval = ((cfgte->unique_flags0 & 0x80) == 0 && (cfgte->unique_flags1 & 0x80) == 0); } else if (cfgte->resource_type == RES_TYPE_GSCSI) retval = (RES_BUS(cfgte->resource_address) != PMCRAID_VIRTUAL_ENCL_BUS_ID); return retval; } /* attributes supported by pmcraid_event_family */ enum { PMCRAID_AEN_ATTR_UNSPEC, PMCRAID_AEN_ATTR_EVENT, __PMCRAID_AEN_ATTR_MAX, }; #define PMCRAID_AEN_ATTR_MAX (__PMCRAID_AEN_ATTR_MAX - 1) /* commands supported by pmcraid_event_family */ enum { PMCRAID_AEN_CMD_UNSPEC, PMCRAID_AEN_CMD_EVENT, __PMCRAID_AEN_CMD_MAX, }; #define PMCRAID_AEN_CMD_MAX (__PMCRAID_AEN_CMD_MAX - 1) static struct genl_family pmcraid_event_family = { .id = GENL_ID_GENERATE, .name = "pmcraid", .version = 1, .maxattr = PMCRAID_AEN_ATTR_MAX }; /** * pmcraid_netlink_init - registers pmcraid_event_family * * Return value: * 0 if the pmcraid_event_family is successfully registered * with netlink generic, non-zero otherwise */ static int pmcraid_netlink_init(void) { int result; result = genl_register_family(&pmcraid_event_family); if (result) return result; pmcraid_info("registered NETLINK GENERIC group: %d\n", pmcraid_event_family.id); return result; } /** * pmcraid_netlink_release - unregisters pmcraid_event_family * * Return value: * none */ static void pmcraid_netlink_release(void) { genl_unregister_family(&pmcraid_event_family); } /** * pmcraid_notify_aen - sends event msg to user space application * @pinstance: pointer to adapter instance structure * @type: HCAM type * * Return value: * 0 if success, error value in case of any failure. */ static int pmcraid_notify_aen( struct pmcraid_instance *pinstance, struct pmcraid_aen_msg *aen_msg, u32 data_size ) { struct sk_buff *skb; void *msg_header; u32 total_size, nla_genl_hdr_total_size; int result; aen_msg->hostno = (pinstance->host->unique_id << 16 | MINOR(pinstance->cdev.dev)); aen_msg->length = data_size; data_size += sizeof(*aen_msg); total_size = nla_total_size(data_size); /* Add GENL_HDR to total_size */ nla_genl_hdr_total_size = (total_size + (GENL_HDRLEN + ((struct genl_family *)&pmcraid_event_family)->hdrsize) + NLMSG_HDRLEN); skb = genlmsg_new(nla_genl_hdr_total_size, GFP_ATOMIC); if (!skb) { pmcraid_err("Failed to allocate aen data SKB of size: %x\n", total_size); return -ENOMEM; } /* add the genetlink message header */ msg_header = genlmsg_put(skb, 0, 0, &pmcraid_event_family, 0, PMCRAID_AEN_CMD_EVENT); if (!msg_header) { pmcraid_err("failed to copy command details\n"); nlmsg_free(skb); return -ENOMEM; } result = nla_put(skb, PMCRAID_AEN_ATTR_EVENT, data_size, aen_msg); if (result) { pmcraid_err("failed to copy AEN attribute data\n"); nlmsg_free(skb); return -EINVAL; } /* send genetlink multicast message to notify appplications */ result = genlmsg_end(skb, msg_header); if (result < 0) { pmcraid_err("genlmsg_end failed\n"); nlmsg_free(skb); return result; } result = genlmsg_multicast(skb, 0, pmcraid_event_family.id, GFP_ATOMIC); /* If there are no listeners, genlmsg_multicast may return non-zero * value. */ if (result) pmcraid_info("error (%x) sending aen event message\n", result); return result; } /** * pmcraid_notify_ccn - notifies about CCN event msg to user space * @pinstance: pointer adapter instance structure * * Return value: * 0 if success, error value in case of any failure */ static int pmcraid_notify_ccn(struct pmcraid_instance *pinstance) { return pmcraid_notify_aen(pinstance, pinstance->ccn.msg, pinstance->ccn.hcam->data_len + sizeof(struct pmcraid_hcam_hdr)); } /** * pmcraid_notify_ldn - notifies about CCN event msg to user space * @pinstance: pointer adapter instance structure * * Return value: * 0 if success, error value in case of any failure */ static int pmcraid_notify_ldn(struct pmcraid_instance *pinstance) { return pmcraid_notify_aen(pinstance, pinstance->ldn.msg, pinstance->ldn.hcam->data_len + sizeof(struct pmcraid_hcam_hdr)); } /** * pmcraid_notify_ioastate - sends IOA state event msg to user space * @pinstance: pointer adapter instance structure * @evt: controller state event to be sent * * Return value: * 0 if success, error value in case of any failure */ static void pmcraid_notify_ioastate(struct pmcraid_instance *pinstance, u32 evt) { pinstance->scn.ioa_state = evt; pmcraid_notify_aen(pinstance, &pinstance->scn.msg, sizeof(u32)); } /** * pmcraid_handle_config_change - Handle a config change from the adapter * @pinstance: pointer to per adapter instance structure * * Return value: * none */ static void pmcraid_handle_config_change(struct pmcraid_instance *pinstance) { struct pmcraid_config_table_entry *cfg_entry; struct pmcraid_hcam_ccn *ccn_hcam; struct pmcraid_cmd *cmd; struct pmcraid_cmd *cfgcmd; struct pmcraid_resource_entry *res = NULL; unsigned long lock_flags; unsigned long host_lock_flags; u32 new_entry = 1; u32 hidden_entry = 0; u16 fw_version; int rc; ccn_hcam = (struct pmcraid_hcam_ccn *)pinstance->ccn.hcam; cfg_entry = &ccn_hcam->cfg_entry; fw_version = be16_to_cpu(pinstance->inq_data->fw_version); pmcraid_info("CCN(%x): %x timestamp: %llx type: %x lost: %x flags: %x \ res: %x:%x:%x:%x\n", pinstance->ccn.hcam->ilid, pinstance->ccn.hcam->op_code, ((pinstance->ccn.hcam->timestamp1) | ((pinstance->ccn.hcam->timestamp2 & 0xffffffffLL) << 32)), pinstance->ccn.hcam->notification_type, pinstance->ccn.hcam->notification_lost, pinstance->ccn.hcam->flags, pinstance->host->unique_id, RES_IS_VSET(*cfg_entry) ? PMCRAID_VSET_BUS_ID : (RES_IS_GSCSI(*cfg_entry) ? PMCRAID_PHYS_BUS_ID : RES_BUS(cfg_entry->resource_address)), RES_IS_VSET(*cfg_entry) ? (fw_version <= PMCRAID_FW_VERSION_1 ? cfg_entry->unique_flags1 : cfg_entry->array_id & 0xFF) : RES_TARGET(cfg_entry->resource_address), RES_LUN(cfg_entry->resource_address)); /* If this HCAM indicates a lost notification, read the config table */ if (pinstance->ccn.hcam->notification_lost) { cfgcmd = pmcraid_get_free_cmd(pinstance); if (cfgcmd) { pmcraid_info("lost CCN, reading config table\b"); pinstance->reinit_cfg_table = 1; pmcraid_querycfg(cfgcmd); } else { pmcraid_err("lost CCN, no free cmd for querycfg\n"); } goto out_notify_apps; } /* If this resource is not going to be added to mid-layer, just notify * applications and return. If this notification is about hiding a VSET * resource, check if it was exposed already. */ if (pinstance->ccn.hcam->notification_type == NOTIFICATION_TYPE_ENTRY_CHANGED && cfg_entry->resource_type == RES_TYPE_VSET) { if (fw_version <= PMCRAID_FW_VERSION_1) hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0; else hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0; } else if (!pmcraid_expose_resource(fw_version, cfg_entry)) { goto out_notify_apps; } spin_lock_irqsave(&pinstance->resource_lock, lock_flags); list_for_each_entry(res, &pinstance->used_res_q, queue) { rc = memcmp(&res->cfg_entry.resource_address, &cfg_entry->resource_address, sizeof(cfg_entry->resource_address)); if (!rc) { new_entry = 0; break; } } if (new_entry) { if (hidden_entry) { spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); goto out_notify_apps; } /* If there are more number of resources than what driver can * manage, do not notify the applications about the CCN. Just * ignore this notifications and re-register the same HCAM */ if (list_empty(&pinstance->free_res_q)) { spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); pmcraid_err("too many resources attached\n"); spin_lock_irqsave(pinstance->host->host_lock, host_lock_flags); pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); spin_unlock_irqrestore(pinstance->host->host_lock, host_lock_flags); return; } res = list_entry(pinstance->free_res_q.next, struct pmcraid_resource_entry, queue); list_del(&res->queue); res->scsi_dev = NULL; res->reset_progress = 0; list_add_tail(&res->queue, &pinstance->used_res_q); } memcpy(&res->cfg_entry, cfg_entry, pinstance->config_table_entry_size); if (pinstance->ccn.hcam->notification_type == NOTIFICATION_TYPE_ENTRY_DELETED || hidden_entry) { if (res->scsi_dev) { if (fw_version <= PMCRAID_FW_VERSION_1) res->cfg_entry.unique_flags1 &= 0x7F; else res->cfg_entry.array_id &= 0xFF; res->change_detected = RES_CHANGE_DEL; res->cfg_entry.resource_handle = PMCRAID_INVALID_RES_HANDLE; schedule_work(&pinstance->worker_q); } else { /* This may be one of the non-exposed resources */ list_move_tail(&res->queue, &pinstance->free_res_q); } } else if (!res->scsi_dev) { res->change_detected = RES_CHANGE_ADD; schedule_work(&pinstance->worker_q); } spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); out_notify_apps: /* Notify configuration changes to registered applications.*/ if (!pmcraid_disable_aen) pmcraid_notify_ccn(pinstance); cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); if (cmd) pmcraid_send_hcam_cmd(cmd); } /** * pmcraid_get_error_info - return error string for an ioasc * @ioasc: ioasc code * Return Value * none */ static struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc) { int i; for (i = 0; i < ARRAY_SIZE(pmcraid_ioasc_error_table); i++) { if (pmcraid_ioasc_error_table[i].ioasc_code == ioasc) return &pmcraid_ioasc_error_table[i]; } return NULL; } /** * pmcraid_ioasc_logger - log IOASC information based user-settings * @ioasc: ioasc code * @cmd: pointer to command that resulted in 'ioasc' */ void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd) { struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc); if (error_info == NULL || cmd->drv_inst->current_log_level < error_info->log_level) return; /* log the error string */ pmcraid_err("cmd [%x] for resource %x failed with %x(%s)\n", cmd->ioa_cb->ioarcb.cdb[0], cmd->ioa_cb->ioarcb.resource_handle, le32_to_cpu(ioasc), error_info->error_string); } /** * pmcraid_handle_error_log - Handle a config change (error log) from the IOA * * @pinstance: pointer to per adapter instance structure * * Return value: * none */ static void pmcraid_handle_error_log(struct pmcraid_instance *pinstance) { struct pmcraid_hcam_ldn *hcam_ldn; u32 ioasc; hcam_ldn = (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam; pmcraid_info ("LDN(%x): %x type: %x lost: %x flags: %x overlay id: %x\n", pinstance->ldn.hcam->ilid, pinstance->ldn.hcam->op_code, pinstance->ldn.hcam->notification_type, pinstance->ldn.hcam->notification_lost, pinstance->ldn.hcam->flags, pinstance->ldn.hcam->overlay_id); /* log only the errors, no need to log informational log entries */ if (pinstance->ldn.hcam->notification_type != NOTIFICATION_TYPE_ERROR_LOG) return; if (pinstance->ldn.hcam->notification_lost == HOSTRCB_NOTIFICATIONS_LOST) dev_info(&pinstance->pdev->dev, "Error notifications lost\n"); ioasc = le32_to_cpu(hcam_ldn->error_log.fd_ioasc); if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET || ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) { dev_info(&pinstance->pdev->dev, "UnitAttention due to IOA Bus Reset\n"); scsi_report_bus_reset( pinstance->host, RES_BUS(hcam_ldn->error_log.fd_ra)); } return; } /** * pmcraid_process_ccn - Op done function for a CCN. * @cmd: pointer to command struct * * This function is the op done function for a configuration * change notification * * Return value: * none */ static void pmcraid_process_ccn(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); unsigned long lock_flags; pinstance->ccn.cmd = NULL; pmcraid_return_cmd(cmd); /* If driver initiated IOA reset happened while this hcam was pending * with IOA, or IOA bringdown sequence is in progress, no need to * re-register the hcam */ if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET || atomic_read(&pinstance->ccn.ignore) == 1) { return; } else if (ioasc) { dev_info(&pinstance->pdev->dev, "Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc); spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); } else { pmcraid_handle_config_change(pinstance); } } /** * pmcraid_process_ldn - op done function for an LDN * @cmd: pointer to command block * * Return value * none */ static void pmcraid_initiate_reset(struct pmcraid_instance *); static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd); static void pmcraid_process_ldn(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_hcam_ldn *ldn_hcam = (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam; u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); u32 fd_ioasc = le32_to_cpu(ldn_hcam->error_log.fd_ioasc); unsigned long lock_flags; /* return the command block back to freepool */ pinstance->ldn.cmd = NULL; pmcraid_return_cmd(cmd); /* If driver initiated IOA reset happened while this hcam was pending * with IOA, no need to re-register the hcam as reset engine will do it * once reset sequence is complete */ if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET || atomic_read(&pinstance->ccn.ignore) == 1) { return; } else if (!ioasc) { pmcraid_handle_error_log(pinstance); if (fd_ioasc == PMCRAID_IOASC_NR_IOA_RESET_REQUIRED) { spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_initiate_reset(pinstance); spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); return; } if (fd_ioasc == PMCRAID_IOASC_TIME_STAMP_OUT_OF_SYNC) { pinstance->timestamp_error = 1; pmcraid_set_timestamp(cmd); } } else { dev_info(&pinstance->pdev->dev, "Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc); } /* send netlink message for HCAM notification if enabled */ if (!pmcraid_disable_aen) pmcraid_notify_ldn(pinstance); cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA); if (cmd) pmcraid_send_hcam_cmd(cmd); } /** * pmcraid_register_hcams - register HCAMs for CCN and LDN * * @pinstance: pointer per adapter instance structure * * Return Value * none */ static void pmcraid_register_hcams(struct pmcraid_instance *pinstance) { pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA); } /** * pmcraid_unregister_hcams - cancel HCAMs registered already * @cmd: pointer to command used as part of reset sequence */ static void pmcraid_unregister_hcams(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; /* During IOA bringdown, HCAM gets fired and tasklet proceeds with * handling hcam response though it is not necessary. In order to * prevent this, set 'ignore', so that bring-down sequence doesn't * re-send any more hcams */ atomic_set(&pinstance->ccn.ignore, 1); atomic_set(&pinstance->ldn.ignore, 1); /* If adapter reset was forced as part of runtime reset sequence, * start the reset sequence. Reset will be triggered even in case * IOA unit_check. */ if ((pinstance->force_ioa_reset && !pinstance->ioa_bringdown) || pinstance->ioa_unit_check) { pinstance->force_ioa_reset = 0; pinstance->ioa_unit_check = 0; pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; pmcraid_reset_alert(cmd); return; } /* Driver tries to cancel HCAMs by sending ABORT TASK for each HCAM * one after the other. So CCN cancellation will be triggered by * pmcraid_cancel_ldn itself. */ pmcraid_cancel_ldn(cmd); } /** * pmcraid_reset_enable_ioa - re-enable IOA after a hard reset * @pinstance: pointer to adapter instance structure * Return Value * 1 if TRANSITION_TO_OPERATIONAL is active, otherwise 0 */ static void pmcraid_reinit_buffers(struct pmcraid_instance *); static int pmcraid_reset_enable_ioa(struct pmcraid_instance *pinstance) { u32 intrs; pmcraid_reinit_buffers(pinstance); intrs = pmcraid_read_interrupts(pinstance); pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS); if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) { if (!pinstance->interrupt_mode) { iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, pinstance->int_regs. ioa_host_interrupt_mask_reg); iowrite32(INTRS_TRANSITION_TO_OPERATIONAL, pinstance->int_regs.ioa_host_interrupt_clr_reg); } return 1; } else { return 0; } } /** * pmcraid_soft_reset - performs a soft reset and makes IOA become ready * @cmd : pointer to reset command block * * Return Value * none */ static void pmcraid_soft_reset(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u32 int_reg; u32 doorbell; /* There will be an interrupt when Transition to Operational bit is * set so tasklet would execute next reset task. The timeout handler * would re-initiate a reset */ cmd->cmd_done = pmcraid_ioa_reset; cmd->timer.data = (unsigned long)cmd; cmd->timer.expires = jiffies + msecs_to_jiffies(PMCRAID_TRANSOP_TIMEOUT); cmd->timer.function = (void (*)(unsigned long))pmcraid_timeout_handler; if (!timer_pending(&cmd->timer)) add_timer(&cmd->timer); /* Enable destructive diagnostics on IOA if it is not yet in * operational state */ doorbell = DOORBELL_RUNTIME_RESET | DOORBELL_ENABLE_DESTRUCTIVE_DIAGS; /* Since we do RESET_ALERT and Start BIST we have to again write * MSIX Doorbell to indicate the interrupt mode */ if (pinstance->interrupt_mode) { iowrite32(DOORBELL_INTR_MODE_MSIX, pinstance->int_regs.host_ioa_interrupt_reg); ioread32(pinstance->int_regs.host_ioa_interrupt_reg); } iowrite32(doorbell, pinstance->int_regs.host_ioa_interrupt_reg); ioread32(pinstance->int_regs.host_ioa_interrupt_reg), int_reg = ioread32(pinstance->int_regs.ioa_host_interrupt_reg); pmcraid_info("Waiting for IOA to become operational %x:%x\n", ioread32(pinstance->int_regs.host_ioa_interrupt_reg), int_reg); } /** * pmcraid_get_dump - retrieves IOA dump in case of Unit Check interrupt * * @pinstance: pointer to adapter instance structure * * Return Value * none */ static void pmcraid_get_dump(struct pmcraid_instance *pinstance) { pmcraid_info("%s is not yet implemented\n", __func__); } /** * pmcraid_fail_outstanding_cmds - Fails all outstanding ops. * @pinstance: pointer to adapter instance structure * * This function fails all outstanding ops. If they are submitted to IOA * already, it sends cancel all messages if IOA is still accepting IOARCBs, * otherwise just completes the commands and returns the cmd blocks to free * pool. * * Return value: * none */ static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance) { struct pmcraid_cmd *cmd, *temp; unsigned long lock_flags; /* pending command list is protected by pending_pool_lock. Its * traversal must be done as within this lock */ spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags); list_for_each_entry_safe(cmd, temp, &pinstance->pending_cmd_pool, free_list) { list_del(&cmd->free_list); spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags); cmd->ioa_cb->ioasa.ioasc = cpu_to_le32(PMCRAID_IOASC_IOA_WAS_RESET); cmd->ioa_cb->ioasa.ilid = cpu_to_be32(PMCRAID_DRIVER_ILID); /* In case the command timer is still running */ del_timer(&cmd->timer); /* If this is an IO command, complete it by invoking scsi_done * function. If this is one of the internal commands other * than pmcraid_ioa_reset and HCAM commands invoke cmd_done to * complete it */ if (cmd->scsi_cmd) { struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; __le32 resp = cmd->ioa_cb->ioarcb.response_handle; scsi_cmd->result |= DID_ERROR << 16; scsi_dma_unmap(scsi_cmd); pmcraid_return_cmd(cmd); pmcraid_info("failing(%d) CDB[0] = %x result: %x\n", le32_to_cpu(resp) >> 2, cmd->ioa_cb->ioarcb.cdb[0], scsi_cmd->result); scsi_cmd->scsi_done(scsi_cmd); } else if (cmd->cmd_done == pmcraid_internal_done || cmd->cmd_done == pmcraid_erp_done) { cmd->cmd_done(cmd); } else if (cmd->cmd_done != pmcraid_ioa_reset && cmd->cmd_done != pmcraid_ioa_shutdown_done) { pmcraid_return_cmd(cmd); } atomic_dec(&pinstance->outstanding_cmds); spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags); } spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags); } /** * pmcraid_ioa_reset - Implementation of IOA reset logic * * @cmd: pointer to the cmd block to be used for entire reset process * * This function executes most of the steps required for IOA reset. This gets * called by user threads (modprobe/insmod/rmmod) timer, tasklet and midlayer's * 'eh_' thread. Access to variables used for controlling the reset sequence is * synchronized using host lock. Various functions called during reset process * would make use of a single command block, pointer to which is also stored in * adapter instance structure. * * Return Value * None */ static void pmcraid_ioa_reset(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; u8 reset_complete = 0; pinstance->ioa_reset_in_progress = 1; if (pinstance->reset_cmd != cmd) { pmcraid_err("reset is called with different command block\n"); pinstance->reset_cmd = cmd; } pmcraid_info("reset_engine: state = %d, command = %p\n", pinstance->ioa_state, cmd); switch (pinstance->ioa_state) { case IOA_STATE_DEAD: /* If IOA is offline, whatever may be the reset reason, just * return. callers might be waiting on the reset wait_q, wake * up them */ pmcraid_err("IOA is offline no reset is possible\n"); reset_complete = 1; break; case IOA_STATE_IN_BRINGDOWN: /* we enter here, once ioa shutdown command is processed by IOA * Alert IOA for a possible reset. If reset alert fails, IOA * goes through hard-reset */ pmcraid_disable_interrupts(pinstance, ~0); pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; pmcraid_reset_alert(cmd); break; case IOA_STATE_UNKNOWN: /* We may be called during probe or resume. Some pre-processing * is required for prior to reset */ scsi_block_requests(pinstance->host); /* If asked to reset while IOA was processing responses or * there are any error responses then IOA may require * hard-reset. */ if (pinstance->ioa_hard_reset == 0) { if (ioread32(pinstance->ioa_status) & INTRS_TRANSITION_TO_OPERATIONAL) { pmcraid_info("sticky bit set, bring-up\n"); pinstance->ioa_state = IOA_STATE_IN_BRINGUP; pmcraid_reinit_cmdblk(cmd); pmcraid_identify_hrrq(cmd); } else { pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET; pmcraid_soft_reset(cmd); } } else { /* Alert IOA of a possible reset and wait for critical * operation in progress bit to reset */ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT; pmcraid_reset_alert(cmd); } break; case IOA_STATE_IN_RESET_ALERT: /* If critical operation in progress bit is reset or wait gets * timed out, reset proceeds with starting BIST on the IOA. * pmcraid_ioa_hard_reset keeps a count of reset attempts. If * they are 3 or more, reset engine marks IOA dead and returns */ pinstance->ioa_state = IOA_STATE_IN_HARD_RESET; pmcraid_start_bist(cmd); break; case IOA_STATE_IN_HARD_RESET: pinstance->ioa_reset_attempts++; /* retry reset if we haven't reached maximum allowed limit */ if (pinstance->ioa_reset_attempts > PMCRAID_RESET_ATTEMPTS) { pinstance->ioa_reset_attempts = 0; pmcraid_err("IOA didn't respond marking it as dead\n"); pinstance->ioa_state = IOA_STATE_DEAD; if (pinstance->ioa_bringdown) pmcraid_notify_ioastate(pinstance, PMC_DEVICE_EVENT_SHUTDOWN_FAILED); else pmcraid_notify_ioastate(pinstance, PMC_DEVICE_EVENT_RESET_FAILED); reset_complete = 1; break; } /* Once either bist or pci reset is done, restore PCI config * space. If this fails, proceed with hard reset again */ pci_restore_state(pinstance->pdev); /* fail all pending commands */ pmcraid_fail_outstanding_cmds(pinstance); /* check if unit check is active, if so extract dump */ if (pinstance->ioa_unit_check) { pmcraid_info("unit check is active\n");