diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/ipr.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/ipr.c')
-rw-r--r-- | drivers/scsi/ipr.c | 6083 |
1 files changed, 6083 insertions, 0 deletions
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c new file mode 100644 index 00000000000..5441531c0d8 --- /dev/null +++ b/drivers/scsi/ipr.c @@ -0,0 +1,6083 @@ +/* + * ipr.c -- driver for IBM Power Linux RAID adapters + * + * Written By: Brian King <brking@us.ibm.com>, IBM Corporation + * + * Copyright (C) 2003, 2004 IBM Corporation + * + * 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 + * + */ + +/* + * Notes: + * + * This driver is used to control the following SCSI adapters: + * + * IBM iSeries: 5702, 5703, 2780, 5709, 570A, 570B + * + * IBM pSeries: PCI-X Dual Channel Ultra 320 SCSI RAID Adapter + * PCI-X Dual Channel Ultra 320 SCSI Adapter + * PCI-X Dual Channel Ultra 320 SCSI RAID Enablement Card + * Embedded SCSI adapter on p615 and p655 systems + * + * Supported Hardware Features: + * - Ultra 320 SCSI controller + * - PCI-X host interface + * - Embedded PowerPC RISC Processor and Hardware XOR DMA Engine + * - Non-Volatile Write Cache + * - Supports attachment of non-RAID disks, tape, and optical devices + * - RAID Levels 0, 5, 10 + * - Hot spare + * - Background Parity Checking + * - Background Data Scrubbing + * - Ability to increase the capacity of an existing RAID 5 disk array + * by adding disks + * + * Driver Features: + * - Tagged command queuing + * - Adapter microcode download + * - PCI hot plug + * - SCSI device hot plug + * + */ + +#include <linux/config.h> +#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 <asm/io.h> +#include <asm/irq.h> +#include <asm/processor.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_request.h> +#include "ipr.h" + +/* + * Global Data + */ +static struct list_head ipr_ioa_head = LIST_HEAD_INIT(ipr_ioa_head); +static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL; +static unsigned int ipr_max_speed = 1; +static int ipr_testmode = 0; +static unsigned int ipr_fastfail = 0; +static unsigned int ipr_transop_timeout = IPR_OPERATIONAL_TIMEOUT; +static DEFINE_SPINLOCK(ipr_driver_lock); + +/* This table describes the differences between DMA controller chips */ +static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { + { /* Gemstone and Citrine */ + .mailbox = 0x0042C, + .cache_line_size = 0x20, + { + .set_interrupt_mask_reg = 0x0022C, + .clr_interrupt_mask_reg = 0x00230, + .sense_interrupt_mask_reg = 0x0022C, + .clr_interrupt_reg = 0x00228, + .sense_interrupt_reg = 0x00224, + .ioarrin_reg = 0x00404, + .sense_uproc_interrupt_reg = 0x00214, + .set_uproc_interrupt_reg = 0x00214, + .clr_uproc_interrupt_reg = 0x00218 + } + }, + { /* Snipe and Scamp */ + .mailbox = 0x0052C, + .cache_line_size = 0x20, + { + .set_interrupt_mask_reg = 0x00288, + .clr_interrupt_mask_reg = 0x0028C, + .sense_interrupt_mask_reg = 0x00288, + .clr_interrupt_reg = 0x00284, + .sense_interrupt_reg = 0x00280, + .ioarrin_reg = 0x00504, + .sense_uproc_interrupt_reg = 0x00290, + .set_uproc_interrupt_reg = 0x00290, + .clr_uproc_interrupt_reg = 0x00294 + } + }, +}; + +static const struct ipr_chip_t ipr_chip[] = { + { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, &ipr_chip_cfg[1] }, + { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, &ipr_chip_cfg[1] } +}; + +static int ipr_max_bus_speeds [] = { + IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE +}; + +MODULE_AUTHOR("Brian King <brking@us.ibm.com>"); +MODULE_DESCRIPTION("IBM Power RAID SCSI Adapter Driver"); +module_param_named(max_speed, ipr_max_speed, uint, 0); +MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320"); +module_param_named(log_level, ipr_log_level, uint, 0); +MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver"); +module_param_named(testmode, ipr_testmode, int, 0); +MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations"); +module_param_named(fastfail, ipr_fastfail, int, 0); +MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries"); +module_param_named(transop_timeout, ipr_transop_timeout, int, 0); +MODULE_PARM_DESC(transop_timeout, "Time in seconds to wait for adapter to come operational (default: 300)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(IPR_DRIVER_VERSION); + +static const char *ipr_gpdd_dev_end_states[] = { + "Command complete", + "Terminated by host", + "Terminated by device reset", + "Terminated by bus reset", + "Unknown", + "Command not started" +}; + +static const char *ipr_gpdd_dev_bus_phases[] = { + "Bus free", + "Arbitration", + "Selection", + "Message out", + "Command", + "Message in", + "Data out", + "Data in", + "Status", + "Reselection", + "Unknown" +}; + +/* A constant array of IOASCs/URCs/Error Messages */ +static const +struct ipr_error_table_t ipr_error_table[] = { + {0x00000000, 1, 1, + "8155: An unknown error was received"}, + {0x00330000, 0, 0, + "Soft underlength error"}, + {0x005A0000, 0, 0, + "Command to be cancelled not found"}, + {0x00808000, 0, 0, + "Qualified success"}, + {0x01080000, 1, 1, + "FFFE: Soft device bus error recovered by the IOA"}, + {0x01170600, 0, 1, + "FFF9: Device sector reassign successful"}, + {0x01170900, 0, 1, + "FFF7: Media error recovered by device rewrite procedures"}, + {0x01180200, 0, 1, + "7001: IOA sector reassignment successful"}, + {0x01180500, 0, 1, + "FFF9: Soft media error. Sector reassignment recommended"}, + {0x01180600, 0, 1, + "FFF7: Media error recovered by IOA rewrite procedures"}, + {0x01418000, 0, 1, + "FF3D: Soft PCI bus error recovered by the IOA"}, + {0x01440000, 1, 1, + "FFF6: Device hardware error recovered by the IOA"}, + {0x01448100, 0, 1, + "FFF6: Device hardware error recovered by the device"}, + {0x01448200, 1, 1, + "FF3D: Soft IOA error recovered by the IOA"}, + {0x01448300, 0, 1, + "FFFA: Undefined device response recovered by the IOA"}, + {0x014A0000, 1, 1, + "FFF6: Device bus error, message or command phase"}, + {0x015D0000, 0, 1, + "FFF6: Failure prediction threshold exceeded"}, + {0x015D9200, 0, 1, + "8009: Impending cache battery pack failure"}, + {0x02040400, 0, 0, + "34FF: Disk device format in progress"}, + {0x023F0000, 0, 0, + "Synchronization required"}, + {0x024E0000, 0, 0, + "No ready, IOA shutdown"}, + {0x025A0000, 0, 0, + "Not ready, IOA has been shutdown"}, + {0x02670100, 0, 1, + "3020: Storage subsystem configuration error"}, + {0x03110B00, 0, 0, + "FFF5: Medium error, data unreadable, recommend reassign"}, + {0x03110C00, 0, 0, + "7000: Medium error, data unreadable, do not reassign"}, + {0x03310000, 0, 1, + "FFF3: Disk media format bad"}, + {0x04050000, 0, 1, + "3002: Addressed device failed to respond to selection"}, + {0x04080000, 1, 1, + "3100: Device bus error"}, + {0x04080100, 0, 1, + "3109: IOA timed out a device command"}, + {0x04088000, 0, 0, + "3120: SCSI bus is not operational"}, + {0x04118000, 0, 1, + "9000: IOA reserved area data check"}, + {0x04118100, 0, 1, + "9001: IOA reserved area invalid data pattern"}, + {0x04118200, 0, 1, + "9002: IOA reserved area LRC error"}, + {0x04320000, 0, 1, + "102E: Out of alternate sectors for disk storage"}, + {0x04330000, 1, 1, + "FFF4: Data transfer underlength error"}, + {0x04338000, 1, 1, + "FFF4: Data transfer overlength error"}, + {0x043E0100, 0, 1, + "3400: Logical unit failure"}, + {0x04408500, 0, 1, + "FFF4: Device microcode is corrupt"}, + {0x04418000, 1, 1, + "8150: PCI bus error"}, + {0x04430000, 1, 0, + "Unsupported device bus message received"}, + {0x04440000, 1, 1, + "FFF4: Disk device problem"}, + {0x04448200, 1, 1, + "8150: Permanent IOA failure"}, + {0x04448300, 0, 1, + "3010: Disk device returned wrong response to IOA"}, + {0x04448400, 0, 1, + "8151: IOA microcode error"}, + {0x04448500, 0, 0, + "Device bus status error"}, + {0x04448600, 0, 1, + "8157: IOA error requiring IOA reset to recover"}, + {0x04490000, 0, 0, + "Message reject received from the device"}, + {0x04449200, 0, 1, + "8008: A permanent cache battery pack failure occurred"}, + {0x0444A000, 0, 1, + "9090: Disk unit has been modified after the last known status"}, + {0x0444A200, 0, 1, + "9081: IOA detected device error"}, + {0x0444A300, 0, 1, + "9082: IOA detected device error"}, + {0x044A0000, 1, 1, + "3110: Device bus error, message or command phase"}, + {0x04670400, 0, 1, + "9091: Incorrect hardware configuration change has been detected"}, + {0x046E0000, 0, 1, + "FFF4: Command to logical unit failed"}, + {0x05240000, 1, 0, + "Illegal request, invalid request type or request packet"}, + {0x05250000, 0, 0, + "Illegal request, invalid resource handle"}, + {0x05260000, 0, 0, + "Illegal request, invalid field in parameter list"}, + {0x05260100, 0, 0, + "Illegal request, parameter not supported"}, + {0x05260200, 0, 0, + "Illegal request, parameter value invalid"}, + {0x052C0000, 0, 0, + "Illegal request, command sequence error"}, + {0x06040500, 0, 1, + "9031: Array protection temporarily suspended, protection resuming"}, + {0x06040600, 0, 1, + "9040: Array protection temporarily suspended, protection resuming"}, + {0x06290000, 0, 1, + "FFFB: SCSI bus was reset"}, + {0x06290500, 0, 0, + "FFFE: SCSI bus transition to single ended"}, + {0x06290600, 0, 0, + "FFFE: SCSI bus transition to LVD"}, + {0x06298000, 0, 1, + "FFFB: SCSI bus was reset by another initiator"}, + {0x063F0300, 0, 1, + "3029: A device replacement has occurred"}, + {0x064C8000, 0, 1, + "9051: IOA cache data exists for a missing or failed device"}, + {0x06670100, 0, 1, + "9025: Disk unit is not supported at its physical location"}, + {0x06670600, 0, 1, + "3020: IOA detected a SCSI bus configuration error"}, + {0x06678000, 0, 1, + "3150: SCSI bus configuration error"}, + {0x06690200, 0, 1, + "9041: Array protection temporarily suspended"}, + {0x06698200, 0, 1, + "9042: Corrupt array parity detected on specified device"}, + {0x066B0200, 0, 1, + "9030: Array no longer protected due to missing or failed disk unit"}, + {0x066B8200, 0, 1, + "9032: Array exposed but still protected"}, + {0x07270000, 0, 0, + "Failure due to other device"}, + {0x07278000, 0, 1, + "9008: IOA does not support functions expected by devices"}, + {0x07278100, 0, 1, + "9010: Cache data associated with attached devices cannot be found"}, + {0x07278200, 0, 1, + "9011: Cache data belongs to devices other than those attached"}, + {0x07278400, 0, 1, + "9020: Array missing 2 or more devices with only 1 device present"}, + {0x07278500, 0, 1, + "9021: Array missing 2 or more devices with 2 or more devices present"}, + {0x07278600, 0, 1, + "9022: Exposed array is missing a required device"}, + {0x07278700, 0, 1, + "9023: Array member(s) not at required physical locations"}, + {0x07278800, 0, 1, + "9024: Array not functional due to present hardware configuration"}, + {0x07278900, 0, 1, + "9026: Array not functional due to present hardware configuration"}, + {0x07278A00, 0, 1, + "9027: Array is missing a device and parity is out of sync"}, + {0x07278B00, 0, 1, + "9028: Maximum number of arrays already exist"}, + {0x07278C00, 0, 1, + "9050: Required cache data cannot be located for a disk unit"}, + {0x07278D00, 0, 1, + "9052: Cache data exists for a device that has been modified"}, + {0x07278F00, 0, 1, + "9054: IOA resources not available due to previous problems"}, + {0x07279100, 0, 1, + "9092: Disk unit requires initialization before use"}, + {0x07279200, 0, 1, + "9029: Incorrect hardware configuration change has been detected"}, + {0x07279600, 0, 1, + "9060: One or more disk pairs are missing from an array"}, + {0x07279700, 0, 1, + "9061: One or more disks are missing from an array"}, + {0x07279800, 0, 1, + "9062: One or more disks are missing from an array"}, + {0x07279900, 0, 1, + "9063: Maximum number of functional arrays has been exceeded"}, + {0x0B260000, 0, 0, + "Aborted command, invalid descriptor"}, + {0x0B5A0000, 0, 0, + "Command terminated by host"} +}; + +static const struct ipr_ses_table_entry ipr_ses_table[] = { + { "2104-DL1 ", "XXXXXXXXXXXXXXXX", 80 }, + { "2104-TL1 ", "XXXXXXXXXXXXXXXX", 80 }, + { "HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 7 slot */ + { "HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 5 slot */ + { "HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Bowtie */ + { "HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* MartinFenning */ + { "2104-DU3 ", "XXXXXXXXXXXXXXXX", 160 }, + { "2104-TU3 ", "XXXXXXXXXXXXXXXX", 160 }, + { "HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 }, + { "HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 }, + { "St V1S2 ", "XXXXXXXXXXXXXXXX", 160 }, + { "HSBPD4M PU3SCSI", "XXXXXXX*XXXXXXXX", 160 }, + { "VSBPD1H U3SCSI", "XXXXXXX*XXXXXXXX", 160 } +}; + +/* + * Function Prototypes + */ +static int ipr_reset_alert(struct ipr_cmnd *); +static void ipr_process_ccn(struct ipr_cmnd *); +static void ipr_process_error(struct ipr_cmnd *); +static void ipr_reset_ioa_job(struct ipr_cmnd *); +static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *, + enum ipr_shutdown_type); + +#ifdef CONFIG_SCSI_IPR_TRACE +/** + * ipr_trc_hook - Add a trace entry to the driver trace + * @ipr_cmd: ipr command struct + * @type: trace type + * @add_data: additional data + * + * Return value: + * none + **/ +static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd, + u8 type, u32 add_data) +{ + struct ipr_trace_entry *trace_entry; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + trace_entry = &ioa_cfg->trace[ioa_cfg->trace_index++]; + trace_entry->time = jiffies; + trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0]; + trace_entry->type = type; + trace_entry->cmd_index = ipr_cmd->cmd_index; + trace_entry->res_handle = ipr_cmd->ioarcb.res_handle; + trace_entry->u.add_data = add_data; +} +#else +#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0) +#endif + +/** + * ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse + * @ipr_cmd: ipr command struct + * + * Return value: + * none + **/ +static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioasa *ioasa = &ipr_cmd->ioasa; + + memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt)); + ioarcb->write_data_transfer_length = 0; + ioarcb->read_data_transfer_length = 0; + ioarcb->write_ioadl_len = 0; + ioarcb->read_ioadl_len = 0; + ioasa->ioasc = 0; + ioasa->residual_data_len = 0; + + ipr_cmd->scsi_cmd = NULL; + ipr_cmd->sense_buffer[0] = 0; + ipr_cmd->dma_use_sg = 0; +} + +/** + * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block + * @ipr_cmd: ipr command struct + * + * Return value: + * none + **/ +static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd) +{ + ipr_reinit_ipr_cmnd(ipr_cmd); + ipr_cmd->u.scratch = 0; + ipr_cmd->sibling = NULL; + init_timer(&ipr_cmd->timer); +} + +/** + * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block + * @ioa_cfg: ioa config struct + * + * Return value: + * pointer to ipr command struct + **/ +static +struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_cmnd *ipr_cmd; + + ipr_cmd = list_entry(ioa_cfg->free_q.next, struct ipr_cmnd, queue); + list_del(&ipr_cmd->queue); + ipr_init_ipr_cmnd(ipr_cmd); + + return ipr_cmd; +} + +/** + * ipr_unmap_sglist - Unmap scatterlist if mapped + * @ioa_cfg: ioa config struct + * @ipr_cmd: ipr command struct + * + * Return value: + * nothing + **/ +static void ipr_unmap_sglist(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_cmnd *ipr_cmd) +{ + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + + if (ipr_cmd->dma_use_sg) { + if (scsi_cmd->use_sg > 0) { + pci_unmap_sg(ioa_cfg->pdev, scsi_cmd->request_buffer, + scsi_cmd->use_sg, + scsi_cmd->sc_data_direction); + } else { + pci_unmap_single(ioa_cfg->pdev, ipr_cmd->dma_handle, + scsi_cmd->request_bufflen, + scsi_cmd->sc_data_direction); + } + } +} + +/** + * ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts + * @ioa_cfg: ioa config struct + * @clr_ints: interrupts to clear + * + * This function masks all interrupts on the adapter, then clears the + * interrupts specified in the mask + * + * Return value: + * none + **/ +static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg, + u32 clr_ints) +{ + volatile u32 int_reg; + + /* Stop new interrupts */ + ioa_cfg->allow_interrupts = 0; + + /* Set interrupt mask to stop all new interrupts */ + writel(~0, ioa_cfg->regs.set_interrupt_mask_reg); + + /* Clear any pending interrupts */ + writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); +} + +/** + * ipr_save_pcix_cmd_reg - Save PCI-X command register + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) +{ + int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX); + + if (pcix_cmd_reg == 0) { + dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n"); + return -EIO; + } + + if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD, + &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) { + dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n"); + return -EIO; + } + + ioa_cfg->saved_pcix_cmd_reg |= PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO; + return 0; +} + +/** + * ipr_set_pcix_cmd_reg - Setup PCI-X command register + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) +{ + int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX); + + if (pcix_cmd_reg) { + if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD, + ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) { + dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n"); + return -EIO; + } + } else { + dev_err(&ioa_cfg->pdev->dev, + "Failed to setup PCI-X command register\n"); + return -EIO; + } + + return 0; +} + +/** + * ipr_scsi_eh_done - mid-layer done function for aborted ops + * @ipr_cmd: ipr command struct + * + * This function is invoked by the interrupt handler for + * ops generated by the SCSI mid-layer which are being aborted. + * + * Return value: + * none + **/ +static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + + scsi_cmd->result |= (DID_ERROR << 16); + + ipr_unmap_sglist(ioa_cfg, ipr_cmd); + scsi_cmd->scsi_done(scsi_cmd); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); +} + +/** + * ipr_fail_all_ops - Fails all outstanding ops. + * @ioa_cfg: ioa config struct + * + * This function fails all outstanding ops. + * + * Return value: + * none + **/ +static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_cmnd *ipr_cmd, *temp; + + ENTER; + list_for_each_entry_safe(ipr_cmd, temp, &ioa_cfg->pending_q, queue) { + list_del(&ipr_cmd->queue); + + ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_IOA_WAS_RESET); + ipr_cmd->ioasa.ilid = cpu_to_be32(IPR_DRIVER_ILID); + + if (ipr_cmd->scsi_cmd) + ipr_cmd->done = ipr_scsi_eh_done; + + ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET); + del_timer(&ipr_cmd->timer); + ipr_cmd->done(ipr_cmd); + } + + LEAVE; +} + +/** + * ipr_do_req - Send driver initiated requests. + * @ipr_cmd: ipr command struct + * @done: done function + * @timeout_func: timeout function + * @timeout: timeout value + * + * This function sends the specified command to the adapter with the + * timeout given. The done function is invoked on command completion. + * + * Return value: + * none + **/ +static void ipr_do_req(struct ipr_cmnd *ipr_cmd, + void (*done) (struct ipr_cmnd *), + void (*timeout_func) (struct ipr_cmnd *), u32 timeout) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + + ipr_cmd->done = done; + + ipr_cmd->timer.data = (unsigned long) ipr_cmd; + ipr_cmd->timer.expires = jiffies + timeout; + ipr_cmd->timer.function = (void (*)(unsigned long))timeout_func; + + add_timer(&ipr_cmd->timer); + + ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0); + + mb(); + writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr), + ioa_cfg->regs.ioarrin_reg); +} + +/** + * ipr_internal_cmd_done - Op done function for an internally generated op. + * @ipr_cmd: ipr command struct + * + * This function is the op done function for an internally generated, + * blocking op. It simply wakes the sleeping thread. + * + * Return value: + * none + **/ +static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd) +{ + if (ipr_cmd->sibling) + ipr_cmd->sibling = NULL; + else + complete(&ipr_cmd->completion); +} + +/** + * ipr_send_blocking_cmd - Send command and sleep on its completion. + * @ipr_cmd: ipr command struct + * @timeout_func: function to invoke if command times out + * @timeout: timeout + * + * Return value: + * none + **/ +static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd, + void (*timeout_func) (struct ipr_cmnd *ipr_cmd), + u32 timeout) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + init_completion(&ipr_cmd->completion); + ipr_do_req(ipr_cmd, ipr_internal_cmd_done, timeout_func, timeout); + + spin_unlock_irq(ioa_cfg->host->host_lock); + wait_for_completion(&ipr_cmd->completion); + spin_lock_irq(ioa_cfg->host->host_lock); +} + +/** + * ipr_send_hcam - Send an HCAM to the adapter. + * @ioa_cfg: ioa config struct + * @type: HCAM type + * @hostrcb: hostrcb struct + * + * This function will send a Host Controlled Async command to the adapter. + * If HCAMs are currently not allowed to be issued to the adapter, it will + * place the hostrcb on the free queue. + * + * Return value: + * none + **/ +static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_cmnd *ipr_cmd; + struct ipr_ioarcb *ioarcb; + + if (ioa_cfg->allow_cmds) { + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q); + + ipr_cmd->u.hostrcb = hostrcb; + ioarcb = &ipr_cmd->ioarcb; + + ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_HCAM; + ioarcb->cmd_pkt.cdb[0] = IPR_HOST_CONTROLLED_ASYNC; + ioarcb->cmd_pkt.cdb[1] = type; + ioarcb->cmd_pkt.cdb[7] = (sizeof(hostrcb->hcam) >> 8) & 0xff; + ioarcb->cmd_pkt.cdb[8] = sizeof(hostrcb->hcam) & 0xff; + + ioarcb->read_data_transfer_length = cpu_to_be32(sizeof(hostrcb->hcam)); + ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ipr_cmd->ioadl[0].flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | sizeof(hostrcb->hcam)); + ipr_cmd->ioadl[0].address = cpu_to_be32(hostrcb->hostrcb_dma); + + if (type == IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE) + ipr_cmd->done = ipr_process_ccn; + else + ipr_cmd->done = ipr_process_error; + + ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR); + + mb(); + writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr), + ioa_cfg->regs.ioarrin_reg); + } else { + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q); + } +} + +/** + * ipr_init_res_entry - Initialize a resource entry struct. + * @res: resource entry struct + * + * Return value: + * none + **/ +static void ipr_init_res_entry(struct ipr_resource_entry *res) +{ + res->needs_sync_complete = 1; + res->in_erp = 0; + res->add_to_ml = 0; + res->del_from_ml = 0; + res->resetting_device = 0; + res->sdev = NULL; +} + +/** + * ipr_handle_config_change - Handle a config change from the adapter + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb + * + * Return value: + * none + **/ +static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_resource_entry *res = NULL; + struct ipr_config_table_entry *cfgte; + u32 is_ndn = 1; + + cfgte = &hostrcb->hcam.u.ccn.cfgte; + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (!memcmp(&res->cfgte.res_addr, &cfgte->res_addr, + sizeof(cfgte->res_addr))) { + is_ndn = 0; + break; + } + } + + if (is_ndn) { + if (list_empty(&ioa_cfg->free_res_q)) { + ipr_send_hcam(ioa_cfg, + IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, + hostrcb); + return; + } + + res = list_entry(ioa_cfg->free_res_q.next, + struct ipr_resource_entry, queue); + + list_del(&res->queue); + ipr_init_res_entry(res); + list_add_tail(&res->queue, &ioa_cfg->used_res_q); + } + + memcpy(&res->cfgte, cfgte, sizeof(struct ipr_config_table_entry)); + + if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) { + if (res->sdev) { + res->sdev->hostdata = NULL; + res->del_from_ml = 1; + if (ioa_cfg->allow_ml_add_del) + schedule_work(&ioa_cfg->work_q); + } else + list_move_tail(&res->queue, &ioa_cfg->free_res_q); + } else if (!res->sdev) { + res->add_to_ml = 1; + if (ioa_cfg->allow_ml_add_del) + schedule_work(&ioa_cfg->work_q); + } + + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); +} + +/** + * ipr_process_ccn - Op done function for a CCN. + * @ipr_cmd: ipr command struct + * + * This function is the op done function for a configuration + * change notification host controlled async from the adapter. + * + * Return value: + * none + **/ +static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + list_del(&hostrcb->queue); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + + if (ioasc) { + if (ioasc != IPR_IOASC_IOA_WAS_RESET) + dev_err(&ioa_cfg->pdev->dev, + "Host RCB failed with IOASC: 0x%08X\n", ioasc); + + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); + } else { + ipr_handle_config_change(ioa_cfg, hostrcb); + } +} + +/** + * ipr_log_vpd - Log the passed VPD to the error log. + * @vpids: vendor/product id struct + * @serial_num: serial number string + * + * Return value: + * none + **/ +static void ipr_log_vpd(struct ipr_std_inq_vpids *vpids, u8 *serial_num) +{ + char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + + IPR_SERIAL_NUM_LEN]; + + memcpy(buffer, vpids->vendor_id, IPR_VENDOR_ID_LEN); + memcpy(buffer + IPR_VENDOR_ID_LEN, vpids->product_id, + IPR_PROD_ID_LEN); + buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0'; + ipr_err("Vendor/Product ID: %s\n", buffer); + + memcpy(buffer, serial_num, IPR_SERIAL_NUM_LEN); + buffer[IPR_SERIAL_NUM_LEN] = '\0'; + ipr_err(" Serial Number: %s\n", buffer); +} + +/** + * ipr_log_cache_error - Log a cache error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_cache_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_hostrcb_type_02_error *error = + &hostrcb->hcam.u.error.u.type_02_error; + + ipr_err("-----Current Configuration-----\n"); + ipr_err("Cache Directory Card Information:\n"); + ipr_log_vpd(&error->ioa_vpids, error->ioa_sn); + ipr_err("Adapter Card Information:\n"); + ipr_log_vpd(&error->cfc_vpids, error->cfc_sn); + + ipr_err("-----Expected Configuration-----\n"); + ipr_err("Cache Directory Card Information:\n"); + ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpids, + error->ioa_last_attached_to_cfc_sn); + ipr_err("Adapter Card Information:\n"); + ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpids, + error->cfc_last_attached_to_ioa_sn); + + ipr_err("Additional IOA Data: %08X %08X %08X\n", + be32_to_cpu(error->ioa_data[0]), + be32_to_cpu(error->ioa_data[1]), + be32_to_cpu(error->ioa_data[2])); +} + +/** + * ipr_log_config_error - Log a configuration error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_config_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + int errors_logged, i; + struct ipr_hostrcb_device_data_entry *dev_entry; + struct ipr_hostrcb_type_03_error *error; + + error = &hostrcb->hcam.u.error.u.type_03_error; + errors_logged = be32_to_cpu(error->errors_logged); + + ipr_err("Device Errors Detected/Logged: %d/%d\n", + be32_to_cpu(error->errors_detected), errors_logged); + + dev_entry = error->dev_entry; + + for (i = 0; i < errors_logged; i++, dev_entry++) { + ipr_err_separator; + + if (dev_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) { + ipr_err("Device %d: missing\n", i + 1); + } else { + ipr_err("Device %d: %d:%d:%d:%d\n", i + 1, + ioa_cfg->host->host_no, dev_entry->dev_res_addr.bus, + dev_entry->dev_res_addr.target, dev_entry->dev_res_addr.lun); + } + ipr_log_vpd(&dev_entry->dev_vpids, dev_entry->dev_sn); + + ipr_err("-----New Device Information-----\n"); + ipr_log_vpd(&dev_entry->new_dev_vpids, dev_entry->new_dev_sn); + + ipr_err("Cache Directory Card Information:\n"); + ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpids, + dev_entry->ioa_last_with_dev_sn); + + ipr_err("Adapter Card Information:\n"); + ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpids, + dev_entry->cfc_last_with_dev_sn); + + ipr_err("Additional IOA Data: %08X %08X %08X %08X %08X\n", + be32_to_cpu(dev_entry->ioa_data[0]), + be32_to_cpu(dev_entry->ioa_data[1]), + be32_to_cpu(dev_entry->ioa_data[2]), + be32_to_cpu(dev_entry->ioa_data[3]), + be32_to_cpu(dev_entry->ioa_data[4])); + } +} + +/** + * ipr_log_array_error - Log an array configuration error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + int i; + struct ipr_hostrcb_type_04_error *error; + struct ipr_hostrcb_array_data_entry *array_entry; + const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' }; + + error = &hostrcb->hcam.u.error.u.type_04_error; + + ipr_err_separator; + + ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n", + error->protection_level, + ioa_cfg->host->host_no, + error->last_func_vset_res_addr.bus, + error->last_func_vset_res_addr.target, + error->last_func_vset_res_addr.lun); + + ipr_err_separator; + + array_entry = error->array_member; + + for (i = 0; i < 18; i++) { + if (!memcmp(array_entry->serial_num, zero_sn, IPR_SERIAL_NUM_LEN)) + continue; + + if (be32_to_cpu(error->exposed_mode_adn) == i) { + ipr_err("Exposed Array Member %d:\n", i); + } else { + ipr_err("Array Member %d:\n", i); + } + + ipr_log_vpd(&array_entry->vpids, array_entry->serial_num); + + if (array_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) { + ipr_err("Current Location: unknown\n"); + } else { + ipr_err("Current Location: %d:%d:%d:%d\n", + ioa_cfg->host->host_no, + array_entry->dev_res_addr.bus, + array_entry->dev_res_addr.target, + array_entry->dev_res_addr.lun); + } + + if (array_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) { + ipr_err("Expected Location: unknown\n"); + } else { + ipr_err("Expected Location: %d:%d:%d:%d\n", + ioa_cfg->host->host_no, + array_entry->expected_dev_res_addr.bus, + array_entry->expected_dev_res_addr.target, + array_entry->expected_dev_res_addr.lun); + } + + ipr_err_separator; + + if (i == 9) + array_entry = error->array_member2; + else + array_entry++; + } +} + +/** + * ipr_log_generic_error - Log an adapter error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + int i; + int ioa_data_len = be32_to_cpu(hostrcb->hcam.length); + + if (ioa_data_len == 0) + return; + + ipr_err("IOA Error Data:\n"); + ipr_err("Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + + for (i = 0; i < ioa_data_len / 4; i += 4) { + ipr_err("%08X: %08X %08X %08X %08X\n", i*4, + be32_to_cpu(hostrcb->hcam.u.raw.data[i]), + be32_to_cpu(hostrcb->hcam.u.raw.data[i+1]), + be32_to_cpu(hostrcb->hcam.u.raw.data[i+ |