/* * ipr.c -- driver for IBM Power Linux RAID adapters * * Written By: Brian King , 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 #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 "ipr.h" /* * Global Data */ static LIST_HEAD(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 = 0; static unsigned int ipr_enable_cache = 1; static unsigned int ipr_debug = 0; static unsigned int ipr_dual_ioa_raid = 1; 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, Citrine, Obsidian, and Obsidian-E */ .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_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, &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 "); 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_param_named(enable_cache, ipr_enable_cache, int, 0); MODULE_PARM_DESC(enable_cache, "Enable adapter's non-volatile write cache (default: 1)"); module_param_named(debug, ipr_debug, int, 0); MODULE_PARM_DESC(debug, "Enable device driver debugging logging. Set to 1 to enable. (default: 0)"); module_param_named(dual_ioa_raid, ipr_dual_ioa_raid, int, 0); MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to enable. (default: 1)"); MODULE_LICENSE("GPL"); MODULE_VERSION(IPR_DRIVER_VERSION); /* A constant array of IOASCs/URCs/Error Messages */ static const struct ipr_error_table_t ipr_error_table[] = { {0x00000000, 1, IPR_DEFAULT_LOG_LEVEL, "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, IPR_DEFAULT_LOG_LEVEL, "FFFE: Soft device bus error recovered by the IOA"}, {0x01088100, 0, IPR_DEFAULT_LOG_LEVEL, "4101: Soft device bus fabric error"}, {0x01170600, 0, IPR_DEFAULT_LOG_LEVEL, "FFF9: Device sector reassign successful"}, {0x01170900, 0, IPR_DEFAULT_LOG_LEVEL, "FFF7: Media error recovered by device rewrite procedures"}, {0x01180200, 0, IPR_DEFAULT_LOG_LEVEL, "7001: IOA sector reassignment successful"}, {0x01180500, 0, IPR_DEFAULT_LOG_LEVEL, "FFF9: Soft media error. Sector reassignment recommended"}, {0x01180600, 0, IPR_DEFAULT_LOG_LEVEL, "FFF7: Media error recovered by IOA rewrite procedures"}, {0x01418000, 0, IPR_DEFAULT_LOG_LEVEL, "FF3D: Soft PCI bus error recovered by the IOA"}, {0x01440000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF6: Device hardware error recovered by the IOA"}, {0x01448100, 0, IPR_DEFAULT_LOG_LEVEL, "FFF6: Device hardware error recovered by the device"}, {0x01448200, 1, IPR_DEFAULT_LOG_LEVEL, "FF3D: Soft IOA error recovered by the IOA"}, {0x01448300, 0, IPR_DEFAULT_LOG_LEVEL, "FFFA: Undefined device response recovered by the IOA"}, {0x014A0000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF6: Device bus error, message or command phase"}, {0x014A8000, 0, IPR_DEFAULT_LOG_LEVEL, "FFFE: Task Management Function failed"}, {0x015D0000, 0, IPR_DEFAULT_LOG_LEVEL, "FFF6: Failure prediction threshold exceeded"}, {0x015D9200, 0, IPR_DEFAULT_LOG_LEVEL, "8009: Impending cache battery pack failure"}, {0x02040400, 0, 0, "34FF: Disk device format in progress"}, {0x02048000, 0, IPR_DEFAULT_LOG_LEVEL, "9070: IOA requested reset"}, {0x023F0000, 0, 0, "Synchronization required"}, {0x024E0000, 0, 0, "No ready, IOA shutdown"}, {0x025A0000, 0, 0, "Not ready, IOA has been shutdown"}, {0x02670100, 0, IPR_DEFAULT_LOG_LEVEL, "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, IPR_DEFAULT_LOG_LEVEL, "FFF3: Disk media format bad"}, {0x04050000, 0, IPR_DEFAULT_LOG_LEVEL, "3002: Addressed device failed to respond to selection"}, {0x04080000, 1, IPR_DEFAULT_LOG_LEVEL, "3100: Device bus error"}, {0x04080100, 0, IPR_DEFAULT_LOG_LEVEL, "3109: IOA timed out a device command"}, {0x04088000, 0, 0, "3120: SCSI bus is not operational"}, {0x04088100, 0, IPR_DEFAULT_LOG_LEVEL, "4100: Hard device bus fabric error"}, {0x04118000, 0, IPR_DEFAULT_LOG_LEVEL, "9000: IOA reserved area data check"}, {0x04118100, 0, IPR_DEFAULT_LOG_LEVEL, "9001: IOA reserved area invalid data pattern"}, {0x04118200, 0, IPR_DEFAULT_LOG_LEVEL, "9002: IOA reserved area LRC error"}, {0x04320000, 0, IPR_DEFAULT_LOG_LEVEL, "102E: Out of alternate sectors for disk storage"}, {0x04330000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF4: Data transfer underlength error"}, {0x04338000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF4: Data transfer overlength error"}, {0x043E0100, 0, IPR_DEFAULT_LOG_LEVEL, "3400: Logical unit failure"}, {0x04408500, 0, IPR_DEFAULT_LOG_LEVEL, "FFF4: Device microcode is corrupt"}, {0x04418000, 1, IPR_DEFAULT_LOG_LEVEL, "8150: PCI bus error"}, {0x04430000, 1, 0, "Unsupported device bus message received"}, {0x04440000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF4: Disk device problem"}, {0x04448200, 1, IPR_DEFAULT_LOG_LEVEL, "8150: Permanent IOA failure"}, {0x04448300, 0, IPR_DEFAULT_LOG_LEVEL, "3010: Disk device returned wrong response to IOA"}, {0x04448400, 0, IPR_DEFAULT_LOG_LEVEL, "8151: IOA microcode error"}, {0x04448500, 0, 0, "Device bus status error"}, {0x04448600, 0, IPR_DEFAULT_LOG_LEVEL, "8157: IOA error requiring IOA reset to recover"}, {0x04448700, 0, 0, "ATA device status error"}, {0x04490000, 0, 0, "Message reject received from the device"}, {0x04449200, 0, IPR_DEFAULT_LOG_LEVEL, "8008: A permanent cache battery pack failure occurred"}, {0x0444A000, 0, IPR_DEFAULT_LOG_LEVEL, "9090: Disk unit has been modified after the last known status"}, {0x0444A200, 0, IPR_DEFAULT_LOG_LEVEL, "9081: IOA detected device error"}, {0x0444A300, 0, IPR_DEFAULT_LOG_LEVEL, "9082: IOA detected device error"}, {0x044A0000, 1, IPR_DEFAULT_LOG_LEVEL, "3110: Device bus error, message or command phase"}, {0x044A8000, 1, IPR_DEFAULT_LOG_LEVEL, "3110: SAS Command / Task Management Function failed"}, {0x04670400, 0, IPR_DEFAULT_LOG_LEVEL, "9091: Incorrect hardware configuration change has been detected"}, {0x04678000, 0, IPR_DEFAULT_LOG_LEVEL, "9073: Invalid multi-adapter configuration"}, {0x04678100, 0, IPR_DEFAULT_LOG_LEVEL, "4010: Incorrect connection between cascaded expanders"}, {0x04678200, 0, IPR_DEFAULT_LOG_LEVEL, "4020: Connections exceed IOA design limits"}, {0x04678300, 0, IPR_DEFAULT_LOG_LEVEL, "4030: Incorrect multipath connection"}, {0x04679000, 0, IPR_DEFAULT_LOG_LEVEL, "4110: Unsupported enclosure function"}, {0x046E0000, 0, IPR_DEFAULT_LOG_LEVEL, "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"}, {0x05258000, 0, 0, "Illegal request, commands not allowed to this device"}, {0x05258100, 0, 0, "Illegal request, command not allowed to a secondary adapter"}, {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"}, {0x052C8000, 1, 0, "Illegal request, dual adapter support not enabled"}, {0x06040500, 0, IPR_DEFAULT_LOG_LEVEL, "9031: Array protection temporarily suspended, protection resuming"}, {0x06040600, 0, IPR_DEFAULT_LOG_LEVEL, "9040: Array protection temporarily suspended, protection resuming"}, {0x06288000, 0, IPR_DEFAULT_LOG_LEVEL, "3140: Device bus not ready to ready transition"}, {0x06290000, 0, IPR_DEFAULT_LOG_LEVEL, "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, IPR_DEFAULT_LOG_LEVEL, "FFFB: SCSI bus was reset by another initiator"}, {0x063F0300, 0, IPR_DEFAULT_LOG_LEVEL, "3029: A device replacement has occurred"}, {0x064C8000, 0, IPR_DEFAULT_LOG_LEVEL, "9051: IOA cache data exists for a missing or failed device"}, {0x064C8100, 0, IPR_DEFAULT_LOG_LEVEL, "9055: Auxiliary cache IOA contains cache data needed by the primary IOA"}, {0x06670100, 0, IPR_DEFAULT_LOG_LEVEL, "9025: Disk unit is not supported at its physical location"}, {0x06670600, 0, IPR_DEFAULT_LOG_LEVEL, "3020: IOA detected a SCSI bus configuration error"}, {0x06678000, 0, IPR_DEFAULT_LOG_LEVEL, "3150: SCSI bus configuration error"}, {0x06678100, 0, IPR_DEFAULT_LOG_LEVEL, "9074: Asymmetric advanced function disk configuration"}, {0x06678300, 0, IPR_DEFAULT_LOG_LEVEL, "4040: Incomplete multipath connection between IOA and enclosure"}, {0x06678400, 0, IPR_DEFAULT_LOG_LEVEL, "4041: Incomplete multipath connection between enclosure and device"}, {0x06678500, 0, IPR_DEFAULT_LOG_LEVEL, "9075: Incomplete multipath connection between IOA and remote IOA"}, {0x06678600, 0, IPR_DEFAULT_LOG_LEVEL, "9076: Configuration error, missing remote IOA"}, {0x06679100, 0, IPR_DEFAULT_LOG_LEVEL, "4050: Enclosure does not support a required multipath function"}, {0x06690200, 0, IPR_DEFAULT_LOG_LEVEL, "9041: Array protection temporarily suspended"}, {0x06698200, 0, IPR_DEFAULT_LOG_LEVEL, "9042: Corrupt array parity detected on specified device"}, {0x066B0200, 0, IPR_DEFAULT_LOG_LEVEL, "9030: Array no longer protected due to missing or failed disk unit"}, {0x066B8000, 0, IPR_DEFAULT_LOG_LEVEL, "9071: Link operational transition"}, {0x066B8100, 0, IPR_DEFAULT_LOG_LEVEL, "9072: Link not operational transition"}, {0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL, "9032: Array exposed but still protected"}, {0x066B8300, 0, IPR_DEFAULT_LOG_LEVEL + 1, "70DD: Device forced failed by disrupt device command"}, {0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL, "4061: Multipath redundancy level got better"}, {0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL, "4060: Multipath redundancy level got worse"}, {0x07270000, 0, 0, "Failure due to other device"}, {0x07278000, 0, IPR_DEFAULT_LOG_LEVEL, "9008: IOA does not support functions expected by devices"}, {0x07278100, 0, IPR_DEFAULT_LOG_LEVEL, "9010: Cache data associated with attached devices cannot be found"}, {0x07278200, 0, IPR_DEFAULT_LOG_LEVEL, "9011: Cache data belongs to devices other than those attached"}, {0x07278400, 0, IPR_DEFAULT_LOG_LEVEL, "9020: Array missing 2 or more devices with only 1 device present"}, {0x07278500, 0, IPR_DEFAULT_LOG_LEVEL, "9021: Array missing 2 or more devices with 2 or more devices present"}, {0x07278600, 0, IPR_DEFAULT_LOG_LEVEL, "9022: Exposed array is missing a required device"}, {0x07278700, 0, IPR_DEFAULT_LOG_LEVEL, "9023: Array member(s) not at required physical locations"}, {0x07278800, 0, IPR_DEFAULT_LOG_LEVEL, "9024: Array not functional due to present hardware configuration"}, {0x07278900, 0, IPR_DEFAULT_LOG_LEVEL, "9026: Array not functional due to present hardware configuration"}, {0x07278A00, 0, IPR_DEFAULT_LOG_LEVEL, "9027: Array is missing a device and parity is out of sync"}, {0x07278B00, 0, IPR_DEFAULT_LOG_LEVEL, "9028: Maximum number of arrays already exist"}, {0x07278C00, 0, IPR_DEFAULT_LOG_LEVEL, "9050: Required cache data cannot be located for a disk unit"}, {0x07278D00, 0, IPR_DEFAULT_LOG_LEVEL, "9052: Cache data exists for a device that has been modified"}, {0x07278F00, 0, IPR_DEFAULT_LOG_LEVEL, "9054: IOA resources not available due to previous problems"}, {0x07279100, 0, IPR_DEFAULT_LOG_LEVEL, "9092: Disk unit requires initialization before use"}, {0x07279200, 0, IPR_DEFAULT_LOG_LEVEL, "9029: Incorrect hardware configuration change has been detected"}, {0x07279600, 0, IPR_DEFAULT_LOG_LEVEL, "9060: One or more disk pairs are missing from an array"}, {0x07279700, 0, IPR_DEFAULT_LOG_LEVEL, "9061: One or more disks are missing from an array"}, {0x07279800, 0, IPR_DEFAULT_LOG_LEVEL, "9062: One or more disks are missing from an array"}, {0x07279900, 0, IPR_DEFAULT_LOG_LEVEL, "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->ata_op_code = ipr_cmd->ioarcb.add_data.u.regs.command; trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff; 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; dma_addr_t dma_addr = be32_to_cpu(ioarcb->ioarcb_host_pci_addr); 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; ioarcb->write_ioadl_addr = cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, ioadl)); ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr; ioasa->ioasc = 0; ioasa->residual_data_len = 0; ioasa->u.gata.status = 0; ipr_cmd->scsi_cmd = NULL; ipr_cmd->qc = 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_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) return 0; 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; } } return 0; } /** * ipr_sata_eh_done - done function for aborted SATA commands * @ipr_cmd: ipr command struct * * This function is invoked for ops generated to SATA * devices which are being aborted. * * Return value: * none **/ static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ata_queued_cmd *qc = ipr_cmd->qc; struct ipr_sata_port *sata_port = qc->ap->private_data; qc->err_mask |= AC_ERR_OTHER; sata_port->ioasa.status |= ATA_BUSY; list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); ata_qc_complete(qc); } /** * 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); scsi_dma_unmap(ipr_cmd->scsi_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; else if (ipr_cmd->qc) ipr_cmd->done = ipr_sata_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 = 0; res->in_erp = 0; res->add_to_ml = 0; res->del_from_ml = 0; res->resetting_device = 0; res->sdev = NULL; res->sata_port = 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->del_from_ml = 1; res->cfgte.res_handle = IPR_INVALID_RES_HANDLE; 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); } } /** * strip_and_pad_whitespace - Strip and pad trailing whitespace. * @i: index into buffer * @buf: string to modify * * This function will strip all trailing whitespace, pad the end * of the string with a single space, and NULL terminate the string. * * Return value: * new length of string **/ static int strip_and_pad_whitespace(int i, char *buf) { while (i && buf[i] == ' ') i--; buf[i+1] = ' '; buf[i+2] = '\0'; return i + 2; } /** * ipr_log_vpd_compact - Log the passed extended VPD compactly. * @prefix: string to print at start of printk * @hostrcb: hostrcb pointer * @vpd: vendor/product id/sn struct * * Return value: * none **/ static void ipr_log_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb, struct ipr_vpd *vpd) { char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN + 3]; int i = 0; memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN); i = strip_and_pad_whitespace(IPR_VENDOR_ID_LEN - 1, buffer); memcpy(&buffer[i], vpd->vpids.product_id, IPR_PROD_ID_LEN); i = strip_and_pad_whitespace(i + IPR_PROD_ID_LEN - 1, buffer); memcpy(&buffer[i], vpd->sn, IPR_SERIAL_NUM_LEN); buffer[IPR_SERIAL_NUM_LEN + i] = '\0'; ipr_hcam_err(hostrcb, "%s VPID/SN: %s\n", prefix, buffer); } /** * ipr_log_vpd - Log the passed VPD to the error log. * @vpd: vendor/product id/sn struct * * Return value: * none **/ static void ipr_log_vpd(struct ipr_vpd *vpd) { char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN]; memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN); memcpy(buffer + IPR_VENDOR_ID_LEN, vpd->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, vpd->sn, IPR_SERIAL_NUM_LEN); buffer[IPR_SERIAL_NUM_LEN] = '\0'; ipr_err(" Serial Number: %s\n", buffer); } /** * ipr_log_ext_vpd_compact - Log the passed extended VPD compactly. * @prefix: string to print at start of printk * @hostrcb: hostrcb pointer * @vpd: vendor/product id/sn/wwn struct * * Return value: * none **/ static void ipr_log_ext_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb, struct ipr_ext_vpd *vpd) { ipr_log_vpd_compact(prefix, hostrcb, &vpd->vpd); ipr_hcam_err(hostrcb, "%s WWN: %08X%08X\n", prefix, be32_to_cpu(vpd->wwid[0]), be32_to_cpu(vpd->wwid[1])); } /** * ipr_log_ext_vpd - Log the passed extended VPD to the error log. * @vpd: vendor/product id/sn/wwn struct * * Return value: * none **/ static void ipr_log_ext_vpd(struct ipr_ext_vpd *vpd) { ipr_log_vpd(&vpd->vpd); ipr_err(" WWN: %08X%08X\n", be32_to_cpu(vpd->wwid[0]), be32_to_cpu(vpd->wwid[1])); } /** * ipr_log_enhanced_cache_error - Log a cache error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_cache_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_12_error *error = &hostrcb->hcam.u.error.u.type_12_error; ipr_err("-----Current Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&error->ioa_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&error->cfc_vpd); ipr_err("-----Expected Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&error->ioa_last_attached_to_cfc_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&error->cfc_last_attached_to_ioa_vpd); 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_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_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_vpd(&error->cfc_vpd); ipr_err("-----Expected Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpd); 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_enhanced_config_error - Log a configuration error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_config_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int errors_logged, i; struct ipr_hostrcb_device_data_entry_enhanced *dev_entry; struct ipr_hostrcb_type_13_error *error; error = &hostrcb->hcam.u.error.u.type_13_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; for (i = 0; i < errors_logged; i++, dev_entry++) { ipr_err_separator; ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1); ipr_log_ext_vpd(&dev_entry->vpd); ipr_err("-----New Device Information-----\n"); ipr_log_ext_vpd(&dev_entry->new_vpd); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&dev_entry->ioa_last_with_dev_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&dev_entry->cfc_last_with_dev_vpd); } } /** * 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; for (i = 0; i < errors_logged; i++, dev_entry++) { ipr_err_separator; ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1); ipr_log_vpd(&dev_entry->vpd); ipr_err("-----New Device Information-----\n"); ipr_log_vpd(&dev_entry->new_vpd); ipr_err("Cache Directory Card Information:\n"); ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpd); 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_enhanced_array_error - Log an array configuration error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_array_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int i, num_entries; struct ipr_hostrcb_type_14_error *error; struct ipr_hostrcb_array_data_entry_enhanced *array_entry; const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' }; error = &hostrcb->hcam.u.error.u.type_14_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; num_entries = min_t(u32, be32_to_cpu(error->num_entries), sizeof(error->array_member)); for (i = 0; i < num_entries; i++, array_entry++) { if (!memcmp(array_entry->vpd.vpd.sn, 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_ext_vpd(&array_entry->vpd); ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location"); ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr, "Expected Location"); ipr_err_separator; } } /** * 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->vpd.sn, 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->vpd); ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location"); ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr, "Expected Location"); ipr_err_separator; if (i == 9) array_entry = error->array_member2; else array_entry++; } } /** * ipr_log_hex_data - Log additional hex IOA error data. * @ioa_cfg: ioa config struct * @data: IOA error data * @len: data length * * Return value: * none **/ static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len) { int i; if (len == 0) return; if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL) len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP); for (i = 0; i < len / 4; i += 4) { ipr_err("%08X: %08X %08X %08X %08X\n", i*4, be32_to_cpu(data[i]), be32_to_cpu(data[i+1]), be32_to_cpu(data[i+2]), be32_to_cpu(data[i+3])); } } /** * ipr_log_enhanced_dual_ioa_error - Log an enhanced dual adapter error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_17_error *error; error = &hostrcb->hcam.u.error.u.type_17_error; error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; strstrip(error->failure_reason); ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason, be32_to_cpu(hostrcb->hcam.u.error.prc)); ipr_log_ext_vpd_compact("Remote IOA", hostrcb, &error->vpd); ipr_log_hex_data(ioa_cfg, error->data, be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_17_error, data))); } /** * ipr_log_dual_ioa_error - Log a dual adapter error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_07_error *error; error = &hostrcb->hcam.u.error.u.type_07_error; error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; strstrip(error->failure_reason); ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason, be32_to_cpu(hostrcb->hcam.u.error.prc)); ipr_log_vpd_compact("Remote IOA", hostrcb, &error->vpd); ipr_log_hex_data(ioa_cfg, error->data, be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_07_error, data))); } static const struct { u8 active; char *desc; } path_active_desc[] = { { IPR_PATH_NO_INFO, "Path" }, { IPR_PATH_ACTIVE, "Active path" }, { IPR_PATH_NOT_ACTIVE, "Inactive path" } }; static const struct { u8 state; char *desc; } path_state_desc[] = { { IPR_PATH_STATE_NO_INFO, "has no path state information available" }, { IPR_PATH_HEALTHY, "is healthy" }, { IPR_PATH_DEGRADED, "is degraded" }, { IPR_PATH_FAILED, "is failed" } }; /** * ipr_log_fabric_path - Log a fabric path error * @hostrcb: hostrcb struct * @fabric: fabric descriptor * * Return value: * none **/ static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb, struct ipr_hostrcb_fabric_desc *fabric) { int i, j; u8 path_state = fabric->path_state; u8 active = path_state & IPR_PATH_ACTIVE_MASK; u8 state = path_state & IPR_PATH_STATE_MASK; for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) { if (path_active_desc[i].active != active) continue; for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) { if (path_state_desc[j].state != state) continue; if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port); } else if (fabric->cascaded_expander == 0xff) { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port, fabric->phy); } else if (fabric->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port, fabric->cascaded_expander); } else { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port, fabric->cascaded_expander, fabric->phy); } return; } } ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state, fabric->ioa_port, fabric->cascaded_expander, fabric->phy); } static const struct { u8 type; char *desc; } path_type_desc[] = { { IPR_PATH_CFG_IOA_PORT, "IOA port" }, { IPR_PATH_CFG_EXP_PORT, "Expander port" }, { IPR_PATH_CFG_DEVICE_PORT, "Device port" }, { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" } }; static const struct { u8 status; char *desc; } path_status_desc[] = { { IPR_PATH_CFG_NO_PROB, "Functional" }, { IPR_PATH_CFG_DEGRADED, "Degraded" }, { IPR_PATH_CFG_FAILED, "Failed" }, { IPR_PATH_CFG_SUSPECT, "Suspect" }, { IPR_PATH_NOT_DETECTED, "Missing" }, { IPR_PATH_INCORRECT_CONN, "Incorrectly connected" } }; static const char *link_rate[] = { "unknown", "disabled", "phy reset problem", "spinup hold", "port selector", "unknown", "unknown", "unknown", "1.5Gbps", "3.0Gbps", "unknown", "unknown", "unknown", "unknown", "unknown", "unknown" }; /** * ipr_log_path_elem - Log a fabric path element. * @hostrcb: hostrcb struct * @cfg: fabric path element struct * * Return value: * none **/ static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb, struct ipr_hostrcb_config_element *cfg) { int i, j; u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK; u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK; if (type == IPR_PATH_CFG_NOT_EXIST) return; for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) { if (path_type_desc[i].type != type) continue; for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) { if (path_status_desc[j].status != status) continue; if (type == IPR_PATH_CFG_IOA_PORT) { ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else { if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else if (cfg->cascaded_expander == 0xff) { ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, " "WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else if (cfg->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, " "WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, cfg->cascaded_expander, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else { ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s " "WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } } return; } } ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s " "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } /** * ipr_log_fabric_error - Log a fabric error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_20_error *error; struct ipr_hostrcb_fabric_desc *fabric; struct ipr_hostrcb_config_element *cfg; int i, add_len; error = &hostrcb->hcam.u.error.u.type_20_error; error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; ipr_hcam_err(hostrcb, "%s\n", error->failure_reason); add_len = be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_20_error, desc)); for (i = 0, fabric = error->desc; i < error->num_entries; i++) { ipr_log_fabric_path(hostrcb, fabric); for_each_fabric_cfg(fabric, cfg) ipr_log_path_elem(hostrcb, cfg); add_len -= be16_to_cpu(fabric->length); fabric = (struct ipr_hostrcb_fabric_desc *) ((unsigned long)fabric + be16_to_cpu(fabric->length)); } ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len); } /** * 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) { ipr_log_hex_data(ioa_cfg, hostrcb->hcam.u.raw.data, be32_to_cpu(hostrcb->hcam.length)); } /** * ipr_get_error - Find the specfied IOASC in the ipr_error_table. * @ioasc: IOASC * * This function will return the index of into the ipr_error_table * for the specified IOASC. If the IOASC is not in the table, * 0 will be returned, which points to the entry used for unknown errors. * * Return value: * index into the ipr_error_table **/ static u32 ipr_get_error(u32 ioasc) { int i; for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++) if (ipr_error_table[i].ioasc == (ioasc & IPR_IOASC_IOASC_MASK)) return i; return 0; } /** * ipr_handle_log_data - Log an adapter error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * This function logs an adapter error to the system. * * Return value: * none **/ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { u32 ioasc; int error_index; if (hostrcb->hcam.notify_type != IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY) return; if (hostrcb->hcam.notifications_lost == IPR_HOST_RCB_NOTIFICATIONS_LOST) dev_err(&ioa_cfg->pdev->dev, "Error notifications lost\n"); ioasc = be32_to_cpu(hostrcb->hcam.u.error.failing_dev_ioasc); if (ioasc == IPR_IOASC_BUS_WAS_RESET || ioasc == IPR_IOASC_BUS_WAS_RESET_BY_OTHER) { /* Tell the midlayer we had a bus reset so it will handle the UA properly */ scsi_report_bus_reset(ioa_cfg->host, hostrcb->hcam.u.error.failing_dev_res_addr.bus); } error_index = ipr_get_error(ioasc); if (!ipr_error_table[error_index].log_hcam) return; ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error); /* Set indication we have logged an error */ ioa_cfg->errors_logged++; if (ioa_cfg->log_level < ipr_error_table[error_index].log_hcam) return; if (be32_to_cpu(hostrcb->hcam.length) > sizeof(hostrcb->hcam.u.raw)) hostrcb->hcam.length = cpu_to_be32(sizeof(hostrcb->hcam.u.raw)); switch (hostrcb->hcam.overlay_id) { case IPR_HOST_RCB_OVERLAY_ID_2: ipr_log_cache_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_3: ipr_log_config_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_4: case IPR_HOST_RCB_OVERLAY_ID_6: ipr_log_array_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_7: ipr_log_dual_ioa_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_12: ipr_log_enhanced_cache_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_13: ipr_log_enhanced_config_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_14: case IPR_HOST_RCB_OVERLAY_ID_16: ipr_log_enhanced_array_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_17: ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_20: ipr_log_fabric_error(ioa_cfg, hostrcb); break; case IPR_HOST_RCB_OVERLAY_ID_1: case IPR_HOST_RCB_OVERLAY_ID_DEFAULT: default: ipr_log_generic_error(ioa_cfg, hostrcb); break; } } /** * ipr_process_error - Op done function for an adapter error log. * @ipr_cmd: ipr command struct * * This function is the op done function for an error log host * controlled async from the adapter. It will log the error and * send the HCAM back to the adapter. * * Return value: * none **/ static void ipr_process_error(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); u32 fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.failing_dev_ioasc); list_del(&hostrcb->queue); list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); if (!ioasc) { ipr_handle_log_data(ioa_cfg, hostrcb); if (fd_ioasc == IPR_IOASC_NR_IOA_RESET_REQUIRED) ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_ABBREV); } else 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_LOG_DATA, hostrcb); } /** * ipr_timeout - An internally generated op has timed out. * @ipr_cmd: ipr command struct * * This function blocks host requests and initiates an * adapter reset. * * Return value: * none **/ static void ipr_timeout(struct ipr_cmnd *ipr_cmd) { unsigned long lock_flags = 0; struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; ENTER; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); ioa_cfg->errors_logged++; dev_err(&ioa_cfg->pdev->dev, "Adapter being reset due to command timeout.\n"); if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) ioa_cfg->sdt_state = GET_DUMP; if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd) ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); LEAVE; } /** * ipr_oper_timeout - Adapter timed out transitioning to operational * @ipr_cmd: ipr command struct * * This function blocks host requests and initiates an * adapter reset. * * Return value: * none **/ static void ipr_oper_timeout(struct ipr_cmnd *ipr_cmd) { unsigned long lock_flags = 0; struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; ENTER; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); ioa_cfg->errors_logged++; dev_err(&ioa_cfg->pdev->dev, "Adapter timed out transitioning to operational.\n"); if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) ioa_cfg->sdt_state = GET_DUMP; if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd) { if (ipr_fastfail) ioa_cfg->reset_retries += IPR_NUM_RESET_RELOAD_RETRIES; ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); LEAVE; } /** * ipr_reset_reload - Reset/Reload the IOA * @ioa_cfg: ioa config struct * @shutdown_type: shutdown type * * This function resets the adapter and re-initializes it. * This function assumes that all new host commands have been stopped. * Return value: * SUCCESS / FAILED **/ static int ipr_reset_reload(struct ipr_ioa_cfg *ioa_cfg, enum ipr_shutdown_type shutdown_type) { if (!ioa_cfg->in_reset_reload) ipr_initiate_ioa_reset(ioa_cfg, shutdown_type); spin_unlock_irq(ioa_cfg->host->host_lock); wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); spin_lock_irq(ioa_cfg->host->host_lock); /* If we got hit with a host reset while we were already resetting the adapter for some reason, and the reset failed. */ if (ioa_cfg->ioa_is_dead) { ipr_trace; return FAILED; } return SUCCESS; } /** * ipr_find_ses_entry - Find matching SES in SES table * @res: resource entry struct of SES * * Return value: * pointer to SES table entry / NULL on failure **/ static const struct ipr_ses_table_entry * ipr_find_ses_entry(struct ipr_resource_entry *res) { int i, j, matches; const struct ipr_ses_table_entry *ste = ipr_ses_table; for (i = 0; i < ARRAY_SIZE(ipr_ses_table); i++, ste++) { for (j = 0, matches = 0; j < IPR_PROD_ID_LEN; j++) { if (ste->compare_product_id_byte[j] == 'X') { if (res->cfgte.std_inq_data.vpids.product_id[j] == ste->product_id[j]) matches++; else break; } else matches++; } if (matches == IPR_PROD_ID_LEN) return ste; } return NULL; } /** * ipr_get_max_scsi_speed - Determine max SCSI speed for a given bus * @ioa_cfg: ioa config struct * @bus: SCSI bus * @bus_width: bus width * * Return value: * SCSI bus speed in units of 100KHz, 1600 is 160 MHz * For a 2-byte wide SCSI bus, the maximum transfer speed is * twice the maximum transfer rate (e.g. for a wide enabled bus, * max 160MHz = max 320MB/sec). **/ static u32 ipr_get_max_scsi_speed(struct ipr_ioa_cfg *ioa_cfg, u8 bus, u8 bus_width) { struct ipr_resource_entry *res; const struct ipr_ses_table_entry *ste; u32 max_xfer_rate = IPR_MAX_SCSI_RATE(bus_width); /* Loop through each config table entry in the config table buffer */ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { if (!(IPR_IS_SES_DEVICE(res->cfgte.std_inq_data))) continue; if (bus != res->cfgte.res_addr.bus) continue; if (!(ste = ipr_find_ses_entry(res))) continue; max_xfer_rate = (ste->max_bus_speed_limit * 10) / (bus_width / 8); } return max_xfer_rate; } /** * ipr_wait_iodbg_ack - Wait for an IODEBUG ACK from the IOA * @ioa_cfg: ioa config struct * @max_delay: max delay in micro-seconds to wait * * Waits for an IODEBUG ACK from the IOA, doing busy looping. * * Return value: * 0 on success / other on failure **/ static int ipr_wait_iodbg_ack(struct ipr_ioa_cfg *ioa_cfg, int max_delay) { volatile u32 pcii_reg; int delay = 1; /* Read interrupt reg until IOA signals IO Debug Acknowledge */ while (delay < max_delay) { pcii_reg = readl(ioa_cfg->regs.sense_interrupt_reg); if (pcii_reg & IPR_PCII_IO_DEBUG_ACKNOWLEDGE) return 0; /* udelay cannot be used if delay is more than a few milliseconds */ if ((delay / 1000) > MAX_UDELAY_MS) mdelay(delay / 1000); else udelay(delay); delay += delay; } return -EIO; } /** * ipr_get_ldump_data_section - Dump IOA memory * @ioa_cfg: ioa config struct * @start_addr: adapter address to dump * @dest: destination kernel buffer * @length_in_words: length to dump in 4 byte words * * Return value: * 0 on success / -EIO on failure **/ static int ipr_get_ldump_data_section(struct ipr_ioa_cfg *ioa_cfg, u32 start_addr, __be32 *dest, u32 length_in_words) { volatile u32 temp_pcii_reg; int i, delay = 0; /* Write IOA interrupt reg starting LDUMP state */ writel((IPR_UPROCI_RESET_ALERT | IPR_UPROCI_IO_DEBUG_ALERT), ioa_cfg->regs.set_uproc_interrupt_reg); /* Wait for IO debug acknowledge */ if (ipr_wait_iodbg_ack(ioa_cfg, IPR_LDUMP_MAX_LONG_ACK_DELAY_IN_USEC)) { dev_err(&ioa_cfg->pdev->dev, "IOA dump long data transfer timeout\n"); return -EIO; } /* Signal LDUMP interlocked - clear IO debug ack */ writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_reg); /* Write Mailbox with starting address */ writel(start_addr, ioa_cfg->ioa_mailbox); /* Signal address valid - clear IOA Reset alert */ writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.clr_uproc_interrupt_reg); for (i = 0; i < length_in_words; i++) { /* Wait for IO debug acknowledge */ if (ipr_wait_iodbg_ack(ioa_cfg, IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC)) { dev_err(&ioa_cfg->pdev->dev, "IOA dump short data transfer timeout\n"); return -EIO; } /* Read data from mailbox and increment destination pointer */ *dest = cpu_to_be32(readl(ioa_cfg->ioa_mailbox)); dest++; /* For all but the last word of data, signal data received */ if (i < (length_in_words - 1)) { /* Signal dump data received - Clear IO debug Ack */ writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_reg); } } /* Signal end of block transfer. Set reset alert then clear IO debug ack */ writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.set_uproc_interrupt_reg); writel(IPR_UPROCI_IO_DEBUG_ALERT, ioa_cfg->regs.clr_uproc_interrupt_reg); /* Signal dump data received - Clear IO debug Ack */ writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_reg); /* Wait for IOA to signal LDUMP exit - IOA reset alert will be cleared */ while (delay < IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC) { temp_pcii_reg = readl(ioa_cfg->regs.sense_uproc_interrupt_reg); if (!(temp_pcii_reg & IPR_UPROCI_RESET_ALERT)) return 0; udelay(10); delay += 10; } return 0; } #ifdef CONFIG_SCSI_IPR_DUMP /** * ipr_sdt_copy - Copy Smart Dump Table to kernel buffer * @ioa_cfg: ioa config struct * @pci_address: adapter address * @length: length of data to copy * * Copy data from PCI adapter to kernel buffer. * Note: length MUST be a 4 byte multiple * Return value: * 0 on success / other on failure **/ static int ipr_sdt_copy(struct ipr_ioa_cfg *ioa_cfg, unsigned long pci_address, u32 length) { int bytes_copied = 0; int cur_len, rc, rem_len, rem_page_len; __be32 *page; unsigned long lock_flags = 0; struct ipr_ioa_dump *ioa_dump = &ioa_cfg->dump->ioa_dump; while (bytes_copied < length && (ioa_dump->hdr.len + bytes_copied) < IPR_MAX_IOA_DUMP_SIZE) { if (ioa_dump->page_offset >= PAGE_SIZE || ioa_dump->page_offset == 0) { page = (__be32 *)__get_free_page(GFP_ATOMIC); if (!page) { ipr_trace; return bytes_copied; } ioa_dump->page_offset = 0; ioa_dump->ioa_data[ioa_dump->next_page_index] = page; ioa_dump->next_page_index++; } else page = ioa_dump->ioa_data[ioa_dump->next_page_index - 1]; rem_len = length - bytes_copied; rem_page_len = PAGE_SIZE - ioa_dump->page_offset; cur_len = min(rem_len, rem_page_len); spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); if (ioa_cfg->sdt_state == ABORT_DUMP) { rc = -EIO; } else { rc = ipr_get_ldump_data_section(ioa_cfg, pci_address + bytes_copied, &page[ioa_dump->page_offset / 4], (cur_len / sizeof(u32))); } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); if (!rc) { ioa_dump->page_offset += cur_len; bytes_copied += cur_len; } else { ipr_trace; break; } schedule(); } return bytes_copied; } /** * ipr_init_dump_entry_hdr - Initialize a dump entry header. * @hdr: dump entry header struct * * Return value: * nothing **/ static void ipr_init_dump_entry_hdr(struct ipr_dump_entry_header *hdr) { hdr->eye_catcher = IPR_DUMP_EYE_CATCHER; hdr->num_elems = 1; hdr->offset = sizeof(*hdr); hdr->status = IPR_DUMP_STATUS_SUCCESS; } /** * ipr_dump_ioa_type_data - Fill in the adapter type in the dump. * @ioa_cfg: ioa config struct * @driver_dump: driver dump struct * * Return value: * nothing **/ static void ipr_dump_ioa_type_data(struct ipr_ioa_cfg *ioa_cfg, struct ipr_driver_dump *driver_dump) { struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data; ipr_init_dump_entry_hdr(&driver_dump->ioa_type_entry.hdr); driver_dump->ioa_type_entry.hdr.len = sizeof(struct ipr_dump_ioa_type_entry) - sizeof(struct ipr_dump_entry_header); driver_dump->ioa_type_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY; driver_dump->ioa_type_entry.hdr.id = IPR_DUMP_DRIVER_TYPE_ID; driver_dump->ioa_type_entry.type = ioa_cfg->type; driver_dump->ioa_type_entry.fw_version = (ucode_vpd->major_release << 24) | (ucode_vpd->card_type << 16) | (ucode_vpd->minor_release[0] << 8) | ucode_vpd->minor_release[1]; driver_dump->hdr.num_entries++; } /** * ipr_dump_version_data - Fill in the driver version in the dump. * @ioa_cfg: ioa config struct * @driver_dump: driver dump struct * * Return value: * nothing **/ static void ipr_dump_version_data(struct ipr_ioa_cfg *ioa_cfg, struct ipr_driver_dump *driver_dump) { ipr_init_dump_entry_hdr(&driver_dump->version_entry.hdr); driver_dump->version_entry.hdr.len = sizeof(struct ipr_dump_version_entry) - sizeof(struct ipr_dump_entry_header); driver_dump->version_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII; driver_dump->version_entry.hdr.id = IPR_DUMP_DRIVER_VERSION_ID; strcpy(driver_dump->version_entry.version, IPR_DRIVER_VERSION); driver_dump->hdr.num_entries++; } /** * ipr_dump_trace_data - Fill in the IOA trace in the dump. * @ioa_cfg: ioa config struct * @driver_dump: driver dump struct * * Return value: * nothing **/ static void ipr_dump_trace_data(struct ipr_ioa_cfg *ioa_cfg, struct ipr_driver_dum