xref: /openbmc/linux/drivers/scsi/ipr.c (revision 637b5c3ebc1c2ca4f802fa2def950fed1f5877e6)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * ipr.c -- driver for IBM Power Linux RAID adapters
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Written By: Brian King <brking@us.ibm.com>, IBM Corporation
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 2003, 2004 IBM Corporation
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds /*
111da177e4SLinus Torvalds  * Notes:
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * This driver is used to control the following SCSI adapters:
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * IBM iSeries: 5702, 5703, 2780, 5709, 570A, 570B
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  * IBM pSeries: PCI-X Dual Channel Ultra 320 SCSI RAID Adapter
181da177e4SLinus Torvalds  *              PCI-X Dual Channel Ultra 320 SCSI Adapter
191da177e4SLinus Torvalds  *              PCI-X Dual Channel Ultra 320 SCSI RAID Enablement Card
201da177e4SLinus Torvalds  *              Embedded SCSI adapter on p615 and p655 systems
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  * Supported Hardware Features:
231da177e4SLinus Torvalds  *	- Ultra 320 SCSI controller
241da177e4SLinus Torvalds  *	- PCI-X host interface
251da177e4SLinus Torvalds  *	- Embedded PowerPC RISC Processor and Hardware XOR DMA Engine
261da177e4SLinus Torvalds  *	- Non-Volatile Write Cache
271da177e4SLinus Torvalds  *	- Supports attachment of non-RAID disks, tape, and optical devices
281da177e4SLinus Torvalds  *	- RAID Levels 0, 5, 10
291da177e4SLinus Torvalds  *	- Hot spare
301da177e4SLinus Torvalds  *	- Background Parity Checking
311da177e4SLinus Torvalds  *	- Background Data Scrubbing
321da177e4SLinus Torvalds  *	- Ability to increase the capacity of an existing RAID 5 disk array
331da177e4SLinus Torvalds  *		by adding disks
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  * Driver Features:
361da177e4SLinus Torvalds  *	- Tagged command queuing
371da177e4SLinus Torvalds  *	- Adapter microcode download
381da177e4SLinus Torvalds  *	- PCI hot plug
391da177e4SLinus Torvalds  *	- SCSI device hot plug
401da177e4SLinus Torvalds  *
411da177e4SLinus Torvalds  */
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #include <linux/fs.h>
441da177e4SLinus Torvalds #include <linux/init.h>
451da177e4SLinus Torvalds #include <linux/types.h>
461da177e4SLinus Torvalds #include <linux/errno.h>
471da177e4SLinus Torvalds #include <linux/kernel.h>
485a0e3ad6STejun Heo #include <linux/slab.h>
494d4dd706SKleber Sacilotto de Souza #include <linux/vmalloc.h>
501da177e4SLinus Torvalds #include <linux/ioport.h>
511da177e4SLinus Torvalds #include <linux/delay.h>
521da177e4SLinus Torvalds #include <linux/pci.h>
531da177e4SLinus Torvalds #include <linux/wait.h>
541da177e4SLinus Torvalds #include <linux/spinlock.h>
551da177e4SLinus Torvalds #include <linux/sched.h>
561da177e4SLinus Torvalds #include <linux/interrupt.h>
571da177e4SLinus Torvalds #include <linux/blkdev.h>
581da177e4SLinus Torvalds #include <linux/firmware.h>
591da177e4SLinus Torvalds #include <linux/module.h>
601da177e4SLinus Torvalds #include <linux/moduleparam.h>
6135a39691SBrian King #include <linux/libata.h>
620ce3a7e5SBrian King #include <linux/hdreg.h>
63f72919ecSWayne Boyer #include <linux/reboot.h>
643e7ebdfaSWayne Boyer #include <linux/stringify.h>
651da177e4SLinus Torvalds #include <asm/io.h>
661da177e4SLinus Torvalds #include <asm/irq.h>
671da177e4SLinus Torvalds #include <asm/processor.h>
681da177e4SLinus Torvalds #include <scsi/scsi.h>
691da177e4SLinus Torvalds #include <scsi/scsi_host.h>
701da177e4SLinus Torvalds #include <scsi/scsi_tcq.h>
711da177e4SLinus Torvalds #include <scsi/scsi_eh.h>
721da177e4SLinus Torvalds #include <scsi/scsi_cmnd.h>
731da177e4SLinus Torvalds #include "ipr.h"
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds /*
761da177e4SLinus Torvalds  *   Global Data
771da177e4SLinus Torvalds  */
78b7d68ca3SDenis Cheng static LIST_HEAD(ipr_ioa_head);
791da177e4SLinus Torvalds static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL;
801da177e4SLinus Torvalds static unsigned int ipr_max_speed = 1;
811da177e4SLinus Torvalds static int ipr_testmode = 0;
821da177e4SLinus Torvalds static unsigned int ipr_fastfail = 0;
835469cb5bSBrian King static unsigned int ipr_transop_timeout = 0;
84d3c74871Sbrking@us.ibm.com static unsigned int ipr_debug = 0;
853e7ebdfaSWayne Boyer static unsigned int ipr_max_devs = IPR_DEFAULT_SIS64_DEVS;
86ac09c349SBrian King static unsigned int ipr_dual_ioa_raid = 1;
87cb05cbb3SWen Xiong static unsigned int ipr_number_of_msix = 16;
884fdd7c7aSBrian King static unsigned int ipr_fast_reboot;
891da177e4SLinus Torvalds static DEFINE_SPINLOCK(ipr_driver_lock);
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds /* This table describes the differences between DMA controller chips */
921da177e4SLinus Torvalds static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
9360e7486bSBrian King 	{ /* Gemstone, Citrine, Obsidian, and Obsidian-E */
941da177e4SLinus Torvalds 		.mailbox = 0x0042C,
9589aad428SBrian King 		.max_cmds = 100,
961da177e4SLinus Torvalds 		.cache_line_size = 0x20,
977dd21308SBrian King 		.clear_isr = 1,
98b53d124aSwenxiong@linux.vnet.ibm.com 		.iopoll_weight = 0,
991da177e4SLinus Torvalds 		{
1001da177e4SLinus Torvalds 			.set_interrupt_mask_reg = 0x0022C,
1011da177e4SLinus Torvalds 			.clr_interrupt_mask_reg = 0x00230,
102214777baSWayne Boyer 			.clr_interrupt_mask_reg32 = 0x00230,
1031da177e4SLinus Torvalds 			.sense_interrupt_mask_reg = 0x0022C,
104214777baSWayne Boyer 			.sense_interrupt_mask_reg32 = 0x0022C,
1051da177e4SLinus Torvalds 			.clr_interrupt_reg = 0x00228,
106214777baSWayne Boyer 			.clr_interrupt_reg32 = 0x00228,
1071da177e4SLinus Torvalds 			.sense_interrupt_reg = 0x00224,
108214777baSWayne Boyer 			.sense_interrupt_reg32 = 0x00224,
1091da177e4SLinus Torvalds 			.ioarrin_reg = 0x00404,
1101da177e4SLinus Torvalds 			.sense_uproc_interrupt_reg = 0x00214,
111214777baSWayne Boyer 			.sense_uproc_interrupt_reg32 = 0x00214,
1121da177e4SLinus Torvalds 			.set_uproc_interrupt_reg = 0x00214,
113214777baSWayne Boyer 			.set_uproc_interrupt_reg32 = 0x00214,
114214777baSWayne Boyer 			.clr_uproc_interrupt_reg = 0x00218,
115214777baSWayne Boyer 			.clr_uproc_interrupt_reg32 = 0x00218
1161da177e4SLinus Torvalds 		}
1171da177e4SLinus Torvalds 	},
1181da177e4SLinus Torvalds 	{ /* Snipe and Scamp */
1191da177e4SLinus Torvalds 		.mailbox = 0x0052C,
12089aad428SBrian King 		.max_cmds = 100,
1211da177e4SLinus Torvalds 		.cache_line_size = 0x20,
1227dd21308SBrian King 		.clear_isr = 1,
123b53d124aSwenxiong@linux.vnet.ibm.com 		.iopoll_weight = 0,
1241da177e4SLinus Torvalds 		{
1251da177e4SLinus Torvalds 			.set_interrupt_mask_reg = 0x00288,
1261da177e4SLinus Torvalds 			.clr_interrupt_mask_reg = 0x0028C,
127214777baSWayne Boyer 			.clr_interrupt_mask_reg32 = 0x0028C,
1281da177e4SLinus Torvalds 			.sense_interrupt_mask_reg = 0x00288,
129214777baSWayne Boyer 			.sense_interrupt_mask_reg32 = 0x00288,
1301da177e4SLinus Torvalds 			.clr_interrupt_reg = 0x00284,
131214777baSWayne Boyer 			.clr_interrupt_reg32 = 0x00284,
1321da177e4SLinus Torvalds 			.sense_interrupt_reg = 0x00280,
133214777baSWayne Boyer 			.sense_interrupt_reg32 = 0x00280,
1341da177e4SLinus Torvalds 			.ioarrin_reg = 0x00504,
1351da177e4SLinus Torvalds 			.sense_uproc_interrupt_reg = 0x00290,
136214777baSWayne Boyer 			.sense_uproc_interrupt_reg32 = 0x00290,
1371da177e4SLinus Torvalds 			.set_uproc_interrupt_reg = 0x00290,
138214777baSWayne Boyer 			.set_uproc_interrupt_reg32 = 0x00290,
139214777baSWayne Boyer 			.clr_uproc_interrupt_reg = 0x00294,
140214777baSWayne Boyer 			.clr_uproc_interrupt_reg32 = 0x00294
1411da177e4SLinus Torvalds 		}
1421da177e4SLinus Torvalds 	},
143a74c1639SWayne Boyer 	{ /* CRoC */
144110def85SWayne Boyer 		.mailbox = 0x00044,
14589aad428SBrian King 		.max_cmds = 1000,
146a74c1639SWayne Boyer 		.cache_line_size = 0x20,
1477dd21308SBrian King 		.clear_isr = 0,
148b53d124aSwenxiong@linux.vnet.ibm.com 		.iopoll_weight = 64,
149a74c1639SWayne Boyer 		{
150a74c1639SWayne Boyer 			.set_interrupt_mask_reg = 0x00010,
151a74c1639SWayne Boyer 			.clr_interrupt_mask_reg = 0x00018,
152214777baSWayne Boyer 			.clr_interrupt_mask_reg32 = 0x0001C,
153a74c1639SWayne Boyer 			.sense_interrupt_mask_reg = 0x00010,
154214777baSWayne Boyer 			.sense_interrupt_mask_reg32 = 0x00014,
155a74c1639SWayne Boyer 			.clr_interrupt_reg = 0x00008,
156214777baSWayne Boyer 			.clr_interrupt_reg32 = 0x0000C,
157a74c1639SWayne Boyer 			.sense_interrupt_reg = 0x00000,
158214777baSWayne Boyer 			.sense_interrupt_reg32 = 0x00004,
159a74c1639SWayne Boyer 			.ioarrin_reg = 0x00070,
160a74c1639SWayne Boyer 			.sense_uproc_interrupt_reg = 0x00020,
161214777baSWayne Boyer 			.sense_uproc_interrupt_reg32 = 0x00024,
162a74c1639SWayne Boyer 			.set_uproc_interrupt_reg = 0x00020,
163214777baSWayne Boyer 			.set_uproc_interrupt_reg32 = 0x00024,
164dcbad00eSWayne Boyer 			.clr_uproc_interrupt_reg = 0x00028,
165214777baSWayne Boyer 			.clr_uproc_interrupt_reg32 = 0x0002C,
166214777baSWayne Boyer 			.init_feedback_reg = 0x0005C,
167dcbad00eSWayne Boyer 			.dump_addr_reg = 0x00064,
1688701f185SWayne Boyer 			.dump_data_reg = 0x00068,
1698701f185SWayne Boyer 			.endian_swap_reg = 0x00084
170a74c1639SWayne Boyer 		}
171a74c1639SWayne Boyer 	},
1721da177e4SLinus Torvalds };
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds static const struct ipr_chip_t ipr_chip[] = {
175a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
176a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
177a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
178a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
179a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, true, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
180a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
181a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
182a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, true, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] },
183a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, true, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] },
184a299ee62SChristoph Hellwig 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_RATTLESNAKE, true, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
1851da177e4SLinus Torvalds };
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds static int ipr_max_bus_speeds[] = {
1881da177e4SLinus Torvalds 	IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
1891da177e4SLinus Torvalds };
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds MODULE_AUTHOR("Brian King <brking@us.ibm.com>");
1921da177e4SLinus Torvalds MODULE_DESCRIPTION("IBM Power RAID SCSI Adapter Driver");
1931da177e4SLinus Torvalds module_param_named(max_speed, ipr_max_speed, uint, 0);
1941da177e4SLinus Torvalds MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320");
1951da177e4SLinus Torvalds module_param_named(log_level, ipr_log_level, uint, 0);
1961da177e4SLinus Torvalds MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver");
1971da177e4SLinus Torvalds module_param_named(testmode, ipr_testmode, int, 0);
1981da177e4SLinus Torvalds MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations");
1992cf22be0SWayne Boyer module_param_named(fastfail, ipr_fastfail, int, S_IRUGO | S_IWUSR);
2001da177e4SLinus Torvalds MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries");
2011da177e4SLinus Torvalds module_param_named(transop_timeout, ipr_transop_timeout, int, 0);
2021da177e4SLinus Torvalds MODULE_PARM_DESC(transop_timeout, "Time in seconds to wait for adapter to come operational (default: 300)");
2032cf22be0SWayne Boyer module_param_named(debug, ipr_debug, int, S_IRUGO | S_IWUSR);
204d3c74871Sbrking@us.ibm.com MODULE_PARM_DESC(debug, "Enable device driver debugging logging. Set to 1 to enable. (default: 0)");
205ac09c349SBrian King module_param_named(dual_ioa_raid, ipr_dual_ioa_raid, int, 0);
206ac09c349SBrian King MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to enable. (default: 1)");
2073e7ebdfaSWayne Boyer module_param_named(max_devs, ipr_max_devs, int, 0);
2083e7ebdfaSWayne Boyer MODULE_PARM_DESC(max_devs, "Specify the maximum number of physical devices. "
2093e7ebdfaSWayne Boyer 		 "[Default=" __stringify(IPR_DEFAULT_SIS64_DEVS) "]");
21005a6538aSwenxiong@linux.vnet.ibm.com module_param_named(number_of_msix, ipr_number_of_msix, int, 0);
211cb05cbb3SWen Xiong MODULE_PARM_DESC(number_of_msix, "Specify the number of MSIX interrupts to use on capable adapters (1 - 16).  (default:16)");
2124fdd7c7aSBrian King module_param_named(fast_reboot, ipr_fast_reboot, int, S_IRUGO | S_IWUSR);
2134fdd7c7aSBrian King MODULE_PARM_DESC(fast_reboot, "Skip adapter shutdown during reboot. Set to 1 to enable. (default: 0)");
2141da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2151da177e4SLinus Torvalds MODULE_VERSION(IPR_DRIVER_VERSION);
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /*  A constant array of IOASCs/URCs/Error Messages */
2181da177e4SLinus Torvalds static const
2191da177e4SLinus Torvalds struct ipr_error_table_t ipr_error_table[] = {
220933916f3SBrian King 	{0x00000000, 1, IPR_DEFAULT_LOG_LEVEL,
2211da177e4SLinus Torvalds 	"8155: An unknown error was received"},
2221da177e4SLinus Torvalds 	{0x00330000, 0, 0,
2231da177e4SLinus Torvalds 	"Soft underlength error"},
2241da177e4SLinus Torvalds 	{0x005A0000, 0, 0,
2251da177e4SLinus Torvalds 	"Command to be cancelled not found"},
2261da177e4SLinus Torvalds 	{0x00808000, 0, 0,
2271da177e4SLinus Torvalds 	"Qualified success"},
228933916f3SBrian King 	{0x01080000, 1, IPR_DEFAULT_LOG_LEVEL,
2291da177e4SLinus Torvalds 	"FFFE: Soft device bus error recovered by the IOA"},
230933916f3SBrian King 	{0x01088100, 0, IPR_DEFAULT_LOG_LEVEL,
231896bbd21SBrian King 	"4101: Soft device bus fabric error"},
2325aa3a333SWayne Boyer 	{0x01100100, 0, IPR_DEFAULT_LOG_LEVEL,
2335aa3a333SWayne Boyer 	"FFFC: Logical block guard error recovered by the device"},
2345aa3a333SWayne Boyer 	{0x01100300, 0, IPR_DEFAULT_LOG_LEVEL,
2355aa3a333SWayne Boyer 	"FFFC: Logical block reference tag error recovered by the device"},
2365aa3a333SWayne Boyer 	{0x01108300, 0, IPR_DEFAULT_LOG_LEVEL,
2375aa3a333SWayne Boyer 	"4171: Recovered scatter list tag / sequence number error"},
2385aa3a333SWayne Boyer 	{0x01109000, 0, IPR_DEFAULT_LOG_LEVEL,
2395aa3a333SWayne Boyer 	"FF3D: Recovered logical block CRC error on IOA to Host transfer"},
2405aa3a333SWayne Boyer 	{0x01109200, 0, IPR_DEFAULT_LOG_LEVEL,
2415aa3a333SWayne Boyer 	"4171: Recovered logical block sequence number error on IOA to Host transfer"},
2425aa3a333SWayne Boyer 	{0x0110A000, 0, IPR_DEFAULT_LOG_LEVEL,
2435aa3a333SWayne Boyer 	"FFFD: Recovered logical block reference tag error detected by the IOA"},
2445aa3a333SWayne Boyer 	{0x0110A100, 0, IPR_DEFAULT_LOG_LEVEL,
2455aa3a333SWayne Boyer 	"FFFD: Logical block guard error recovered by the IOA"},
246933916f3SBrian King 	{0x01170600, 0, IPR_DEFAULT_LOG_LEVEL,
2471da177e4SLinus Torvalds 	"FFF9: Device sector reassign successful"},
248933916f3SBrian King 	{0x01170900, 0, IPR_DEFAULT_LOG_LEVEL,
2491da177e4SLinus Torvalds 	"FFF7: Media error recovered by device rewrite procedures"},
250933916f3SBrian King 	{0x01180200, 0, IPR_DEFAULT_LOG_LEVEL,
2511da177e4SLinus Torvalds 	"7001: IOA sector reassignment successful"},
252933916f3SBrian King 	{0x01180500, 0, IPR_DEFAULT_LOG_LEVEL,
2531da177e4SLinus Torvalds 	"FFF9: Soft media error. Sector reassignment recommended"},
254933916f3SBrian King 	{0x01180600, 0, IPR_DEFAULT_LOG_LEVEL,
2551da177e4SLinus Torvalds 	"FFF7: Media error recovered by IOA rewrite procedures"},
256933916f3SBrian King 	{0x01418000, 0, IPR_DEFAULT_LOG_LEVEL,
2571da177e4SLinus Torvalds 	"FF3D: Soft PCI bus error recovered by the IOA"},
258933916f3SBrian King 	{0x01440000, 1, IPR_DEFAULT_LOG_LEVEL,
2591da177e4SLinus Torvalds 	"FFF6: Device hardware error recovered by the IOA"},
260933916f3SBrian King 	{0x01448100, 0, IPR_DEFAULT_LOG_LEVEL,
2611da177e4SLinus Torvalds 	"FFF6: Device hardware error recovered by the device"},
262933916f3SBrian King 	{0x01448200, 1, IPR_DEFAULT_LOG_LEVEL,
2631da177e4SLinus Torvalds 	"FF3D: Soft IOA error recovered by the IOA"},
264933916f3SBrian King 	{0x01448300, 0, IPR_DEFAULT_LOG_LEVEL,
2651da177e4SLinus Torvalds 	"FFFA: Undefined device response recovered by the IOA"},
266933916f3SBrian King 	{0x014A0000, 1, IPR_DEFAULT_LOG_LEVEL,
2671da177e4SLinus Torvalds 	"FFF6: Device bus error, message or command phase"},
268933916f3SBrian King 	{0x014A8000, 0, IPR_DEFAULT_LOG_LEVEL,
26935a39691SBrian King 	"FFFE: Task Management Function failed"},
270933916f3SBrian King 	{0x015D0000, 0, IPR_DEFAULT_LOG_LEVEL,
2711da177e4SLinus Torvalds 	"FFF6: Failure prediction threshold exceeded"},
272933916f3SBrian King 	{0x015D9200, 0, IPR_DEFAULT_LOG_LEVEL,
2731da177e4SLinus Torvalds 	"8009: Impending cache battery pack failure"},
274ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x02040100, 0, 0,
275ed7bd661Swenxiong@linux.vnet.ibm.com 	"Logical Unit in process of becoming ready"},
276ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x02040200, 0, 0,
277ed7bd661Swenxiong@linux.vnet.ibm.com 	"Initializing command required"},
2781da177e4SLinus Torvalds 	{0x02040400, 0, 0,
2791da177e4SLinus Torvalds 	"34FF: Disk device format in progress"},
280ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x02040C00, 0, 0,
281ed7bd661Swenxiong@linux.vnet.ibm.com 	"Logical unit not accessible, target port in unavailable state"},
28265f56475SBrian King 	{0x02048000, 0, IPR_DEFAULT_LOG_LEVEL,
28365f56475SBrian King 	"9070: IOA requested reset"},
2841da177e4SLinus Torvalds 	{0x023F0000, 0, 0,
2851da177e4SLinus Torvalds 	"Synchronization required"},
286ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x02408500, 0, 0,
287ed7bd661Swenxiong@linux.vnet.ibm.com 	"IOA microcode download required"},
288ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x02408600, 0, 0,
289ed7bd661Swenxiong@linux.vnet.ibm.com 	"Device bus connection is prohibited by host"},
2901da177e4SLinus Torvalds 	{0x024E0000, 0, 0,
2911da177e4SLinus Torvalds 	"No ready, IOA shutdown"},
2921da177e4SLinus Torvalds 	{0x025A0000, 0, 0,
2931da177e4SLinus Torvalds 	"Not ready, IOA has been shutdown"},
294933916f3SBrian King 	{0x02670100, 0, IPR_DEFAULT_LOG_LEVEL,
2951da177e4SLinus Torvalds 	"3020: Storage subsystem configuration error"},
2961da177e4SLinus Torvalds 	{0x03110B00, 0, 0,
2971da177e4SLinus Torvalds 	"FFF5: Medium error, data unreadable, recommend reassign"},
2981da177e4SLinus Torvalds 	{0x03110C00, 0, 0,
2991da177e4SLinus Torvalds 	"7000: Medium error, data unreadable, do not reassign"},
300933916f3SBrian King 	{0x03310000, 0, IPR_DEFAULT_LOG_LEVEL,
3011da177e4SLinus Torvalds 	"FFF3: Disk media format bad"},
302933916f3SBrian King 	{0x04050000, 0, IPR_DEFAULT_LOG_LEVEL,
3031da177e4SLinus Torvalds 	"3002: Addressed device failed to respond to selection"},
304933916f3SBrian King 	{0x04080000, 1, IPR_DEFAULT_LOG_LEVEL,
3051da177e4SLinus Torvalds 	"3100: Device bus error"},
306933916f3SBrian King 	{0x04080100, 0, IPR_DEFAULT_LOG_LEVEL,
3071da177e4SLinus Torvalds 	"3109: IOA timed out a device command"},
3081da177e4SLinus Torvalds 	{0x04088000, 0, 0,
3091da177e4SLinus Torvalds 	"3120: SCSI bus is not operational"},
310933916f3SBrian King 	{0x04088100, 0, IPR_DEFAULT_LOG_LEVEL,
311896bbd21SBrian King 	"4100: Hard device bus fabric error"},
3125aa3a333SWayne Boyer 	{0x04100100, 0, IPR_DEFAULT_LOG_LEVEL,
3135aa3a333SWayne Boyer 	"310C: Logical block guard error detected by the device"},
3145aa3a333SWayne Boyer 	{0x04100300, 0, IPR_DEFAULT_LOG_LEVEL,
3155aa3a333SWayne Boyer 	"310C: Logical block reference tag error detected by the device"},
3165aa3a333SWayne Boyer 	{0x04108300, 1, IPR_DEFAULT_LOG_LEVEL,
3175aa3a333SWayne Boyer 	"4170: Scatter list tag / sequence number error"},
3185aa3a333SWayne Boyer 	{0x04109000, 1, IPR_DEFAULT_LOG_LEVEL,
3195aa3a333SWayne Boyer 	"8150: Logical block CRC error on IOA to Host transfer"},
3205aa3a333SWayne Boyer 	{0x04109200, 1, IPR_DEFAULT_LOG_LEVEL,
3215aa3a333SWayne Boyer 	"4170: Logical block sequence number error on IOA to Host transfer"},
3225aa3a333SWayne Boyer 	{0x0410A000, 0, IPR_DEFAULT_LOG_LEVEL,
3235aa3a333SWayne Boyer 	"310D: Logical block reference tag error detected by the IOA"},
3245aa3a333SWayne Boyer 	{0x0410A100, 0, IPR_DEFAULT_LOG_LEVEL,
3255aa3a333SWayne Boyer 	"310D: Logical block guard error detected by the IOA"},
326933916f3SBrian King 	{0x04118000, 0, IPR_DEFAULT_LOG_LEVEL,
3271da177e4SLinus Torvalds 	"9000: IOA reserved area data check"},
328933916f3SBrian King 	{0x04118100, 0, IPR_DEFAULT_LOG_LEVEL,
3291da177e4SLinus Torvalds 	"9001: IOA reserved area invalid data pattern"},
330933916f3SBrian King 	{0x04118200, 0, IPR_DEFAULT_LOG_LEVEL,
3311da177e4SLinus Torvalds 	"9002: IOA reserved area LRC error"},
3325aa3a333SWayne Boyer 	{0x04118300, 1, IPR_DEFAULT_LOG_LEVEL,
3335aa3a333SWayne Boyer 	"Hardware Error, IOA metadata access error"},
334933916f3SBrian King 	{0x04320000, 0, IPR_DEFAULT_LOG_LEVEL,
3351da177e4SLinus Torvalds 	"102E: Out of alternate sectors for disk storage"},
336933916f3SBrian King 	{0x04330000, 1, IPR_DEFAULT_LOG_LEVEL,
3371da177e4SLinus Torvalds 	"FFF4: Data transfer underlength error"},
338933916f3SBrian King 	{0x04338000, 1, IPR_DEFAULT_LOG_LEVEL,
3391da177e4SLinus Torvalds 	"FFF4: Data transfer overlength error"},
340933916f3SBrian King 	{0x043E0100, 0, IPR_DEFAULT_LOG_LEVEL,
3411da177e4SLinus Torvalds 	"3400: Logical unit failure"},
342933916f3SBrian King 	{0x04408500, 0, IPR_DEFAULT_LOG_LEVEL,
3431da177e4SLinus Torvalds 	"FFF4: Device microcode is corrupt"},
344933916f3SBrian King 	{0x04418000, 1, IPR_DEFAULT_LOG_LEVEL,
3451da177e4SLinus Torvalds 	"8150: PCI bus error"},
3461da177e4SLinus Torvalds 	{0x04430000, 1, 0,
3471da177e4SLinus Torvalds 	"Unsupported device bus message received"},
348933916f3SBrian King 	{0x04440000, 1, IPR_DEFAULT_LOG_LEVEL,
3491da177e4SLinus Torvalds 	"FFF4: Disk device problem"},
350933916f3SBrian King 	{0x04448200, 1, IPR_DEFAULT_LOG_LEVEL,
3511da177e4SLinus Torvalds 	"8150: Permanent IOA failure"},
352933916f3SBrian King 	{0x04448300, 0, IPR_DEFAULT_LOG_LEVEL,
3531da177e4SLinus Torvalds 	"3010: Disk device returned wrong response to IOA"},
354933916f3SBrian King 	{0x04448400, 0, IPR_DEFAULT_LOG_LEVEL,
3551da177e4SLinus Torvalds 	"8151: IOA microcode error"},
3561da177e4SLinus Torvalds 	{0x04448500, 0, 0,
3571da177e4SLinus Torvalds 	"Device bus status error"},
358933916f3SBrian King 	{0x04448600, 0, IPR_DEFAULT_LOG_LEVEL,
3591da177e4SLinus Torvalds 	"8157: IOA error requiring IOA reset to recover"},
36035a39691SBrian King 	{0x04448700, 0, 0,
36135a39691SBrian King 	"ATA device status error"},
3621da177e4SLinus Torvalds 	{0x04490000, 0, 0,
3631da177e4SLinus Torvalds 	"Message reject received from the device"},
364933916f3SBrian King 	{0x04449200, 0, IPR_DEFAULT_LOG_LEVEL,
3651da177e4SLinus Torvalds 	"8008: A permanent cache battery pack failure occurred"},
366933916f3SBrian King 	{0x0444A000, 0, IPR_DEFAULT_LOG_LEVEL,
3671da177e4SLinus Torvalds 	"9090: Disk unit has been modified after the last known status"},
368933916f3SBrian King 	{0x0444A200, 0, IPR_DEFAULT_LOG_LEVEL,
3691da177e4SLinus Torvalds 	"9081: IOA detected device error"},
370933916f3SBrian King 	{0x0444A300, 0, IPR_DEFAULT_LOG_LEVEL,
3711da177e4SLinus Torvalds 	"9082: IOA detected device error"},
372933916f3SBrian King 	{0x044A0000, 1, IPR_DEFAULT_LOG_LEVEL,
3731da177e4SLinus Torvalds 	"3110: Device bus error, message or command phase"},
374933916f3SBrian King 	{0x044A8000, 1, IPR_DEFAULT_LOG_LEVEL,
37535a39691SBrian King 	"3110: SAS Command / Task Management Function failed"},
376933916f3SBrian King 	{0x04670400, 0, IPR_DEFAULT_LOG_LEVEL,
3771da177e4SLinus Torvalds 	"9091: Incorrect hardware configuration change has been detected"},
378933916f3SBrian King 	{0x04678000, 0, IPR_DEFAULT_LOG_LEVEL,
379b0df54bbSbrking@us.ibm.com 	"9073: Invalid multi-adapter configuration"},
380933916f3SBrian King 	{0x04678100, 0, IPR_DEFAULT_LOG_LEVEL,
381896bbd21SBrian King 	"4010: Incorrect connection between cascaded expanders"},
382933916f3SBrian King 	{0x04678200, 0, IPR_DEFAULT_LOG_LEVEL,
383896bbd21SBrian King 	"4020: Connections exceed IOA design limits"},
384933916f3SBrian King 	{0x04678300, 0, IPR_DEFAULT_LOG_LEVEL,
385896bbd21SBrian King 	"4030: Incorrect multipath connection"},
386933916f3SBrian King 	{0x04679000, 0, IPR_DEFAULT_LOG_LEVEL,
387896bbd21SBrian King 	"4110: Unsupported enclosure function"},
388ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x04679800, 0, IPR_DEFAULT_LOG_LEVEL,
389ed7bd661Swenxiong@linux.vnet.ibm.com 	"4120: SAS cable VPD cannot be read"},
390933916f3SBrian King 	{0x046E0000, 0, IPR_DEFAULT_LOG_LEVEL,
3911da177e4SLinus Torvalds 	"FFF4: Command to logical unit failed"},
3921da177e4SLinus Torvalds 	{0x05240000, 1, 0,
3931da177e4SLinus Torvalds 	"Illegal request, invalid request type or request packet"},
3941da177e4SLinus Torvalds 	{0x05250000, 0, 0,
3951da177e4SLinus Torvalds 	"Illegal request, invalid resource handle"},
396b0df54bbSbrking@us.ibm.com 	{0x05258000, 0, 0,
397b0df54bbSbrking@us.ibm.com 	"Illegal request, commands not allowed to this device"},
398b0df54bbSbrking@us.ibm.com 	{0x05258100, 0, 0,
399b0df54bbSbrking@us.ibm.com 	"Illegal request, command not allowed to a secondary adapter"},
4005aa3a333SWayne Boyer 	{0x05258200, 0, 0,
4015aa3a333SWayne Boyer 	"Illegal request, command not allowed to a non-optimized resource"},
4021da177e4SLinus Torvalds 	{0x05260000, 0, 0,
4031da177e4SLinus Torvalds 	"Illegal request, invalid field in parameter list"},
4041da177e4SLinus Torvalds 	{0x05260100, 0, 0,
4051da177e4SLinus Torvalds 	"Illegal request, parameter not supported"},
4061da177e4SLinus Torvalds 	{0x05260200, 0, 0,
4071da177e4SLinus Torvalds 	"Illegal request, parameter value invalid"},
4081da177e4SLinus Torvalds 	{0x052C0000, 0, 0,
4091da177e4SLinus Torvalds 	"Illegal request, command sequence error"},
410b0df54bbSbrking@us.ibm.com 	{0x052C8000, 1, 0,
411b0df54bbSbrking@us.ibm.com 	"Illegal request, dual adapter support not enabled"},
412ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x052C8100, 1, 0,
413ed7bd661Swenxiong@linux.vnet.ibm.com 	"Illegal request, another cable connector was physically disabled"},
414ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x054E8000, 1, 0,
415ed7bd661Swenxiong@linux.vnet.ibm.com 	"Illegal request, inconsistent group id/group count"},
416933916f3SBrian King 	{0x06040500, 0, IPR_DEFAULT_LOG_LEVEL,
4171da177e4SLinus Torvalds 	"9031: Array protection temporarily suspended, protection resuming"},
418933916f3SBrian King 	{0x06040600, 0, IPR_DEFAULT_LOG_LEVEL,
4191da177e4SLinus Torvalds 	"9040: Array protection temporarily suspended, protection resuming"},
420ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x060B0100, 0, IPR_DEFAULT_LOG_LEVEL,
421ed7bd661Swenxiong@linux.vnet.ibm.com 	"4080: IOA exceeded maximum operating temperature"},
422ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x060B8000, 0, IPR_DEFAULT_LOG_LEVEL,
423ed7bd661Swenxiong@linux.vnet.ibm.com 	"4085: Service required"},
42481471b07SWen Xiong 	{0x060B8100, 0, IPR_DEFAULT_LOG_LEVEL,
42581471b07SWen Xiong 	"4086: SAS Adapter Hardware Configuration Error"},
426933916f3SBrian King 	{0x06288000, 0, IPR_DEFAULT_LOG_LEVEL,
427896bbd21SBrian King 	"3140: Device bus not ready to ready transition"},
428933916f3SBrian King 	{0x06290000, 0, IPR_DEFAULT_LOG_LEVEL,
4291da177e4SLinus Torvalds 	"FFFB: SCSI bus was reset"},
4301da177e4SLinus Torvalds 	{0x06290500, 0, 0,
4311da177e4SLinus Torvalds 	"FFFE: SCSI bus transition to single ended"},
4321da177e4SLinus Torvalds 	{0x06290600, 0, 0,
4331da177e4SLinus Torvalds 	"FFFE: SCSI bus transition to LVD"},
434933916f3SBrian King 	{0x06298000, 0, IPR_DEFAULT_LOG_LEVEL,
4351da177e4SLinus Torvalds 	"FFFB: SCSI bus was reset by another initiator"},
436933916f3SBrian King 	{0x063F0300, 0, IPR_DEFAULT_LOG_LEVEL,
4371da177e4SLinus Torvalds 	"3029: A device replacement has occurred"},
438ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x063F8300, 0, IPR_DEFAULT_LOG_LEVEL,
439ed7bd661Swenxiong@linux.vnet.ibm.com 	"4102: Device bus fabric performance degradation"},
440933916f3SBrian King 	{0x064C8000, 0, IPR_DEFAULT_LOG_LEVEL,
4411da177e4SLinus Torvalds 	"9051: IOA cache data exists for a missing or failed device"},
442933916f3SBrian King 	{0x064C8100, 0, IPR_DEFAULT_LOG_LEVEL,
443b0df54bbSbrking@us.ibm.com 	"9055: Auxiliary cache IOA contains cache data needed by the primary IOA"},
444933916f3SBrian King 	{0x06670100, 0, IPR_DEFAULT_LOG_LEVEL,
4451da177e4SLinus Torvalds 	"9025: Disk unit is not supported at its physical location"},
446933916f3SBrian King 	{0x06670600, 0, IPR_DEFAULT_LOG_LEVEL,
4471da177e4SLinus Torvalds 	"3020: IOA detected a SCSI bus configuration error"},
448933916f3SBrian King 	{0x06678000, 0, IPR_DEFAULT_LOG_LEVEL,
4491da177e4SLinus Torvalds 	"3150: SCSI bus configuration error"},
450933916f3SBrian King 	{0x06678100, 0, IPR_DEFAULT_LOG_LEVEL,
451b0df54bbSbrking@us.ibm.com 	"9074: Asymmetric advanced function disk configuration"},
452933916f3SBrian King 	{0x06678300, 0, IPR_DEFAULT_LOG_LEVEL,
453896bbd21SBrian King 	"4040: Incomplete multipath connection between IOA and enclosure"},
454933916f3SBrian King 	{0x06678400, 0, IPR_DEFAULT_LOG_LEVEL,
455896bbd21SBrian King 	"4041: Incomplete multipath connection between enclosure and device"},
456933916f3SBrian King 	{0x06678500, 0, IPR_DEFAULT_LOG_LEVEL,
457896bbd21SBrian King 	"9075: Incomplete multipath connection between IOA and remote IOA"},
458933916f3SBrian King 	{0x06678600, 0, IPR_DEFAULT_LOG_LEVEL,
459896bbd21SBrian King 	"9076: Configuration error, missing remote IOA"},
460933916f3SBrian King 	{0x06679100, 0, IPR_DEFAULT_LOG_LEVEL,
461896bbd21SBrian King 	"4050: Enclosure does not support a required multipath function"},
462ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x06679800, 0, IPR_DEFAULT_LOG_LEVEL,
463ed7bd661Swenxiong@linux.vnet.ibm.com 	"4121: Configuration error, required cable is missing"},
464ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x06679900, 0, IPR_DEFAULT_LOG_LEVEL,
465ed7bd661Swenxiong@linux.vnet.ibm.com 	"4122: Cable is not plugged into the correct location on remote IOA"},
466ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x06679A00, 0, IPR_DEFAULT_LOG_LEVEL,
467ed7bd661Swenxiong@linux.vnet.ibm.com 	"4123: Configuration error, invalid cable vital product data"},
468ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x06679B00, 0, IPR_DEFAULT_LOG_LEVEL,
469ed7bd661Swenxiong@linux.vnet.ibm.com 	"4124: Configuration error, both cable ends are plugged into the same IOA"},
470b75424fcSWayne Boyer 	{0x06690000, 0, IPR_DEFAULT_LOG_LEVEL,
471b75424fcSWayne Boyer 	"4070: Logically bad block written on device"},
472933916f3SBrian King 	{0x06690200, 0, IPR_DEFAULT_LOG_LEVEL,
4731da177e4SLinus Torvalds 	"9041: Array protection temporarily suspended"},
474933916f3SBrian King 	{0x06698200, 0, IPR_DEFAULT_LOG_LEVEL,
4751da177e4SLinus Torvalds 	"9042: Corrupt array parity detected on specified device"},
476933916f3SBrian King 	{0x066B0200, 0, IPR_DEFAULT_LOG_LEVEL,
4771da177e4SLinus Torvalds 	"9030: Array no longer protected due to missing or failed disk unit"},
478933916f3SBrian King 	{0x066B8000, 0, IPR_DEFAULT_LOG_LEVEL,
479b0df54bbSbrking@us.ibm.com 	"9071: Link operational transition"},
480933916f3SBrian King 	{0x066B8100, 0, IPR_DEFAULT_LOG_LEVEL,
481b0df54bbSbrking@us.ibm.com 	"9072: Link not operational transition"},
482933916f3SBrian King 	{0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL,
4831da177e4SLinus Torvalds 	"9032: Array exposed but still protected"},
4847b3871fdSBrian King 	{0x066B8300, 0, IPR_DEBUG_LOG_LEVEL,
485e435340cSBrian King 	"70DD: Device forced failed by disrupt device command"},
486933916f3SBrian King 	{0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL,
487896bbd21SBrian King 	"4061: Multipath redundancy level got better"},
488933916f3SBrian King 	{0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL,
489896bbd21SBrian King 	"4060: Multipath redundancy level got worse"},
4907b3871fdSBrian King 	{0x06808100, 0, IPR_DEBUG_LOG_LEVEL,
491f8ee25d7SWen Xiong 	"9083: Device raw mode enabled"},
4927b3871fdSBrian King 	{0x06808200, 0, IPR_DEBUG_LOG_LEVEL,
493f8ee25d7SWen Xiong 	"9084: Device raw mode disabled"},
4941da177e4SLinus Torvalds 	{0x07270000, 0, 0,
4951da177e4SLinus Torvalds 	"Failure due to other device"},
496933916f3SBrian King 	{0x07278000, 0, IPR_DEFAULT_LOG_LEVEL,
4971da177e4SLinus Torvalds 	"9008: IOA does not support functions expected by devices"},
498933916f3SBrian King 	{0x07278100, 0, IPR_DEFAULT_LOG_LEVEL,
4991da177e4SLinus Torvalds 	"9010: Cache data associated with attached devices cannot be found"},
500933916f3SBrian King 	{0x07278200, 0, IPR_DEFAULT_LOG_LEVEL,
5011da177e4SLinus Torvalds 	"9011: Cache data belongs to devices other than those attached"},
502933916f3SBrian King 	{0x07278400, 0, IPR_DEFAULT_LOG_LEVEL,
5031da177e4SLinus Torvalds 	"9020: Array missing 2 or more devices with only 1 device present"},
504933916f3SBrian King 	{0x07278500, 0, IPR_DEFAULT_LOG_LEVEL,
5051da177e4SLinus Torvalds 	"9021: Array missing 2 or more devices with 2 or more devices present"},
506933916f3SBrian King 	{0x07278600, 0, IPR_DEFAULT_LOG_LEVEL,
5071da177e4SLinus Torvalds 	"9022: Exposed array is missing a required device"},
508933916f3SBrian King 	{0x07278700, 0, IPR_DEFAULT_LOG_LEVEL,
5091da177e4SLinus Torvalds 	"9023: Array member(s) not at required physical locations"},
510933916f3SBrian King 	{0x07278800, 0, IPR_DEFAULT_LOG_LEVEL,
5111da177e4SLinus Torvalds 	"9024: Array not functional due to present hardware configuration"},
512933916f3SBrian King 	{0x07278900, 0, IPR_DEFAULT_LOG_LEVEL,
5131da177e4SLinus Torvalds 	"9026: Array not functional due to present hardware configuration"},
514933916f3SBrian King 	{0x07278A00, 0, IPR_DEFAULT_LOG_LEVEL,
5151da177e4SLinus Torvalds 	"9027: Array is missing a device and parity is out of sync"},
516933916f3SBrian King 	{0x07278B00, 0, IPR_DEFAULT_LOG_LEVEL,
5171da177e4SLinus Torvalds 	"9028: Maximum number of arrays already exist"},
518933916f3SBrian King 	{0x07278C00, 0, IPR_DEFAULT_LOG_LEVEL,
5191da177e4SLinus Torvalds 	"9050: Required cache data cannot be located for a disk unit"},
520933916f3SBrian King 	{0x07278D00, 0, IPR_DEFAULT_LOG_LEVEL,
5211da177e4SLinus Torvalds 	"9052: Cache data exists for a device that has been modified"},
522933916f3SBrian King 	{0x07278F00, 0, IPR_DEFAULT_LOG_LEVEL,
5231da177e4SLinus Torvalds 	"9054: IOA resources not available due to previous problems"},
524933916f3SBrian King 	{0x07279100, 0, IPR_DEFAULT_LOG_LEVEL,
5251da177e4SLinus Torvalds 	"9092: Disk unit requires initialization before use"},
526933916f3SBrian King 	{0x07279200, 0, IPR_DEFAULT_LOG_LEVEL,
5271da177e4SLinus Torvalds 	"9029: Incorrect hardware configuration change has been detected"},
528933916f3SBrian King 	{0x07279600, 0, IPR_DEFAULT_LOG_LEVEL,
5291da177e4SLinus Torvalds 	"9060: One or more disk pairs are missing from an array"},
530933916f3SBrian King 	{0x07279700, 0, IPR_DEFAULT_LOG_LEVEL,
5311da177e4SLinus Torvalds 	"9061: One or more disks are missing from an array"},
532933916f3SBrian King 	{0x07279800, 0, IPR_DEFAULT_LOG_LEVEL,
5331da177e4SLinus Torvalds 	"9062: One or more disks are missing from an array"},
534933916f3SBrian King 	{0x07279900, 0, IPR_DEFAULT_LOG_LEVEL,
5351da177e4SLinus Torvalds 	"9063: Maximum number of functional arrays has been exceeded"},
536ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x07279A00, 0, 0,
537ed7bd661Swenxiong@linux.vnet.ibm.com 	"Data protect, other volume set problem"},
5381da177e4SLinus Torvalds 	{0x0B260000, 0, 0,
5391da177e4SLinus Torvalds 	"Aborted command, invalid descriptor"},
540ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x0B3F9000, 0, 0,
541ed7bd661Swenxiong@linux.vnet.ibm.com 	"Target operating conditions have changed, dual adapter takeover"},
542ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x0B530200, 0, 0,
543ed7bd661Swenxiong@linux.vnet.ibm.com 	"Aborted command, medium removal prevented"},
5441da177e4SLinus Torvalds 	{0x0B5A0000, 0, 0,
545ed7bd661Swenxiong@linux.vnet.ibm.com 	"Command terminated by host"},
546ed7bd661Swenxiong@linux.vnet.ibm.com 	{0x0B5B8000, 0, 0,
547ed7bd661Swenxiong@linux.vnet.ibm.com 	"Aborted command, command terminated by host"}
5481da177e4SLinus Torvalds };
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds static const struct ipr_ses_table_entry ipr_ses_table[] = {
5511da177e4SLinus Torvalds 	{ "2104-DL1        ", "XXXXXXXXXXXXXXXX", 80 },
5521da177e4SLinus Torvalds 	{ "2104-TL1        ", "XXXXXXXXXXXXXXXX", 80 },
5531da177e4SLinus Torvalds 	{ "HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 7 slot */
5541da177e4SLinus Torvalds 	{ "HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 5 slot */
5551da177e4SLinus Torvalds 	{ "HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Bowtie */
5561da177e4SLinus Torvalds 	{ "HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* MartinFenning */
5571da177e4SLinus Torvalds 	{ "2104-DU3        ", "XXXXXXXXXXXXXXXX", 160 },
5581da177e4SLinus Torvalds 	{ "2104-TU3        ", "XXXXXXXXXXXXXXXX", 160 },
5591da177e4SLinus Torvalds 	{ "HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 },
5601da177e4SLinus Torvalds 	{ "HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 },
5611da177e4SLinus Torvalds 	{ "St  V1S2        ", "XXXXXXXXXXXXXXXX", 160 },
5621da177e4SLinus Torvalds 	{ "HSBPD4M  PU3SCSI", "XXXXXXX*XXXXXXXX", 160 },
5631da177e4SLinus Torvalds 	{ "VSBPD1H   U3SCSI", "XXXXXXX*XXXXXXXX", 160 }
5641da177e4SLinus Torvalds };
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds /*
5671da177e4SLinus Torvalds  *  Function Prototypes
5681da177e4SLinus Torvalds  */
5691da177e4SLinus Torvalds static int ipr_reset_alert(struct ipr_cmnd *);
5701da177e4SLinus Torvalds static void ipr_process_ccn(struct ipr_cmnd *);
5711da177e4SLinus Torvalds static void ipr_process_error(struct ipr_cmnd *);
5721da177e4SLinus Torvalds static void ipr_reset_ioa_job(struct ipr_cmnd *);
5731da177e4SLinus Torvalds static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *,
5741da177e4SLinus Torvalds 				   enum ipr_shutdown_type);
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds #ifdef CONFIG_SCSI_IPR_TRACE
5771da177e4SLinus Torvalds /**
5781da177e4SLinus Torvalds  * ipr_trc_hook - Add a trace entry to the driver trace
5791da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
5801da177e4SLinus Torvalds  * @type:		trace type
5811da177e4SLinus Torvalds  * @add_data:	additional data
5821da177e4SLinus Torvalds  *
5831da177e4SLinus Torvalds  * Return value:
5841da177e4SLinus Torvalds  * 	none
5851da177e4SLinus Torvalds  **/
5861da177e4SLinus Torvalds static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
5871da177e4SLinus Torvalds 			 u8 type, u32 add_data)
5881da177e4SLinus Torvalds {
5891da177e4SLinus Torvalds 	struct ipr_trace_entry *trace_entry;
5901da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
591bb7c5433SBrian King 	unsigned int trace_index;
5921da177e4SLinus Torvalds 
593bb7c5433SBrian King 	trace_index = atomic_add_return(1, &ioa_cfg->trace_index) & IPR_TRACE_INDEX_MASK;
594bb7c5433SBrian King 	trace_entry = &ioa_cfg->trace[trace_index];
5951da177e4SLinus Torvalds 	trace_entry->time = jiffies;
5961da177e4SLinus Torvalds 	trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0];
5971da177e4SLinus Torvalds 	trace_entry->type = type;
598a32c055fSWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64)
599a32c055fSWayne Boyer 		trace_entry->ata_op_code = ipr_cmd->i.ata_ioadl.regs.command;
600a32c055fSWayne Boyer 	else
601a32c055fSWayne Boyer 		trace_entry->ata_op_code = ipr_cmd->ioarcb.u.add_data.u.regs.command;
60235a39691SBrian King 	trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff;
6031da177e4SLinus Torvalds 	trace_entry->res_handle = ipr_cmd->ioarcb.res_handle;
6041da177e4SLinus Torvalds 	trace_entry->u.add_data = add_data;
60556d6aa33Swenxiong@linux.vnet.ibm.com 	wmb();
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds #else
6081da177e4SLinus Torvalds #define ipr_trc_hook(ipr_cmd, type, add_data) do { } while (0)
6091da177e4SLinus Torvalds #endif
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds /**
612172cd6e1SBrian King  * ipr_lock_and_done - Acquire lock and complete command
613172cd6e1SBrian King  * @ipr_cmd:	ipr command struct
614172cd6e1SBrian King  *
615172cd6e1SBrian King  * Return value:
616172cd6e1SBrian King  *	none
617172cd6e1SBrian King  **/
618172cd6e1SBrian King static void ipr_lock_and_done(struct ipr_cmnd *ipr_cmd)
619172cd6e1SBrian King {
620172cd6e1SBrian King 	unsigned long lock_flags;
621172cd6e1SBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
622172cd6e1SBrian King 
623172cd6e1SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
624172cd6e1SBrian King 	ipr_cmd->done(ipr_cmd);
625172cd6e1SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
626172cd6e1SBrian King }
627172cd6e1SBrian King 
628172cd6e1SBrian King /**
6291da177e4SLinus Torvalds  * ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse
6301da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
6311da177e4SLinus Torvalds  *
6321da177e4SLinus Torvalds  * Return value:
6331da177e4SLinus Torvalds  * 	none
6341da177e4SLinus Torvalds  **/
6351da177e4SLinus Torvalds static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
6361da177e4SLinus Torvalds {
6371da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
63896d21f00SWayne Boyer 	struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa;
63996d21f00SWayne Boyer 	struct ipr_ioasa64 *ioasa64 = &ipr_cmd->s.ioasa64;
640a32c055fSWayne Boyer 	dma_addr_t dma_addr = ipr_cmd->dma_addr;
64105a6538aSwenxiong@linux.vnet.ibm.com 	int hrrq_id;
6421da177e4SLinus Torvalds 
64305a6538aSwenxiong@linux.vnet.ibm.com 	hrrq_id = ioarcb->cmd_pkt.hrrq_id;
6441da177e4SLinus Torvalds 	memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt));
64505a6538aSwenxiong@linux.vnet.ibm.com 	ioarcb->cmd_pkt.hrrq_id = hrrq_id;
646a32c055fSWayne Boyer 	ioarcb->data_transfer_length = 0;
6471da177e4SLinus Torvalds 	ioarcb->read_data_transfer_length = 0;
648a32c055fSWayne Boyer 	ioarcb->ioadl_len = 0;
6491da177e4SLinus Torvalds 	ioarcb->read_ioadl_len = 0;
650a32c055fSWayne Boyer 
65196d21f00SWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64) {
652a32c055fSWayne Boyer 		ioarcb->u.sis64_addr_data.data_ioadl_addr =
653a32c055fSWayne Boyer 			cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ioadl64));
65496d21f00SWayne Boyer 		ioasa64->u.gata.status = 0;
65596d21f00SWayne Boyer 	} else {
65651b1c7e1SBrian King 		ioarcb->write_ioadl_addr =
657a32c055fSWayne Boyer 			cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, i.ioadl));
65851b1c7e1SBrian King 		ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
65996d21f00SWayne Boyer 		ioasa->u.gata.status = 0;
660a32c055fSWayne Boyer 	}
661a32c055fSWayne Boyer 
66296d21f00SWayne Boyer 	ioasa->hdr.ioasc = 0;
66396d21f00SWayne Boyer 	ioasa->hdr.residual_data_len = 0;
6641da177e4SLinus Torvalds 	ipr_cmd->scsi_cmd = NULL;
66535a39691SBrian King 	ipr_cmd->qc = NULL;
6661da177e4SLinus Torvalds 	ipr_cmd->sense_buffer[0] = 0;
6671da177e4SLinus Torvalds 	ipr_cmd->dma_use_sg = 0;
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds /**
6711da177e4SLinus Torvalds  * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block
6721da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
673a96099e2SLee Jones  * @fast_done:	fast done function call-back
6741da177e4SLinus Torvalds  *
6751da177e4SLinus Torvalds  * Return value:
6761da177e4SLinus Torvalds  * 	none
6771da177e4SLinus Torvalds  **/
678172cd6e1SBrian King static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd,
679172cd6e1SBrian King 			      void (*fast_done) (struct ipr_cmnd *))
6801da177e4SLinus Torvalds {
6811da177e4SLinus Torvalds 	ipr_reinit_ipr_cmnd(ipr_cmd);
6821da177e4SLinus Torvalds 	ipr_cmd->u.scratch = 0;
6831da177e4SLinus Torvalds 	ipr_cmd->sibling = NULL;
6846cdb0817SBrian King 	ipr_cmd->eh_comp = NULL;
685172cd6e1SBrian King 	ipr_cmd->fast_done = fast_done;
686738c6ec5SKees Cook 	timer_setup(&ipr_cmd->timer, NULL, 0);
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds /**
69000bfef2cSBrian King  * __ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block
691a96099e2SLee Jones  * @hrrq:	hrr queue
69200bfef2cSBrian King  *
69300bfef2cSBrian King  * Return value:
69400bfef2cSBrian King  * 	pointer to ipr command struct
69500bfef2cSBrian King  **/
69600bfef2cSBrian King static
69705a6538aSwenxiong@linux.vnet.ibm.com struct ipr_cmnd *__ipr_get_free_ipr_cmnd(struct ipr_hrr_queue *hrrq)
69800bfef2cSBrian King {
69905a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_cmnd *ipr_cmd = NULL;
70000bfef2cSBrian King 
70105a6538aSwenxiong@linux.vnet.ibm.com 	if (likely(!list_empty(&hrrq->hrrq_free_q))) {
70205a6538aSwenxiong@linux.vnet.ibm.com 		ipr_cmd = list_entry(hrrq->hrrq_free_q.next,
70305a6538aSwenxiong@linux.vnet.ibm.com 			struct ipr_cmnd, queue);
70400bfef2cSBrian King 		list_del(&ipr_cmd->queue);
70505a6538aSwenxiong@linux.vnet.ibm.com 	}
70605a6538aSwenxiong@linux.vnet.ibm.com 
70700bfef2cSBrian King 
70800bfef2cSBrian King 	return ipr_cmd;
70900bfef2cSBrian King }
71000bfef2cSBrian King 
71100bfef2cSBrian King /**
71200bfef2cSBrian King  * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block and initialize it
7131da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
7141da177e4SLinus Torvalds  *
7151da177e4SLinus Torvalds  * Return value:
7161da177e4SLinus Torvalds  *	pointer to ipr command struct
7171da177e4SLinus Torvalds  **/
7181da177e4SLinus Torvalds static
7191da177e4SLinus Torvalds struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg)
7201da177e4SLinus Torvalds {
72105a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_cmnd *ipr_cmd =
72205a6538aSwenxiong@linux.vnet.ibm.com 		__ipr_get_free_ipr_cmnd(&ioa_cfg->hrrq[IPR_INIT_HRRQ]);
723172cd6e1SBrian King 	ipr_init_ipr_cmnd(ipr_cmd, ipr_lock_and_done);
7241da177e4SLinus Torvalds 	return ipr_cmd;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds /**
7281da177e4SLinus Torvalds  * ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts
7291da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
7301da177e4SLinus Torvalds  * @clr_ints:     interrupts to clear
7311da177e4SLinus Torvalds  *
7321da177e4SLinus Torvalds  * This function masks all interrupts on the adapter, then clears the
7331da177e4SLinus Torvalds  * interrupts specified in the mask
7341da177e4SLinus Torvalds  *
7351da177e4SLinus Torvalds  * Return value:
7361da177e4SLinus Torvalds  * 	none
7371da177e4SLinus Torvalds  **/
7381da177e4SLinus Torvalds static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
7391da177e4SLinus Torvalds 					  u32 clr_ints)
7401da177e4SLinus Torvalds {
74156d6aa33Swenxiong@linux.vnet.ibm.com 	int i;
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds 	/* Stop new interrupts */
74456d6aa33Swenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
74556d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&ioa_cfg->hrrq[i]._lock);
74656d6aa33Swenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].allow_interrupts = 0;
74756d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ioa_cfg->hrrq[i]._lock);
74856d6aa33Swenxiong@linux.vnet.ibm.com 	}
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	/* Set interrupt mask to stop all new interrupts */
751214777baSWayne Boyer 	if (ioa_cfg->sis64)
752214777baSWayne Boyer 		writeq(~0, ioa_cfg->regs.set_interrupt_mask_reg);
753214777baSWayne Boyer 	else
7541da177e4SLinus Torvalds 		writel(~0, ioa_cfg->regs.set_interrupt_mask_reg);
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 	/* Clear any pending interrupts */
757214777baSWayne Boyer 	if (ioa_cfg->sis64)
758214777baSWayne Boyer 		writel(~0, ioa_cfg->regs.clr_interrupt_reg);
759214777baSWayne Boyer 	writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg32);
7604dc83399SLee Jones 	readl(ioa_cfg->regs.sense_interrupt_reg);
7611da177e4SLinus Torvalds }
7621da177e4SLinus Torvalds 
7631da177e4SLinus Torvalds /**
7641da177e4SLinus Torvalds  * ipr_save_pcix_cmd_reg - Save PCI-X command register
7651da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
7661da177e4SLinus Torvalds  *
7671da177e4SLinus Torvalds  * Return value:
7681da177e4SLinus Torvalds  * 	0 on success / -EIO on failure
7691da177e4SLinus Torvalds  **/
7701da177e4SLinus Torvalds static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
7711da177e4SLinus Torvalds {
7721da177e4SLinus Torvalds 	int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
7731da177e4SLinus Torvalds 
7747dce0e1cSBrian King 	if (pcix_cmd_reg == 0)
7757dce0e1cSBrian King 		return 0;
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
7781da177e4SLinus Torvalds 				 &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
7791da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n");
7801da177e4SLinus Torvalds 		return -EIO;
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	ioa_cfg->saved_pcix_cmd_reg |= PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO;
7841da177e4SLinus Torvalds 	return 0;
7851da177e4SLinus Torvalds }
7861da177e4SLinus Torvalds 
7871da177e4SLinus Torvalds /**
7881da177e4SLinus Torvalds  * ipr_set_pcix_cmd_reg - Setup PCI-X command register
7891da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
7901da177e4SLinus Torvalds  *
7911da177e4SLinus Torvalds  * Return value:
7921da177e4SLinus Torvalds  * 	0 on success / -EIO on failure
7931da177e4SLinus Torvalds  **/
7941da177e4SLinus Torvalds static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
7951da177e4SLinus Torvalds {
7961da177e4SLinus Torvalds 	int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 	if (pcix_cmd_reg) {
7991da177e4SLinus Torvalds 		if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
8001da177e4SLinus Torvalds 					  ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
8011da177e4SLinus Torvalds 			dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n");
8021da177e4SLinus Torvalds 			return -EIO;
8031da177e4SLinus Torvalds 		}
8041da177e4SLinus Torvalds 	}
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds 	return 0;
8071da177e4SLinus Torvalds }
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds /**
810f646f325SBrian King  * __ipr_sata_eh_done - done function for aborted SATA commands
811f646f325SBrian King  * @ipr_cmd:	ipr command struct
812f646f325SBrian King  *
813f646f325SBrian King  * This function is invoked for ops generated to SATA
814f646f325SBrian King  * devices which are being aborted.
815f646f325SBrian King  *
816f646f325SBrian King  * Return value:
817f646f325SBrian King  * 	none
818f646f325SBrian King  **/
819f646f325SBrian King static void __ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
820f646f325SBrian King {
821f646f325SBrian King 	struct ata_queued_cmd *qc = ipr_cmd->qc;
822f646f325SBrian King 	struct ipr_sata_port *sata_port = qc->ap->private_data;
823f646f325SBrian King 
824f646f325SBrian King 	qc->err_mask |= AC_ERR_OTHER;
825f646f325SBrian King 	sata_port->ioasa.status |= ATA_BUSY;
826f646f325SBrian King 	ata_qc_complete(qc);
827f646f325SBrian King 	if (ipr_cmd->eh_comp)
828f646f325SBrian King 		complete(ipr_cmd->eh_comp);
829f646f325SBrian King 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
830f646f325SBrian King }
831f646f325SBrian King 
832f646f325SBrian King /**
83335a39691SBrian King  * ipr_sata_eh_done - done function for aborted SATA commands
83435a39691SBrian King  * @ipr_cmd:	ipr command struct
83535a39691SBrian King  *
83635a39691SBrian King  * This function is invoked for ops generated to SATA
83735a39691SBrian King  * devices which are being aborted.
83835a39691SBrian King  *
83935a39691SBrian King  * Return value:
84035a39691SBrian King  * 	none
84135a39691SBrian King  **/
84235a39691SBrian King static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
84335a39691SBrian King {
844f646f325SBrian King 	struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
845f646f325SBrian King 	unsigned long hrrq_flags;
84635a39691SBrian King 
847f646f325SBrian King 	spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
848f646f325SBrian King 	__ipr_sata_eh_done(ipr_cmd);
849f646f325SBrian King 	spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
850f646f325SBrian King }
851f646f325SBrian King 
852f646f325SBrian King /**
853f646f325SBrian King  * __ipr_scsi_eh_done - mid-layer done function for aborted ops
854f646f325SBrian King  * @ipr_cmd:	ipr command struct
855f646f325SBrian King  *
856f646f325SBrian King  * This function is invoked by the interrupt handler for
857f646f325SBrian King  * ops generated by the SCSI mid-layer which are being aborted.
858f646f325SBrian King  *
859f646f325SBrian King  * Return value:
860f646f325SBrian King  * 	none
861f646f325SBrian King  **/
862f646f325SBrian King static void __ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
863f646f325SBrian King {
864f646f325SBrian King 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
865f646f325SBrian King 
866f646f325SBrian King 	scsi_cmd->result |= (DID_ERROR << 16);
867f646f325SBrian King 
868f646f325SBrian King 	scsi_dma_unmap(ipr_cmd->scsi_cmd);
869f646f325SBrian King 	scsi_cmd->scsi_done(scsi_cmd);
87066a0d59cSBrian King 	if (ipr_cmd->eh_comp)
87166a0d59cSBrian King 		complete(ipr_cmd->eh_comp);
87205a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
87335a39691SBrian King }
87435a39691SBrian King 
87535a39691SBrian King /**
8761da177e4SLinus Torvalds  * ipr_scsi_eh_done - mid-layer done function for aborted ops
8771da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
8781da177e4SLinus Torvalds  *
8791da177e4SLinus Torvalds  * This function is invoked by the interrupt handler for
8801da177e4SLinus Torvalds  * ops generated by the SCSI mid-layer which are being aborted.
8811da177e4SLinus Torvalds  *
8821da177e4SLinus Torvalds  * Return value:
8831da177e4SLinus Torvalds  * 	none
8841da177e4SLinus Torvalds  **/
8851da177e4SLinus Torvalds static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
8861da177e4SLinus Torvalds {
887f646f325SBrian King 	unsigned long hrrq_flags;
888f646f325SBrian King 	struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
8891da177e4SLinus Torvalds 
890f646f325SBrian King 	spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
891f646f325SBrian King 	__ipr_scsi_eh_done(ipr_cmd);
892f646f325SBrian King 	spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
8931da177e4SLinus Torvalds }
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds /**
8961da177e4SLinus Torvalds  * ipr_fail_all_ops - Fails all outstanding ops.
8971da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
8981da177e4SLinus Torvalds  *
8991da177e4SLinus Torvalds  * This function fails all outstanding ops.
9001da177e4SLinus Torvalds  *
9011da177e4SLinus Torvalds  * Return value:
9021da177e4SLinus Torvalds  * 	none
9031da177e4SLinus Torvalds  **/
9041da177e4SLinus Torvalds static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg)
9051da177e4SLinus Torvalds {
9061da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd, *temp;
90705a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds 	ENTER;
91005a6538aSwenxiong@linux.vnet.ibm.com 	for_each_hrrq(hrrq, ioa_cfg) {
91156d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&hrrq->_lock);
91205a6538aSwenxiong@linux.vnet.ibm.com 		list_for_each_entry_safe(ipr_cmd,
91305a6538aSwenxiong@linux.vnet.ibm.com 					temp, &hrrq->hrrq_pending_q, queue) {
9141da177e4SLinus Torvalds 			list_del(&ipr_cmd->queue);
9151da177e4SLinus Torvalds 
91605a6538aSwenxiong@linux.vnet.ibm.com 			ipr_cmd->s.ioasa.hdr.ioasc =
91705a6538aSwenxiong@linux.vnet.ibm.com 				cpu_to_be32(IPR_IOASC_IOA_WAS_RESET);
91805a6538aSwenxiong@linux.vnet.ibm.com 			ipr_cmd->s.ioasa.hdr.ilid =
91905a6538aSwenxiong@linux.vnet.ibm.com 				cpu_to_be32(IPR_DRIVER_ILID);
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 			if (ipr_cmd->scsi_cmd)
922f646f325SBrian King 				ipr_cmd->done = __ipr_scsi_eh_done;
92335a39691SBrian King 			else if (ipr_cmd->qc)
924f646f325SBrian King 				ipr_cmd->done = __ipr_sata_eh_done;
9251da177e4SLinus Torvalds 
92605a6538aSwenxiong@linux.vnet.ibm.com 			ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH,
92705a6538aSwenxiong@linux.vnet.ibm.com 				     IPR_IOASC_IOA_WAS_RESET);
9281da177e4SLinus Torvalds 			del_timer(&ipr_cmd->timer);
9291da177e4SLinus Torvalds 			ipr_cmd->done(ipr_cmd);
9301da177e4SLinus Torvalds 		}
93156d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
93205a6538aSwenxiong@linux.vnet.ibm.com 	}
9331da177e4SLinus Torvalds 	LEAVE;
9341da177e4SLinus Torvalds }
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds /**
937a32c055fSWayne Boyer  * ipr_send_command -  Send driver initiated requests.
938a32c055fSWayne Boyer  * @ipr_cmd:		ipr command struct
939a32c055fSWayne Boyer  *
940a32c055fSWayne Boyer  * This function sends a command to the adapter using the correct write call.
941a32c055fSWayne Boyer  * In the case of sis64, calculate the ioarcb size required. Then or in the
942a32c055fSWayne Boyer  * appropriate bits.
943a32c055fSWayne Boyer  *
944a32c055fSWayne Boyer  * Return value:
945a32c055fSWayne Boyer  * 	none
946a32c055fSWayne Boyer  **/
947a32c055fSWayne Boyer static void ipr_send_command(struct ipr_cmnd *ipr_cmd)
948a32c055fSWayne Boyer {
949a32c055fSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
950a32c055fSWayne Boyer 	dma_addr_t send_dma_addr = ipr_cmd->dma_addr;
951a32c055fSWayne Boyer 
952a32c055fSWayne Boyer 	if (ioa_cfg->sis64) {
953a32c055fSWayne Boyer 		/* The default size is 256 bytes */
954a32c055fSWayne Boyer 		send_dma_addr |= 0x1;
955a32c055fSWayne Boyer 
956a32c055fSWayne Boyer 		/* If the number of ioadls * size of ioadl > 128 bytes,
957a32c055fSWayne Boyer 		   then use a 512 byte ioarcb */
958a32c055fSWayne Boyer 		if (ipr_cmd->dma_use_sg * sizeof(struct ipr_ioadl64_desc) > 128 )
959a32c055fSWayne Boyer 			send_dma_addr |= 0x4;
960a32c055fSWayne Boyer 		writeq(send_dma_addr, ioa_cfg->regs.ioarrin_reg);
961a32c055fSWayne Boyer 	} else
962a32c055fSWayne Boyer 		writel(send_dma_addr, ioa_cfg->regs.ioarrin_reg);
963a32c055fSWayne Boyer }
964a32c055fSWayne Boyer 
965a32c055fSWayne Boyer /**
9661da177e4SLinus Torvalds  * ipr_do_req -  Send driver initiated requests.
9671da177e4SLinus Torvalds  * @ipr_cmd:		ipr command struct
9681da177e4SLinus Torvalds  * @done:			done function
9691da177e4SLinus Torvalds  * @timeout_func:	timeout function
9701da177e4SLinus Torvalds  * @timeout:		timeout value
9711da177e4SLinus Torvalds  *
9721da177e4SLinus Torvalds  * This function sends the specified command to the adapter with the
9731da177e4SLinus Torvalds  * timeout given. The done function is invoked on command completion.
9741da177e4SLinus Torvalds  *
9751da177e4SLinus Torvalds  * Return value:
9761da177e4SLinus Torvalds  * 	none
9771da177e4SLinus Torvalds  **/
9781da177e4SLinus Torvalds static void ipr_do_req(struct ipr_cmnd *ipr_cmd,
9791da177e4SLinus Torvalds 		       void (*done) (struct ipr_cmnd *),
980738c6ec5SKees Cook 		       void (*timeout_func) (struct timer_list *), u32 timeout)
9811da177e4SLinus Torvalds {
98205a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	ipr_cmd->done = done;
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	ipr_cmd->timer.expires = jiffies + timeout;
987841b86f3SKees Cook 	ipr_cmd->timer.function = timeout_func;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 	add_timer(&ipr_cmd->timer);
9901da177e4SLinus Torvalds 
9911da177e4SLinus Torvalds 	ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0);
9921da177e4SLinus Torvalds 
993a32c055fSWayne Boyer 	ipr_send_command(ipr_cmd);
9941da177e4SLinus Torvalds }
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds /**
9971da177e4SLinus Torvalds  * ipr_internal_cmd_done - Op done function for an internally generated op.
9981da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
9991da177e4SLinus Torvalds  *
10001da177e4SLinus Torvalds  * This function is the op done function for an internally generated,
10011da177e4SLinus Torvalds  * blocking op. It simply wakes the sleeping thread.
10021da177e4SLinus Torvalds  *
10031da177e4SLinus Torvalds  * Return value:
10041da177e4SLinus Torvalds  * 	none
10051da177e4SLinus Torvalds  **/
10061da177e4SLinus Torvalds static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd)
10071da177e4SLinus Torvalds {
10081da177e4SLinus Torvalds 	if (ipr_cmd->sibling)
10091da177e4SLinus Torvalds 		ipr_cmd->sibling = NULL;
10101da177e4SLinus Torvalds 	else
10111da177e4SLinus Torvalds 		complete(&ipr_cmd->completion);
10121da177e4SLinus Torvalds }
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds /**
1015a32c055fSWayne Boyer  * ipr_init_ioadl - initialize the ioadl for the correct SIS type
1016a32c055fSWayne Boyer  * @ipr_cmd:	ipr command struct
1017a32c055fSWayne Boyer  * @dma_addr:	dma address
1018a32c055fSWayne Boyer  * @len:	transfer length
1019a32c055fSWayne Boyer  * @flags:	ioadl flag value
1020a32c055fSWayne Boyer  *
1021a32c055fSWayne Boyer  * This function initializes an ioadl in the case where there is only a single
1022a32c055fSWayne Boyer  * descriptor.
1023a32c055fSWayne Boyer  *
1024a32c055fSWayne Boyer  * Return value:
1025a32c055fSWayne Boyer  * 	nothing
1026a32c055fSWayne Boyer  **/
1027a32c055fSWayne Boyer static void ipr_init_ioadl(struct ipr_cmnd *ipr_cmd, dma_addr_t dma_addr,
1028a32c055fSWayne Boyer 			   u32 len, int flags)
1029a32c055fSWayne Boyer {
1030a32c055fSWayne Boyer 	struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl;
1031a32c055fSWayne Boyer 	struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64;
1032a32c055fSWayne Boyer 
1033a32c055fSWayne Boyer 	ipr_cmd->dma_use_sg = 1;
1034a32c055fSWayne Boyer 
1035a32c055fSWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64) {
1036a32c055fSWayne Boyer 		ioadl64->flags = cpu_to_be32(flags);
1037a32c055fSWayne Boyer 		ioadl64->data_len = cpu_to_be32(len);
1038a32c055fSWayne Boyer 		ioadl64->address = cpu_to_be64(dma_addr);
1039a32c055fSWayne Boyer 
1040a32c055fSWayne Boyer 		ipr_cmd->ioarcb.ioadl_len =
1041a32c055fSWayne Boyer 		       	cpu_to_be32(sizeof(struct ipr_ioadl64_desc));
1042a32c055fSWayne Boyer 		ipr_cmd->ioarcb.data_transfer_length = cpu_to_be32(len);
1043a32c055fSWayne Boyer 	} else {
1044a32c055fSWayne Boyer 		ioadl->flags_and_data_len = cpu_to_be32(flags | len);
1045a32c055fSWayne Boyer 		ioadl->address = cpu_to_be32(dma_addr);
1046a32c055fSWayne Boyer 
1047a32c055fSWayne Boyer 		if (flags == IPR_IOADL_FLAGS_READ_LAST) {
1048a32c055fSWayne Boyer 			ipr_cmd->ioarcb.read_ioadl_len =
1049a32c055fSWayne Boyer 				cpu_to_be32(sizeof(struct ipr_ioadl_desc));
1050a32c055fSWayne Boyer 			ipr_cmd->ioarcb.read_data_transfer_length = cpu_to_be32(len);
1051a32c055fSWayne Boyer 		} else {
1052a32c055fSWayne Boyer 			ipr_cmd->ioarcb.ioadl_len =
1053a32c055fSWayne Boyer 			       	cpu_to_be32(sizeof(struct ipr_ioadl_desc));
1054a32c055fSWayne Boyer 			ipr_cmd->ioarcb.data_transfer_length = cpu_to_be32(len);
1055a32c055fSWayne Boyer 		}
1056a32c055fSWayne Boyer 	}
1057a32c055fSWayne Boyer }
1058a32c055fSWayne Boyer 
1059a32c055fSWayne Boyer /**
10601da177e4SLinus Torvalds  * ipr_send_blocking_cmd - Send command and sleep on its completion.
10611da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
10621da177e4SLinus Torvalds  * @timeout_func:	function to invoke if command times out
10631da177e4SLinus Torvalds  * @timeout:	timeout
10641da177e4SLinus Torvalds  *
10651da177e4SLinus Torvalds  * Return value:
10661da177e4SLinus Torvalds  * 	none
10671da177e4SLinus Torvalds  **/
10681da177e4SLinus Torvalds static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
1069738c6ec5SKees Cook 				  void (*timeout_func) (struct timer_list *),
10701da177e4SLinus Torvalds 				  u32 timeout)
10711da177e4SLinus Torvalds {
10721da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
10731da177e4SLinus Torvalds 
10741da177e4SLinus Torvalds 	init_completion(&ipr_cmd->completion);
10751da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_internal_cmd_done, timeout_func, timeout);
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 	spin_unlock_irq(ioa_cfg->host->host_lock);
10781da177e4SLinus Torvalds 	wait_for_completion(&ipr_cmd->completion);
10791da177e4SLinus Torvalds 	spin_lock_irq(ioa_cfg->host->host_lock);
10801da177e4SLinus Torvalds }
10811da177e4SLinus Torvalds 
108205a6538aSwenxiong@linux.vnet.ibm.com static int ipr_get_hrrq_index(struct ipr_ioa_cfg *ioa_cfg)
108305a6538aSwenxiong@linux.vnet.ibm.com {
10843f1c0581SBrian King 	unsigned int hrrq;
10853f1c0581SBrian King 
108605a6538aSwenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq_num == 1)
10873f1c0581SBrian King 		hrrq = 0;
10883f1c0581SBrian King 	else {
10893f1c0581SBrian King 		hrrq = atomic_add_return(1, &ioa_cfg->hrrq_index);
10903f1c0581SBrian King 		hrrq = (hrrq % (ioa_cfg->hrrq_num - 1)) + 1;
10913f1c0581SBrian King 	}
10923f1c0581SBrian King 	return hrrq;
109305a6538aSwenxiong@linux.vnet.ibm.com }
109405a6538aSwenxiong@linux.vnet.ibm.com 
10951da177e4SLinus Torvalds /**
10961da177e4SLinus Torvalds  * ipr_send_hcam - Send an HCAM to the adapter.
10971da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
10981da177e4SLinus Torvalds  * @type:		HCAM type
10991da177e4SLinus Torvalds  * @hostrcb:	hostrcb struct
11001da177e4SLinus Torvalds  *
11011da177e4SLinus Torvalds  * This function will send a Host Controlled Async command to the adapter.
11021da177e4SLinus Torvalds  * If HCAMs are currently not allowed to be issued to the adapter, it will
11031da177e4SLinus Torvalds  * place the hostrcb on the free queue.
11041da177e4SLinus Torvalds  *
11051da177e4SLinus Torvalds  * Return value:
11061da177e4SLinus Torvalds  * 	none
11071da177e4SLinus Torvalds  **/
11081da177e4SLinus Torvalds static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
11091da177e4SLinus Torvalds 			  struct ipr_hostrcb *hostrcb)
11101da177e4SLinus Torvalds {
11111da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd;
11121da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb;
11131da177e4SLinus Torvalds 
111456d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) {
11151da177e4SLinus Torvalds 		ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
111605a6538aSwenxiong@linux.vnet.ibm.com 		list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
11171da177e4SLinus Torvalds 		list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q);
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 		ipr_cmd->u.hostrcb = hostrcb;
11201da177e4SLinus Torvalds 		ioarcb = &ipr_cmd->ioarcb;
11211da177e4SLinus Torvalds 
11221da177e4SLinus Torvalds 		ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
11231da177e4SLinus Torvalds 		ioarcb->cmd_pkt.request_type = IPR_RQTYPE_HCAM;
11241da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[0] = IPR_HOST_CONTROLLED_ASYNC;
11251da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[1] = type;
11261da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[7] = (sizeof(hostrcb->hcam) >> 8) & 0xff;
11271da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[8] = sizeof(hostrcb->hcam) & 0xff;
11281da177e4SLinus Torvalds 
1129a32c055fSWayne Boyer 		ipr_init_ioadl(ipr_cmd, hostrcb->hostrcb_dma,
1130a32c055fSWayne Boyer 			       sizeof(hostrcb->hcam), IPR_IOADL_FLAGS_READ_LAST);
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 		if (type == IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE)
11331da177e4SLinus Torvalds 			ipr_cmd->done = ipr_process_ccn;
11341da177e4SLinus Torvalds 		else
11351da177e4SLinus Torvalds 			ipr_cmd->done = ipr_process_error;
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 		ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR);
11381da177e4SLinus Torvalds 
1139a32c055fSWayne Boyer 		ipr_send_command(ipr_cmd);
11401da177e4SLinus Torvalds 	} else {
11411da177e4SLinus Torvalds 		list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
11421da177e4SLinus Torvalds 	}
11431da177e4SLinus Torvalds }
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds /**
11463e7ebdfaSWayne Boyer  * ipr_update_ata_class - Update the ata class in the resource entry
11471da177e4SLinus Torvalds  * @res:	resource entry struct
11483e7ebdfaSWayne Boyer  * @proto:	cfgte device bus protocol value
11491da177e4SLinus Torvalds  *
11501da177e4SLinus Torvalds  * Return value:
11511da177e4SLinus Torvalds  * 	none
11521da177e4SLinus Torvalds  **/
11533e7ebdfaSWayne Boyer static void ipr_update_ata_class(struct ipr_resource_entry *res, unsigned int proto)
11541da177e4SLinus Torvalds {
11553e7ebdfaSWayne Boyer 	switch (proto) {
11563e7ebdfaSWayne Boyer 	case IPR_PROTO_SATA:
11573e7ebdfaSWayne Boyer 	case IPR_PROTO_SAS_STP:
11583e7ebdfaSWayne Boyer 		res->ata_class = ATA_DEV_ATA;
11593e7ebdfaSWayne Boyer 		break;
11603e7ebdfaSWayne Boyer 	case IPR_PROTO_SATA_ATAPI:
11613e7ebdfaSWayne Boyer 	case IPR_PROTO_SAS_STP_ATAPI:
11623e7ebdfaSWayne Boyer 		res->ata_class = ATA_DEV_ATAPI;
11633e7ebdfaSWayne Boyer 		break;
11643e7ebdfaSWayne Boyer 	default:
11653e7ebdfaSWayne Boyer 		res->ata_class = ATA_DEV_UNKNOWN;
11663e7ebdfaSWayne Boyer 		break;
11674a0b746fSJason Yan 	}
11683e7ebdfaSWayne Boyer }
11693e7ebdfaSWayne Boyer 
11703e7ebdfaSWayne Boyer /**
11713e7ebdfaSWayne Boyer  * ipr_init_res_entry - Initialize a resource entry struct.
11723e7ebdfaSWayne Boyer  * @res:	resource entry struct
11733e7ebdfaSWayne Boyer  * @cfgtew:	config table entry wrapper struct
11743e7ebdfaSWayne Boyer  *
11753e7ebdfaSWayne Boyer  * Return value:
11763e7ebdfaSWayne Boyer  * 	none
11773e7ebdfaSWayne Boyer  **/
11783e7ebdfaSWayne Boyer static void ipr_init_res_entry(struct ipr_resource_entry *res,
11793e7ebdfaSWayne Boyer 			       struct ipr_config_table_entry_wrapper *cfgtew)
11803e7ebdfaSWayne Boyer {
11813e7ebdfaSWayne Boyer 	int found = 0;
11823e7ebdfaSWayne Boyer 	unsigned int proto;
11833e7ebdfaSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = res->ioa_cfg;
11843e7ebdfaSWayne Boyer 	struct ipr_resource_entry *gscsi_res = NULL;
11853e7ebdfaSWayne Boyer 
1186ee0a90faSbrking@us.ibm.com 	res->needs_sync_complete = 0;
11871da177e4SLinus Torvalds 	res->in_erp = 0;
11881da177e4SLinus Torvalds 	res->add_to_ml = 0;
11891da177e4SLinus Torvalds 	res->del_from_ml = 0;
11901da177e4SLinus Torvalds 	res->resetting_device = 0;
11910b1f8d44SWendy Xiong 	res->reset_occurred = 0;
11921da177e4SLinus Torvalds 	res->sdev = NULL;
119335a39691SBrian King 	res->sata_port = NULL;
11943e7ebdfaSWayne Boyer 
11953e7ebdfaSWayne Boyer 	if (ioa_cfg->sis64) {
11963e7ebdfaSWayne Boyer 		proto = cfgtew->u.cfgte64->proto;
1197359d96e7SBrian King 		res->flags = be16_to_cpu(cfgtew->u.cfgte64->flags);
1198359d96e7SBrian King 		res->res_flags = be16_to_cpu(cfgtew->u.cfgte64->res_flags);
11993e7ebdfaSWayne Boyer 		res->qmodel = IPR_QUEUEING_MODEL64(res);
1200438b0331SWayne Boyer 		res->type = cfgtew->u.cfgte64->res_type;
12013e7ebdfaSWayne Boyer 
12023e7ebdfaSWayne Boyer 		memcpy(res->res_path, &cfgtew->u.cfgte64->res_path,
12033e7ebdfaSWayne Boyer 			sizeof(res->res_path));
12043e7ebdfaSWayne Boyer 
12053e7ebdfaSWayne Boyer 		res->bus = 0;
12060cb992edSWayne Boyer 		memcpy(&res->dev_lun.scsi_lun, &cfgtew->u.cfgte64->lun,
12070cb992edSWayne Boyer 			sizeof(res->dev_lun.scsi_lun));
12083e7ebdfaSWayne Boyer 		res->lun = scsilun_to_int(&res->dev_lun);
12093e7ebdfaSWayne Boyer 
12103e7ebdfaSWayne Boyer 		if (res->type == IPR_RES_TYPE_GENERIC_SCSI) {
12113e7ebdfaSWayne Boyer 			list_for_each_entry(gscsi_res, &ioa_cfg->used_res_q, queue) {
12123e7ebdfaSWayne Boyer 				if (gscsi_res->dev_id == cfgtew->u.cfgte64->dev_id) {
12133e7ebdfaSWayne Boyer 					found = 1;
12143e7ebdfaSWayne Boyer 					res->target = gscsi_res->target;
12153e7ebdfaSWayne Boyer 					break;
12163e7ebdfaSWayne Boyer 				}
12173e7ebdfaSWayne Boyer 			}
12183e7ebdfaSWayne Boyer 			if (!found) {
12193e7ebdfaSWayne Boyer 				res->target = find_first_zero_bit(ioa_cfg->target_ids,
12203e7ebdfaSWayne Boyer 								  ioa_cfg->max_devs_supported);
12213e7ebdfaSWayne Boyer 				set_bit(res->target, ioa_cfg->target_ids);
12223e7ebdfaSWayne Boyer 			}
12233e7ebdfaSWayne Boyer 		} else if (res->type == IPR_RES_TYPE_IOAFP) {
12243e7ebdfaSWayne Boyer 			res->bus = IPR_IOAFP_VIRTUAL_BUS;
12253e7ebdfaSWayne Boyer 			res->target = 0;
12263e7ebdfaSWayne Boyer 		} else if (res->type == IPR_RES_TYPE_ARRAY) {
12273e7ebdfaSWayne Boyer 			res->bus = IPR_ARRAY_VIRTUAL_BUS;
12283e7ebdfaSWayne Boyer 			res->target = find_first_zero_bit(ioa_cfg->array_ids,
12293e7ebdfaSWayne Boyer 							  ioa_cfg->max_devs_supported);
12303e7ebdfaSWayne Boyer 			set_bit(res->target, ioa_cfg->array_ids);
12313e7ebdfaSWayne Boyer 		} else if (res->type == IPR_RES_TYPE_VOLUME_SET) {
12323e7ebdfaSWayne Boyer 			res->bus = IPR_VSET_VIRTUAL_BUS;
12333e7ebdfaSWayne Boyer 			res->target = find_first_zero_bit(ioa_cfg->vset_ids,
12343e7ebdfaSWayne Boyer 							  ioa_cfg->max_devs_supported);
12353e7ebdfaSWayne Boyer 			set_bit(res->target, ioa_cfg->vset_ids);
12363e7ebdfaSWayne Boyer 		} else {
12373e7ebdfaSWayne Boyer 			res->target = find_first_zero_bit(ioa_cfg->target_ids,
12383e7ebdfaSWayne Boyer 							  ioa_cfg->max_devs_supported);
12393e7ebdfaSWayne Boyer 			set_bit(res->target, ioa_cfg->target_ids);
12403e7ebdfaSWayne Boyer 		}
12413e7ebdfaSWayne Boyer 	} else {
12423e7ebdfaSWayne Boyer 		proto = cfgtew->u.cfgte->proto;
12433e7ebdfaSWayne Boyer 		res->qmodel = IPR_QUEUEING_MODEL(res);
12443e7ebdfaSWayne Boyer 		res->flags = cfgtew->u.cfgte->flags;
12453e7ebdfaSWayne Boyer 		if (res->flags & IPR_IS_IOA_RESOURCE)
12463e7ebdfaSWayne Boyer 			res->type = IPR_RES_TYPE_IOAFP;
12473e7ebdfaSWayne Boyer 		else
12483e7ebdfaSWayne Boyer 			res->type = cfgtew->u.cfgte->rsvd_subtype & 0x0f;
12493e7ebdfaSWayne Boyer 
12503e7ebdfaSWayne Boyer 		res->bus = cfgtew->u.cfgte->res_addr.bus;
12513e7ebdfaSWayne Boyer 		res->target = cfgtew->u.cfgte->res_addr.target;
12523e7ebdfaSWayne Boyer 		res->lun = cfgtew->u.cfgte->res_addr.lun;
125346d74563SWayne Boyer 		res->lun_wwn = get_unaligned_be64(cfgtew->u.cfgte->lun_wwn);
12543e7ebdfaSWayne Boyer 	}
12553e7ebdfaSWayne Boyer 
12563e7ebdfaSWayne Boyer 	ipr_update_ata_class(res, proto);
12573e7ebdfaSWayne Boyer }
12583e7ebdfaSWayne Boyer 
12593e7ebdfaSWayne Boyer /**
12603e7ebdfaSWayne Boyer  * ipr_is_same_device - Determine if two devices are the same.
12613e7ebdfaSWayne Boyer  * @res:	resource entry struct
12623e7ebdfaSWayne Boyer  * @cfgtew:	config table entry wrapper struct
12633e7ebdfaSWayne Boyer  *
12643e7ebdfaSWayne Boyer  * Return value:
12653e7ebdfaSWayne Boyer  * 	1 if the devices are the same / 0 otherwise
12663e7ebdfaSWayne Boyer  **/
12673e7ebdfaSWayne Boyer static int ipr_is_same_device(struct ipr_resource_entry *res,
12683e7ebdfaSWayne Boyer 			      struct ipr_config_table_entry_wrapper *cfgtew)
12693e7ebdfaSWayne Boyer {
12703e7ebdfaSWayne Boyer 	if (res->ioa_cfg->sis64) {
12713e7ebdfaSWayne Boyer 		if (!memcmp(&res->dev_id, &cfgtew->u.cfgte64->dev_id,
12723e7ebdfaSWayne Boyer 					sizeof(cfgtew->u.cfgte64->dev_id)) &&
12730cb992edSWayne Boyer 			!memcmp(&res->dev_lun.scsi_lun, &cfgtew->u.cfgte64->lun,
12743e7ebdfaSWayne Boyer 					sizeof(cfgtew->u.cfgte64->lun))) {
12753e7ebdfaSWayne Boyer 			return 1;
12763e7ebdfaSWayne Boyer 		}
12773e7ebdfaSWayne Boyer 	} else {
12783e7ebdfaSWayne Boyer 		if (res->bus == cfgtew->u.cfgte->res_addr.bus &&
12793e7ebdfaSWayne Boyer 		    res->target == cfgtew->u.cfgte->res_addr.target &&
12803e7ebdfaSWayne Boyer 		    res->lun == cfgtew->u.cfgte->res_addr.lun)
12813e7ebdfaSWayne Boyer 			return 1;
12823e7ebdfaSWayne Boyer 	}
12833e7ebdfaSWayne Boyer 
12843e7ebdfaSWayne Boyer 	return 0;
12853e7ebdfaSWayne Boyer }
12863e7ebdfaSWayne Boyer 
12873e7ebdfaSWayne Boyer /**
1288b3b3b407SBrian King  * __ipr_format_res_path - Format the resource path for printing.
12893e7ebdfaSWayne Boyer  * @res_path:	resource path
1290a96099e2SLee Jones  * @buffer:	buffer
1291b3b3b407SBrian King  * @len:	length of buffer provided
12923e7ebdfaSWayne Boyer  *
12933e7ebdfaSWayne Boyer  * Return value:
12943e7ebdfaSWayne Boyer  * 	pointer to buffer
12953e7ebdfaSWayne Boyer  **/
1296b3b3b407SBrian King static char *__ipr_format_res_path(u8 *res_path, char *buffer, int len)
12973e7ebdfaSWayne Boyer {
12983e7ebdfaSWayne Boyer 	int i;
12995adcbeb3SWayne Boyer 	char *p = buffer;
13003e7ebdfaSWayne Boyer 
130146d74563SWayne Boyer 	*p = '\0';
13026f0cf424STakashi Iwai 	p += scnprintf(p, buffer + len - p, "%02X", res_path[0]);
13035adcbeb3SWayne Boyer 	for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++)
13046f0cf424STakashi Iwai 		p += scnprintf(p, buffer + len - p, "-%02X", res_path[i]);
13053e7ebdfaSWayne Boyer 
13063e7ebdfaSWayne Boyer 	return buffer;
13073e7ebdfaSWayne Boyer }
13083e7ebdfaSWayne Boyer 
13093e7ebdfaSWayne Boyer /**
1310b3b3b407SBrian King  * ipr_format_res_path - Format the resource path for printing.
1311b3b3b407SBrian King  * @ioa_cfg:	ioa config struct
1312b3b3b407SBrian King  * @res_path:	resource path
1313a96099e2SLee Jones  * @buffer:	buffer
1314b3b3b407SBrian King  * @len:	length of buffer provided
1315b3b3b407SBrian King  *
1316b3b3b407SBrian King  * Return value:
1317b3b3b407SBrian King  *	pointer to buffer
1318b3b3b407SBrian King  **/
1319b3b3b407SBrian King static char *ipr_format_res_path(struct ipr_ioa_cfg *ioa_cfg,
1320b3b3b407SBrian King 				 u8 *res_path, char *buffer, int len)
1321b3b3b407SBrian King {
1322b3b3b407SBrian King 	char *p = buffer;
1323b3b3b407SBrian King 
1324b3b3b407SBrian King 	*p = '\0';
13256f0cf424STakashi Iwai 	p += scnprintf(p, buffer + len - p, "%d/", ioa_cfg->host->host_no);
1326b3b3b407SBrian King 	__ipr_format_res_path(res_path, p, len - (buffer - p));
1327b3b3b407SBrian King 	return buffer;
1328b3b3b407SBrian King }
1329b3b3b407SBrian King 
1330b3b3b407SBrian King /**
13313e7ebdfaSWayne Boyer  * ipr_update_res_entry - Update the resource entry.
13323e7ebdfaSWayne Boyer  * @res:	resource entry struct
13333e7ebdfaSWayne Boyer  * @cfgtew:	config table entry wrapper struct
13343e7ebdfaSWayne Boyer  *
13353e7ebdfaSWayne Boyer  * Return value:
13363e7ebdfaSWayne Boyer  *      none
13373e7ebdfaSWayne Boyer  **/
13383e7ebdfaSWayne Boyer static void ipr_update_res_entry(struct ipr_resource_entry *res,
13393e7ebdfaSWayne Boyer 				 struct ipr_config_table_entry_wrapper *cfgtew)
13403e7ebdfaSWayne Boyer {
13413e7ebdfaSWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
13423e7ebdfaSWayne Boyer 	unsigned int proto;
13433e7ebdfaSWayne Boyer 	int new_path = 0;
13443e7ebdfaSWayne Boyer 
13453e7ebdfaSWayne Boyer 	if (res->ioa_cfg->sis64) {
1346359d96e7SBrian King 		res->flags = be16_to_cpu(cfgtew->u.cfgte64->flags);
1347359d96e7SBrian King 		res->res_flags = be16_to_cpu(cfgtew->u.cfgte64->res_flags);
134875576bb9SWayne Boyer 		res->type = cfgtew->u.cfgte64->res_type;
13493e7ebdfaSWayne Boyer 
13503e7ebdfaSWayne Boyer 		memcpy(&res->std_inq_data, &cfgtew->u.cfgte64->std_inq_data,
13513e7ebdfaSWayne Boyer 			sizeof(struct ipr_std_inq_data));
13523e7ebdfaSWayne Boyer 
13533e7ebdfaSWayne Boyer 		res->qmodel = IPR_QUEUEING_MODEL64(res);
13543e7ebdfaSWayne Boyer 		proto = cfgtew->u.cfgte64->proto;
13553e7ebdfaSWayne Boyer 		res->res_handle = cfgtew->u.cfgte64->res_handle;
13563e7ebdfaSWayne Boyer 		res->dev_id = cfgtew->u.cfgte64->dev_id;
13573e7ebdfaSWayne Boyer 
13583e7ebdfaSWayne Boyer 		memcpy(&res->dev_lun.scsi_lun, &cfgtew->u.cfgte64->lun,
13593e7ebdfaSWayne Boyer 			sizeof(res->dev_lun.scsi_lun));
13603e7ebdfaSWayne Boyer 
13613e7ebdfaSWayne Boyer 		if (memcmp(res->res_path, &cfgtew->u.cfgte64->res_path,
13623e7ebdfaSWayne Boyer 					sizeof(res->res_path))) {
13633e7ebdfaSWayne Boyer 			memcpy(res->res_path, &cfgtew->u.cfgte64->res_path,
13643e7ebdfaSWayne Boyer 				sizeof(res->res_path));
13653e7ebdfaSWayne Boyer 			new_path = 1;
13663e7ebdfaSWayne Boyer 		}
13673e7ebdfaSWayne Boyer 
13683e7ebdfaSWayne Boyer 		if (res->sdev && new_path)
13693e7ebdfaSWayne Boyer 			sdev_printk(KERN_INFO, res->sdev, "Resource path: %s\n",
1370b3b3b407SBrian King 				    ipr_format_res_path(res->ioa_cfg,
1371b3b3b407SBrian King 					res->res_path, buffer, sizeof(buffer)));
13723e7ebdfaSWayne Boyer 	} else {
13733e7ebdfaSWayne Boyer 		res->flags = cfgtew->u.cfgte->flags;
13743e7ebdfaSWayne Boyer 		if (res->flags & IPR_IS_IOA_RESOURCE)
13753e7ebdfaSWayne Boyer 			res->type = IPR_RES_TYPE_IOAFP;
13763e7ebdfaSWayne Boyer 		else
13773e7ebdfaSWayne Boyer 			res->type = cfgtew->u.cfgte->rsvd_subtype & 0x0f;
13783e7ebdfaSWayne Boyer 
13793e7ebdfaSWayne Boyer 		memcpy(&res->std_inq_data, &cfgtew->u.cfgte->std_inq_data,
13803e7ebdfaSWayne Boyer 			sizeof(struct ipr_std_inq_data));
13813e7ebdfaSWayne Boyer 
13823e7ebdfaSWayne Boyer 		res->qmodel = IPR_QUEUEING_MODEL(res);
13833e7ebdfaSWayne Boyer 		proto = cfgtew->u.cfgte->proto;
13843e7ebdfaSWayne Boyer 		res->res_handle = cfgtew->u.cfgte->res_handle;
13853e7ebdfaSWayne Boyer 	}
13863e7ebdfaSWayne Boyer 
13873e7ebdfaSWayne Boyer 	ipr_update_ata_class(res, proto);
13883e7ebdfaSWayne Boyer }
13893e7ebdfaSWayne Boyer 
13903e7ebdfaSWayne Boyer /**
13913e7ebdfaSWayne Boyer  * ipr_clear_res_target - Clear the bit in the bit map representing the target
13923e7ebdfaSWayne Boyer  * 			  for the resource.
13933e7ebdfaSWayne Boyer  * @res:	resource entry struct
13943e7ebdfaSWayne Boyer  *
13953e7ebdfaSWayne Boyer  * Return value:
13963e7ebdfaSWayne Boyer  *      none
13973e7ebdfaSWayne Boyer  **/
13983e7ebdfaSWayne Boyer static void ipr_clear_res_target(struct ipr_resource_entry *res)
13993e7ebdfaSWayne Boyer {
14003e7ebdfaSWayne Boyer 	struct ipr_resource_entry *gscsi_res = NULL;
14013e7ebdfaSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = res->ioa_cfg;
14023e7ebdfaSWayne Boyer 
14033e7ebdfaSWayne Boyer 	if (!ioa_cfg->sis64)
14043e7ebdfaSWayne Boyer 		return;
14053e7ebdfaSWayne Boyer 
14063e7ebdfaSWayne Boyer 	if (res->bus == IPR_ARRAY_VIRTUAL_BUS)
14073e7ebdfaSWayne Boyer 		clear_bit(res->target, ioa_cfg->array_ids);
14083e7ebdfaSWayne Boyer 	else if (res->bus == IPR_VSET_VIRTUAL_BUS)
14093e7ebdfaSWayne Boyer 		clear_bit(res->target, ioa_cfg->vset_ids);
14103e7ebdfaSWayne Boyer 	else if (res->bus == 0 && res->type == IPR_RES_TYPE_GENERIC_SCSI) {
14113e7ebdfaSWayne Boyer 		list_for_each_entry(gscsi_res, &ioa_cfg->used_res_q, queue)
14123e7ebdfaSWayne Boyer 			if (gscsi_res->dev_id == res->dev_id && gscsi_res != res)
14133e7ebdfaSWayne Boyer 				return;
14143e7ebdfaSWayne Boyer 		clear_bit(res->target, ioa_cfg->target_ids);
14153e7ebdfaSWayne Boyer 
14163e7ebdfaSWayne Boyer 	} else if (res->bus == 0)
14173e7ebdfaSWayne Boyer 		clear_bit(res->target, ioa_cfg->target_ids);
14181da177e4SLinus Torvalds }
14191da177e4SLinus Torvalds 
14201da177e4SLinus Torvalds /**
14211da177e4SLinus Torvalds  * ipr_handle_config_change - Handle a config change from the adapter
14221da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
14231da177e4SLinus Torvalds  * @hostrcb:	hostrcb
14241da177e4SLinus Torvalds  *
14251da177e4SLinus Torvalds  * Return value:
14261da177e4SLinus Torvalds  * 	none
14271da177e4SLinus Torvalds  **/
14281da177e4SLinus Torvalds static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg,
14291da177e4SLinus Torvalds 				     struct ipr_hostrcb *hostrcb)
14301da177e4SLinus Torvalds {
14311da177e4SLinus Torvalds 	struct ipr_resource_entry *res = NULL;
14323e7ebdfaSWayne Boyer 	struct ipr_config_table_entry_wrapper cfgtew;
14333e7ebdfaSWayne Boyer 	__be32 cc_res_handle;
14343e7ebdfaSWayne Boyer 
14351da177e4SLinus Torvalds 	u32 is_ndn = 1;
14361da177e4SLinus Torvalds 
14373e7ebdfaSWayne Boyer 	if (ioa_cfg->sis64) {
14383e7ebdfaSWayne Boyer 		cfgtew.u.cfgte64 = &hostrcb->hcam.u.ccn.u.cfgte64;
14393e7ebdfaSWayne Boyer 		cc_res_handle = cfgtew.u.cfgte64->res_handle;
14403e7ebdfaSWayne Boyer 	} else {
14413e7ebdfaSWayne Boyer 		cfgtew.u.cfgte = &hostrcb->hcam.u.ccn.u.cfgte;
14423e7ebdfaSWayne Boyer 		cc_res_handle = cfgtew.u.cfgte->res_handle;
14433e7ebdfaSWayne Boyer 	}
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds 	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
14463e7ebdfaSWayne Boyer 		if (res->res_handle == cc_res_handle) {
14471da177e4SLinus Torvalds 			is_ndn = 0;
14481da177e4SLinus Torvalds 			break;
14491da177e4SLinus Torvalds 		}
14501da177e4SLinus Torvalds 	}
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 	if (is_ndn) {
14531da177e4SLinus Torvalds 		if (list_empty(&ioa_cfg->free_res_q)) {
14541da177e4SLinus Torvalds 			ipr_send_hcam(ioa_cfg,
14551da177e4SLinus Torvalds 				      IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE,
14561da177e4SLinus Torvalds 				      hostrcb);
14571da177e4SLinus Torvalds 			return;
14581da177e4SLinus Torvalds 		}
14591da177e4SLinus Torvalds 
14601da177e4SLinus Torvalds 		res = list_entry(ioa_cfg->free_res_q.next,
14611da177e4SLinus Torvalds 				 struct ipr_resource_entry, queue);
14621da177e4SLinus Torvalds 
14631da177e4SLinus Torvalds 		list_del(&res->queue);
14643e7ebdfaSWayne Boyer 		ipr_init_res_entry(res, &cfgtew);
14651da177e4SLinus Torvalds 		list_add_tail(&res->queue, &ioa_cfg->used_res_q);
14661da177e4SLinus Torvalds 	}
14671da177e4SLinus Torvalds 
14683e7ebdfaSWayne Boyer 	ipr_update_res_entry(res, &cfgtew);
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds 	if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) {
14711da177e4SLinus Torvalds 		if (res->sdev) {
14721da177e4SLinus Torvalds 			res->del_from_ml = 1;
14733e7ebdfaSWayne Boyer 			res->res_handle = IPR_INVALID_RES_HANDLE;
14741da177e4SLinus Torvalds 			schedule_work(&ioa_cfg->work_q);
14753e7ebdfaSWayne Boyer 		} else {
14763e7ebdfaSWayne Boyer 			ipr_clear_res_target(res);
14771da177e4SLinus Torvalds 			list_move_tail(&res->queue, &ioa_cfg->free_res_q);
14783e7ebdfaSWayne Boyer 		}
14795767a1c4SKleber Sacilotto de Souza 	} else if (!res->sdev || res->del_from_ml) {
14801da177e4SLinus Torvalds 		res->add_to_ml = 1;
14811da177e4SLinus Torvalds 		schedule_work(&ioa_cfg->work_q);
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds 
14841da177e4SLinus Torvalds 	ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
14851da177e4SLinus Torvalds }
14861da177e4SLinus Torvalds 
14871da177e4SLinus Torvalds /**
14881da177e4SLinus Torvalds  * ipr_process_ccn - Op done function for a CCN.
14891da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
14901da177e4SLinus Torvalds  *
14911da177e4SLinus Torvalds  * This function is the op done function for a configuration
14921da177e4SLinus Torvalds  * change notification host controlled async from the adapter.
14931da177e4SLinus Torvalds  *
14941da177e4SLinus Torvalds  * Return value:
14951da177e4SLinus Torvalds  * 	none
14961da177e4SLinus Torvalds  **/
14971da177e4SLinus Torvalds static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd)
14981da177e4SLinus Torvalds {
14991da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
15001da177e4SLinus Torvalds 	struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
150196d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
15021da177e4SLinus Torvalds 
1503afc3f83cSBrian King 	list_del_init(&hostrcb->queue);
150405a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
15051da177e4SLinus Torvalds 
15061da177e4SLinus Torvalds 	if (ioasc) {
15074fdd7c7aSBrian King 		if (ioasc != IPR_IOASC_IOA_WAS_RESET &&
15084fdd7c7aSBrian King 		    ioasc != IPR_IOASC_ABORTED_CMD_TERM_BY_HOST)
15091da177e4SLinus Torvalds 			dev_err(&ioa_cfg->pdev->dev,
15101da177e4SLinus Torvalds 				"Host RCB failed with IOASC: 0x%08X\n", ioasc);
15111da177e4SLinus Torvalds 
15121da177e4SLinus Torvalds 		ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
15131da177e4SLinus Torvalds 	} else {
15141da177e4SLinus Torvalds 		ipr_handle_config_change(ioa_cfg, hostrcb);
15151da177e4SLinus Torvalds 	}
15161da177e4SLinus Torvalds }
15171da177e4SLinus Torvalds 
15181da177e4SLinus Torvalds /**
15198cf093e2SBrian King  * strip_and_pad_whitespace - Strip and pad trailing whitespace.
15208cf093e2SBrian King  * @i:		index into buffer
15218cf093e2SBrian King  * @buf:		string to modify
15228cf093e2SBrian King  *
15238cf093e2SBrian King  * This function will strip all trailing whitespace, pad the end
15248cf093e2SBrian King  * of the string with a single space, and NULL terminate the string.
15258cf093e2SBrian King  *
15268cf093e2SBrian King  * Return value:
15278cf093e2SBrian King  * 	new length of string
15288cf093e2SBrian King  **/
15298cf093e2SBrian King static int strip_and_pad_whitespace(int i, char *buf)
15308cf093e2SBrian King {
15318cf093e2SBrian King 	while (i && buf[i] == ' ')
15328cf093e2SBrian King 		i--;
15338cf093e2SBrian King 	buf[i+1] = ' ';
15348cf093e2SBrian King 	buf[i+2] = '\0';
15358cf093e2SBrian King 	return i + 2;
15368cf093e2SBrian King }
15378cf093e2SBrian King 
15388cf093e2SBrian King /**
15398cf093e2SBrian King  * ipr_log_vpd_compact - Log the passed extended VPD compactly.
15408cf093e2SBrian King  * @prefix:		string to print at start of printk
15418cf093e2SBrian King  * @hostrcb:	hostrcb pointer
15428cf093e2SBrian King  * @vpd:		vendor/product id/sn struct
15438cf093e2SBrian King  *
15448cf093e2SBrian King  * Return value:
15458cf093e2SBrian King  * 	none
15468cf093e2SBrian King  **/
15478cf093e2SBrian King static void ipr_log_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb,
15488cf093e2SBrian King 				struct ipr_vpd *vpd)
15498cf093e2SBrian King {
15508cf093e2SBrian King 	char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN + 3];
15518cf093e2SBrian King 	int i = 0;
15528cf093e2SBrian King 
15538cf093e2SBrian King 	memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN);
15548cf093e2SBrian King 	i = strip_and_pad_whitespace(IPR_VENDOR_ID_LEN - 1, buffer);
15558cf093e2SBrian King 
15568cf093e2SBrian King 	memcpy(&buffer[i], vpd->vpids.product_id, IPR_PROD_ID_LEN);
15578cf093e2SBrian King 	i = strip_and_pad_whitespace(i + IPR_PROD_ID_LEN - 1, buffer);
15588cf093e2SBrian King 
15598cf093e2SBrian King 	memcpy(&buffer[i], vpd->sn, IPR_SERIAL_NUM_LEN);
15608cf093e2SBrian King 	buffer[IPR_SERIAL_NUM_LEN + i] = '\0';
15618cf093e2SBrian King 
15628cf093e2SBrian King 	ipr_hcam_err(hostrcb, "%s VPID/SN: %s\n", prefix, buffer);
15638cf093e2SBrian King }
15648cf093e2SBrian King 
15658cf093e2SBrian King /**
15661da177e4SLinus Torvalds  * ipr_log_vpd - Log the passed VPD to the error log.
1567cfc32139Sbrking@us.ibm.com  * @vpd:		vendor/product id/sn struct
15681da177e4SLinus Torvalds  *
15691da177e4SLinus Torvalds  * Return value:
15701da177e4SLinus Torvalds  * 	none
15711da177e4SLinus Torvalds  **/
1572cfc32139Sbrking@us.ibm.com static void ipr_log_vpd(struct ipr_vpd *vpd)
15731da177e4SLinus Torvalds {
15741da177e4SLinus Torvalds 	char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN
15751da177e4SLinus Torvalds 		    + IPR_SERIAL_NUM_LEN];
15761da177e4SLinus Torvalds 
1577cfc32139Sbrking@us.ibm.com 	memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN);
1578cfc32139Sbrking@us.ibm.com 	memcpy(buffer + IPR_VENDOR_ID_LEN, vpd->vpids.product_id,
15791da177e4SLinus Torvalds 	       IPR_PROD_ID_LEN);
15801da177e4SLinus Torvalds 	buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0';
15811da177e4SLinus Torvalds 	ipr_err("Vendor/Product ID: %s\n", buffer);
15821da177e4SLinus Torvalds 
1583cfc32139Sbrking@us.ibm.com 	memcpy(buffer, vpd->sn, IPR_SERIAL_NUM_LEN);
15841da177e4SLinus Torvalds 	buffer[IPR_SERIAL_NUM_LEN] = '\0';
15851da177e4SLinus Torvalds 	ipr_err("    Serial Number: %s\n", buffer);
15861da177e4SLinus Torvalds }
15871da177e4SLinus Torvalds 
15881da177e4SLinus Torvalds /**
15898cf093e2SBrian King  * ipr_log_ext_vpd_compact - Log the passed extended VPD compactly.
15908cf093e2SBrian King  * @prefix:		string to print at start of printk
15918cf093e2SBrian King  * @hostrcb:	hostrcb pointer
15928cf093e2SBrian King  * @vpd:		vendor/product id/sn/wwn struct
15938cf093e2SBrian King  *
15948cf093e2SBrian King  * Return value:
15958cf093e2SBrian King  * 	none
15968cf093e2SBrian King  **/
15978cf093e2SBrian King static void ipr_log_ext_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb,
15988cf093e2SBrian King 				    struct ipr_ext_vpd *vpd)
15998cf093e2SBrian King {
16008cf093e2SBrian King 	ipr_log_vpd_compact(prefix, hostrcb, &vpd->vpd);
16018cf093e2SBrian King 	ipr_hcam_err(hostrcb, "%s WWN: %08X%08X\n", prefix,
16028cf093e2SBrian King 		     be32_to_cpu(vpd->wwid[0]), be32_to_cpu(vpd->wwid[1]));
16038cf093e2SBrian King }
16048cf093e2SBrian King 
16058cf093e2SBrian King /**
1606ee0f05b8Sbrking@us.ibm.com  * ipr_log_ext_vpd - Log the passed extended VPD to the error log.
1607ee0f05b8Sbrking@us.ibm.com  * @vpd:		vendor/product id/sn/wwn struct
1608ee0f05b8Sbrking@us.ibm.com  *
1609ee0f05b8Sbrking@us.ibm.com  * Return value:
1610ee0f05b8Sbrking@us.ibm.com  * 	none
1611ee0f05b8Sbrking@us.ibm.com  **/
1612ee0f05b8Sbrking@us.ibm.com static void ipr_log_ext_vpd(struct ipr_ext_vpd *vpd)
1613ee0f05b8Sbrking@us.ibm.com {
1614ee0f05b8Sbrking@us.ibm.com 	ipr_log_vpd(&vpd->vpd);
1615ee0f05b8Sbrking@us.ibm.com 	ipr_err("    WWN: %08X%08X\n", be32_to_cpu(vpd->wwid[0]),
1616ee0f05b8Sbrking@us.ibm.com 		be32_to_cpu(vpd->wwid[1]));
1617ee0f05b8Sbrking@us.ibm.com }
1618ee0f05b8Sbrking@us.ibm.com 
1619ee0f05b8Sbrking@us.ibm.com /**
1620ee0f05b8Sbrking@us.ibm.com  * ipr_log_enhanced_cache_error - Log a cache error.
1621ee0f05b8Sbrking@us.ibm.com  * @ioa_cfg:	ioa config struct
1622ee0f05b8Sbrking@us.ibm.com  * @hostrcb:	hostrcb struct
1623ee0f05b8Sbrking@us.ibm.com  *
1624ee0f05b8Sbrking@us.ibm.com  * Return value:
1625ee0f05b8Sbrking@us.ibm.com  * 	none
1626ee0f05b8Sbrking@us.ibm.com  **/
1627ee0f05b8Sbrking@us.ibm.com static void ipr_log_enhanced_cache_error(struct ipr_ioa_cfg *ioa_cfg,
1628ee0f05b8Sbrking@us.ibm.com 					 struct ipr_hostrcb *hostrcb)
1629ee0f05b8Sbrking@us.ibm.com {
16304565e370SWayne Boyer 	struct ipr_hostrcb_type_12_error *error;
16314565e370SWayne Boyer 
16324565e370SWayne Boyer 	if (ioa_cfg->sis64)
16334565e370SWayne Boyer 		error = &hostrcb->hcam.u.error64.u.type_12_error;
16344565e370SWayne Boyer 	else
16354565e370SWayne Boyer 		error = &hostrcb->hcam.u.error.u.type_12_error;
1636ee0f05b8Sbrking@us.ibm.com 
1637ee0f05b8Sbrking@us.ibm.com 	ipr_err("-----Current Configuration-----\n");
1638ee0f05b8Sbrking@us.ibm.com 	ipr_err("Cache Directory Card Information:\n");
1639ee0f05b8Sbrking@us.ibm.com 	ipr_log_ext_vpd(&error->ioa_vpd);
1640ee0f05b8Sbrking@us.ibm.com 	ipr_err("Adapter Card Information:\n");
1641ee0f05b8Sbrking@us.ibm.com 	ipr_log_ext_vpd(&error->cfc_vpd);
1642ee0f05b8Sbrking@us.ibm.com 
1643ee0f05b8Sbrking@us.ibm.com 	ipr_err("-----Expected Configuration-----\n");
1644ee0f05b8Sbrking@us.ibm.com 	ipr_err("Cache Directory Card Information:\n");
1645ee0f05b8Sbrking@us.ibm.com 	ipr_log_ext_vpd(&error->ioa_last_attached_to_cfc_vpd);
1646ee0f05b8Sbrking@us.ibm.com 	ipr_err("Adapter Card Information:\n");
1647ee0f05b8Sbrking@us.ibm.com 	ipr_log_ext_vpd(&error->cfc_last_attached_to_ioa_vpd);
1648ee0f05b8Sbrking@us.ibm.com 
1649ee0f05b8Sbrking@us.ibm.com 	ipr_err("Additional IOA Data: %08X %08X %08X\n",
1650ee0f05b8Sbrking@us.ibm.com 		     be32_to_cpu(error->ioa_data[0]),
1651ee0f05b8Sbrking@us.ibm.com 		     be32_to_cpu(error->ioa_data[1]),
1652ee0f05b8Sbrking@us.ibm.com 		     be32_to_cpu(error->ioa_data[2]));
1653ee0f05b8Sbrking@us.ibm.com }
1654ee0f05b8Sbrking@us.ibm.com 
1655ee0f05b8Sbrking@us.ibm.com /**
16561da177e4SLinus Torvalds  * ipr_log_cache_error - Log a cache error.
16571da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
16581da177e4SLinus Torvalds  * @hostrcb:	hostrcb struct
16591da177e4SLinus Torvalds  *
16601da177e4SLinus Torvalds  * Return value:
16611da177e4SLinus Torvalds  * 	none
16621da177e4SLinus Torvalds  **/
16631da177e4SLinus Torvalds static void ipr_log_cache_error(struct ipr_ioa_cfg *ioa_cfg,
16641da177e4SLinus Torvalds 				struct ipr_hostrcb *hostrcb)
16651da177e4SLinus Torvalds {
16661da177e4SLinus Torvalds 	struct ipr_hostrcb_type_02_error *error =
16671da177e4SLinus Torvalds 		&hostrcb->hcam.u.error.u.type_02_error;
16681da177e4SLinus Torvalds 
16691da177e4SLinus Torvalds 	ipr_err("-----Current Configuration-----\n");
16701da177e4SLinus Torvalds 	ipr_err("Cache Directory Card Information:\n");
1671cfc32139Sbrking@us.ibm.com 	ipr_log_vpd(&error->ioa_vpd);
16721da177e4SLinus Torvalds 	ipr_err("Adapter Card Information:\n");
1673cfc32139Sbrking@us.ibm.com 	ipr_log_vpd(&error->cfc_vpd);
16741da177e4SLinus Torvalds 
16751da177e4SLinus Torvalds 	ipr_err("-----Expected Configuration-----\n");
16761da177e4SLinus Torvalds 	ipr_err("Cache Directory Card Information:\n");
1677cfc32139Sbrking@us.ibm.com 	ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpd);
16781da177e4SLinus Torvalds 	ipr_err("Adapter Card Information:\n");
1679cfc32139Sbrking@us.ibm.com 	ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpd);
16801da177e4SLinus Torvalds 
16811da177e4SLinus Torvalds 	ipr_err("Additional IOA Data: %08X %08X %08X\n",
16821da177e4SLinus Torvalds 		     be32_to_cpu(error->ioa_data[0]),
16831da177e4SLinus Torvalds 		     be32_to_cpu(error->ioa_data[1]),
16841da177e4SLinus Torvalds 		     be32_to_cpu(error->ioa_data[2]));
16851da177e4SLinus Torvalds }
16861da177e4SLinus Torvalds 
16871da177e4SLinus Torvalds /**
1688ee0f05b8Sbrking@us.ibm.com  * ipr_log_enhanced_config_error - Log a configuration error.
1689ee0f05b8Sbrking@us.ibm.com  * @ioa_cfg:	ioa config struct
1690ee0f05b8Sbrking@us.ibm.com  * @hostrcb:	hostrcb struct
1691ee0f05b8Sbrking@us.ibm.com  *
1692ee0f05b8Sbrking@us.ibm.com  * Return value:
1693ee0f05b8Sbrking@us.ibm.com  * 	none
1694ee0f05b8Sbrking@us.ibm.com  **/
1695ee0f05b8Sbrking@us.ibm.com static void ipr_log_enhanced_config_error(struct ipr_ioa_cfg *ioa_cfg,
1696ee0f05b8Sbrking@us.ibm.com 					  struct ipr_hostrcb *hostrcb)
1697ee0f05b8Sbrking@us.ibm.com {
1698ee0f05b8Sbrking@us.ibm.com 	int errors_logged, i;
1699ee0f05b8Sbrking@us.ibm.com 	struct ipr_hostrcb_device_data_entry_enhanced *dev_entry;
1700ee0f05b8Sbrking@us.ibm.com 	struct ipr_hostrcb_type_13_error *error;
1701ee0f05b8Sbrking@us.ibm.com 
1702ee0f05b8Sbrking@us.ibm.com 	error = &hostrcb->hcam.u.error.u.type_13_error;
1703ee0f05b8Sbrking@us.ibm.com 	errors_logged = be32_to_cpu(error->errors_logged);
1704ee0f05b8Sbrking@us.ibm.com 
1705ee0f05b8Sbrking@us.ibm.com 	ipr_err("Device Errors Detected/Logged: %d/%d\n",
1706ee0f05b8Sbrking@us.ibm.com 		be32_to_cpu(error->errors_detected), errors_logged);
1707ee0f05b8Sbrking@us.ibm.com 
1708ee0f05b8Sbrking@us.ibm.com 	dev_entry = error->dev;
1709ee0f05b8Sbrking@us.ibm.com 
1710ee0f05b8Sbrking@us.ibm.com 	for (i = 0; i < errors_logged; i++, dev_entry++) {
1711ee0f05b8Sbrking@us.ibm.com 		ipr_err_separator;
1712ee0f05b8Sbrking@us.ibm.com 
1713ee0f05b8Sbrking@us.ibm.com 		ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1);
1714ee0f05b8Sbrking@us.ibm.com 		ipr_log_ext_vpd(&dev_entry->vpd);
1715ee0f05b8Sbrking@us.ibm.com 
1716ee0f05b8Sbrking@us.ibm.com 		ipr_err("-----New Device Information-----\n");
1717ee0f05b8Sbrking@us.ibm.com 		ipr_log_ext_vpd(&dev_entry->new_vpd);
1718ee0f05b8Sbrking@us.ibm.com 
1719ee0f05b8Sbrking@us.ibm.com 		ipr_err("Cache Directory Card Information:\n");
1720ee0f05b8Sbrking@us.ibm.com 		ipr_log_ext_vpd(&dev_entry->ioa_last_with_dev_vpd);
1721ee0f05b8Sbrking@us.ibm.com 
1722ee0f05b8Sbrking@us.ibm.com 		ipr_err("Adapter Card Information:\n");
1723ee0f05b8Sbrking@us.ibm.com 		ipr_log_ext_vpd(&dev_entry->cfc_last_with_dev_vpd);
1724ee0f05b8Sbrking@us.ibm.com 	}
1725ee0f05b8Sbrking@us.ibm.com }
1726ee0f05b8Sbrking@us.ibm.com 
1727ee0f05b8Sbrking@us.ibm.com /**
17284565e370SWayne Boyer  * ipr_log_sis64_config_error - Log a device error.
17294565e370SWayne Boyer  * @ioa_cfg:	ioa config struct
17304565e370SWayne Boyer  * @hostrcb:	hostrcb struct
17314565e370SWayne Boyer  *
17324565e370SWayne Boyer  * Return value:
17334565e370SWayne Boyer  * 	none
17344565e370SWayne Boyer  **/
17354565e370SWayne Boyer static void ipr_log_sis64_config_error(struct ipr_ioa_cfg *ioa_cfg,
17364565e370SWayne Boyer 				       struct ipr_hostrcb *hostrcb)
17374565e370SWayne Boyer {
17384565e370SWayne Boyer 	int errors_logged, i;
17394565e370SWayne Boyer 	struct ipr_hostrcb64_device_data_entry_enhanced *dev_entry;
17404565e370SWayne Boyer 	struct ipr_hostrcb_type_23_error *error;
17414565e370SWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
17424565e370SWayne Boyer 
17434565e370SWayne Boyer 	error = &hostrcb->hcam.u.error64.u.type_23_error;
17444565e370SWayne Boyer 	errors_logged = be32_to_cpu(error->errors_logged);
17454565e370SWayne Boyer 
17464565e370SWayne Boyer 	ipr_err("Device Errors Detected/Logged: %d/%d\n",
17474565e370SWayne Boyer 		be32_to_cpu(error->errors_detected), errors_logged);
17484565e370SWayne Boyer 
17494565e370SWayne Boyer 	dev_entry = error->dev;
17504565e370SWayne Boyer 
17514565e370SWayne Boyer 	for (i = 0; i < errors_logged; i++, dev_entry++) {
17524565e370SWayne Boyer 		ipr_err_separator;
17534565e370SWayne Boyer 
17544565e370SWayne Boyer 		ipr_err("Device %d : %s", i + 1,
1755b3b3b407SBrian King 			__ipr_format_res_path(dev_entry->res_path,
1756b3b3b407SBrian King 					      buffer, sizeof(buffer)));
17574565e370SWayne Boyer 		ipr_log_ext_vpd(&dev_entry->vpd);
17584565e370SWayne Boyer 
17594565e370SWayne Boyer 		ipr_err("-----New Device Information-----\n");
17604565e370SWayne Boyer 		ipr_log_ext_vpd(&dev_entry->new_vpd);
17614565e370SWayne Boyer 
17624565e370SWayne Boyer 		ipr_err("Cache Directory Card Information:\n");
17634565e370SWayne Boyer 		ipr_log_ext_vpd(&dev_entry->ioa_last_with_dev_vpd);
17644565e370SWayne Boyer 
17654565e370SWayne Boyer 		ipr_err("Adapter Card Information:\n");
17664565e370SWayne Boyer 		ipr_log_ext_vpd(&dev_entry->cfc_last_with_dev_vpd);
17674565e370SWayne Boyer 	}
17684565e370SWayne Boyer }
17694565e370SWayne Boyer 
17704565e370SWayne Boyer /**
17711da177e4SLinus Torvalds  * ipr_log_config_error - Log a configuration error.
17721da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
17731da177e4SLinus Torvalds  * @hostrcb:	hostrcb struct
17741da177e4SLinus Torvalds  *
17751da177e4SLinus Torvalds  * Return value:
17761da177e4SLinus Torvalds  * 	none
17771da177e4SLinus Torvalds  **/
17781da177e4SLinus Torvalds static void ipr_log_config_error(struct ipr_ioa_cfg *ioa_cfg,
17791da177e4SLinus Torvalds 				 struct ipr_hostrcb *hostrcb)
17801da177e4SLinus Torvalds {
17811da177e4SLinus Torvalds 	int errors_logged, i;
17821da177e4SLinus Torvalds 	struct ipr_hostrcb_device_data_entry *dev_entry;
17831da177e4SLinus Torvalds 	struct ipr_hostrcb_type_03_error *error;
17841da177e4SLinus Torvalds 
17851da177e4SLinus Torvalds 	error = &hostrcb->hcam.u.error.u.type_03_error;
17861da177e4SLinus Torvalds 	errors_logged = be32_to_cpu(error->errors_logged);
17871da177e4SLinus Torvalds 
17881da177e4SLinus Torvalds 	ipr_err("Device Errors Detected/Logged: %d/%d\n",
17891da177e4SLinus Torvalds 		be32_to_cpu(error->errors_detected), errors_logged);
17901da177e4SLinus Torvalds 
1791cfc32139Sbrking@us.ibm.com 	dev_entry = error->dev;
17921da177e4SLinus Torvalds 
17931da177e4SLinus Torvalds 	for (i = 0; i < errors_logged; i++, dev_entry++) {
17941da177e4SLinus Torvalds 		ipr_err_separator;
17951da177e4SLinus Torvalds 
1796fa15b1f6Sbrking@us.ibm.com 		ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1);
1797cfc32139Sbrking@us.ibm.com 		ipr_log_vpd(&dev_entry->vpd);
17981da177e4SLinus Torvalds 
17991da177e4SLinus Torvalds 		ipr_err("-----New Device Information-----\n");
1800cfc32139Sbrking@us.ibm.com 		ipr_log_vpd(&dev_entry->new_vpd);
18011da177e4SLinus Torvalds 
18021da177e4SLinus Torvalds 		ipr_err("Cache Directory Card Information:\n");
1803cfc32139Sbrking@us.ibm.com 		ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpd);
18041da177e4SLinus Torvalds 
18051da177e4SLinus Torvalds 		ipr_err("Adapter Card Information:\n");
1806cfc32139Sbrking@us.ibm.com 		ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpd);
18071da177e4SLinus Torvalds 
18081da177e4SLinus Torvalds 		ipr_err("Additional IOA Data: %08X %08X %08X %08X %08X\n",
18091da177e4SLinus Torvalds 			be32_to_cpu(dev_entry->ioa_data[0]),
18101da177e4SLinus Torvalds 			be32_to_cpu(dev_entry->ioa_data[1]),
18111da177e4SLinus Torvalds 			be32_to_cpu(dev_entry->ioa_data[2]),
18121da177e4SLinus Torvalds 			be32_to_cpu(dev_entry->ioa_data[3]),
18131da177e4SLinus Torvalds 			be32_to_cpu(dev_entry->ioa_data[4]));
18141da177e4SLinus Torvalds 	}
18151da177e4SLinus Torvalds }
18161da177e4SLinus Torvalds 
18171da177e4SLinus Torvalds /**
1818ee0f05b8Sbrking@us.ibm.com  * ipr_log_enhanced_array_error - Log an array configuration error.
1819ee0f05b8Sbrking@us.ibm.com  * @ioa_cfg:	ioa config struct
1820ee0f05b8Sbrking@us.ibm.com  * @hostrcb:	hostrcb struct
1821ee0f05b8Sbrking@us.ibm.com  *
1822ee0f05b8Sbrking@us.ibm.com  * Return value:
1823ee0f05b8Sbrking@us.ibm.com  * 	none
1824ee0f05b8Sbrking@us.ibm.com  **/
1825ee0f05b8Sbrking@us.ibm.com static void ipr_log_enhanced_array_error(struct ipr_ioa_cfg *ioa_cfg,
1826ee0f05b8Sbrking@us.ibm.com 					 struct ipr_hostrcb *hostrcb)
1827ee0f05b8Sbrking@us.ibm.com {
1828ee0f05b8Sbrking@us.ibm.com 	int i, num_entries;
1829ee0f05b8Sbrking@us.ibm.com 	struct ipr_hostrcb_type_14_error *error;
1830ee0f05b8Sbrking@us.ibm.com 	struct ipr_hostrcb_array_data_entry_enhanced *array_entry;
1831ee0f05b8Sbrking@us.ibm.com 	const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' };
1832ee0f05b8Sbrking@us.ibm.com 
1833ee0f05b8Sbrking@us.ibm.com 	error = &hostrcb->hcam.u.error.u.type_14_error;
1834ee0f05b8Sbrking@us.ibm.com 
1835ee0f05b8Sbrking@us.ibm.com 	ipr_err_separator;
1836ee0f05b8Sbrking@us.ibm.com 
1837ee0f05b8Sbrking@us.ibm.com 	ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n",
1838ee0f05b8Sbrking@us.ibm.com 		error->protection_level,
1839ee0f05b8Sbrking@us.ibm.com 		ioa_cfg->host->host_no,
1840ee0f05b8Sbrking@us.ibm.com 		error->last_func_vset_res_addr.bus,
1841ee0f05b8Sbrking@us.ibm.com 		error->last_func_vset_res_addr.target,
1842ee0f05b8Sbrking@us.ibm.com 		error->last_func_vset_res_addr.lun);
1843ee0f05b8Sbrking@us.ibm.com 
1844ee0f05b8Sbrking@us.ibm.com 	ipr_err_separator;
1845ee0f05b8Sbrking@us.ibm.com 
1846ee0f05b8Sbrking@us.ibm.com 	array_entry = error->array_member;
1847ee0f05b8Sbrking@us.ibm.com 	num_entries = min_t(u32, be32_to_cpu(error->num_entries),
18487262026fSWayne Boyer 			    ARRAY_SIZE(error->array_member));
1849ee0f05b8Sbrking@us.ibm.com 
1850ee0f05b8Sbrking@us.ibm.com 	for (i = 0; i < num_entries; i++, array_entry++) {
1851ee0f05b8Sbrking@us.ibm.com 		if (!memcmp(array_entry->vpd.vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN))
1852ee0f05b8Sbrking@us.ibm.com 			continue;
1853ee0f05b8Sbrking@us.ibm.com 
1854ee0f05b8Sbrking@us.ibm.com 		if (be32_to_cpu(error->exposed_mode_adn) == i)
1855ee0f05b8Sbrking@us.ibm.com 			ipr_err("Exposed Array Member %d:\n", i);
1856ee0f05b8Sbrking@us.ibm.com 		else
1857ee0f05b8Sbrking@us.ibm.com 			ipr_err("Array Member %d:\n", i);
1858ee0f05b8Sbrking@us.ibm.com 
1859ee0f05b8Sbrking@us.ibm.com 		ipr_log_ext_vpd(&array_entry->vpd);
1860ee0f05b8Sbrking@us.ibm.com 		ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location");
1861ee0f05b8Sbrking@us.ibm.com 		ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr,
1862ee0f05b8Sbrking@us.ibm.com 				 "Expected Location");
1863ee0f05b8Sbrking@us.ibm.com 
1864ee0f05b8Sbrking@us.ibm.com 		ipr_err_separator;
1865ee0f05b8Sbrking@us.ibm.com 	}
1866ee0f05b8Sbrking@us.ibm.com }
1867ee0f05b8Sbrking@us.ibm.com 
1868ee0f05b8Sbrking@us.ibm.com /**
18691da177e4SLinus Torvalds  * ipr_log_array_error - Log an array configuration error.
18701da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
18711da177e4SLinus Torvalds  * @hostrcb:	hostrcb struct
18721da177e4SLinus Torvalds  *
18731da177e4SLinus Torvalds  * Return value:
18741da177e4SLinus Torvalds  * 	none
18751da177e4SLinus Torvalds  **/
18761da177e4SLinus Torvalds static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg,
18771da177e4SLinus Torvalds 				struct ipr_hostrcb *hostrcb)
18781da177e4SLinus Torvalds {
18791da177e4SLinus Torvalds 	int i;
18801da177e4SLinus Torvalds 	struct ipr_hostrcb_type_04_error *error;
18811da177e4SLinus Torvalds 	struct ipr_hostrcb_array_data_entry *array_entry;
18821da177e4SLinus Torvalds 	const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' };
18831da177e4SLinus Torvalds 
18841da177e4SLinus Torvalds 	error = &hostrcb->hcam.u.error.u.type_04_error;
18851da177e4SLinus Torvalds 
18861da177e4SLinus Torvalds 	ipr_err_separator;
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds 	ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n",
18891da177e4SLinus Torvalds 		error->protection_level,
18901da177e4SLinus Torvalds 		ioa_cfg->host->host_no,
18911da177e4SLinus Torvalds 		error->last_func_vset_res_addr.bus,
18921da177e4SLinus Torvalds 		error->last_func_vset_res_addr.target,
18931da177e4SLinus Torvalds 		error->last_func_vset_res_addr.lun);
18941da177e4SLinus Torvalds 
18951da177e4SLinus Torvalds 	ipr_err_separator;
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds 	array_entry = error->array_member;
18981da177e4SLinus Torvalds 
18991da177e4SLinus Torvalds 	for (i = 0; i < 18; i++) {
1900cfc32139Sbrking@us.ibm.com 		if (!memcmp(array_entry->vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN))
19011da177e4SLinus Torvalds 			continue;
19021da177e4SLinus Torvalds 
1903fa15b1f6Sbrking@us.ibm.com 		if (be32_to_cpu(error->exposed_mode_adn) == i)
19041da177e4SLinus Torvalds 			ipr_err("Exposed Array Member %d:\n", i);
1905fa15b1f6Sbrking@us.ibm.com 		else
19061da177e4SLinus Torvalds 			ipr_err("Array Member %d:\n", i);
19071da177e4SLinus Torvalds 
1908cfc32139Sbrking@us.ibm.com 		ipr_log_vpd(&array_entry->vpd);
19091da177e4SLinus Torvalds 
1910fa15b1f6Sbrking@us.ibm.com 		ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location");
1911fa15b1f6Sbrking@us.ibm.com 		ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr,
1912fa15b1f6Sbrking@us.ibm.com 				 "Expected Location");
19131da177e4SLinus Torvalds 
19141da177e4SLinus Torvalds 		ipr_err_separator;
19151da177e4SLinus Torvalds 
19161da177e4SLinus Torvalds 		if (i == 9)
19171da177e4SLinus Torvalds 			array_entry = error->array_member2;
19181da177e4SLinus Torvalds 		else
19191da177e4SLinus Torvalds 			array_entry++;
19201da177e4SLinus Torvalds 	}
19211da177e4SLinus Torvalds }
19221da177e4SLinus Torvalds 
19231da177e4SLinus Torvalds /**
1924b0df54bbSbrking@us.ibm.com  * ipr_log_hex_data - Log additional hex IOA error data.
1925ac719abaSBrian King  * @ioa_cfg:	ioa config struct
1926b0df54bbSbrking@us.ibm.com  * @data:		IOA error data
1927b0df54bbSbrking@us.ibm.com  * @len:		data length
1928b0df54bbSbrking@us.ibm.com  *
1929b0df54bbSbrking@us.ibm.com  * Return value:
1930b0df54bbSbrking@us.ibm.com  * 	none
1931b0df54bbSbrking@us.ibm.com  **/
1932359d96e7SBrian King static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, __be32 *data, int len)
1933b0df54bbSbrking@us.ibm.com {
1934b0df54bbSbrking@us.ibm.com 	int i;
1935b0df54bbSbrking@us.ibm.com 
1936b0df54bbSbrking@us.ibm.com 	if (len == 0)
1937b0df54bbSbrking@us.ibm.com 		return;
1938b0df54bbSbrking@us.ibm.com 
1939ac719abaSBrian King 	if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL)
1940ac719abaSBrian King 		len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP);
1941ac719abaSBrian King 
1942b0df54bbSbrking@us.ibm.com 	for (i = 0; i < len / 4; i += 4) {
1943b0df54bbSbrking@us.ibm.com 		ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
1944b0df54bbSbrking@us.ibm.com 			be32_to_cpu(data[i]),
1945b0df54bbSbrking@us.ibm.com 			be32_to_cpu(data[i+1]),
1946b0df54bbSbrking@us.ibm.com 			be32_to_cpu(data[i+2]),
1947b0df54bbSbrking@us.ibm.com 			be32_to_cpu(data[i+3]));
1948b0df54bbSbrking@us.ibm.com 	}
1949b0df54bbSbrking@us.ibm.com }
1950b0df54bbSbrking@us.ibm.com 
1951b0df54bbSbrking@us.ibm.com /**
1952ee0f05b8Sbrking@us.ibm.com  * ipr_log_enhanced_dual_ioa_error - Log an enhanced dual adapter error.
1953ee0f05b8Sbrking@us.ibm.com  * @ioa_cfg:	ioa config struct
1954ee0f05b8Sbrking@us.ibm.com  * @hostrcb:	hostrcb struct
1955ee0f05b8Sbrking@us.ibm.com  *
1956ee0f05b8Sbrking@us.ibm.com  * Return value:
1957ee0f05b8Sbrking@us.ibm.com  * 	none
1958ee0f05b8Sbrking@us.ibm.com  **/
1959ee0f05b8Sbrking@us.ibm.com static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
1960ee0f05b8Sbrking@us.ibm.com 					    struct ipr_hostrcb *hostrcb)
1961ee0f05b8Sbrking@us.ibm.com {
1962ee0f05b8Sbrking@us.ibm.com 	struct ipr_hostrcb_type_17_error *error;
1963ee0f05b8Sbrking@us.ibm.com 
19644565e370SWayne Boyer 	if (ioa_cfg->sis64)
19654565e370SWayne Boyer 		error = &hostrcb->hcam.u.error64.u.type_17_error;
19664565e370SWayne Boyer 	else
1967ee0f05b8Sbrking@us.ibm.com 		error = &hostrcb->hcam.u.error.u.type_17_error;
19684565e370SWayne Boyer 
1969ee0f05b8Sbrking@us.ibm.com 	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
1970ca54cb8cSKOSAKI Motohiro 	strim(error->failure_reason);
1971ee0f05b8Sbrking@us.ibm.com 
19728cf093e2SBrian King 	ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
19738cf093e2SBrian King 		     be32_to_cpu(hostrcb->hcam.u.error.prc));
19748cf093e2SBrian King 	ipr_log_ext_vpd_compact("Remote IOA", hostrcb, &error->vpd);
1975ac719abaSBrian King 	ipr_log_hex_data(ioa_cfg, error->data,
1976ee0f05b8Sbrking@us.ibm.com 			 be32_to_cpu(hostrcb->hcam.length) -
1977ee0f05b8Sbrking@us.ibm.com 			 (offsetof(struct ipr_hostrcb_error, u) +
1978ee0f05b8Sbrking@us.ibm.com 			  offsetof(struct ipr_hostrcb_type_17_error, data)));
1979ee0f05b8Sbrking@us.ibm.com }
1980ee0f05b8Sbrking@us.ibm.com 
1981ee0f05b8Sbrking@us.ibm.com /**
1982b0df54bbSbrking@us.ibm.com  * ipr_log_dual_ioa_error - Log a dual adapter error.
1983b0df54bbSbrking@us.ibm.com  * @ioa_cfg:	ioa config struct
1984b0df54bbSbrking@us.ibm.com  * @hostrcb:	hostrcb struct
1985b0df54bbSbrking@us.ibm.com  *
1986b0df54bbSbrking@us.ibm.com  * Return value:
1987b0df54bbSbrking@us.ibm.com  * 	none
1988b0df54bbSbrking@us.ibm.com  **/
1989b0df54bbSbrking@us.ibm.com static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
1990b0df54bbSbrking@us.ibm.com 				   struct ipr_hostrcb *hostrcb)
1991b0df54bbSbrking@us.ibm.com {
1992b0df54bbSbrking@us.ibm.com 	struct ipr_hostrcb_type_07_error *error;
1993b0df54bbSbrking@us.ibm.com 
1994b0df54bbSbrking@us.ibm.com 	error = &hostrcb->hcam.u.error.u.type_07_error;
1995b0df54bbSbrking@us.ibm.com 	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
1996ca54cb8cSKOSAKI Motohiro 	strim(error->failure_reason);
1997b0df54bbSbrking@us.ibm.com 
19988cf093e2SBrian King 	ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
19998cf093e2SBrian King 		     be32_to_cpu(hostrcb->hcam.u.error.prc));
20008cf093e2SBrian King 	ipr_log_vpd_compact("Remote IOA", hostrcb, &error->vpd);
2001ac719abaSBrian King 	ipr_log_hex_data(ioa_cfg, error->data,
2002b0df54bbSbrking@us.ibm.com 			 be32_to_cpu(hostrcb->hcam.length) -
2003b0df54bbSbrking@us.ibm.com 			 (offsetof(struct ipr_hostrcb_error, u) +
2004b0df54bbSbrking@us.ibm.com 			  offsetof(struct ipr_hostrcb_type_07_error, data)));
2005b0df54bbSbrking@us.ibm.com }
2006b0df54bbSbrking@us.ibm.com 
200749dc6a18SBrian King static const struct {
200849dc6a18SBrian King 	u8 active;
200949dc6a18SBrian King 	char *desc;
201049dc6a18SBrian King } path_active_desc[] = {
201149dc6a18SBrian King 	{ IPR_PATH_NO_INFO, "Path" },
201249dc6a18SBrian King 	{ IPR_PATH_ACTIVE, "Active path" },
201349dc6a18SBrian King 	{ IPR_PATH_NOT_ACTIVE, "Inactive path" }
201449dc6a18SBrian King };
201549dc6a18SBrian King 
201649dc6a18SBrian King static const struct {
201749dc6a18SBrian King 	u8 state;
201849dc6a18SBrian King 	char *desc;
201949dc6a18SBrian King } path_state_desc[] = {
202049dc6a18SBrian King 	{ IPR_PATH_STATE_NO_INFO, "has no path state information available" },
202149dc6a18SBrian King 	{ IPR_PATH_HEALTHY, "is healthy" },
202249dc6a18SBrian King 	{ IPR_PATH_DEGRADED, "is degraded" },
202349dc6a18SBrian King 	{ IPR_PATH_FAILED, "is failed" }
202449dc6a18SBrian King };
202549dc6a18SBrian King 
202649dc6a18SBrian King /**
202749dc6a18SBrian King  * ipr_log_fabric_path - Log a fabric path error
202849dc6a18SBrian King  * @hostrcb:	hostrcb struct
202949dc6a18SBrian King  * @fabric:		fabric descriptor
203049dc6a18SBrian King  *
203149dc6a18SBrian King  * Return value:
203249dc6a18SBrian King  * 	none
203349dc6a18SBrian King  **/
203449dc6a18SBrian King static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb,
203549dc6a18SBrian King 				struct ipr_hostrcb_fabric_desc *fabric)
203649dc6a18SBrian King {
203749dc6a18SBrian King 	int i, j;
203849dc6a18SBrian King 	u8 path_state = fabric->path_state;
203949dc6a18SBrian King 	u8 active = path_state & IPR_PATH_ACTIVE_MASK;
204049dc6a18SBrian King 	u8 state = path_state & IPR_PATH_STATE_MASK;
204149dc6a18SBrian King 
204249dc6a18SBrian King 	for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) {
204349dc6a18SBrian King 		if (path_active_desc[i].active != active)
204449dc6a18SBrian King 			continue;
204549dc6a18SBrian King 
204649dc6a18SBrian King 		for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) {
204749dc6a18SBrian King 			if (path_state_desc[j].state != state)
204849dc6a18SBrian King 				continue;
204949dc6a18SBrian King 
205049dc6a18SBrian King 			if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) {
205149dc6a18SBrian King 				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n",
205249dc6a18SBrian King 					     path_active_desc[i].desc, path_state_desc[j].desc,
205349dc6a18SBrian King 					     fabric->ioa_port);
205449dc6a18SBrian King 			} else if (fabric->cascaded_expander == 0xff) {
205549dc6a18SBrian King 				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n",
205649dc6a18SBrian King 					     path_active_desc[i].desc, path_state_desc[j].desc,
205749dc6a18SBrian King 					     fabric->ioa_port, fabric->phy);
205849dc6a18SBrian King 			} else if (fabric->phy == 0xff) {
205949dc6a18SBrian King 				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n",
206049dc6a18SBrian King 					     path_active_desc[i].desc, path_state_desc[j].desc,
206149dc6a18SBrian King 					     fabric->ioa_port, fabric->cascaded_expander);
206249dc6a18SBrian King 			} else {
206349dc6a18SBrian King 				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n",
206449dc6a18SBrian King 					     path_active_desc[i].desc, path_state_desc[j].desc,
206549dc6a18SBrian King 					     fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
206649dc6a18SBrian King 			}
206749dc6a18SBrian King 			return;
206849dc6a18SBrian King 		}
206949dc6a18SBrian King 	}
207049dc6a18SBrian King 
207149dc6a18SBrian King 	ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state,
207249dc6a18SBrian King 		fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
207349dc6a18SBrian King }
207449dc6a18SBrian King 
20754565e370SWayne Boyer /**
20764565e370SWayne Boyer  * ipr_log64_fabric_path - Log a fabric path error
20774565e370SWayne Boyer  * @hostrcb:	hostrcb struct
20784565e370SWayne Boyer  * @fabric:		fabric descriptor
20794565e370SWayne Boyer  *
20804565e370SWayne Boyer  * Return value:
20814565e370SWayne Boyer  * 	none
20824565e370SWayne Boyer  **/
20834565e370SWayne Boyer static void ipr_log64_fabric_path(struct ipr_hostrcb *hostrcb,
20844565e370SWayne Boyer 				  struct ipr_hostrcb64_fabric_desc *fabric)
20854565e370SWayne Boyer {
20864565e370SWayne Boyer 	int i, j;
20874565e370SWayne Boyer 	u8 path_state = fabric->path_state;
20884565e370SWayne Boyer 	u8 active = path_state & IPR_PATH_ACTIVE_MASK;
20894565e370SWayne Boyer 	u8 state = path_state & IPR_PATH_STATE_MASK;
20904565e370SWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
20914565e370SWayne Boyer 
20924565e370SWayne Boyer 	for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) {
20934565e370SWayne Boyer 		if (path_active_desc[i].active != active)
20944565e370SWayne Boyer 			continue;
20954565e370SWayne Boyer 
20964565e370SWayne Boyer 		for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) {
20974565e370SWayne Boyer 			if (path_state_desc[j].state != state)
20984565e370SWayne Boyer 				continue;
20994565e370SWayne Boyer 
21004565e370SWayne Boyer 			ipr_hcam_err(hostrcb, "%s %s: Resource Path=%s\n",
21014565e370SWayne Boyer 				     path_active_desc[i].desc, path_state_desc[j].desc,
2102b3b3b407SBrian King 				     ipr_format_res_path(hostrcb->ioa_cfg,
2103b3b3b407SBrian King 						fabric->res_path,
2104b3b3b407SBrian King 						buffer, sizeof(buffer)));
21054565e370SWayne Boyer 			return;
21064565e370SWayne Boyer 		}
21074565e370SWayne Boyer 	}
21084565e370SWayne Boyer 
21094565e370SWayne Boyer 	ipr_err("Path state=%02X Resource Path=%s\n", path_state,
2110b3b3b407SBrian King 		ipr_format_res_path(hostrcb->ioa_cfg, fabric->res_path,
2111b3b3b407SBrian King 				    buffer, sizeof(buffer)));
21124565e370SWayne Boyer }
21134565e370SWayne Boyer 
211449dc6a18SBrian King static const struct {
211549dc6a18SBrian King 	u8 type;
211649dc6a18SBrian King 	char *desc;
211749dc6a18SBrian King } path_type_desc[] = {
211849dc6a18SBrian King 	{ IPR_PATH_CFG_IOA_PORT, "IOA port" },
211949dc6a18SBrian King 	{ IPR_PATH_CFG_EXP_PORT, "Expander port" },
212049dc6a18SBrian King 	{ IPR_PATH_CFG_DEVICE_PORT, "Device port" },
212149dc6a18SBrian King 	{ IPR_PATH_CFG_DEVICE_LUN, "Device LUN" }
212249dc6a18SBrian King };
212349dc6a18SBrian King 
212449dc6a18SBrian King static const struct {
212549dc6a18SBrian King 	u8 status;
212649dc6a18SBrian King 	char *desc;
212749dc6a18SBrian King } path_status_desc[] = {
212849dc6a18SBrian King 	{ IPR_PATH_CFG_NO_PROB, "Functional" },
212949dc6a18SBrian King 	{ IPR_PATH_CFG_DEGRADED, "Degraded" },
213049dc6a18SBrian King 	{ IPR_PATH_CFG_FAILED, "Failed" },
213149dc6a18SBrian King 	{ IPR_PATH_CFG_SUSPECT, "Suspect" },
213249dc6a18SBrian King 	{ IPR_PATH_NOT_DETECTED, "Missing" },
213349dc6a18SBrian King 	{ IPR_PATH_INCORRECT_CONN, "Incorrectly connected" }
213449dc6a18SBrian King };
213549dc6a18SBrian King 
213649dc6a18SBrian King static const char *link_rate[] = {
213749dc6a18SBrian King 	"unknown",
213849dc6a18SBrian King 	"disabled",
213949dc6a18SBrian King 	"phy reset problem",
214049dc6a18SBrian King 	"spinup hold",
214149dc6a18SBrian King 	"port selector",
214249dc6a18SBrian King 	"unknown",
214349dc6a18SBrian King 	"unknown",
214449dc6a18SBrian King 	"unknown",
214549dc6a18SBrian King 	"1.5Gbps",
214649dc6a18SBrian King 	"3.0Gbps",
214749dc6a18SBrian King 	"unknown",
214849dc6a18SBrian King 	"unknown",
214949dc6a18SBrian King 	"unknown",
215049dc6a18SBrian King 	"unknown",
215149dc6a18SBrian King 	"unknown",
215249dc6a18SBrian King 	"unknown"
215349dc6a18SBrian King };
215449dc6a18SBrian King 
215549dc6a18SBrian King /**
215649dc6a18SBrian King  * ipr_log_path_elem - Log a fabric path element.
215749dc6a18SBrian King  * @hostrcb:	hostrcb struct
215849dc6a18SBrian King  * @cfg:		fabric path element struct
215949dc6a18SBrian King  *
216049dc6a18SBrian King  * Return value:
216149dc6a18SBrian King  * 	none
216249dc6a18SBrian King  **/
216349dc6a18SBrian King static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb,
216449dc6a18SBrian King 			      struct ipr_hostrcb_config_element *cfg)
216549dc6a18SBrian King {
216649dc6a18SBrian King 	int i, j;
216749dc6a18SBrian King 	u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK;
216849dc6a18SBrian King 	u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK;
216949dc6a18SBrian King 
217049dc6a18SBrian King 	if (type == IPR_PATH_CFG_NOT_EXIST)
217149dc6a18SBrian King 		return;
217249dc6a18SBrian King 
217349dc6a18SBrian King 	for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) {
217449dc6a18SBrian King 		if (path_type_desc[i].type != type)
217549dc6a18SBrian King 			continue;
217649dc6a18SBrian King 
217749dc6a18SBrian King 		for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) {
217849dc6a18SBrian King 			if (path_status_desc[j].status != status)
217949dc6a18SBrian King 				continue;
218049dc6a18SBrian King 
218149dc6a18SBrian King 			if (type == IPR_PATH_CFG_IOA_PORT) {
218249dc6a18SBrian King 				ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n",
218349dc6a18SBrian King 					     path_status_desc[j].desc, path_type_desc[i].desc,
218449dc6a18SBrian King 					     cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
218549dc6a18SBrian King 					     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
218649dc6a18SBrian King 			} else {
218749dc6a18SBrian King 				if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) {
218849dc6a18SBrian King 					ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n",
218949dc6a18SBrian King 						     path_status_desc[j].desc, path_type_desc[i].desc,
219049dc6a18SBrian King 						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
219149dc6a18SBrian King 						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
219249dc6a18SBrian King 				} else if (cfg->cascaded_expander == 0xff) {
219349dc6a18SBrian King 					ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, "
219449dc6a18SBrian King 						     "WWN=%08X%08X\n", path_status_desc[j].desc,
219549dc6a18SBrian King 						     path_type_desc[i].desc, cfg->phy,
219649dc6a18SBrian King 						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
219749dc6a18SBrian King 						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
219849dc6a18SBrian King 				} else if (cfg->phy == 0xff) {
219949dc6a18SBrian King 					ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, "
220049dc6a18SBrian King 						     "WWN=%08X%08X\n", path_status_desc[j].desc,
220149dc6a18SBrian King 						     path_type_desc[i].desc, cfg->cascaded_expander,
220249dc6a18SBrian King 						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
220349dc6a18SBrian King 						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
220449dc6a18SBrian King 				} else {
220549dc6a18SBrian King 					ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s "
220649dc6a18SBrian King 						     "WWN=%08X%08X\n", path_status_desc[j].desc,
220749dc6a18SBrian King 						     path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy,
220849dc6a18SBrian King 						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
220949dc6a18SBrian King 						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
221049dc6a18SBrian King 				}
221149dc6a18SBrian King 			}
221249dc6a18SBrian King 			return;
221349dc6a18SBrian King 		}
221449dc6a18SBrian King 	}
221549dc6a18SBrian King 
221649dc6a18SBrian King 	ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s "
221749dc6a18SBrian King 		     "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy,
221849dc6a18SBrian King 		     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
221949dc6a18SBrian King 		     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
222049dc6a18SBrian King }
222149dc6a18SBrian King 
222249dc6a18SBrian King /**
22234565e370SWayne Boyer  * ipr_log64_path_elem - Log a fabric path element.
22244565e370SWayne Boyer  * @hostrcb:	hostrcb struct
22254565e370SWayne Boyer  * @cfg:		fabric path element struct
22264565e370SWayne Boyer  *
22274565e370SWayne Boyer  * Return value:
22284565e370SWayne Boyer  * 	none
22294565e370SWayne Boyer  **/
22304565e370SWayne Boyer static void ipr_log64_path_elem(struct ipr_hostrcb *hostrcb,
22314565e370SWayne Boyer 				struct ipr_hostrcb64_config_element *cfg)
22324565e370SWayne Boyer {
22334565e370SWayne Boyer 	int i, j;
22344565e370SWayne Boyer 	u8 desc_id = cfg->descriptor_id & IPR_DESCRIPTOR_MASK;
22354565e370SWayne Boyer 	u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK;
22364565e370SWayne Boyer 	u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK;
22374565e370SWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
22384565e370SWayne Boyer 
22394565e370SWayne Boyer 	if (type == IPR_PATH_CFG_NOT_EXIST || desc_id != IPR_DESCRIPTOR_SIS64)
22404565e370SWayne Boyer 		return;
22414565e370SWayne Boyer 
22424565e370SWayne Boyer 	for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) {
22434565e370SWayne Boyer 		if (path_type_desc[i].type != type)
22444565e370SWayne Boyer 			continue;
22454565e370SWayne Boyer 
22464565e370SWayne Boyer 		for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) {
22474565e370SWayne Boyer 			if (path_status_desc[j].status != status)
22484565e370SWayne Boyer 				continue;
22494565e370SWayne Boyer 
22504565e370SWayne Boyer 			ipr_hcam_err(hostrcb, "%s %s: Resource Path=%s, Link rate=%s, WWN=%08X%08X\n",
22514565e370SWayne Boyer 				     path_status_desc[j].desc, path_type_desc[i].desc,
2252b3b3b407SBrian King 				     ipr_format_res_path(hostrcb->ioa_cfg,
2253b3b3b407SBrian King 					cfg->res_path, buffer, sizeof(buffer)),
22544565e370SWayne Boyer 					link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
2255b3b3b407SBrian King 					be32_to_cpu(cfg->wwid[0]),
2256b3b3b407SBrian King 					be32_to_cpu(cfg->wwid[1]));
22574565e370SWayne Boyer 			return;
22584565e370SWayne Boyer 		}
22594565e370SWayne Boyer 	}
22604565e370SWayne Boyer 	ipr_hcam_err(hostrcb, "Path element=%02X: Resource Path=%s, Link rate=%s "
22614565e370SWayne Boyer 		     "WWN=%08X%08X\n", cfg->type_status,
2262b3b3b407SBrian King 		     ipr_format_res_path(hostrcb->ioa_cfg,
2263b3b3b407SBrian King 			cfg->res_path, buffer, sizeof(buffer)),
22644565e370SWayne Boyer 			link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
22654565e370SWayne Boyer 			be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
22664565e370SWayne Boyer }
22674565e370SWayne Boyer 
22684565e370SWayne Boyer /**
226949dc6a18SBrian King  * ipr_log_fabric_error - Log a fabric error.
227049dc6a18SBrian King  * @ioa_cfg:	ioa config struct
227149dc6a18SBrian King  * @hostrcb:	hostrcb struct
227249dc6a18SBrian King  *
227349dc6a18SBrian King  * Return value:
227449dc6a18SBrian King  * 	none
227549dc6a18SBrian King  **/
227649dc6a18SBrian King static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
227749dc6a18SBrian King 				 struct ipr_hostrcb *hostrcb)
227849dc6a18SBrian King {
227949dc6a18SBrian King 	struct ipr_hostrcb_type_20_error *error;
228049dc6a18SBrian King 	struct ipr_hostrcb_fabric_desc *fabric;
228149dc6a18SBrian King 	struct ipr_hostrcb_config_element *cfg;
228249dc6a18SBrian King 	int i, add_len;
228349dc6a18SBrian King 
228449dc6a18SBrian King 	error = &hostrcb->hcam.u.error.u.type_20_error;
228549dc6a18SBrian King 	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
228649dc6a18SBrian King 	ipr_hcam_err(hostrcb, "%s\n", error->failure_reason);
228749dc6a18SBrian King 
228849dc6a18SBrian King 	add_len = be32_to_cpu(hostrcb->hcam.length) -
228949dc6a18SBrian King 		(offsetof(struct ipr_hostrcb_error, u) +
229049dc6a18SBrian King 		 offsetof(struct ipr_hostrcb_type_20_error, desc));
229149dc6a18SBrian King 
229249dc6a18SBrian King 	for (i = 0, fabric = error->desc; i < error->num_entries; i++) {
229349dc6a18SBrian King 		ipr_log_fabric_path(hostrcb, fabric);
229449dc6a18SBrian King 		for_each_fabric_cfg(fabric, cfg)
229549dc6a18SBrian King 			ipr_log_path_elem(hostrcb, cfg);
229649dc6a18SBrian King 
229749dc6a18SBrian King 		add_len -= be16_to_cpu(fabric->length);
229849dc6a18SBrian King 		fabric = (struct ipr_hostrcb_fabric_desc *)
229949dc6a18SBrian King 			((unsigned long)fabric + be16_to_cpu(fabric->length));
230049dc6a18SBrian King 	}
230149dc6a18SBrian King 
2302359d96e7SBrian King 	ipr_log_hex_data(ioa_cfg, (__be32 *)fabric, add_len);
230349dc6a18SBrian King }
230449dc6a18SBrian King 
2305b0df54bbSbrking@us.ibm.com /**
23064565e370SWayne Boyer  * ipr_log_sis64_array_error - Log a sis64 array error.
23074565e370SWayne Boyer  * @ioa_cfg:	ioa config struct
23084565e370SWayne Boyer  * @hostrcb:	hostrcb struct
23094565e370SWayne Boyer  *
23104565e370SWayne Boyer  * Return value:
23114565e370SWayne Boyer  * 	none
23124565e370SWayne Boyer  **/
23134565e370SWayne Boyer static void ipr_log_sis64_array_error(struct ipr_ioa_cfg *ioa_cfg,
23144565e370SWayne Boyer 				      struct ipr_hostrcb *hostrcb)
23154565e370SWayne Boyer {
23164565e370SWayne Boyer 	int i, num_entries;
23174565e370SWayne Boyer 	struct ipr_hostrcb_type_24_error *error;
23184565e370SWayne Boyer 	struct ipr_hostrcb64_array_data_entry *array_entry;
23194565e370SWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
23204565e370SWayne Boyer 	const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' };
23214565e370SWayne Boyer 
23224565e370SWayne Boyer 	error = &hostrcb->hcam.u.error64.u.type_24_error;
23234565e370SWayne Boyer 
23244565e370SWayne Boyer 	ipr_err_separator;
23254565e370SWayne Boyer 
23264565e370SWayne Boyer 	ipr_err("RAID %s Array Configuration: %s\n",
23274565e370SWayne Boyer 		error->protection_level,
2328b3b3b407SBrian King 		ipr_format_res_path(ioa_cfg, error->last_res_path,
2329b3b3b407SBrian King 			buffer, sizeof(buffer)));
23304565e370SWayne Boyer 
23314565e370SWayne Boyer 	ipr_err_separator;
23324565e370SWayne Boyer 
23334565e370SWayne Boyer 	array_entry = error->array_member;
23347262026fSWayne Boyer 	num_entries = min_t(u32, error->num_entries,
23357262026fSWayne Boyer 			    ARRAY_SIZE(error->array_member));
23364565e370SWayne Boyer 
23374565e370SWayne Boyer 	for (i = 0; i < num_entries; i++, array_entry++) {
23384565e370SWayne Boyer 
23394565e370SWayne Boyer 		if (!memcmp(array_entry->vpd.vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN))
23404565e370SWayne Boyer 			continue;
23414565e370SWayne Boyer 
23424565e370SWayne Boyer 		if (error->exposed_mode_adn == i)
23434565e370SWayne Boyer 			ipr_err("Exposed Array Member %d:\n", i);
23444565e370SWayne Boyer 		else
23454565e370SWayne Boyer 			ipr_err("Array Member %d:\n", i);
23464565e370SWayne Boyer 
23474565e370SWayne Boyer 		ipr_err("Array Member %d:\n", i);
23484565e370SWayne Boyer 		ipr_log_ext_vpd(&array_entry->vpd);
23497262026fSWayne Boyer 		ipr_err("Current Location: %s\n",
2350b3b3b407SBrian King 			 ipr_format_res_path(ioa_cfg, array_entry->res_path,
2351b3b3b407SBrian King 				buffer, sizeof(buffer)));
23527262026fSWayne Boyer 		ipr_err("Expected Location: %s\n",
2353b3b3b407SBrian King 			 ipr_format_res_path(ioa_cfg,
2354b3b3b407SBrian King 				array_entry->expected_res_path,
23555adcbeb3SWayne Boyer 				buffer, sizeof(buffer)));
23564565e370SWayne Boyer 
23574565e370SWayne Boyer 		ipr_err_separator;
23584565e370SWayne Boyer 	}
23594565e370SWayne Boyer }
23604565e370SWayne Boyer 
23614565e370SWayne Boyer /**
23624565e370SWayne Boyer  * ipr_log_sis64_fabric_error - Log a sis64 fabric error.
23634565e370SWayne Boyer  * @ioa_cfg:	ioa config struct
23644565e370SWayne Boyer  * @hostrcb:	hostrcb struct
23654565e370SWayne Boyer  *
23664565e370SWayne Boyer  * Return value:
23674565e370SWayne Boyer  * 	none
23684565e370SWayne Boyer  **/
23694565e370SWayne Boyer static void ipr_log_sis64_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
23704565e370SWayne Boyer 				       struct ipr_hostrcb *hostrcb)
23714565e370SWayne Boyer {
23724565e370SWayne Boyer 	struct ipr_hostrcb_type_30_error *error;
23734565e370SWayne Boyer 	struct ipr_hostrcb64_fabric_desc *fabric;
23744565e370SWayne Boyer 	struct ipr_hostrcb64_config_element *cfg;
23754565e370SWayne Boyer 	int i, add_len;
23764565e370SWayne Boyer 
23774565e370SWayne Boyer 	error = &hostrcb->hcam.u.error64.u.type_30_error;
23784565e370SWayne Boyer 
23794565e370SWayne Boyer 	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
23804565e370SWayne Boyer 	ipr_hcam_err(hostrcb, "%s\n", error->failure_reason);
23814565e370SWayne Boyer 
23824565e370SWayne Boyer 	add_len = be32_to_cpu(hostrcb->hcam.length) -
23834565e370SWayne Boyer 		(offsetof(struct ipr_hostrcb64_error, u) +
23844565e370SWayne Boyer 		 offsetof(struct ipr_hostrcb_type_30_error, desc));
23854565e370SWayne Boyer 
23864565e370SWayne Boyer 	for (i = 0, fabric = error->desc; i < error->num_entries; i++) {
23874565e370SWayne Boyer 		ipr_log64_fabric_path(hostrcb, fabric);
23884565e370SWayne Boyer 		for_each_fabric_cfg(fabric, cfg)
23894565e370SWayne Boyer 			ipr_log64_path_elem(hostrcb, cfg);
23904565e370SWayne Boyer 
23914565e370SWayne Boyer 		add_len -= be16_to_cpu(fabric->length);
23924565e370SWayne Boyer 		fabric = (struct ipr_hostrcb64_fabric_desc *)
23934565e370SWayne Boyer 			((unsigned long)fabric + be16_to_cpu(fabric->length));
23944565e370SWayne Boyer 	}
23954565e370SWayne Boyer 
2396359d96e7SBrian King 	ipr_log_hex_data(ioa_cfg, (__be32 *)fabric, add_len);
23974565e370SWayne Boyer }
23984565e370SWayne Boyer 
23994565e370SWayne Boyer /**
240015c5a5e0SWen Xiong  * ipr_log_sis64_service_required_error - Log a sis64 service required error.
240115c5a5e0SWen Xiong  * @ioa_cfg:    ioa config struct
240215c5a5e0SWen Xiong  * @hostrcb:    hostrcb struct
240315c5a5e0SWen Xiong  *
240415c5a5e0SWen Xiong  * Return value:
240515c5a5e0SWen Xiong  *      none
240615c5a5e0SWen Xiong  **/
240715c5a5e0SWen Xiong static void ipr_log_sis64_service_required_error(struct ipr_ioa_cfg *ioa_cfg,
240815c5a5e0SWen Xiong 				       struct ipr_hostrcb *hostrcb)
240915c5a5e0SWen Xiong {
241015c5a5e0SWen Xiong 	struct ipr_hostrcb_type_41_error *error;
241115c5a5e0SWen Xiong 
241215c5a5e0SWen Xiong 	error = &hostrcb->hcam.u.error64.u.type_41_error;
241315c5a5e0SWen Xiong 
241415c5a5e0SWen Xiong 	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
241515c5a5e0SWen Xiong 	ipr_err("Primary Failure Reason: %s\n", error->failure_reason);
241615c5a5e0SWen Xiong 	ipr_log_hex_data(ioa_cfg, error->data,
241715c5a5e0SWen Xiong 			 be32_to_cpu(hostrcb->hcam.length) -
241815c5a5e0SWen Xiong 			 (offsetof(struct ipr_hostrcb_error, u) +
241915c5a5e0SWen Xiong 			  offsetof(struct ipr_hostrcb_type_41_error, data)));
242015c5a5e0SWen Xiong }
242115c5a5e0SWen Xiong /**
24221da177e4SLinus Torvalds  * ipr_log_generic_error - Log an adapter error.
24231da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
24241da177e4SLinus Torvalds  * @hostrcb:	hostrcb struct
24251da177e4SLinus Torvalds  *
24261da177e4SLinus Torvalds  * Return value:
24271da177e4SLinus Torvalds  * 	none
24281da177e4SLinus Torvalds  **/
24291da177e4SLinus Torvalds static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg,
24301da177e4SLinus Torvalds 				  struct ipr_hostrcb *hostrcb)
24311da177e4SLinus Torvalds {
2432ac719abaSBrian King 	ipr_log_hex_data(ioa_cfg, hostrcb->hcam.u.raw.data,
2433b0df54bbSbrking@us.ibm.com 			 be32_to_cpu(hostrcb->hcam.length));
24341da177e4SLinus Torvalds }
24351da177e4SLinus Torvalds 
24361da177e4SLinus Torvalds /**
2437169b9ec8SWendy Xiong  * ipr_log_sis64_device_error - Log a cache error.
2438169b9ec8SWendy Xiong  * @ioa_cfg:	ioa config struct
2439169b9ec8SWendy Xiong  * @hostrcb:	hostrcb struct
2440169b9ec8SWendy Xiong  *
2441169b9ec8SWendy Xiong  * Return value:
2442169b9ec8SWendy Xiong  * 	none
2443169b9ec8SWendy Xiong  **/
2444169b9ec8SWendy Xiong static void ipr_log_sis64_device_error(struct ipr_ioa_cfg *ioa_cfg,
2445169b9ec8SWendy Xiong 					 struct ipr_hostrcb *hostrcb)
2446169b9ec8SWendy Xiong {
2447169b9ec8SWendy Xiong 	struct ipr_hostrcb_type_21_error *error;
2448169b9ec8SWendy Xiong 	char buffer[IPR_MAX_RES_PATH_LENGTH];
2449169b9ec8SWendy Xiong 
2450169b9ec8SWendy Xiong 	error = &hostrcb->hcam.u.error64.u.type_21_error;
2451169b9ec8SWendy Xiong 
2452169b9ec8SWendy Xiong 	ipr_err("-----Failing Device Information-----\n");
2453169b9ec8SWendy Xiong 	ipr_err("World Wide Unique ID: %08X%08X%08X%08X\n",
2454169b9ec8SWendy Xiong 		be32_to_cpu(error->wwn[0]), be32_to_cpu(error->wwn[1]),
2455169b9ec8SWendy Xiong 		 be32_to_cpu(error->wwn[2]), be32_to_cpu(error->wwn[3]));
2456169b9ec8SWendy Xiong 	ipr_err("Device Resource Path: %s\n",
2457169b9ec8SWendy Xiong 		__ipr_format_res_path(error->res_path,
2458169b9ec8SWendy Xiong 				      buffer, sizeof(buffer)));
2459169b9ec8SWendy Xiong 	error->primary_problem_desc[sizeof(error->primary_problem_desc) - 1] = '\0';
2460169b9ec8SWendy Xiong 	error->second_problem_desc[sizeof(error->second_problem_desc) - 1] = '\0';
2461169b9ec8SWendy Xiong 	ipr_err("Primary Problem Description: %s\n", error->primary_problem_desc);
2462169b9ec8SWendy Xiong 	ipr_err("Secondary Problem Description:  %s\n", error->second_problem_desc);
2463169b9ec8SWendy Xiong 	ipr_err("SCSI Sense Data:\n");
2464169b9ec8SWendy Xiong 	ipr_log_hex_data(ioa_cfg, error->sense_data, sizeof(error->sense_data));
2465169b9ec8SWendy Xiong 	ipr_err("SCSI Command Descriptor Block: \n");
2466169b9ec8SWendy Xiong 	ipr_log_hex_data(ioa_cfg, error->cdb, sizeof(error->cdb));
2467169b9ec8SWendy Xiong 
2468169b9ec8SWendy Xiong 	ipr_err("Additional IOA Data:\n");
2469169b9ec8SWendy Xiong 	ipr_log_hex_data(ioa_cfg, error->ioa_data, be32_to_cpu(error->length_of_error));
2470169b9ec8SWendy Xiong }
2471169b9ec8SWendy Xiong 
2472169b9ec8SWendy Xiong /**
24731da177e4SLinus Torvalds  * ipr_get_error - Find the specfied IOASC in the ipr_error_table.
24741da177e4SLinus Torvalds  * @ioasc:	IOASC
24751da177e4SLinus Torvalds  *
24761da177e4SLinus Torvalds  * This function will return the index of into the ipr_error_table
24771da177e4SLinus Torvalds  * for the specified IOASC. If the IOASC is not in the table,
24781da177e4SLinus Torvalds  * 0 will be returned, which points to the entry used for unknown errors.
24791da177e4SLinus Torvalds  *
24801da177e4SLinus Torvalds  * Return value:
24811da177e4SLinus Torvalds  * 	index into the ipr_error_table
24821da177e4SLinus Torvalds  **/
24831da177e4SLinus Torvalds static u32 ipr_get_error(u32 ioasc)
24841da177e4SLinus Torvalds {
24851da177e4SLinus Torvalds 	int i;
24861da177e4SLinus Torvalds 
24871da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++)
248835a39691SBrian King 		if (ipr_error_table[i].ioasc == (ioasc & IPR_IOASC_IOASC_MASK))
24891da177e4SLinus Torvalds 			return i;
24901da177e4SLinus Torvalds 
24911da177e4SLinus Torvalds 	return 0;
24921da177e4SLinus Torvalds }
24931da177e4SLinus Torvalds 
24941da177e4SLinus Torvalds /**
24951da177e4SLinus Torvalds  * ipr_handle_log_data - Log an adapter error.
24961da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
24971da177e4SLinus Torvalds  * @hostrcb:	hostrcb struct
24981da177e4SLinus Torvalds  *
24991da177e4SLinus Torvalds  * This function logs an adapter error to the system.
25001da177e4SLinus Torvalds  *
25011da177e4SLinus Torvalds  * Return value:
25021da177e4SLinus Torvalds  * 	none
25031da177e4SLinus Torvalds  **/
25041da177e4SLinus Torvalds static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
25051da177e4SLinus Torvalds 				struct ipr_hostrcb *hostrcb)
25061da177e4SLinus Torvalds {
25071da177e4SLinus Torvalds 	u32 ioasc;
25081da177e4SLinus Torvalds 	int error_index;
25093185ea63Swenxiong@linux.vnet.ibm.com 	struct ipr_hostrcb_type_21_error *error;
25101da177e4SLinus Torvalds 
25111da177e4SLinus Torvalds 	if (hostrcb->hcam.notify_type != IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY)
25121da177e4SLinus Torvalds 		return;
25131da177e4SLinus Torvalds 
25141da177e4SLinus Torvalds 	if (hostrcb->hcam.notifications_lost == IPR_HOST_RCB_NOTIFICATIONS_LOST)
25151da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev, "Error notifications lost\n");
25161da177e4SLinus Torvalds 
25174565e370SWayne Boyer 	if (ioa_cfg->sis64)
25184565e370SWayne Boyer 		ioasc = be32_to_cpu(hostrcb->hcam.u.error64.fd_ioasc);
25194565e370SWayne Boyer 	else
25204565e370SWayne Boyer 		ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc);
25211da177e4SLinus Torvalds 
25224565e370SWayne Boyer 	if (!ioa_cfg->sis64 && (ioasc == IPR_IOASC_BUS_WAS_RESET ||
25234565e370SWayne Boyer 	    ioasc == IPR_IOASC_BUS_WAS_RESET_BY_OTHER)) {
25241da177e4SLinus Torvalds 		/* Tell the midlayer we had a bus reset so it will handle the UA properly */
25251da177e4SLinus Torvalds 		scsi_report_bus_reset(ioa_cfg->host,
25264565e370SWayne Boyer 				      hostrcb->hcam.u.error.fd_res_addr.bus);
25271da177e4SLinus Torvalds 	}
25281da177e4SLinus Torvalds 
25291da177e4SLinus Torvalds 	error_index = ipr_get_error(ioasc);
25301da177e4SLinus Torvalds 
25311da177e4SLinus Torvalds 	if (!ipr_error_table[error_index].log_hcam)
25321da177e4SLinus Torvalds 		return;
25331da177e4SLinus Torvalds 
25343185ea63Swenxiong@linux.vnet.ibm.com 	if (ioasc == IPR_IOASC_HW_CMD_FAILED &&
25353185ea63Swenxiong@linux.vnet.ibm.com 	    hostrcb->hcam.overlay_id == IPR_HOST_RCB_OVERLAY_ID_21) {
25363185ea63Swenxiong@linux.vnet.ibm.com 		error = &hostrcb->hcam.u.error64.u.type_21_error;
25373185ea63Swenxiong@linux.vnet.ibm.com 
25383185ea63Swenxiong@linux.vnet.ibm.com 		if (((be32_to_cpu(error->sense_data[0]) & 0x0000ff00) >> 8) == ILLEGAL_REQUEST &&
25393185ea63Swenxiong@linux.vnet.ibm.com 			ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL)
25403185ea63Swenxiong@linux.vnet.ibm.com 				return;
25413185ea63Swenxiong@linux.vnet.ibm.com 	}
25423185ea63Swenxiong@linux.vnet.ibm.com 
254349dc6a18SBrian King 	ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error);
25441da177e4SLinus Torvalds 
25451da177e4SLinus Torvalds 	/* Set indication we have logged an error */
25461da177e4SLinus Torvalds 	ioa_cfg->errors_logged++;
25471da177e4SLinus Torvalds 
2548933916f3SBrian King 	if (ioa_cfg->log_level < ipr_error_table[error_index].log_hcam)
25491da177e4SLinus Torvalds 		return;
2550cf852037Sbrking@us.ibm.com 	if (be32_to_cpu(hostrcb->hcam.length) > sizeof(hostrcb->hcam.u.raw))
2551cf852037Sbrking@us.ibm.com 		hostrcb->hcam.length = cpu_to_be32(sizeof(hostrcb->hcam.u.raw));
25521da177e4SLinus Torvalds 
25531da177e4SLinus Torvalds 	switch (hostrcb->hcam.overlay_id) {
25541da177e4SLinus Torvalds 	case IPR_HOST_RCB_OVERLAY_ID_2:
25551da177e4SLinus Torvalds 		ipr_log_cache_error(ioa_cfg, hostrcb);
25561da177e4SLinus Torvalds 		break;
25571da177e4SLinus Torvalds 	case IPR_HOST_RCB_OVERLAY_ID_3:
25581da177e4SLinus Torvalds 		ipr_log_config_error(ioa_cfg, hostrcb);
25591da177e4SLinus Torvalds 		break;
25601da177e4SLinus Torvalds 	case IPR_HOST_RCB_OVERLAY_ID_4:
25611da177e4SLinus Torvalds 	case IPR_HOST_RCB_OVERLAY_ID_6:
25621da177e4SLinus Torvalds 		ipr_log_array_error(ioa_cfg, hostrcb);
25631da177e4SLinus Torvalds 		break;
2564b0df54bbSbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_7:
2565b0df54bbSbrking@us.ibm.com 		ipr_log_dual_ioa_error(ioa_cfg, hostrcb);
2566b0df54bbSbrking@us.ibm.com 		break;
2567ee0f05b8Sbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_12:
2568ee0f05b8Sbrking@us.ibm.com 		ipr_log_enhanced_cache_error(ioa_cfg, hostrcb);
2569ee0f05b8Sbrking@us.ibm.com 		break;
2570ee0f05b8Sbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_13:
2571ee0f05b8Sbrking@us.ibm.com 		ipr_log_enhanced_config_error(ioa_cfg, hostrcb);
2572ee0f05b8Sbrking@us.ibm.com 		break;
2573ee0f05b8Sbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_14:
2574ee0f05b8Sbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_16:
2575ee0f05b8Sbrking@us.ibm.com 		ipr_log_enhanced_array_error(ioa_cfg, hostrcb);
2576ee0f05b8Sbrking@us.ibm.com 		break;
2577ee0f05b8Sbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_17:
2578ee0f05b8Sbrking@us.ibm.com 		ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb);
2579ee0f05b8Sbrking@us.ibm.com 		break;
258049dc6a18SBrian King 	case IPR_HOST_RCB_OVERLAY_ID_20:
258149dc6a18SBrian King 		ipr_log_fabric_error(ioa_cfg, hostrcb);
258249dc6a18SBrian King 		break;
2583169b9ec8SWendy Xiong 	case IPR_HOST_RCB_OVERLAY_ID_21:
2584169b9ec8SWendy Xiong 		ipr_log_sis64_device_error(ioa_cfg, hostrcb);
2585169b9ec8SWendy Xiong 		break;
25864565e370SWayne Boyer 	case IPR_HOST_RCB_OVERLAY_ID_23:
25874565e370SWayne Boyer 		ipr_log_sis64_config_error(ioa_cfg, hostrcb);
25884565e370SWayne Boyer 		break;
25894565e370SWayne Boyer 	case IPR_HOST_RCB_OVERLAY_ID_24:
25904565e370SWayne Boyer 	case IPR_HOST_RCB_OVERLAY_ID_26:
25914565e370SWayne Boyer 		ipr_log_sis64_array_error(ioa_cfg, hostrcb);
25924565e370SWayne Boyer 		break;
25934565e370SWayne Boyer 	case IPR_HOST_RCB_OVERLAY_ID_30:
25944565e370SWayne Boyer 		ipr_log_sis64_fabric_error(ioa_cfg, hostrcb);
25954565e370SWayne Boyer 		break;
259615c5a5e0SWen Xiong 	case IPR_HOST_RCB_OVERLAY_ID_41:
259715c5a5e0SWen Xiong 		ipr_log_sis64_service_required_error(ioa_cfg, hostrcb);
259815c5a5e0SWen Xiong 		break;
2599cf852037Sbrking@us.ibm.com 	case IPR_HOST_RCB_OVERLAY_ID_1:
26001da177e4SLinus Torvalds 	case IPR_HOST_RCB_OVERLAY_ID_DEFAULT:
26011da177e4SLinus Torvalds 	default:
2602a9cfca96Sbrking@us.ibm.com 		ipr_log_generic_error(ioa_cfg, hostrcb);
26031da177e4SLinus Torvalds 		break;
26041da177e4SLinus Torvalds 	}
26051da177e4SLinus Torvalds }
26061da177e4SLinus Torvalds 
2607afc3f83cSBrian King static struct ipr_hostrcb *ipr_get_free_hostrcb(struct ipr_ioa_cfg *ioa)
2608afc3f83cSBrian King {
2609afc3f83cSBrian King 	struct ipr_hostrcb *hostrcb;
2610afc3f83cSBrian King 
2611afc3f83cSBrian King 	hostrcb = list_first_entry_or_null(&ioa->hostrcb_free_q,
2612afc3f83cSBrian King 					struct ipr_hostrcb, queue);
2613afc3f83cSBrian King 
2614afc3f83cSBrian King 	if (unlikely(!hostrcb)) {
2615afc3f83cSBrian King 		dev_info(&ioa->pdev->dev, "Reclaiming async error buffers.");
2616afc3f83cSBrian King 		hostrcb = list_first_entry_or_null(&ioa->hostrcb_report_q,
2617afc3f83cSBrian King 						struct ipr_hostrcb, queue);
2618afc3f83cSBrian King 	}
2619afc3f83cSBrian King 
2620afc3f83cSBrian King 	list_del_init(&hostrcb->queue);
2621afc3f83cSBrian King 	return hostrcb;
2622afc3f83cSBrian King }
2623afc3f83cSBrian King 
26241da177e4SLinus Torvalds /**
26251da177e4SLinus Torvalds  * ipr_process_error - Op done function for an adapter error log.
26261da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
26271da177e4SLinus Torvalds  *
26281da177e4SLinus Torvalds  * This function is the op done function for an error log host
26291da177e4SLinus Torvalds  * controlled async from the adapter. It will log the error and
26301da177e4SLinus Torvalds  * send the HCAM back to the adapter.
26311da177e4SLinus Torvalds  *
26321da177e4SLinus Torvalds  * Return value:
26331da177e4SLinus Torvalds  * 	none
26341da177e4SLinus Torvalds  **/
26351da177e4SLinus Torvalds static void ipr_process_error(struct ipr_cmnd *ipr_cmd)
26361da177e4SLinus Torvalds {
26371da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
26381da177e4SLinus Torvalds 	struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
263996d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
26404565e370SWayne Boyer 	u32 fd_ioasc;
26414565e370SWayne Boyer 
26424565e370SWayne Boyer 	if (ioa_cfg->sis64)
26434565e370SWayne Boyer 		fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error64.fd_ioasc);
26444565e370SWayne Boyer 	else
26454565e370SWayne Boyer 		fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc);
26461da177e4SLinus Torvalds 
2647afc3f83cSBrian King 	list_del_init(&hostrcb->queue);
264805a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
26491da177e4SLinus Torvalds 
26501da177e4SLinus Torvalds 	if (!ioasc) {
26511da177e4SLinus Torvalds 		ipr_handle_log_data(ioa_cfg, hostrcb);
265265f56475SBrian King 		if (fd_ioasc == IPR_IOASC_NR_IOA_RESET_REQUIRED)
265365f56475SBrian King 			ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_ABBREV);
26544fdd7c7aSBrian King 	} else if (ioasc != IPR_IOASC_IOA_WAS_RESET &&
26554fdd7c7aSBrian King 		   ioasc != IPR_IOASC_ABORTED_CMD_TERM_BY_HOST) {
26561da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
26571da177e4SLinus Torvalds 			"Host RCB failed with IOASC: 0x%08X\n", ioasc);
26581da177e4SLinus Torvalds 	}
26591da177e4SLinus Torvalds 
2660afc3f83cSBrian King 	list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_report_q);
26618a4236a2SBrian King 	schedule_work(&ioa_cfg->work_q);
2662afc3f83cSBrian King 	hostrcb = ipr_get_free_hostrcb(ioa_cfg);
2663afc3f83cSBrian King 
26641da177e4SLinus Torvalds 	ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb);
26651da177e4SLinus Torvalds }
26661da177e4SLinus Torvalds 
26671da177e4SLinus Torvalds /**
26681da177e4SLinus Torvalds  * ipr_timeout -  An internally generated op has timed out.
2669a96099e2SLee Jones  * @t: Timer context used to fetch ipr command struct
26701da177e4SLinus Torvalds  *
26711da177e4SLinus Torvalds  * This function blocks host requests and initiates an
26721da177e4SLinus Torvalds  * adapter reset.
26731da177e4SLinus Torvalds  *
26741da177e4SLinus Torvalds  * Return value:
26751da177e4SLinus Torvalds  * 	none
26761da177e4SLinus Torvalds  **/
2677738c6ec5SKees Cook static void ipr_timeout(struct timer_list *t)
26781da177e4SLinus Torvalds {
2679738c6ec5SKees Cook 	struct ipr_cmnd *ipr_cmd = from_timer(ipr_cmd, t, timer);
26801da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
26811da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
26821da177e4SLinus Torvalds 
26831da177e4SLinus Torvalds 	ENTER;
26841da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
26851da177e4SLinus Torvalds 
26861da177e4SLinus Torvalds 	ioa_cfg->errors_logged++;
26871da177e4SLinus Torvalds 	dev_err(&ioa_cfg->pdev->dev,
26881da177e4SLinus Torvalds 		"Adapter being reset due to command timeout.\n");
26891da177e4SLinus Torvalds 
26901da177e4SLinus Torvalds 	if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
26911da177e4SLinus Torvalds 		ioa_cfg->sdt_state = GET_DUMP;
26921da177e4SLinus Torvalds 
26931da177e4SLinus Torvalds 	if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd)
26941da177e4SLinus Torvalds 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
26951da177e4SLinus Torvalds 
26961da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
26971da177e4SLinus Torvalds 	LEAVE;
26981da177e4SLinus Torvalds }
26991da177e4SLinus Torvalds 
27001da177e4SLinus Torvalds /**
27011da177e4SLinus Torvalds  * ipr_oper_timeout -  Adapter timed out transitioning to operational
2702a96099e2SLee Jones  * @t: Timer context used to fetch ipr command struct
27031da177e4SLinus Torvalds  *
27041da177e4SLinus Torvalds  * This function blocks host requests and initiates an
27051da177e4SLinus Torvalds  * adapter reset.
27061da177e4SLinus Torvalds  *
27071da177e4SLinus Torvalds  * Return value:
27081da177e4SLinus Torvalds  * 	none
27091da177e4SLinus Torvalds  **/
2710738c6ec5SKees Cook static void ipr_oper_timeout(struct timer_list *t)
27111da177e4SLinus Torvalds {
2712738c6ec5SKees Cook 	struct ipr_cmnd *ipr_cmd = from_timer(ipr_cmd, t, timer);
27131da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
27141da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
27151da177e4SLinus Torvalds 
27161da177e4SLinus Torvalds 	ENTER;
27171da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
27181da177e4SLinus Torvalds 
27191da177e4SLinus Torvalds 	ioa_cfg->errors_logged++;
27201da177e4SLinus Torvalds 	dev_err(&ioa_cfg->pdev->dev,
27211da177e4SLinus Torvalds 		"Adapter timed out transitioning to operational.\n");
27221da177e4SLinus Torvalds 
27231da177e4SLinus Torvalds 	if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
27241da177e4SLinus Torvalds 		ioa_cfg->sdt_state = GET_DUMP;
27251da177e4SLinus Torvalds 
27261da177e4SLinus Torvalds 	if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd) {
27271da177e4SLinus Torvalds 		if (ipr_fastfail)
27281da177e4SLinus Torvalds 			ioa_cfg->reset_retries += IPR_NUM_RESET_RELOAD_RETRIES;
27291da177e4SLinus Torvalds 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
27301da177e4SLinus Torvalds 	}
27311da177e4SLinus Torvalds 
27321da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
27331da177e4SLinus Torvalds 	LEAVE;
27341da177e4SLinus Torvalds }
27351da177e4SLinus Torvalds 
27361da177e4SLinus Torvalds /**
27371da177e4SLinus Torvalds  * ipr_find_ses_entry - Find matching SES in SES table
27381da177e4SLinus Torvalds  * @res:	resource entry struct of SES
27391da177e4SLinus Torvalds  *
27401da177e4SLinus Torvalds  * Return value:
27411da177e4SLinus Torvalds  * 	pointer to SES table entry / NULL on failure
27421da177e4SLinus Torvalds  **/
27431da177e4SLinus Torvalds static const struct ipr_ses_table_entry *
27441da177e4SLinus Torvalds ipr_find_ses_entry(struct ipr_resource_entry *res)
27451da177e4SLinus Torvalds {
27461da177e4SLinus Torvalds 	int i, j, matches;
27473e7ebdfaSWayne Boyer 	struct ipr_std_inq_vpids *vpids;
27481da177e4SLinus Torvalds 	const struct ipr_ses_table_entry *ste = ipr_ses_table;
27491da177e4SLinus Torvalds 
27501da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(ipr_ses_table); i++, ste++) {
27511da177e4SLinus Torvalds 		for (j = 0, matches = 0; j < IPR_PROD_ID_LEN; j++) {
27521da177e4SLinus Torvalds 			if (ste->compare_product_id_byte[j] == 'X') {
27533e7ebdfaSWayne Boyer 				vpids = &res->std_inq_data.vpids;
27543e7ebdfaSWayne Boyer 				if (vpids->product_id[j] == ste->product_id[j])
27551da177e4SLinus Torvalds 					matches++;
27561da177e4SLinus Torvalds 				else
27571da177e4SLinus Torvalds 					break;
27581da177e4SLinus Torvalds 			} else
27591da177e4SLinus Torvalds 				matches++;
27601da177e4SLinus Torvalds 		}
27611da177e4SLinus Torvalds 
27621da177e4SLinus Torvalds 		if (matches == IPR_PROD_ID_LEN)
27631da177e4SLinus Torvalds 			return ste;
27641da177e4SLinus Torvalds 	}
27651da177e4SLinus Torvalds 
27661da177e4SLinus Torvalds 	return NULL;
27671da177e4SLinus Torvalds }
27681da177e4SLinus Torvalds 
27691da177e4SLinus Torvalds /**
27701da177e4SLinus Torvalds  * ipr_get_max_scsi_speed - Determine max SCSI speed for a given bus
27711da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
27721da177e4SLinus Torvalds  * @bus:		SCSI bus
27731da177e4SLinus Torvalds  * @bus_width:	bus width
27741da177e4SLinus Torvalds  *
27751da177e4SLinus Torvalds  * Return value:
27761da177e4SLinus Torvalds  *	SCSI bus speed in units of 100KHz, 1600 is 160 MHz
27771da177e4SLinus Torvalds  *	For a 2-byte wide SCSI bus, the maximum transfer speed is
27781da177e4SLinus Torvalds  *	twice the maximum transfer rate (e.g. for a wide enabled bus,
27791da177e4SLinus Torvalds  *	max 160MHz = max 320MB/sec).
27801da177e4SLinus Torvalds  **/
27811da177e4SLinus Torvalds static u32 ipr_get_max_scsi_speed(struct ipr_ioa_cfg *ioa_cfg, u8 bus, u8 bus_width)
27821da177e4SLinus Torvalds {
27831da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
27841da177e4SLinus Torvalds 	const struct ipr_ses_table_entry *ste;
27851da177e4SLinus Torvalds 	u32 max_xfer_rate = IPR_MAX_SCSI_RATE(bus_width);
27861da177e4SLinus Torvalds 
27871da177e4SLinus Torvalds 	/* Loop through each config table entry in the config table buffer */
27881da177e4SLinus Torvalds 	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
27893e7ebdfaSWayne Boyer 		if (!(IPR_IS_SES_DEVICE(res->std_inq_data)))
27901da177e4SLinus Torvalds 			continue;
27911da177e4SLinus Torvalds 
27923e7ebdfaSWayne Boyer 		if (bus != res->bus)
27931da177e4SLinus Torvalds 			continue;
27941da177e4SLinus Torvalds 
27951da177e4SLinus Torvalds 		if (!(ste = ipr_find_ses_entry(res)))
27961da177e4SLinus Torvalds 			continue;
27971da177e4SLinus Torvalds 
27981da177e4SLinus Torvalds 		max_xfer_rate = (ste->max_bus_speed_limit * 10) / (bus_width / 8);
27991da177e4SLinus Torvalds 	}
28001da177e4SLinus Torvalds 
28011da177e4SLinus Torvalds 	return max_xfer_rate;
28021da177e4SLinus Torvalds }
28031da177e4SLinus Torvalds 
28041da177e4SLinus Torvalds /**
28051da177e4SLinus Torvalds  * ipr_wait_iodbg_ack - Wait for an IODEBUG ACK from the IOA
28061da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
28071da177e4SLinus Torvalds  * @max_delay:		max delay in micro-seconds to wait
28081da177e4SLinus Torvalds  *
28091da177e4SLinus Torvalds  * Waits for an IODEBUG ACK from the IOA, doing busy looping.
28101da177e4SLinus Torvalds  *
28111da177e4SLinus Torvalds  * Return value:
28121da177e4SLinus Torvalds  * 	0 on success / other on failure
28131da177e4SLinus Torvalds  **/
28141da177e4SLinus Torvalds static int ipr_wait_iodbg_ack(struct ipr_ioa_cfg *ioa_cfg, int max_delay)
28151da177e4SLinus Torvalds {
28161da177e4SLinus Torvalds 	volatile u32 pcii_reg;
28171da177e4SLinus Torvalds 	int delay = 1;
28181da177e4SLinus Torvalds 
28191da177e4SLinus Torvalds 	/* Read interrupt reg until IOA signals IO Debug Acknowledge */
28201da177e4SLinus Torvalds 	while (delay < max_delay) {
28211da177e4SLinus Torvalds 		pcii_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
28221da177e4SLinus Torvalds 
28231da177e4SLinus Torvalds 		if (pcii_reg & IPR_PCII_IO_DEBUG_ACKNOWLEDGE)
28241da177e4SLinus Torvalds 			return 0;
28251da177e4SLinus Torvalds 
28261da177e4SLinus Torvalds 		/* udelay cannot be used if delay is more than a few milliseconds */
28271da177e4SLinus Torvalds 		if ((delay / 1000) > MAX_UDELAY_MS)
28281da177e4SLinus Torvalds 			mdelay(delay / 1000);
28291da177e4SLinus Torvalds 		else
28301da177e4SLinus Torvalds 			udelay(delay);
28311da177e4SLinus Torvalds 
28321da177e4SLinus Torvalds 		delay += delay;
28331da177e4SLinus Torvalds 	}
28341da177e4SLinus Torvalds 	return -EIO;
28351da177e4SLinus Torvalds }
28361da177e4SLinus Torvalds 
28371da177e4SLinus Torvalds /**
2838dcbad00eSWayne Boyer  * ipr_get_sis64_dump_data_section - Dump IOA memory
2839dcbad00eSWayne Boyer  * @ioa_cfg:			ioa config struct
2840dcbad00eSWayne Boyer  * @start_addr:			adapter address to dump
2841dcbad00eSWayne Boyer  * @dest:			destination kernel buffer
2842dcbad00eSWayne Boyer  * @length_in_words:		length to dump in 4 byte words
2843dcbad00eSWayne Boyer  *
2844dcbad00eSWayne Boyer  * Return value:
2845dcbad00eSWayne Boyer  * 	0 on success
2846dcbad00eSWayne Boyer  **/
2847dcbad00eSWayne Boyer static int ipr_get_sis64_dump_data_section(struct ipr_ioa_cfg *ioa_cfg,
2848dcbad00eSWayne Boyer 					   u32 start_addr,
2849dcbad00eSWayne Boyer 					   __be32 *dest, u32 length_in_words)
2850dcbad00eSWayne Boyer {
2851dcbad00eSWayne Boyer 	int i;
2852dcbad00eSWayne Boyer 
2853dcbad00eSWayne Boyer 	for (i = 0; i < length_in_words; i++) {
2854dcbad00eSWayne Boyer 		writel(start_addr+(i*4), ioa_cfg->regs.dump_addr_reg);
2855dcbad00eSWayne Boyer 		*dest = cpu_to_be32(readl(ioa_cfg->regs.dump_data_reg));
2856dcbad00eSWayne Boyer 		dest++;
2857dcbad00eSWayne Boyer 	}
2858dcbad00eSWayne Boyer 
2859dcbad00eSWayne Boyer 	return 0;
2860dcbad00eSWayne Boyer }
2861dcbad00eSWayne Boyer 
2862dcbad00eSWayne Boyer /**
28631da177e4SLinus Torvalds  * ipr_get_ldump_data_section - Dump IOA memory
28641da177e4SLinus Torvalds  * @ioa_cfg:			ioa config struct
28651da177e4SLinus Torvalds  * @start_addr:			adapter address to dump
28661da177e4SLinus Torvalds  * @dest:				destination kernel buffer
28671da177e4SLinus Torvalds  * @length_in_words:	length to dump in 4 byte words
28681da177e4SLinus Torvalds  *
28691da177e4SLinus Torvalds  * Return value:
28701da177e4SLinus Torvalds  * 	0 on success / -EIO on failure
28711da177e4SLinus Torvalds  **/
28721da177e4SLinus Torvalds static int ipr_get_ldump_data_section(struct ipr_ioa_cfg *ioa_cfg,
28731da177e4SLinus Torvalds 				      u32 start_addr,
28741da177e4SLinus Torvalds 				      __be32 *dest, u32 length_in_words)
28751da177e4SLinus Torvalds {
28761da177e4SLinus Torvalds 	volatile u32 temp_pcii_reg;
28771da177e4SLinus Torvalds 	int i, delay = 0;
28781da177e4SLinus Torvalds 
2879dcbad00eSWayne Boyer 	if (ioa_cfg->sis64)
2880dcbad00eSWayne Boyer 		return ipr_get_sis64_dump_data_section(ioa_cfg, start_addr,
2881dcbad00eSWayne Boyer 						       dest, length_in_words);
2882dcbad00eSWayne Boyer 
28831da177e4SLinus Torvalds 	/* Write IOA interrupt reg starting LDUMP state  */
28841da177e4SLinus Torvalds 	writel((IPR_UPROCI_RESET_ALERT | IPR_UPROCI_IO_DEBUG_ALERT),
2885214777baSWayne Boyer 	       ioa_cfg->regs.set_uproc_interrupt_reg32);
28861da177e4SLinus Torvalds 
28871da177e4SLinus Torvalds 	/* Wait for IO debug acknowledge */
28881da177e4SLinus Torvalds 	if (ipr_wait_iodbg_ack(ioa_cfg,
28891da177e4SLinus Torvalds 			       IPR_LDUMP_MAX_LONG_ACK_DELAY_IN_USEC)) {
28901da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
28911da177e4SLinus Torvalds 			"IOA dump long data transfer timeout\n");
28921da177e4SLinus Torvalds 		return -EIO;
28931da177e4SLinus Torvalds 	}
28941da177e4SLinus Torvalds 
28951da177e4SLinus Torvalds 	/* Signal LDUMP interlocked - clear IO debug ack */
28961da177e4SLinus Torvalds 	writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE,
28971da177e4SLinus Torvalds 	       ioa_cfg->regs.clr_interrupt_reg);
28981da177e4SLinus Torvalds 
28991da177e4SLinus Torvalds 	/* Write Mailbox with starting address */
29001da177e4SLinus Torvalds 	writel(start_addr, ioa_cfg->ioa_mailbox);
29011da177e4SLinus Torvalds 
29021da177e4SLinus Torvalds 	/* Signal address valid - clear IOA Reset alert */
29031da177e4SLinus Torvalds 	writel(IPR_UPROCI_RESET_ALERT,
2904214777baSWayne Boyer 	       ioa_cfg->regs.clr_uproc_interrupt_reg32);
29051da177e4SLinus Torvalds 
29061da177e4SLinus Torvalds 	for (i = 0; i < length_in_words; i++) {
29071da177e4SLinus Torvalds 		/* Wait for IO debug acknowledge */
29081da177e4SLinus Torvalds 		if (ipr_wait_iodbg_ack(ioa_cfg,
29091da177e4SLinus Torvalds 				       IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC)) {
29101da177e4SLinus Torvalds 			dev_err(&ioa_cfg->pdev->dev,
29111da177e4SLinus Torvalds 				"IOA dump short data transfer timeout\n");
29121da177e4SLinus Torvalds 			return -EIO;
29131da177e4SLinus Torvalds 		}
29141da177e4SLinus Torvalds 
29151da177e4SLinus Torvalds 		/* Read data from mailbox and increment destination pointer */
29161da177e4SLinus Torvalds 		*dest = cpu_to_be32(readl(ioa_cfg->ioa_mailbox));
29171da177e4SLinus Torvalds 		dest++;
29181da177e4SLinus Torvalds 
29191da177e4SLinus Torvalds 		/* For all but the last word of data, signal data received */
29201da177e4SLinus Torvalds 		if (i < (length_in_words - 1)) {
29211da177e4SLinus Torvalds 			/* Signal dump data received - Clear IO debug Ack */
29221da177e4SLinus Torvalds 			writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE,
29231da177e4SLinus Torvalds 			       ioa_cfg->regs.clr_interrupt_reg);
29241da177e4SLinus Torvalds 		}
29251da177e4SLinus Torvalds 	}
29261da177e4SLinus Torvalds 
29271da177e4SLinus Torvalds 	/* Signal end of block transfer. Set reset alert then clear IO debug ack */
29281da177e4SLinus Torvalds 	writel(IPR_UPROCI_RESET_ALERT,
2929214777baSWayne Boyer 	       ioa_cfg->regs.set_uproc_interrupt_reg32);
29301da177e4SLinus Torvalds 
29311da177e4SLinus Torvalds 	writel(IPR_UPROCI_IO_DEBUG_ALERT,
2932214777baSWayne Boyer 	       ioa_cfg->regs.clr_uproc_interrupt_reg32);
29331da177e4SLinus Torvalds 
29341da177e4SLinus Torvalds 	/* Signal dump data received - Clear IO debug Ack */
29351da177e4SLinus Torvalds 	writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE,
29361da177e4SLinus Torvalds 	       ioa_cfg->regs.clr_interrupt_reg);
29371da177e4SLinus Torvalds 
29381da177e4SLinus Torvalds 	/* Wait for IOA to signal LDUMP exit - IOA reset alert will be cleared */
29391da177e4SLinus Torvalds 	while (delay < IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC) {
29401da177e4SLinus Torvalds 		temp_pcii_reg =
2941214777baSWayne Boyer 		    readl(ioa_cfg->regs.sense_uproc_interrupt_reg32);
29421da177e4SLinus Torvalds 
29431da177e4SLinus Torvalds 		if (!(temp_pcii_reg & IPR_UPROCI_RESET_ALERT))
29441da177e4SLinus Torvalds 			return 0;
29451da177e4SLinus Torvalds 
29461da177e4SLinus Torvalds 		udelay(10);
29471da177e4SLinus Torvalds 		delay += 10;
29481da177e4SLinus Torvalds 	}
29491da177e4SLinus Torvalds 
29501da177e4SLinus Torvalds 	return 0;
29511da177e4SLinus Torvalds }
29521da177e4SLinus Torvalds 
29531da177e4SLinus Torvalds #ifdef CONFIG_SCSI_IPR_DUMP
29541da177e4SLinus Torvalds /**
29551da177e4SLinus Torvalds  * ipr_sdt_copy - Copy Smart Dump Table to kernel buffer
29561da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
29571da177e4SLinus Torvalds  * @pci_address:	adapter address
29581da177e4SLinus Torvalds  * @length:			length of data to copy
29591da177e4SLinus Torvalds  *
29601da177e4SLinus Torvalds  * Copy data from PCI adapter to kernel buffer.
29611da177e4SLinus Torvalds  * Note: length MUST be a 4 byte multiple
29621da177e4SLinus Torvalds  * Return value:
29631da177e4SLinus Torvalds  * 	0 on success / other on failure
29641da177e4SLinus Torvalds  **/
29651da177e4SLinus Torvalds static int ipr_sdt_copy(struct ipr_ioa_cfg *ioa_cfg,
29661da177e4SLinus Torvalds 			unsigned long pci_address, u32 length)
29671da177e4SLinus Torvalds {
29681da177e4SLinus Torvalds 	int bytes_copied = 0;
29694d4dd706SKleber Sacilotto de Souza 	int cur_len, rc, rem_len, rem_page_len, max_dump_size;
29701da177e4SLinus Torvalds 	__be32 *page;
29711da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
29721da177e4SLinus Torvalds 	struct ipr_ioa_dump *ioa_dump = &ioa_cfg->dump->ioa_dump;
29731da177e4SLinus Torvalds 
29744d4dd706SKleber Sacilotto de Souza 	if (ioa_cfg->sis64)
29754d4dd706SKleber Sacilotto de Souza 		max_dump_size = IPR_FMT3_MAX_IOA_DUMP_SIZE;
29764d4dd706SKleber Sacilotto de Souza 	else
29774d4dd706SKleber Sacilotto de Souza 		max_dump_size = IPR_FMT2_MAX_IOA_DUMP_SIZE;
29784d4dd706SKleber Sacilotto de Souza 
29791da177e4SLinus Torvalds 	while (bytes_copied < length &&
29804d4dd706SKleber Sacilotto de Souza 	       (ioa_dump->hdr.len + bytes_copied) < max_dump_size) {
29811da177e4SLinus Torvalds 		if (ioa_dump->page_offset >= PAGE_SIZE ||
29821da177e4SLinus Torvalds 		    ioa_dump->page_offset == 0) {
29831da177e4SLinus Torvalds 			page = (__be32 *)__get_free_page(GFP_ATOMIC);
29841da177e4SLinus Torvalds 
29851da177e4SLinus Torvalds 			if (!page) {
29861da177e4SLinus Torvalds 				ipr_trace;
29871da177e4SLinus Torvalds 				return bytes_copied;
29881da177e4SLinus Torvalds 			}
29891da177e4SLinus Torvalds 
29901da177e4SLinus Torvalds 			ioa_dump->page_offset = 0;
29911da177e4SLinus Torvalds 			ioa_dump->ioa_data[ioa_dump->next_page_index] = page;
29921da177e4SLinus Torvalds 			ioa_dump->next_page_index++;
29931da177e4SLinus Torvalds 		} else
29941da177e4SLinus Torvalds 			page = ioa_dump->ioa_data[ioa_dump->next_page_index - 1];
29951da177e4SLinus Torvalds 
29961da177e4SLinus Torvalds 		rem_len = length - bytes_copied;
29971da177e4SLinus Torvalds 		rem_page_len = PAGE_SIZE - ioa_dump->page_offset;
29981da177e4SLinus Torvalds 		cur_len = min(rem_len, rem_page_len);
29991da177e4SLinus Torvalds 
30001da177e4SLinus Torvalds 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
30011da177e4SLinus Torvalds 		if (ioa_cfg->sdt_state == ABORT_DUMP) {
30021da177e4SLinus Torvalds 			rc = -EIO;
30031da177e4SLinus Torvalds 		} else {
30041da177e4SLinus Torvalds 			rc = ipr_get_ldump_data_section(ioa_cfg,
30051da177e4SLinus Torvalds 							pci_address + bytes_copied,
30061da177e4SLinus Torvalds 							&page[ioa_dump->page_offset / 4],
30071da177e4SLinus Torvalds 							(cur_len / sizeof(u32)));
30081da177e4SLinus Torvalds 		}
30091da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
30101da177e4SLinus Torvalds 
30111da177e4SLinus Torvalds 		if (!rc) {
30121da177e4SLinus Torvalds 			ioa_dump->page_offset += cur_len;
30131da177e4SLinus Torvalds 			bytes_copied += cur_len;
30141da177e4SLinus Torvalds 		} else {
30151da177e4SLinus Torvalds 			ipr_trace;
30161da177e4SLinus Torvalds 			break;
30171da177e4SLinus Torvalds 		}
30181da177e4SLinus Torvalds 		schedule();
30191da177e4SLinus Torvalds 	}
30201da177e4SLinus Torvalds 
30211da177e4SLinus Torvalds 	return bytes_copied;
30221da177e4SLinus Torvalds }
30231da177e4SLinus Torvalds 
30241da177e4SLinus Torvalds /**
30251da177e4SLinus Torvalds  * ipr_init_dump_entry_hdr - Initialize a dump entry header.
30261da177e4SLinus Torvalds  * @hdr:	dump entry header struct
30271da177e4SLinus Torvalds  *
30281da177e4SLinus Torvalds  * Return value:
30291da177e4SLinus Torvalds  * 	nothing
30301da177e4SLinus Torvalds  **/
30311da177e4SLinus Torvalds static void ipr_init_dump_entry_hdr(struct ipr_dump_entry_header *hdr)
30321da177e4SLinus Torvalds {
30331da177e4SLinus Torvalds 	hdr->eye_catcher = IPR_DUMP_EYE_CATCHER;
30341da177e4SLinus Torvalds 	hdr->num_elems = 1;
30351da177e4SLinus Torvalds 	hdr->offset = sizeof(*hdr);
30361da177e4SLinus Torvalds 	hdr->status = IPR_DUMP_STATUS_SUCCESS;
30371da177e4SLinus Torvalds }
30381da177e4SLinus Torvalds 
30391da177e4SLinus Torvalds /**
30401da177e4SLinus Torvalds  * ipr_dump_ioa_type_data - Fill in the adapter type in the dump.
30411da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
30421da177e4SLinus Torvalds  * @driver_dump:	driver dump struct
30431da177e4SLinus Torvalds  *
30441da177e4SLinus Torvalds  * Return value:
30451da177e4SLinus Torvalds  * 	nothing
30461da177e4SLinus Torvalds  **/
30471da177e4SLinus Torvalds static void ipr_dump_ioa_type_data(struct ipr_ioa_cfg *ioa_cfg,
30481da177e4SLinus Torvalds 				   struct ipr_driver_dump *driver_dump)
30491da177e4SLinus Torvalds {
30501da177e4SLinus Torvalds 	struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
30511da177e4SLinus Torvalds 
30521da177e4SLinus Torvalds 	ipr_init_dump_entry_hdr(&driver_dump->ioa_type_entry.hdr);
30531da177e4SLinus Torvalds 	driver_dump->ioa_type_entry.hdr.len =
30541da177e4SLinus Torvalds 		sizeof(struct ipr_dump_ioa_type_entry) -
30551da177e4SLinus Torvalds 		sizeof(struct ipr_dump_entry_header);
30561da177e4SLinus Torvalds 	driver_dump->ioa_type_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
30571da177e4SLinus Torvalds 	driver_dump->ioa_type_entry.hdr.id = IPR_DUMP_DRIVER_TYPE_ID;
30581da177e4SLinus Torvalds 	driver_dump->ioa_type_entry.type = ioa_cfg->type;
30591da177e4SLinus Torvalds 	driver_dump->ioa_type_entry.fw_version = (ucode_vpd->major_release << 24) |
30601da177e4SLinus Torvalds 		(ucode_vpd->card_type << 16) | (ucode_vpd->minor_release[0] << 8) |
30611da177e4SLinus Torvalds 		ucode_vpd->minor_release[1];
30621da177e4SLinus Torvalds 	driver_dump->hdr.num_entries++;
30631da177e4SLinus Torvalds }
30641da177e4SLinus Torvalds 
30651da177e4SLinus Torvalds /**
30661da177e4SLinus Torvalds  * ipr_dump_version_data - Fill in the driver version in the dump.
30671da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
30681da177e4SLinus Torvalds  * @driver_dump:	driver dump struct
30691da177e4SLinus Torvalds  *
30701da177e4SLinus Torvalds  * Return value:
30711da177e4SLinus Torvalds  * 	nothing
30721da177e4SLinus Torvalds  **/
30731da177e4SLinus Torvalds static void ipr_dump_version_data(struct ipr_ioa_cfg *ioa_cfg,
30741da177e4SLinus Torvalds 				  struct ipr_driver_dump *driver_dump)
30751da177e4SLinus Torvalds {
30761da177e4SLinus Torvalds 	ipr_init_dump_entry_hdr(&driver_dump->version_entry.hdr);
30771da177e4SLinus Torvalds 	driver_dump->version_entry.hdr.len =
30781da177e4SLinus Torvalds 		sizeof(struct ipr_dump_version_entry) -
30791da177e4SLinus Torvalds 		sizeof(struct ipr_dump_entry_header);
30801da177e4SLinus Torvalds 	driver_dump->version_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII;
30811da177e4SLinus Torvalds 	driver_dump->version_entry.hdr.id = IPR_DUMP_DRIVER_VERSION_ID;
30821da177e4SLinus Torvalds 	strcpy(driver_dump->version_entry.version, IPR_DRIVER_VERSION);
30831da177e4SLinus Torvalds 	driver_dump->hdr.num_entries++;
30841da177e4SLinus Torvalds }
30851da177e4SLinus Torvalds 
30861da177e4SLinus Torvalds /**
30871da177e4SLinus Torvalds  * ipr_dump_trace_data - Fill in the IOA trace in the dump.
30881da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
30891da177e4SLinus Torvalds  * @driver_dump:	driver dump struct
30901da177e4SLinus Torvalds  *
30911da177e4SLinus Torvalds  * Return value:
30921da177e4SLinus Torvalds  * 	nothing
30931da177e4SLinus Torvalds  **/
30941da177e4SLinus Torvalds static void ipr_dump_trace_data(struct ipr_ioa_cfg *ioa_cfg,
30951da177e4SLinus Torvalds 				   struct ipr_driver_dump *driver_dump)
30961da177e4SLinus Torvalds {
30971da177e4SLinus Torvalds 	ipr_init_dump_entry_hdr(&driver_dump->trace_entry.hdr);
30981da177e4SLinus Torvalds 	driver_dump->trace_entry.hdr.len =
30991da177e4SLinus Torvalds 		sizeof(struct ipr_dump_trace_entry) -
31001da177e4SLinus Torvalds 		sizeof(struct ipr_dump_entry_header);
31011da177e4SLinus Torvalds 	driver_dump->trace_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
31021da177e4SLinus Torvalds 	driver_dump->trace_entry.hdr.id = IPR_DUMP_TRACE_ID;
31031da177e4SLinus Torvalds 	memcpy(driver_dump->trace_entry.trace, ioa_cfg->trace, IPR_TRACE_SIZE);
31041da177e4SLinus Torvalds 	driver_dump->hdr.num_entries++;
31051da177e4SLinus Torvalds }
31061da177e4SLinus Torvalds 
31071da177e4SLinus Torvalds /**
31081da177e4SLinus Torvalds  * ipr_dump_location_data - Fill in the IOA location in the dump.
31091da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
31101da177e4SLinus Torvalds  * @driver_dump:	driver dump struct
31111da177e4SLinus Torvalds  *
31121da177e4SLinus Torvalds  * Return value:
31131da177e4SLinus Torvalds  * 	nothing
31141da177e4SLinus Torvalds  **/
31151da177e4SLinus Torvalds static void ipr_dump_location_data(struct ipr_ioa_cfg *ioa_cfg,
31161da177e4SLinus Torvalds 				   struct ipr_driver_dump *driver_dump)
31171da177e4SLinus Torvalds {
31181da177e4SLinus Torvalds 	ipr_init_dump_entry_hdr(&driver_dump->location_entry.hdr);
31191da177e4SLinus Torvalds 	driver_dump->location_entry.hdr.len =
31201da177e4SLinus Torvalds 		sizeof(struct ipr_dump_location_entry) -
31211da177e4SLinus Torvalds 		sizeof(struct ipr_dump_entry_header);
31221da177e4SLinus Torvalds 	driver_dump->location_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII;
31231da177e4SLinus Torvalds 	driver_dump->location_entry.hdr.id = IPR_DUMP_LOCATION_ID;
312471610f55SKay Sievers 	strcpy(driver_dump->location_entry.location, dev_name(&ioa_cfg->pdev->dev));
31251da177e4SLinus Torvalds 	driver_dump->hdr.num_entries++;
31261da177e4SLinus Torvalds }
31271da177e4SLinus Torvalds 
31281da177e4SLinus Torvalds /**
31291da177e4SLinus Torvalds  * ipr_get_ioa_dump - Perform a dump of the driver and adapter.
31301da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
31311da177e4SLinus Torvalds  * @dump:		dump struct
31321da177e4SLinus Torvalds  *
31331da177e4SLinus Torvalds  * Return value:
31341da177e4SLinus Torvalds  * 	nothing
31351da177e4SLinus Torvalds  **/
31361da177e4SLinus Torvalds static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
31371da177e4SLinus Torvalds {
31381da177e4SLinus Torvalds 	unsigned long start_addr, sdt_word;
31391da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
31401da177e4SLinus Torvalds 	struct ipr_driver_dump *driver_dump = &dump->driver_dump;
31411da177e4SLinus Torvalds 	struct ipr_ioa_dump *ioa_dump = &dump->ioa_dump;
31424d4dd706SKleber Sacilotto de Souza 	u32 num_entries, max_num_entries, start_off, end_off;
31434d4dd706SKleber Sacilotto de Souza 	u32 max_dump_size, bytes_to_copy, bytes_copied, rc;
31441da177e4SLinus Torvalds 	struct ipr_sdt *sdt;
3145dcbad00eSWayne Boyer 	int valid = 1;
31461da177e4SLinus Torvalds 	int i;
31471da177e4SLinus Torvalds 
31481da177e4SLinus Torvalds 	ENTER;
31491da177e4SLinus Torvalds 
31501da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
31511da177e4SLinus Torvalds 
315241e9a696SBrian King 	if (ioa_cfg->sdt_state != READ_DUMP) {
31531da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
31541da177e4SLinus Torvalds 		return;
31551da177e4SLinus Torvalds 	}
31561da177e4SLinus Torvalds 
3157110def85SWayne Boyer 	if (ioa_cfg->sis64) {
3158110def85SWayne Boyer 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3159110def85SWayne Boyer 		ssleep(IPR_DUMP_DELAY_SECONDS);
3160110def85SWayne Boyer 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3161110def85SWayne Boyer 	}
3162110def85SWayne Boyer 
31631da177e4SLinus Torvalds 	start_addr = readl(ioa_cfg->ioa_mailbox);
31641da177e4SLinus Torvalds 
3165dcbad00eSWayne Boyer 	if (!ioa_cfg->sis64 && !ipr_sdt_is_fmt2(start_addr)) {
31661da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
31671da177e4SLinus Torvalds 			"Invalid dump table format: %lx\n", start_addr);
31681da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
31691da177e4SLinus Torvalds 		return;
31701da177e4SLinus Torvalds 	}
31711da177e4SLinus Torvalds 
31721da177e4SLinus Torvalds 	dev_err(&ioa_cfg->pdev->dev, "Dump of IOA initiated\n");
31731da177e4SLinus Torvalds 
31741da177e4SLinus Torvalds 	driver_dump->hdr.eye_catcher = IPR_DUMP_EYE_CATCHER;
31751da177e4SLinus Torvalds 
31761da177e4SLinus Torvalds 	/* Initialize the overall dump header */
31771da177e4SLinus Torvalds 	driver_dump->hdr.len = sizeof(struct ipr_driver_dump);
31781da177e4SLinus Torvalds 	driver_dump->hdr.num_entries = 1;
31791da177e4SLinus Torvalds 	driver_dump->hdr.first_entry_offset = sizeof(struct ipr_dump_header);
31801da177e4SLinus Torvalds 	driver_dump->hdr.status = IPR_DUMP_STATUS_SUCCESS;
31811da177e4SLinus Torvalds 	driver_dump->hdr.os = IPR_DUMP_OS_LINUX;
31821da177e4SLinus Torvalds 	driver_dump->hdr.driver_name = IPR_DUMP_DRIVER_NAME;
31831da177e4SLinus Torvalds 
31841da177e4SLinus Torvalds 	ipr_dump_version_data(ioa_cfg, driver_dump);
31851da177e4SLinus Torvalds 	ipr_dump_location_data(ioa_cfg, driver_dump);
31861da177e4SLinus Torvalds 	ipr_dump_ioa_type_data(ioa_cfg, driver_dump);
31871da177e4SLinus Torvalds 	ipr_dump_trace_data(ioa_cfg, driver_dump);
31881da177e4SLinus Torvalds 
31891da177e4SLinus Torvalds 	/* Update dump_header */
31901da177e4SLinus Torvalds 	driver_dump->hdr.len += sizeof(struct ipr_dump_entry_header);
31911da177e4SLinus Torvalds 
31921da177e4SLinus Torvalds 	/* IOA Dump entry */
31931da177e4SLinus Torvalds 	ipr_init_dump_entry_hdr(&ioa_dump->hdr);
31941da177e4SLinus Torvalds 	ioa_dump->hdr.len = 0;
31951da177e4SLinus Torvalds 	ioa_dump->hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
31961da177e4SLinus Torvalds 	ioa_dump->hdr.id = IPR_DUMP_IOA_DUMP_ID;
31971da177e4SLinus Torvalds 
31981da177e4SLinus Torvalds 	/* First entries in sdt are actually a list of dump addresses and
31991da177e4SLinus Torvalds 	 lengths to gather the real dump data.  sdt represents the pointer
32001da177e4SLinus Torvalds 	 to the ioa generated dump table.  Dump data will be extracted based
32011da177e4SLinus Torvalds 	 on entries in this table */
32021da177e4SLinus Torvalds 	sdt = &ioa_dump->sdt;
32031da177e4SLinus Torvalds 
32044d4dd706SKleber Sacilotto de Souza 	if (ioa_cfg->sis64) {
32054d4dd706SKleber Sacilotto de Souza 		max_num_entries = IPR_FMT3_NUM_SDT_ENTRIES;
32064d4dd706SKleber Sacilotto de Souza 		max_dump_size = IPR_FMT3_MAX_IOA_DUMP_SIZE;
32074d4dd706SKleber Sacilotto de Souza 	} else {
32084d4dd706SKleber Sacilotto de Souza 		max_num_entries = IPR_FMT2_NUM_SDT_ENTRIES;
32094d4dd706SKleber Sacilotto de Souza 		max_dump_size = IPR_FMT2_MAX_IOA_DUMP_SIZE;
32104d4dd706SKleber Sacilotto de Souza 	}
32114d4dd706SKleber Sacilotto de Souza 
32124d4dd706SKleber Sacilotto de Souza 	bytes_to_copy = offsetof(struct ipr_sdt, entry) +
32134d4dd706SKleber Sacilotto de Souza 			(max_num_entries * sizeof(struct ipr_sdt_entry));
32141da177e4SLinus Torvalds 	rc = ipr_get_ldump_data_section(ioa_cfg, start_addr, (__be32 *)sdt,
32154d4dd706SKleber Sacilotto de Souza 					bytes_to_copy / sizeof(__be32));
32161da177e4SLinus Torvalds 
32171da177e4SLinus Torvalds 	/* Smart Dump table is ready to use and the first entry is valid */
3218dcbad00eSWayne Boyer 	if (rc || ((be32_to_cpu(sdt->hdr.state) != IPR_FMT3_SDT_READY_TO_USE) &&
3219dcbad00eSWayne Boyer 	    (be32_to_cpu(sdt->hdr.state) != IPR_FMT2_SDT_READY_TO_USE))) {
32201da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
32211da177e4SLinus Torvalds 			"Dump of IOA failed. Dump table not valid: %d, %X.\n",
32221da177e4SLinus Torvalds 			rc, be32_to_cpu(sdt->hdr.state));
32231da177e4SLinus Torvalds 		driver_dump->hdr.status = IPR_DUMP_STATUS_FAILED;
32241da177e4SLinus Torvalds 		ioa_cfg->sdt_state = DUMP_OBTAINED;
32251da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
32261da177e4SLinus Torvalds 		return;
32271da177e4SLinus Torvalds 	}
32281da177e4SLinus Torvalds 
32291da177e4SLinus Torvalds 	num_entries = be32_to_cpu(sdt->hdr.num_entries_used);
32301da177e4SLinus Torvalds 
32314d4dd706SKleber Sacilotto de Souza 	if (num_entries > max_num_entries)
32324d4dd706SKleber Sacilotto de Souza 		num_entries = max_num_entries;
32334d4dd706SKleber Sacilotto de Souza 
32344d4dd706SKleber Sacilotto de Souza 	/* Update dump length to the actual data to be copied */
32354d4dd706SKleber Sacilotto de Souza 	dump->driver_dump.hdr.len += sizeof(struct ipr_sdt_header);
32364d4dd706SKleber Sacilotto de Souza 	if (ioa_cfg->sis64)
32374d4dd706SKleber Sacilotto de Souza 		dump->driver_dump.hdr.len += num_entries * sizeof(struct ipr_sdt_entry);
32384d4dd706SKleber Sacilotto de Souza 	else
32394d4dd706SKleber Sacilotto de Souza 		dump->driver_dump.hdr.len += max_num_entries * sizeof(struct ipr_sdt_entry);
32401da177e4SLinus Torvalds 
32411da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
32421da177e4SLinus Torvalds 
32431da177e4SLinus Torvalds 	for (i = 0; i < num_entries; i++) {
32444d4dd706SKleber Sacilotto de Souza 		if (ioa_dump->hdr.len > max_dump_size) {
32451da177e4SLinus Torvalds 			driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS;
32461da177e4SLinus Torvalds 			break;
32471da177e4SLinus Torvalds 		}
32481da177e4SLinus Torvalds 
32491da177e4SLinus Torvalds 		if (sdt->entry[i].flags & IPR_SDT_VALID_ENTRY) {
3250dcbad00eSWayne Boyer 			sdt_word = be32_to_cpu(sdt->entry[i].start_token);
3251dcbad00eSWayne Boyer 			if (ioa_cfg->sis64)
3252dcbad00eSWayne Boyer 				bytes_to_copy = be32_to_cpu(sdt->entry[i].end_token);
3253dcbad00eSWayne Boyer 			else {
32541da177e4SLinus Torvalds 				start_off = sdt_word & IPR_FMT2_MBX_ADDR_MASK;
3255dcbad00eSWayne Boyer 				end_off = be32_to_cpu(sdt->entry[i].end_token);
32561da177e4SLinus Torvalds 
3257dcbad00eSWayne Boyer 				if (ipr_sdt_is_fmt2(sdt_word) && sdt_word)
32581da177e4SLinus Torvalds 					bytes_to_copy = end_off - start_off;
3259dcbad00eSWayne Boyer 				else
3260dcbad00eSWayne Boyer 					valid = 0;
3261dcbad00eSWayne Boyer 			}
3262dcbad00eSWayne Boyer 			if (valid) {
32634d4dd706SKleber Sacilotto de Souza 				if (bytes_to_copy > max_dump_size) {
32641da177e4SLinus Torvalds 					sdt->entry[i].flags &= ~IPR_SDT_VALID_ENTRY;
32651da177e4SLinus Torvalds 					continue;
32661da177e4SLinus Torvalds 				}
32671da177e4SLinus Torvalds 
32681da177e4SLinus Torvalds 				/* Copy data from adapter to driver buffers */
32691da177e4SLinus Torvalds 				bytes_copied = ipr_sdt_copy(ioa_cfg, sdt_word,
32701da177e4SLinus Torvalds 							    bytes_to_copy);
32711da177e4SLinus Torvalds 
32721da177e4SLinus Torvalds 				ioa_dump->hdr.len += bytes_copied;
32731da177e4SLinus Torvalds 
32741da177e4SLinus Torvalds 				if (bytes_copied != bytes_to_copy) {
32751da177e4SLinus Torvalds 					driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS;
32761da177e4SLinus Torvalds 					break;
32771da177e4SLinus Torvalds 				}
32781da177e4SLinus Torvalds 			}
32791da177e4SLinus Torvalds 		}
32801da177e4SLinus Torvalds 	}
32811da177e4SLinus Torvalds 
32821da177e4SLinus Torvalds 	dev_err(&ioa_cfg->pdev->dev, "Dump of IOA completed.\n");
32831da177e4SLinus Torvalds 
32841da177e4SLinus Torvalds 	/* Update dump_header */
32851da177e4SLinus Torvalds 	driver_dump->hdr.len += ioa_dump->hdr.len;
32861da177e4SLinus Torvalds 	wmb();
32871da177e4SLinus Torvalds 	ioa_cfg->sdt_state = DUMP_OBTAINED;
32881da177e4SLinus Torvalds 	LEAVE;
32891da177e4SLinus Torvalds }
32901da177e4SLinus Torvalds 
32911da177e4SLinus Torvalds #else
32921da177e4SLinus Torvalds #define ipr_get_ioa_dump(ioa_cfg, dump) do { } while (0)
32931da177e4SLinus Torvalds #endif
32941da177e4SLinus Torvalds 
32951da177e4SLinus Torvalds /**
32961da177e4SLinus Torvalds  * ipr_release_dump - Free adapter dump memory
32971da177e4SLinus Torvalds  * @kref:	kref struct
32981da177e4SLinus Torvalds  *
32991da177e4SLinus Torvalds  * Return value:
33001da177e4SLinus Torvalds  *	nothing
33011da177e4SLinus Torvalds  **/
33021da177e4SLinus Torvalds static void ipr_release_dump(struct kref *kref)
33031da177e4SLinus Torvalds {
33041da177e4SLinus Torvalds 	struct ipr_dump *dump = container_of(kref, struct ipr_dump, kref);
33051da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg;
33061da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
33071da177e4SLinus Torvalds 	int i;
33081da177e4SLinus Torvalds 
33091da177e4SLinus Torvalds 	ENTER;
33101da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
33111da177e4SLinus Torvalds 	ioa_cfg->dump = NULL;
33121da177e4SLinus Torvalds 	ioa_cfg->sdt_state = INACTIVE;
33131da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
33141da177e4SLinus Torvalds 
33151da177e4SLinus Torvalds 	for (i = 0; i < dump->ioa_dump.next_page_index; i++)
33161da177e4SLinus Torvalds 		free_page((unsigned long) dump->ioa_dump.ioa_data[i]);
33171da177e4SLinus Torvalds 
33184d4dd706SKleber Sacilotto de Souza 	vfree(dump->ioa_dump.ioa_data);
33191da177e4SLinus Torvalds 	kfree(dump);
33201da177e4SLinus Torvalds 	LEAVE;
33211da177e4SLinus Torvalds }
33221da177e4SLinus Torvalds 
3323318ddb34SWen Xiong static void ipr_add_remove_thread(struct work_struct *work)
33241da177e4SLinus Torvalds {
33251da177e4SLinus Torvalds 	unsigned long lock_flags;
33261da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
33271da177e4SLinus Torvalds 	struct scsi_device *sdev;
3328c4028958SDavid Howells 	struct ipr_ioa_cfg *ioa_cfg =
3329318ddb34SWen Xiong 		container_of(work, struct ipr_ioa_cfg, scsi_add_work_q);
33301da177e4SLinus Torvalds 	u8 bus, target, lun;
33311da177e4SLinus Torvalds 	int did_work;
33321da177e4SLinus Torvalds 
33331da177e4SLinus Torvalds 	ENTER;
33341da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
33351da177e4SLinus Torvalds 
33361da177e4SLinus Torvalds restart:
33371da177e4SLinus Torvalds 	do {
33381da177e4SLinus Torvalds 		did_work = 0;
3339f688f96dSBrian King 		if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) {
33401da177e4SLinus Torvalds 			spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
33411da177e4SLinus Torvalds 			return;
33421da177e4SLinus Torvalds 		}
33431da177e4SLinus Torvalds 
33441da177e4SLinus Torvalds 		list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
33451da177e4SLinus Torvalds 			if (res->del_from_ml && res->sdev) {
33461da177e4SLinus Torvalds 				did_work = 1;
33471da177e4SLinus Torvalds 				sdev = res->sdev;
33481da177e4SLinus Torvalds 				if (!scsi_device_get(sdev)) {
33495767a1c4SKleber Sacilotto de Souza 					if (!res->add_to_ml)
33501da177e4SLinus Torvalds 						list_move_tail(&res->queue, &ioa_cfg->free_res_q);
33515767a1c4SKleber Sacilotto de Souza 					else
33525767a1c4SKleber Sacilotto de Souza 						res->del_from_ml = 0;
33531da177e4SLinus Torvalds 					spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
33541da177e4SLinus Torvalds 					scsi_remove_device(sdev);
33551da177e4SLinus Torvalds 					scsi_device_put(sdev);
33561da177e4SLinus Torvalds 					spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
33571da177e4SLinus Torvalds 				}
33581da177e4SLinus Torvalds 				break;
33591da177e4SLinus Torvalds 			}
33601da177e4SLinus Torvalds 		}
33611da177e4SLinus Torvalds 	} while (did_work);
33621da177e4SLinus Torvalds 
33631da177e4SLinus Torvalds 	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
33641da177e4SLinus Torvalds 		if (res->add_to_ml) {
33653e7ebdfaSWayne Boyer 			bus = res->bus;
33663e7ebdfaSWayne Boyer 			target = res->target;
33673e7ebdfaSWayne Boyer 			lun = res->lun;
33681121b794SBrian King 			res->add_to_ml = 0;
33691da177e4SLinus Torvalds 			spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
33701da177e4SLinus Torvalds 			scsi_add_device(ioa_cfg->host, bus, target, lun);
33711da177e4SLinus Torvalds 			spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
33721da177e4SLinus Torvalds 			goto restart;
33731da177e4SLinus Torvalds 		}
33741da177e4SLinus Torvalds 	}
33751da177e4SLinus Torvalds 
3376f688f96dSBrian King 	ioa_cfg->scan_done = 1;
33771da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3378ee959b00STony Jones 	kobject_uevent(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE);
33791da177e4SLinus Torvalds 	LEAVE;
33801da177e4SLinus Torvalds }
33811da177e4SLinus Torvalds 
3382318ddb34SWen Xiong /**
3383318ddb34SWen Xiong  * ipr_worker_thread - Worker thread
3384318ddb34SWen Xiong  * @work:		ioa config struct
3385318ddb34SWen Xiong  *
3386318ddb34SWen Xiong  * Called at task level from a work thread. This function takes care
3387318ddb34SWen Xiong  * of adding and removing device from the mid-layer as configuration
3388318ddb34SWen Xiong  * changes are detected by the adapter.
3389318ddb34SWen Xiong  *
3390318ddb34SWen Xiong  * Return value:
3391318ddb34SWen Xiong  * 	nothing
3392318ddb34SWen Xiong  **/
3393318ddb34SWen Xiong static void ipr_worker_thread(struct work_struct *work)
3394318ddb34SWen Xiong {
3395318ddb34SWen Xiong 	unsigned long lock_flags;
3396318ddb34SWen Xiong 	struct ipr_dump *dump;
3397318ddb34SWen Xiong 	struct ipr_ioa_cfg *ioa_cfg =
3398318ddb34SWen Xiong 		container_of(work, struct ipr_ioa_cfg, work_q);
3399318ddb34SWen Xiong 
3400318ddb34SWen Xiong 	ENTER;
3401318ddb34SWen Xiong 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3402318ddb34SWen Xiong 
3403318ddb34SWen Xiong 	if (ioa_cfg->sdt_state == READ_DUMP) {
3404318ddb34SWen Xiong 		dump = ioa_cfg->dump;
3405318ddb34SWen Xiong 		if (!dump) {
3406318ddb34SWen Xiong 			spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3407318ddb34SWen Xiong 			return;
3408318ddb34SWen Xiong 		}
3409318ddb34SWen Xiong 		kref_get(&dump->kref);
3410318ddb34SWen Xiong 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3411318ddb34SWen Xiong 		ipr_get_ioa_dump(ioa_cfg, dump);
3412318ddb34SWen Xiong 		kref_put(&dump->kref, ipr_release_dump);
3413318ddb34SWen Xiong 
3414318ddb34SWen Xiong 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3415318ddb34SWen Xiong 		if (ioa_cfg->sdt_state == DUMP_OBTAINED && !ioa_cfg->dump_timeout)
3416318ddb34SWen Xiong 			ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
3417318ddb34SWen Xiong 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3418318ddb34SWen Xiong 		return;
3419318ddb34SWen Xiong 	}
3420318ddb34SWen Xiong 
3421318ddb34SWen Xiong 	if (ioa_cfg->scsi_unblock) {
3422318ddb34SWen Xiong 		ioa_cfg->scsi_unblock = 0;
3423318ddb34SWen Xiong 		ioa_cfg->scsi_blocked = 0;
3424318ddb34SWen Xiong 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3425318ddb34SWen Xiong 		scsi_unblock_requests(ioa_cfg->host);
3426318ddb34SWen Xiong 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3427318ddb34SWen Xiong 		if (ioa_cfg->scsi_blocked)
3428318ddb34SWen Xiong 			scsi_block_requests(ioa_cfg->host);
3429318ddb34SWen Xiong 	}
3430318ddb34SWen Xiong 
3431318ddb34SWen Xiong 	if (!ioa_cfg->scan_enabled) {
3432318ddb34SWen Xiong 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3433318ddb34SWen Xiong 		return;
3434318ddb34SWen Xiong 	}
3435318ddb34SWen Xiong 
3436318ddb34SWen Xiong 	schedule_work(&ioa_cfg->scsi_add_work_q);
3437318ddb34SWen Xiong 
3438318ddb34SWen Xiong 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3439318ddb34SWen Xiong 	LEAVE;
3440318ddb34SWen Xiong }
3441318ddb34SWen Xiong 
34421da177e4SLinus Torvalds #ifdef CONFIG_SCSI_IPR_TRACE
34431da177e4SLinus Torvalds /**
34441da177e4SLinus Torvalds  * ipr_read_trace - Dump the adapter trace
34452c3c8beaSChris Wright  * @filp:		open sysfs file
34461da177e4SLinus Torvalds  * @kobj:		kobject struct
344791a69029SZhang Rui  * @bin_attr:		bin_attribute struct
34481da177e4SLinus Torvalds  * @buf:		buffer
34491da177e4SLinus Torvalds  * @off:		offset
34501da177e4SLinus Torvalds  * @count:		buffer size
34511da177e4SLinus Torvalds  *
34521da177e4SLinus Torvalds  * Return value:
34531da177e4SLinus Torvalds  *	number of bytes printed to buffer
34541da177e4SLinus Torvalds  **/
34552c3c8beaSChris Wright static ssize_t ipr_read_trace(struct file *filp, struct kobject *kobj,
345691a69029SZhang Rui 			      struct bin_attribute *bin_attr,
345791a69029SZhang Rui 			      char *buf, loff_t off, size_t count)
34581da177e4SLinus Torvalds {
3459ee959b00STony Jones 	struct device *dev = container_of(kobj, struct device, kobj);
3460ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
34611da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
34621da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
3463d777aaf3SAkinobu Mita 	ssize_t ret;
34641da177e4SLinus Torvalds 
34651da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3466d777aaf3SAkinobu Mita 	ret = memory_read_from_buffer(buf, count, &off, ioa_cfg->trace,
3467d777aaf3SAkinobu Mita 				IPR_TRACE_SIZE);
34681da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3469d777aaf3SAkinobu Mita 
3470d777aaf3SAkinobu Mita 	return ret;
34711da177e4SLinus Torvalds }
34721da177e4SLinus Torvalds 
34731da177e4SLinus Torvalds static struct bin_attribute ipr_trace_attr = {
34741da177e4SLinus Torvalds 	.attr =	{
34751da177e4SLinus Torvalds 		.name = "trace",
34761da177e4SLinus Torvalds 		.mode = S_IRUGO,
34771da177e4SLinus Torvalds 	},
34781da177e4SLinus Torvalds 	.size = 0,
34791da177e4SLinus Torvalds 	.read = ipr_read_trace,
34801da177e4SLinus Torvalds };
34811da177e4SLinus Torvalds #endif
34821da177e4SLinus Torvalds 
34831da177e4SLinus Torvalds /**
34841da177e4SLinus Torvalds  * ipr_show_fw_version - Show the firmware version
3485ee959b00STony Jones  * @dev:	class device struct
3486a96099e2SLee Jones  * @attr:	device attribute (unused)
34871da177e4SLinus Torvalds  * @buf:	buffer
34881da177e4SLinus Torvalds  *
34891da177e4SLinus Torvalds  * Return value:
34901da177e4SLinus Torvalds  *	number of bytes printed to buffer
34911da177e4SLinus Torvalds  **/
3492ee959b00STony Jones static ssize_t ipr_show_fw_version(struct device *dev,
3493ee959b00STony Jones 				   struct device_attribute *attr, char *buf)
34941da177e4SLinus Torvalds {
3495ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
34961da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
34971da177e4SLinus Torvalds 	struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
34981da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
34991da177e4SLinus Torvalds 	int len;
35001da177e4SLinus Torvalds 
35011da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
35021da177e4SLinus Torvalds 	len = snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X\n",
35031da177e4SLinus Torvalds 		       ucode_vpd->major_release, ucode_vpd->card_type,
35041da177e4SLinus Torvalds 		       ucode_vpd->minor_release[0],
35051da177e4SLinus Torvalds 		       ucode_vpd->minor_release[1]);
35061da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
35071da177e4SLinus Torvalds 	return len;
35081da177e4SLinus Torvalds }
35091da177e4SLinus Torvalds 
3510ee959b00STony Jones static struct device_attribute ipr_fw_version_attr = {
35111da177e4SLinus Torvalds 	.attr = {
35121da177e4SLinus Torvalds 		.name =		"fw_version",
35131da177e4SLinus Torvalds 		.mode =		S_IRUGO,
35141da177e4SLinus Torvalds 	},
35151da177e4SLinus Torvalds 	.show = ipr_show_fw_version,
35161da177e4SLinus Torvalds };
35171da177e4SLinus Torvalds 
35181da177e4SLinus Torvalds /**
35191da177e4SLinus Torvalds  * ipr_show_log_level - Show the adapter's error logging level
3520ee959b00STony Jones  * @dev:	class device struct
3521a96099e2SLee Jones  * @attr:	device attribute (unused)
35221da177e4SLinus Torvalds  * @buf:	buffer
35231da177e4SLinus Torvalds  *
35241da177e4SLinus Torvalds  * Return value:
35251da177e4SLinus Torvalds  * 	number of bytes printed to buffer
35261da177e4SLinus Torvalds  **/
3527ee959b00STony Jones static ssize_t ipr_show_log_level(struct device *dev,
3528ee959b00STony Jones 				   struct device_attribute *attr, char *buf)
35291da177e4SLinus Torvalds {
3530ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
35311da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
35321da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
35331da177e4SLinus Torvalds 	int len;
35341da177e4SLinus Torvalds 
35351da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
35361da177e4SLinus Torvalds 	len = snprintf(buf, PAGE_SIZE, "%d\n", ioa_cfg->log_level);
35371da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
35381da177e4SLinus Torvalds 	return len;
35391da177e4SLinus Torvalds }
35401da177e4SLinus Torvalds 
35411da177e4SLinus Torvalds /**
35421da177e4SLinus Torvalds  * ipr_store_log_level - Change the adapter's error logging level
3543ee959b00STony Jones  * @dev:	class device struct
3544a96099e2SLee Jones  * @attr:	device attribute (unused)
35451da177e4SLinus Torvalds  * @buf:	buffer
3546a96099e2SLee Jones  * @count:	buffer size
35471da177e4SLinus Torvalds  *
35481da177e4SLinus Torvalds  * Return value:
35491da177e4SLinus Torvalds  * 	number of bytes printed to buffer
35501da177e4SLinus Torvalds  **/
3551ee959b00STony Jones static ssize_t ipr_store_log_level(struct device *dev,
3552ee959b00STony Jones 				   struct device_attribute *attr,
35531da177e4SLinus Torvalds 				   const char *buf, size_t count)
35541da177e4SLinus Torvalds {
3555ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
35561da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
35571da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
35581da177e4SLinus Torvalds 
35591da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
35601da177e4SLinus Torvalds 	ioa_cfg->log_level = simple_strtoul(buf, NULL, 10);
35611da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
35621da177e4SLinus Torvalds 	return strlen(buf);
35631da177e4SLinus Torvalds }
35641da177e4SLinus Torvalds 
3565ee959b00STony Jones static struct device_attribute ipr_log_level_attr = {
35661da177e4SLinus Torvalds 	.attr = {
35671da177e4SLinus Torvalds 		.name =		"log_level",
35681da177e4SLinus Torvalds 		.mode =		S_IRUGO | S_IWUSR,
35691da177e4SLinus Torvalds 	},
35701da177e4SLinus Torvalds 	.show = ipr_show_log_level,
35711da177e4SLinus Torvalds 	.store = ipr_store_log_level
35721da177e4SLinus Torvalds };
35731da177e4SLinus Torvalds 
35741da177e4SLinus Torvalds /**
35751da177e4SLinus Torvalds  * ipr_store_diagnostics - IOA Diagnostics interface
3576ee959b00STony Jones  * @dev:	device struct
3577a96099e2SLee Jones  * @attr:	device attribute (unused)
35781da177e4SLinus Torvalds  * @buf:	buffer
35791da177e4SLinus Torvalds  * @count:	buffer size
35801da177e4SLinus Torvalds  *
35811da177e4SLinus Torvalds  * This function will reset the adapter and wait a reasonable
35821da177e4SLinus Torvalds  * amount of time for any errors that the adapter might log.
35831da177e4SLinus Torvalds  *
35841da177e4SLinus Torvalds  * Return value:
35851da177e4SLinus Torvalds  * 	count on success / other on failure
35861da177e4SLinus Torvalds  **/
3587ee959b00STony Jones static ssize_t ipr_store_diagnostics(struct device *dev,
3588ee959b00STony Jones 				     struct device_attribute *attr,
35891da177e4SLinus Torvalds 				     const char *buf, size_t count)
35901da177e4SLinus Torvalds {
3591ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
35921da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
35931da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
35941da177e4SLinus Torvalds 	int rc = count;
35951da177e4SLinus Torvalds 
35961da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
35971da177e4SLinus Torvalds 		return -EACCES;
35981da177e4SLinus Torvalds 
3599970ea294SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3600970ea294SBrian King 	while (ioa_cfg->in_reset_reload) {
3601970ea294SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
36021da177e4SLinus Torvalds 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
36031da177e4SLinus Torvalds 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
3604970ea294SBrian King 	}
3605970ea294SBrian King 
36061da177e4SLinus Torvalds 	ioa_cfg->errors_logged = 0;
36071da177e4SLinus Torvalds 	ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL);
36081da177e4SLinus Torvalds 
36091da177e4SLinus Torvalds 	if (ioa_cfg->in_reset_reload) {
36101da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
36111da177e4SLinus Torvalds 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
36121da177e4SLinus Torvalds 
36131da177e4SLinus Torvalds 		/* Wait for a second for any errors to be logged */
36141da177e4SLinus Torvalds 		msleep(1000);
36151da177e4SLinus Torvalds 	} else {
36161da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
36171da177e4SLinus Torvalds 		return -EIO;
36181da177e4SLinus Torvalds 	}
36191da177e4SLinus Torvalds 
36201da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
36211da177e4SLinus Torvalds 	if (ioa_cfg->in_reset_reload || ioa_cfg->errors_logged)
36221da177e4SLinus Torvalds 		rc = -EIO;
36231da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
36241da177e4SLinus Torvalds 
36251da177e4SLinus Torvalds 	return rc;
36261da177e4SLinus Torvalds }
36271da177e4SLinus Torvalds 
3628ee959b00STony Jones static struct device_attribute ipr_diagnostics_attr = {
36291da177e4SLinus Torvalds 	.attr = {
36301da177e4SLinus Torvalds 		.name =		"run_diagnostics",
36311da177e4SLinus Torvalds 		.mode =		S_IWUSR,
36321da177e4SLinus Torvalds 	},
36331da177e4SLinus Torvalds 	.store = ipr_store_diagnostics
36341da177e4SLinus Torvalds };
36351da177e4SLinus Torvalds 
36361da177e4SLinus Torvalds /**
3637f37eb54bSbrking@us.ibm.com  * ipr_show_adapter_state - Show the adapter's state
3638a96099e2SLee Jones  * @dev:	device struct
3639a96099e2SLee Jones  * @attr:	device attribute (unused)
3640f37eb54bSbrking@us.ibm.com  * @buf:	buffer
3641f37eb54bSbrking@us.ibm.com  *
3642f37eb54bSbrking@us.ibm.com  * Return value:
3643f37eb54bSbrking@us.ibm.com  * 	number of bytes printed to buffer
3644f37eb54bSbrking@us.ibm.com  **/
3645ee959b00STony Jones static ssize_t ipr_show_adapter_state(struct device *dev,
3646ee959b00STony Jones 				      struct device_attribute *attr, char *buf)
3647f37eb54bSbrking@us.ibm.com {
3648ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
3649f37eb54bSbrking@us.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
3650f37eb54bSbrking@us.ibm.com 	unsigned long lock_flags = 0;
3651f37eb54bSbrking@us.ibm.com 	int len;
3652f37eb54bSbrking@us.ibm.com 
3653f37eb54bSbrking@us.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
365456d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
3655f37eb54bSbrking@us.ibm.com 		len = snprintf(buf, PAGE_SIZE, "offline\n");
3656f37eb54bSbrking@us.ibm.com 	else
3657f37eb54bSbrking@us.ibm.com 		len = snprintf(buf, PAGE_SIZE, "online\n");
3658f37eb54bSbrking@us.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3659f37eb54bSbrking@us.ibm.com 	return len;
3660f37eb54bSbrking@us.ibm.com }
3661f37eb54bSbrking@us.ibm.com 
3662f37eb54bSbrking@us.ibm.com /**
3663f37eb54bSbrking@us.ibm.com  * ipr_store_adapter_state - Change adapter state
3664ee959b00STony Jones  * @dev:	device struct
3665a96099e2SLee Jones  * @attr:	device attribute (unused)
3666f37eb54bSbrking@us.ibm.com  * @buf:	buffer
3667f37eb54bSbrking@us.ibm.com  * @count:	buffer size
3668f37eb54bSbrking@us.ibm.com  *
3669f37eb54bSbrking@us.ibm.com  * This function will change the adapter's state.
3670f37eb54bSbrking@us.ibm.com  *
3671f37eb54bSbrking@us.ibm.com  * Return value:
3672f37eb54bSbrking@us.ibm.com  * 	count on success / other on failure
3673f37eb54bSbrking@us.ibm.com  **/
3674ee959b00STony Jones static ssize_t ipr_store_adapter_state(struct device *dev,
3675ee959b00STony Jones 				       struct device_attribute *attr,
3676f37eb54bSbrking@us.ibm.com 				       const char *buf, size_t count)
3677f37eb54bSbrking@us.ibm.com {
3678ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
3679f37eb54bSbrking@us.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
3680f37eb54bSbrking@us.ibm.com 	unsigned long lock_flags;
368156d6aa33Swenxiong@linux.vnet.ibm.com 	int result = count, i;
3682f37eb54bSbrking@us.ibm.com 
3683f37eb54bSbrking@us.ibm.com 	if (!capable(CAP_SYS_ADMIN))
3684f37eb54bSbrking@us.ibm.com 		return -EACCES;
3685f37eb54bSbrking@us.ibm.com 
3686f37eb54bSbrking@us.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
368756d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead &&
368856d6aa33Swenxiong@linux.vnet.ibm.com 	    !strncmp(buf, "online", 6)) {
368956d6aa33Swenxiong@linux.vnet.ibm.com 		for (i = 0; i < ioa_cfg->hrrq_num; i++) {
369056d6aa33Swenxiong@linux.vnet.ibm.com 			spin_lock(&ioa_cfg->hrrq[i]._lock);
369156d6aa33Swenxiong@linux.vnet.ibm.com 			ioa_cfg->hrrq[i].ioa_is_dead = 0;
369256d6aa33Swenxiong@linux.vnet.ibm.com 			spin_unlock(&ioa_cfg->hrrq[i]._lock);
369356d6aa33Swenxiong@linux.vnet.ibm.com 		}
369456d6aa33Swenxiong@linux.vnet.ibm.com 		wmb();
3695f37eb54bSbrking@us.ibm.com 		ioa_cfg->reset_retries = 0;
3696f37eb54bSbrking@us.ibm.com 		ioa_cfg->in_ioa_bringdown = 0;
3697f37eb54bSbrking@us.ibm.com 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
3698f37eb54bSbrking@us.ibm.com 	}
3699f37eb54bSbrking@us.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
3700f37eb54bSbrking@us.ibm.com 	wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
3701f37eb54bSbrking@us.ibm.com 
3702f37eb54bSbrking@us.ibm.com 	return result;
3703f37eb54bSbrking@us.ibm.com }
3704f37eb54bSbrking@us.ibm.com 
3705ee959b00STony Jones static struct device_attribute ipr_ioa_state_attr = {
3706f37eb54bSbrking@us.ibm.com 	.attr = {
370749dd0961SBrian King 		.name =		"online_state",
3708f37eb54bSbrking@us.ibm.com 		.mode =		S_IRUGO | S_IWUSR,
3709f37eb54bSbrking@us.ibm.com 	},
3710f37eb54bSbrking@us.ibm.com 	.show = ipr_show_adapter_state,
3711f37eb54bSbrking@us.ibm.com 	.store = ipr_store_adapter_state
3712f37eb54bSbrking@us.ibm.com };
3713f37eb54bSbrking@us.ibm.com 
3714f37eb54bSbrking@us.ibm.com /**
37151da177e4SLinus Torvalds  * ipr_store_reset_adapter - Reset the adapter
3716ee959b00STony Jones  * @dev:	device struct
3717a96099e2SLee Jones  * @attr:	device attribute (unused)
37181da177e4SLinus Torvalds  * @buf:	buffer
37191da177e4SLinus Torvalds  * @count:	buffer size
37201da177e4SLinus Torvalds  *
37211da177e4SLinus Torvalds  * This function will reset the adapter.
37221da177e4SLinus Torvalds  *
37231da177e4SLinus Torvalds  * Return value:
37241da177e4SLinus Torvalds  * 	count on success / other on failure
37251da177e4SLinus Torvalds  **/
3726ee959b00STony Jones static ssize_t ipr_store_reset_adapter(struct device *dev,
3727ee959b00STony Jones 				       struct device_attribute *attr,
37281da177e4SLinus Torvalds 				       const char *buf, size_t count)
37291da177e4SLinus Torvalds {
3730ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
37311da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
37321da177e4SLinus Torvalds 	unsigned long lock_flags;
37331da177e4SLinus Torvalds 	int result = count;
37341da177e4SLinus Torvalds 
37351da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
37361da177e4SLinus Torvalds 		return -EACCES;
37371da177e4SLinus Torvalds 
37381da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
37391da177e4SLinus Torvalds 	if (!ioa_cfg->in_reset_reload)
37401da177e4SLinus Torvalds 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL);
37411da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
37421da177e4SLinus Torvalds 	wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
37431da177e4SLinus Torvalds 
37441da177e4SLinus Torvalds 	return result;
37451da177e4SLinus Torvalds }
37461da177e4SLinus Torvalds 
3747ee959b00STony Jones static struct device_attribute ipr_ioa_reset_attr = {
37481da177e4SLinus Torvalds 	.attr = {
37491da177e4SLinus Torvalds 		.name =		"reset_host",
37501da177e4SLinus Torvalds 		.mode =		S_IWUSR,
37511da177e4SLinus Torvalds 	},
37521da177e4SLinus Torvalds 	.store = ipr_store_reset_adapter
37531da177e4SLinus Torvalds };
37541da177e4SLinus Torvalds 
3755511cbce2SChristoph Hellwig static int ipr_iopoll(struct irq_poll *iop, int budget);
3756b53d124aSwenxiong@linux.vnet.ibm.com  /**
3757b53d124aSwenxiong@linux.vnet.ibm.com  * ipr_show_iopoll_weight - Show ipr polling mode
3758b53d124aSwenxiong@linux.vnet.ibm.com  * @dev:	class device struct
3759a96099e2SLee Jones  * @attr:	device attribute (unused)
3760b53d124aSwenxiong@linux.vnet.ibm.com  * @buf:	buffer
3761b53d124aSwenxiong@linux.vnet.ibm.com  *
3762b53d124aSwenxiong@linux.vnet.ibm.com  * Return value:
3763b53d124aSwenxiong@linux.vnet.ibm.com  *	number of bytes printed to buffer
3764b53d124aSwenxiong@linux.vnet.ibm.com  **/
3765b53d124aSwenxiong@linux.vnet.ibm.com static ssize_t ipr_show_iopoll_weight(struct device *dev,
3766b53d124aSwenxiong@linux.vnet.ibm.com 				   struct device_attribute *attr, char *buf)
3767b53d124aSwenxiong@linux.vnet.ibm.com {
3768b53d124aSwenxiong@linux.vnet.ibm.com 	struct Scsi_Host *shost = class_to_shost(dev);
3769b53d124aSwenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
3770b53d124aSwenxiong@linux.vnet.ibm.com 	unsigned long lock_flags = 0;
3771b53d124aSwenxiong@linux.vnet.ibm.com 	int len;
3772b53d124aSwenxiong@linux.vnet.ibm.com 
3773b53d124aSwenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(shost->host_lock, lock_flags);
3774b53d124aSwenxiong@linux.vnet.ibm.com 	len = snprintf(buf, PAGE_SIZE, "%d\n", ioa_cfg->iopoll_weight);
3775b53d124aSwenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(shost->host_lock, lock_flags);
3776b53d124aSwenxiong@linux.vnet.ibm.com 
3777b53d124aSwenxiong@linux.vnet.ibm.com 	return len;
3778b53d124aSwenxiong@linux.vnet.ibm.com }
3779b53d124aSwenxiong@linux.vnet.ibm.com 
3780b53d124aSwenxiong@linux.vnet.ibm.com /**
3781b53d124aSwenxiong@linux.vnet.ibm.com  * ipr_store_iopoll_weight - Change the adapter's polling mode
3782b53d124aSwenxiong@linux.vnet.ibm.com  * @dev:	class device struct
3783a96099e2SLee Jones  * @attr:	device attribute (unused)
3784b53d124aSwenxiong@linux.vnet.ibm.com  * @buf:	buffer
3785a96099e2SLee Jones  * @count:	buffer size
3786b53d124aSwenxiong@linux.vnet.ibm.com  *
3787b53d124aSwenxiong@linux.vnet.ibm.com  * Return value:
3788b53d124aSwenxiong@linux.vnet.ibm.com  *	number of bytes printed to buffer
3789b53d124aSwenxiong@linux.vnet.ibm.com  **/
3790b53d124aSwenxiong@linux.vnet.ibm.com static ssize_t ipr_store_iopoll_weight(struct device *dev,
3791b53d124aSwenxiong@linux.vnet.ibm.com 					struct device_attribute *attr,
3792b53d124aSwenxiong@linux.vnet.ibm.com 					const char *buf, size_t count)
3793b53d124aSwenxiong@linux.vnet.ibm.com {
3794b53d124aSwenxiong@linux.vnet.ibm.com 	struct Scsi_Host *shost = class_to_shost(dev);
3795b53d124aSwenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
3796b53d124aSwenxiong@linux.vnet.ibm.com 	unsigned long user_iopoll_weight;
3797b53d124aSwenxiong@linux.vnet.ibm.com 	unsigned long lock_flags = 0;
3798b53d124aSwenxiong@linux.vnet.ibm.com 	int i;
3799b53d124aSwenxiong@linux.vnet.ibm.com 
3800b53d124aSwenxiong@linux.vnet.ibm.com 	if (!ioa_cfg->sis64) {
3801511cbce2SChristoph Hellwig 		dev_info(&ioa_cfg->pdev->dev, "irq_poll not supported on this adapter\n");
3802b53d124aSwenxiong@linux.vnet.ibm.com 		return -EINVAL;
3803b53d124aSwenxiong@linux.vnet.ibm.com 	}
3804b53d124aSwenxiong@linux.vnet.ibm.com 	if (kstrtoul(buf, 10, &user_iopoll_weight))
3805b53d124aSwenxiong@linux.vnet.ibm.com 		return -EINVAL;
3806b53d124aSwenxiong@linux.vnet.ibm.com 
3807b53d124aSwenxiong@linux.vnet.ibm.com 	if (user_iopoll_weight > 256) {
3808511cbce2SChristoph Hellwig 		dev_info(&ioa_cfg->pdev->dev, "Invalid irq_poll weight. It must be less than 256\n");
3809b53d124aSwenxiong@linux.vnet.ibm.com 		return -EINVAL;
3810b53d124aSwenxiong@linux.vnet.ibm.com 	}
3811b53d124aSwenxiong@linux.vnet.ibm.com 
3812b53d124aSwenxiong@linux.vnet.ibm.com 	if (user_iopoll_weight == ioa_cfg->iopoll_weight) {
3813511cbce2SChristoph Hellwig 		dev_info(&ioa_cfg->pdev->dev, "Current irq_poll weight has the same weight\n");
3814b53d124aSwenxiong@linux.vnet.ibm.com 		return strlen(buf);
3815b53d124aSwenxiong@linux.vnet.ibm.com 	}
3816b53d124aSwenxiong@linux.vnet.ibm.com 
381789f8b33cSJens Axboe 	if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
3818b53d124aSwenxiong@linux.vnet.ibm.com 		for (i = 1; i < ioa_cfg->hrrq_num; i++)
3819511cbce2SChristoph Hellwig 			irq_poll_disable(&ioa_cfg->hrrq[i].iopoll);
3820b53d124aSwenxiong@linux.vnet.ibm.com 	}
3821b53d124aSwenxiong@linux.vnet.ibm.com 
3822b53d124aSwenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(shost->host_lock, lock_flags);
3823b53d124aSwenxiong@linux.vnet.ibm.com 	ioa_cfg->iopoll_weight = user_iopoll_weight;
382489f8b33cSJens Axboe 	if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
3825b53d124aSwenxiong@linux.vnet.ibm.com 		for (i = 1; i < ioa_cfg->hrrq_num; i++) {
3826511cbce2SChristoph Hellwig 			irq_poll_init(&ioa_cfg->hrrq[i].iopoll,
3827b53d124aSwenxiong@linux.vnet.ibm.com 					ioa_cfg->iopoll_weight, ipr_iopoll);
3828b53d124aSwenxiong@linux.vnet.ibm.com 		}
3829b53d124aSwenxiong@linux.vnet.ibm.com 	}
3830b53d124aSwenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(shost->host_lock, lock_flags);
3831b53d124aSwenxiong@linux.vnet.ibm.com 
3832b53d124aSwenxiong@linux.vnet.ibm.com 	return strlen(buf);
3833b53d124aSwenxiong@linux.vnet.ibm.com }
3834b53d124aSwenxiong@linux.vnet.ibm.com 
3835b53d124aSwenxiong@linux.vnet.ibm.com static struct device_attribute ipr_iopoll_weight_attr = {
3836b53d124aSwenxiong@linux.vnet.ibm.com 	.attr = {
3837b53d124aSwenxiong@linux.vnet.ibm.com 		.name =		"iopoll_weight",
3838b53d124aSwenxiong@linux.vnet.ibm.com 		.mode =		S_IRUGO | S_IWUSR,
3839b53d124aSwenxiong@linux.vnet.ibm.com 	},
3840b53d124aSwenxiong@linux.vnet.ibm.com 	.show = ipr_show_iopoll_weight,
3841b53d124aSwenxiong@linux.vnet.ibm.com 	.store = ipr_store_iopoll_weight
3842b53d124aSwenxiong@linux.vnet.ibm.com };
3843b53d124aSwenxiong@linux.vnet.ibm.com 
38441da177e4SLinus Torvalds /**
38451da177e4SLinus Torvalds  * ipr_alloc_ucode_buffer - Allocates a microcode download buffer
38461da177e4SLinus Torvalds  * @buf_len:		buffer length
38471da177e4SLinus Torvalds  *
38481da177e4SLinus Torvalds  * Allocates a DMA'able buffer in chunks and assembles a scatter/gather
38491da177e4SLinus Torvalds  * list to use for microcode download
38501da177e4SLinus Torvalds  *
38511da177e4SLinus Torvalds  * Return value:
38521da177e4SLinus Torvalds  * 	pointer to sglist / NULL on failure
38531da177e4SLinus Torvalds  **/
38541da177e4SLinus Torvalds static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
38551da177e4SLinus Torvalds {
3856f95dc1bbSBart Van Assche 	int sg_size, order;
38571da177e4SLinus Torvalds 	struct ipr_sglist *sglist;
38581da177e4SLinus Torvalds 
38591da177e4SLinus Torvalds 	/* Get the minimum size per scatter/gather element */
38601da177e4SLinus Torvalds 	sg_size = buf_len / (IPR_MAX_SGLIST - 1);
38611da177e4SLinus Torvalds 
38621da177e4SLinus Torvalds 	/* Get the actual size per element */
38631da177e4SLinus Torvalds 	order = get_order(sg_size);
38641da177e4SLinus Torvalds 
38651da177e4SLinus Torvalds 	/* Allocate a scatter/gather list for the DMA */
3866f95dc1bbSBart Van Assche 	sglist = kzalloc(sizeof(struct ipr_sglist), GFP_KERNEL);
38671da177e4SLinus Torvalds 	if (sglist == NULL) {
38681da177e4SLinus Torvalds 		ipr_trace;
38691da177e4SLinus Torvalds 		return NULL;
38701da177e4SLinus Torvalds 	}
38711da177e4SLinus Torvalds 	sglist->order = order;
3872f95dc1bbSBart Van Assche 	sglist->scatterlist = sgl_alloc_order(buf_len, order, false, GFP_KERNEL,
3873f95dc1bbSBart Van Assche 					      &sglist->num_sg);
3874f95dc1bbSBart Van Assche 	if (!sglist->scatterlist) {
38751da177e4SLinus Torvalds 		kfree(sglist);
38761da177e4SLinus Torvalds 		return NULL;
38771da177e4SLinus Torvalds 	}
38781da177e4SLinus Torvalds 
38791da177e4SLinus Torvalds 	return sglist;
38801da177e4SLinus Torvalds }
38811da177e4SLinus Torvalds 
38821da177e4SLinus Torvalds /**
38831da177e4SLinus Torvalds  * ipr_free_ucode_buffer - Frees a microcode download buffer
3884a96099e2SLee Jones  * @sglist:		scatter/gather list pointer
38851da177e4SLinus Torvalds  *
38861da177e4SLinus Torvalds  * Free a DMA'able ucode download buffer previously allocated with
38871da177e4SLinus Torvalds  * ipr_alloc_ucode_buffer
38881da177e4SLinus Torvalds  *
38891da177e4SLinus Torvalds  * Return value:
38901da177e4SLinus Torvalds  * 	nothing
38911da177e4SLinus Torvalds  **/
38921da177e4SLinus Torvalds static void ipr_free_ucode_buffer(struct ipr_sglist *sglist)
38931da177e4SLinus Torvalds {
3894f95dc1bbSBart Van Assche 	sgl_free_order(sglist->scatterlist, sglist->order);
38951da177e4SLinus Torvalds 	kfree(sglist);
38961da177e4SLinus Torvalds }
38971da177e4SLinus Torvalds 
38981da177e4SLinus Torvalds /**
38991da177e4SLinus Torvalds  * ipr_copy_ucode_buffer - Copy user buffer to kernel buffer
39001da177e4SLinus Torvalds  * @sglist:		scatter/gather list pointer
39011da177e4SLinus Torvalds  * @buffer:		buffer pointer
39021da177e4SLinus Torvalds  * @len:		buffer length
39031da177e4SLinus Torvalds  *
39041da177e4SLinus Torvalds  * Copy a microcode image from a user buffer into a buffer allocated by
39051da177e4SLinus Torvalds  * ipr_alloc_ucode_buffer
39061da177e4SLinus Torvalds  *
39071da177e4SLinus Torvalds  * Return value:
39081da177e4SLinus Torvalds  * 	0 on success / other on failure
39091da177e4SLinus Torvalds  **/
39101da177e4SLinus Torvalds static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist,
39111da177e4SLinus Torvalds 				 u8 *buffer, u32 len)
39121da177e4SLinus Torvalds {
39131da177e4SLinus Torvalds 	int bsize_elem, i, result = 0;
3914c71ae886SMing Lei 	struct scatterlist *sg;
39151da177e4SLinus Torvalds 	void *kaddr;
39161da177e4SLinus Torvalds 
39171da177e4SLinus Torvalds 	/* Determine the actual number of bytes per element */
39181da177e4SLinus Torvalds 	bsize_elem = PAGE_SIZE * (1 << sglist->order);
39191da177e4SLinus Torvalds 
3920c71ae886SMing Lei 	sg = sglist->scatterlist;
39211da177e4SLinus Torvalds 
3922c71ae886SMing Lei 	for (i = 0; i < (len / bsize_elem); i++, sg = sg_next(sg),
3923c71ae886SMing Lei 			buffer += bsize_elem) {
3924c71ae886SMing Lei 		struct page *page = sg_page(sg);
392545711f1aSJens Axboe 
392645711f1aSJens Axboe 		kaddr = kmap(page);
39271da177e4SLinus Torvalds 		memcpy(kaddr, buffer, bsize_elem);
392845711f1aSJens Axboe 		kunmap(page);
39291da177e4SLinus Torvalds 
3930c71ae886SMing Lei 		sg->length = bsize_elem;
39311da177e4SLinus Torvalds 
39321da177e4SLinus Torvalds 		if (result != 0) {
39331da177e4SLinus Torvalds 			ipr_trace;
39341da177e4SLinus Torvalds 			return result;
39351da177e4SLinus Torvalds 		}
39361da177e4SLinus Torvalds 	}
39371da177e4SLinus Torvalds 
39381da177e4SLinus Torvalds 	if (len % bsize_elem) {
3939c71ae886SMing Lei 		struct page *page = sg_page(sg);
394045711f1aSJens Axboe 
394145711f1aSJens Axboe 		kaddr = kmap(page);
39421da177e4SLinus Torvalds 		memcpy(kaddr, buffer, len % bsize_elem);
394345711f1aSJens Axboe 		kunmap(page);
39441da177e4SLinus Torvalds 
3945c71ae886SMing Lei 		sg->length = len % bsize_elem;
39461da177e4SLinus Torvalds 	}
39471da177e4SLinus Torvalds 
39481da177e4SLinus Torvalds 	sglist->buffer_len = len;
39491da177e4SLinus Torvalds 	return result;
39501da177e4SLinus Torvalds }
39511da177e4SLinus Torvalds 
39521da177e4SLinus Torvalds /**
3953a32c055fSWayne Boyer  * ipr_build_ucode_ioadl64 - Build a microcode download IOADL
3954a32c055fSWayne Boyer  * @ipr_cmd:		ipr command struct
3955a32c055fSWayne Boyer  * @sglist:		scatter/gather list
3956a32c055fSWayne Boyer  *
3957a32c055fSWayne Boyer  * Builds a microcode download IOA data list (IOADL).
3958a32c055fSWayne Boyer  *
3959a32c055fSWayne Boyer  **/
3960a32c055fSWayne Boyer static void ipr_build_ucode_ioadl64(struct ipr_cmnd *ipr_cmd,
3961a32c055fSWayne Boyer 				    struct ipr_sglist *sglist)
3962a32c055fSWayne Boyer {
3963a32c055fSWayne Boyer 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
3964a32c055fSWayne Boyer 	struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64;
3965a32c055fSWayne Boyer 	struct scatterlist *scatterlist = sglist->scatterlist;
3966c71ae886SMing Lei 	struct scatterlist *sg;
3967a32c055fSWayne Boyer 	int i;
3968a32c055fSWayne Boyer 
3969a32c055fSWayne Boyer 	ipr_cmd->dma_use_sg = sglist->num_dma_sg;
3970a32c055fSWayne Boyer 	ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
3971a32c055fSWayne Boyer 	ioarcb->data_transfer_length = cpu_to_be32(sglist->buffer_len);
3972a32c055fSWayne Boyer 
3973a32c055fSWayne Boyer 	ioarcb->ioadl_len =
3974a32c055fSWayne Boyer 		cpu_to_be32(sizeof(struct ipr_ioadl64_desc) * ipr_cmd->dma_use_sg);
3975c71ae886SMing Lei 	for_each_sg(scatterlist, sg, ipr_cmd->dma_use_sg, i) {
3976a32c055fSWayne Boyer 		ioadl64[i].flags = cpu_to_be32(IPR_IOADL_FLAGS_WRITE);
3977c71ae886SMing Lei 		ioadl64[i].data_len = cpu_to_be32(sg_dma_len(sg));
3978c71ae886SMing Lei 		ioadl64[i].address = cpu_to_be64(sg_dma_address(sg));
3979a32c055fSWayne Boyer 	}
3980a32c055fSWayne Boyer 
3981a32c055fSWayne Boyer 	ioadl64[i-1].flags |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
3982a32c055fSWayne Boyer }
3983a32c055fSWayne Boyer 
3984a32c055fSWayne Boyer /**
398512baa420Sbrking@us.ibm.com  * ipr_build_ucode_ioadl - Build a microcode download IOADL
39861da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
39871da177e4SLinus Torvalds  * @sglist:		scatter/gather list
39881da177e4SLinus Torvalds  *
398912baa420Sbrking@us.ibm.com  * Builds a microcode download IOA data list (IOADL).
39901da177e4SLinus Torvalds  *
39911da177e4SLinus Torvalds  **/
399212baa420Sbrking@us.ibm.com static void ipr_build_ucode_ioadl(struct ipr_cmnd *ipr_cmd,
399312baa420Sbrking@us.ibm.com 				  struct ipr_sglist *sglist)
39941da177e4SLinus Torvalds {
39951da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
3996a32c055fSWayne Boyer 	struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl;
39971da177e4SLinus Torvalds 	struct scatterlist *scatterlist = sglist->scatterlist;
3998c71ae886SMing Lei 	struct scatterlist *sg;
39991da177e4SLinus Torvalds 	int i;
40001da177e4SLinus Torvalds 
400112baa420Sbrking@us.ibm.com 	ipr_cmd->dma_use_sg = sglist->num_dma_sg;
40021da177e4SLinus Torvalds 	ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
4003a32c055fSWayne Boyer 	ioarcb->data_transfer_length = cpu_to_be32(sglist->buffer_len);
4004a32c055fSWayne Boyer 
4005a32c055fSWayne Boyer 	ioarcb->ioadl_len =
40061da177e4SLinus Torvalds 		cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
40071da177e4SLinus Torvalds 
4008c71ae886SMing Lei 	for_each_sg(scatterlist, sg, ipr_cmd->dma_use_sg, i) {
40091da177e4SLinus Torvalds 		ioadl[i].flags_and_data_len =
4010c71ae886SMing Lei 			cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(sg));
40111da177e4SLinus Torvalds 		ioadl[i].address =
4012c71ae886SMing Lei 			cpu_to_be32(sg_dma_address(sg));
40131da177e4SLinus Torvalds 	}
40141da177e4SLinus Torvalds 
40151da177e4SLinus Torvalds 	ioadl[i-1].flags_and_data_len |=
40161da177e4SLinus Torvalds 		cpu_to_be32(IPR_IOADL_FLAGS_LAST);
40171da177e4SLinus Torvalds }
401812baa420Sbrking@us.ibm.com 
401912baa420Sbrking@us.ibm.com /**
402012baa420Sbrking@us.ibm.com  * ipr_update_ioa_ucode - Update IOA's microcode
402112baa420Sbrking@us.ibm.com  * @ioa_cfg:	ioa config struct
402212baa420Sbrking@us.ibm.com  * @sglist:		scatter/gather list
402312baa420Sbrking@us.ibm.com  *
402412baa420Sbrking@us.ibm.com  * Initiate an adapter reset to update the IOA's microcode
402512baa420Sbrking@us.ibm.com  *
402612baa420Sbrking@us.ibm.com  * Return value:
402712baa420Sbrking@us.ibm.com  * 	0 on success / -EIO on failure
402812baa420Sbrking@us.ibm.com  **/
402912baa420Sbrking@us.ibm.com static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
403012baa420Sbrking@us.ibm.com 				struct ipr_sglist *sglist)
403112baa420Sbrking@us.ibm.com {
403212baa420Sbrking@us.ibm.com 	unsigned long lock_flags;
403312baa420Sbrking@us.ibm.com 
403412baa420Sbrking@us.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
4035970ea294SBrian King 	while (ioa_cfg->in_reset_reload) {
4036970ea294SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4037970ea294SBrian King 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
4038970ea294SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
4039970ea294SBrian King 	}
404012baa420Sbrking@us.ibm.com 
404112baa420Sbrking@us.ibm.com 	if (ioa_cfg->ucode_sglist) {
404212baa420Sbrking@us.ibm.com 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
404312baa420Sbrking@us.ibm.com 		dev_err(&ioa_cfg->pdev->dev,
404412baa420Sbrking@us.ibm.com 			"Microcode download already in progress\n");
40451da177e4SLinus Torvalds 		return -EIO;
40461da177e4SLinus Torvalds 	}
40471da177e4SLinus Torvalds 
4048d73341bfSAnton Blanchard 	sglist->num_dma_sg = dma_map_sg(&ioa_cfg->pdev->dev,
4049d73341bfSAnton Blanchard 					sglist->scatterlist, sglist->num_sg,
4050d73341bfSAnton Blanchard 					DMA_TO_DEVICE);
405112baa420Sbrking@us.ibm.com 
405212baa420Sbrking@us.ibm.com 	if (!sglist->num_dma_sg) {
405312baa420Sbrking@us.ibm.com 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
405412baa420Sbrking@us.ibm.com 		dev_err(&ioa_cfg->pdev->dev,
405512baa420Sbrking@us.ibm.com 			"Failed to map microcode download buffer!\n");
405612baa420Sbrking@us.ibm.com 		return -EIO;
405712baa420Sbrking@us.ibm.com 	}
405812baa420Sbrking@us.ibm.com 
405912baa420Sbrking@us.ibm.com 	ioa_cfg->ucode_sglist = sglist;
406012baa420Sbrking@us.ibm.com 	ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL);
406112baa420Sbrking@us.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
406212baa420Sbrking@us.ibm.com 	wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
406312baa420Sbrking@us.ibm.com 
406412baa420Sbrking@us.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
406512baa420Sbrking@us.ibm.com 	ioa_cfg->ucode_sglist = NULL;
406612baa420Sbrking@us.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
40671da177e4SLinus Torvalds 	return 0;
40681da177e4SLinus Torvalds }
40691da177e4SLinus Torvalds 
40701da177e4SLinus Torvalds /**
40711da177e4SLinus Torvalds  * ipr_store_update_fw - Update the firmware on the adapter
4072a96099e2SLee Jones  * @dev:	device struct
4073a96099e2SLee Jones  * @attr:	device attribute (unused)
40741da177e4SLinus Torvalds  * @buf:	buffer
40751da177e4SLinus Torvalds  * @count:	buffer size
40761da177e4SLinus Torvalds  *
40771da177e4SLinus Torvalds  * This function will update the firmware on the adapter.
40781da177e4SLinus Torvalds  *
40791da177e4SLinus Torvalds  * Return value:
40801da177e4SLinus Torvalds  * 	count on success / other on failure
40811da177e4SLinus Torvalds  **/
4082ee959b00STony Jones static ssize_t ipr_store_update_fw(struct device *dev,
4083ee959b00STony Jones 				   struct device_attribute *attr,
40841da177e4SLinus Torvalds 				   const char *buf, size_t count)
40851da177e4SLinus Torvalds {
4086ee959b00STony Jones 	struct Scsi_Host *shost = class_to_shost(dev);
40871da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
40881da177e4SLinus Torvalds 	struct ipr_ucode_image_header *image_hdr;
40891da177e4SLinus Torvalds 	const struct firmware *fw_entry;
40901da177e4SLinus Torvalds 	struct ipr_sglist *sglist;
40911da177e4SLinus Torvalds 	char fname[100];
40921da177e4SLinus Torvalds 	char *src;
409321b81716SGabriel Krisman Bertazi 	char *endline;
4094d63c7dd5SInsu Yun 	int result, dnld_size;
40951da177e4SLinus Torvalds 
40961da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
40971da177e4SLinus Torvalds 		return -EACCES;
40981da177e4SLinus Torvalds 
4099d63c7dd5SInsu Yun 	snprintf(fname, sizeof(fname), "%s", buf);
41001da177e4SLinus Torvalds 
410121b81716SGabriel Krisman Bertazi 	endline = strchr(fname, '\n');
410221b81716SGabriel Krisman Bertazi 	if (endline)
410321b81716SGabriel Krisman Bertazi 		*endline = '\0';
410421b81716SGabriel Krisman Bertazi 
41051da177e4SLinus Torvalds 	if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
41061da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
41071da177e4SLinus Torvalds 		return -EIO;
41081da177e4SLinus Torvalds 	}
41091da177e4SLinus Torvalds 
41101da177e4SLinus Torvalds 	image_hdr = (struct ipr_ucode_image_header *)fw_entry->data;
41111da177e4SLinus Torvalds 
41121da177e4SLinus Torvalds 	src = (u8 *)image_hdr + be32_to_cpu(image_hdr->header_length);
41131da177e4SLinus Torvalds 	dnld_size = fw_entry->size - be32_to_cpu(image_hdr->header_length);
41141da177e4SLinus Torvalds 	sglist = ipr_alloc_ucode_buffer(dnld_size);
41151da177e4SLinus Torvalds 
41161da177e4SLinus Torvalds 	if (!sglist) {
41171da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev, "Microcode buffer allocation failed\n");
41181da177e4SLinus Torvalds 		release_firmware(fw_entry);
41191da177e4SLinus Torvalds 		return -ENOMEM;
41201da177e4SLinus Torvalds 	}
41211da177e4SLinus Torvalds 
41221da177e4SLinus Torvalds 	result = ipr_copy_ucode_buffer(sglist, src, dnld_size);
41231da177e4SLinus Torvalds 
41241da177e4SLinus Torvalds 	if (result) {
41251da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
41261da177e4SLinus Torvalds 			"Microcode buffer copy to DMA buffer failed\n");
412712baa420Sbrking@us.ibm.com 		goto out;
412812baa420Sbrking@us.ibm.com 	}
412912baa420Sbrking@us.ibm.com 
413014ed9cc7SWayne Boyer 	ipr_info("Updating microcode, please be patient.  This may take up to 30 minutes.\n");
413114ed9cc7SWayne Boyer 
413212baa420Sbrking@us.ibm.com 	result = ipr_update_ioa_ucode(ioa_cfg, sglist);
413312baa420Sbrking@us.ibm.com 
413412baa420Sbrking@us.ibm.com 	if (!result)
413512baa420Sbrking@us.ibm.com 		result = count;
413612baa420Sbrking@us.ibm.com out:
41371da177e4SLinus Torvalds 	ipr_free_ucode_buffer(sglist);
41381da177e4SLinus Torvalds 	release_firmware(fw_entry);
41391da177e4SLinus Torvalds 	return result;
41401da177e4SLinus Torvalds }
41411da177e4SLinus Torvalds 
4142ee959b00STony Jones static struct device_attribute ipr_update_fw_attr = {
41431da177e4SLinus Torvalds 	.attr = {
41441da177e4SLinus Torvalds 		.name =		"update_fw",
41451da177e4SLinus Torvalds 		.mode =		S_IWUSR,
41461da177e4SLinus Torvalds 	},
41471da177e4SLinus Torvalds 	.store = ipr_store_update_fw
41481da177e4SLinus Torvalds };
41491da177e4SLinus Torvalds 
415075576bb9SWayne Boyer /**
415175576bb9SWayne Boyer  * ipr_show_fw_type - Show the adapter's firmware type.
415275576bb9SWayne Boyer  * @dev:	class device struct
4153a96099e2SLee Jones  * @attr:	device attribute (unused)
415475576bb9SWayne Boyer  * @buf:	buffer
415575576bb9SWayne Boyer  *
415675576bb9SWayne Boyer  * Return value:
415775576bb9SWayne Boyer  *	number of bytes printed to buffer
415875576bb9SWayne Boyer  **/
415975576bb9SWayne Boyer static ssize_t ipr_show_fw_type(struct device *dev,
416075576bb9SWayne Boyer 				struct device_attribute *attr, char *buf)
416175576bb9SWayne Boyer {
416275576bb9SWayne Boyer 	struct Scsi_Host *shost = class_to_shost(dev);
416375576bb9SWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
416475576bb9SWayne Boyer 	unsigned long lock_flags = 0;
416575576bb9SWayne Boyer 	int len;
416675576bb9SWayne Boyer 
416775576bb9SWayne Boyer 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
416875576bb9SWayne Boyer 	len = snprintf(buf, PAGE_SIZE, "%d\n", ioa_cfg->sis64);
416975576bb9SWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
417075576bb9SWayne Boyer 	return len;
417175576bb9SWayne Boyer }
417275576bb9SWayne Boyer 
417375576bb9SWayne Boyer static struct device_attribute ipr_ioa_fw_type_attr = {
417475576bb9SWayne Boyer 	.attr = {
417575576bb9SWayne Boyer 		.name =		"fw_type",
417675576bb9SWayne Boyer 		.mode =		S_IRUGO,
417775576bb9SWayne Boyer 	},
417875576bb9SWayne Boyer 	.show = ipr_show_fw_type
417975576bb9SWayne Boyer };
418075576bb9SWayne Boyer 
4181afc3f83cSBrian King static ssize_t ipr_read_async_err_log(struct file *filep, struct kobject *kobj,
4182afc3f83cSBrian King 				struct bin_attribute *bin_attr, char *buf,
4183afc3f83cSBrian King 				loff_t off, size_t count)
4184afc3f83cSBrian King {
4185afc3f83cSBrian King 	struct device *cdev = container_of(kobj, struct device, kobj);
4186afc3f83cSBrian King 	struct Scsi_Host *shost = class_to_shost(cdev);
4187afc3f83cSBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
4188afc3f83cSBrian King 	struct ipr_hostrcb *hostrcb;
4189afc3f83cSBrian King 	unsigned long lock_flags = 0;
4190afc3f83cSBrian King 	int ret;
4191afc3f83cSBrian King 
4192afc3f83cSBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
4193afc3f83cSBrian King 	hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q,
4194afc3f83cSBrian King 					struct ipr_hostrcb, queue);
4195afc3f83cSBrian King 	if (!hostrcb) {
4196afc3f83cSBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4197afc3f83cSBrian King 		return 0;
4198afc3f83cSBrian King 	}
4199afc3f83cSBrian King 	ret = memory_read_from_buffer(buf, count, &off, &hostrcb->hcam,
4200afc3f83cSBrian King 				sizeof(hostrcb->hcam));
4201afc3f83cSBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4202afc3f83cSBrian King 	return ret;
4203afc3f83cSBrian King }
4204afc3f83cSBrian King 
4205afc3f83cSBrian King static ssize_t ipr_next_async_err_log(struct file *filep, struct kobject *kobj,
4206afc3f83cSBrian King 				struct bin_attribute *bin_attr, char *buf,
4207afc3f83cSBrian King 				loff_t off, size_t count)
4208afc3f83cSBrian King {
4209afc3f83cSBrian King 	struct device *cdev = container_of(kobj, struct device, kobj);
4210afc3f83cSBrian King 	struct Scsi_Host *shost = class_to_shost(cdev);
4211afc3f83cSBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
4212afc3f83cSBrian King 	struct ipr_hostrcb *hostrcb;
4213afc3f83cSBrian King 	unsigned long lock_flags = 0;
4214afc3f83cSBrian King 
4215afc3f83cSBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
4216afc3f83cSBrian King 	hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q,
4217afc3f83cSBrian King 					struct ipr_hostrcb, queue);
4218afc3f83cSBrian King 	if (!hostrcb) {
4219afc3f83cSBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4220afc3f83cSBrian King 		return count;
4221afc3f83cSBrian King 	}
4222afc3f83cSBrian King 
4223afc3f83cSBrian King 	/* Reclaim hostrcb before exit */
4224afc3f83cSBrian King 	list_move_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
4225afc3f83cSBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4226afc3f83cSBrian King 	return count;
4227afc3f83cSBrian King }
4228afc3f83cSBrian King 
4229afc3f83cSBrian King static struct bin_attribute ipr_ioa_async_err_log = {
4230afc3f83cSBrian King 	.attr = {
4231afc3f83cSBrian King 		.name =		"async_err_log",
4232afc3f83cSBrian King 		.mode =		S_IRUGO | S_IWUSR,
4233afc3f83cSBrian King 	},
4234afc3f83cSBrian King 	.size = 0,
4235afc3f83cSBrian King 	.read = ipr_read_async_err_log,
4236afc3f83cSBrian King 	.write = ipr_next_async_err_log
4237afc3f83cSBrian King };
4238afc3f83cSBrian King 
4239ee959b00STony Jones static struct device_attribute *ipr_ioa_attrs[] = {
42401da177e4SLinus Torvalds 	&ipr_fw_version_attr,
42411da177e4SLinus Torvalds 	&ipr_log_level_attr,
42421da177e4SLinus Torvalds 	&ipr_diagnostics_attr,
4243f37eb54bSbrking@us.ibm.com 	&ipr_ioa_state_attr,
42441da177e4SLinus Torvalds 	&ipr_ioa_reset_attr,
42451da177e4SLinus Torvalds 	&ipr_update_fw_attr,
424675576bb9SWayne Boyer 	&ipr_ioa_fw_type_attr,
4247b53d124aSwenxiong@linux.vnet.ibm.com 	&ipr_iopoll_weight_attr,
42481da177e4SLinus Torvalds 	NULL,
42491da177e4SLinus Torvalds };
42501da177e4SLinus Torvalds 
42511da177e4SLinus Torvalds #ifdef CONFIG_SCSI_IPR_DUMP
42521da177e4SLinus Torvalds /**
42531da177e4SLinus Torvalds  * ipr_read_dump - Dump the adapter
42542c3c8beaSChris Wright  * @filp:		open sysfs file
42551da177e4SLinus Torvalds  * @kobj:		kobject struct
425691a69029SZhang Rui  * @bin_attr:		bin_attribute struct
42571da177e4SLinus Torvalds  * @buf:		buffer
42581da177e4SLinus Torvalds  * @off:		offset
42591da177e4SLinus Torvalds  * @count:		buffer size
42601da177e4SLinus Torvalds  *
42611da177e4SLinus Torvalds  * Return value:
42621da177e4SLinus Torvalds  *	number of bytes printed to buffer
42631da177e4SLinus Torvalds  **/
42642c3c8beaSChris Wright static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj,
426591a69029SZhang Rui 			     struct bin_attribute *bin_attr,
426691a69029SZhang Rui 			     char *buf, loff_t off, size_t count)
42671da177e4SLinus Torvalds {
4268ee959b00STony Jones 	struct device *cdev = container_of(kobj, struct device, kobj);
42691da177e4SLinus Torvalds 	struct Scsi_Host *shost = class_to_shost(cdev);
42701da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
42711da177e4SLinus Torvalds 	struct ipr_dump *dump;
42721da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
42731da177e4SLinus Torvalds 	char *src;
42744d4dd706SKleber Sacilotto de Souza 	int len, sdt_end;
42751da177e4SLinus Torvalds 	size_t rc = count;
42761da177e4SLinus Torvalds 
42771da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
42781da177e4SLinus Torvalds 		return -EACCES;
42791da177e4SLinus Torvalds 
42801da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
42811da177e4SLinus Torvalds 	dump = ioa_cfg->dump;
42821da177e4SLinus Torvalds 
42831da177e4SLinus Torvalds 	if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump) {
42841da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
42851da177e4SLinus Torvalds 		return 0;
42861da177e4SLinus Torvalds 	}
42871da177e4SLinus Torvalds 	kref_get(&dump->kref);
42881da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
42891da177e4SLinus Torvalds 
42901da177e4SLinus Torvalds 	if (off > dump->driver_dump.hdr.len) {
42911da177e4SLinus Torvalds 		kref_put(&dump->kref, ipr_release_dump);
42921da177e4SLinus Torvalds 		return 0;
42931da177e4SLinus Torvalds 	}
42941da177e4SLinus Torvalds 
42951da177e4SLinus Torvalds 	if (off + count > dump->driver_dump.hdr.len) {
42961da177e4SLinus Torvalds 		count = dump->driver_dump.hdr.len - off;
42971da177e4SLinus Torvalds 		rc = count;
42981da177e4SLinus Torvalds 	}
42991da177e4SLinus Torvalds 
43001da177e4SLinus Torvalds 	if (count && off < sizeof(dump->driver_dump)) {
43011da177e4SLinus Torvalds 		if (off + count > sizeof(dump->driver_dump))
43021da177e4SLinus Torvalds 			len = sizeof(dump->driver_dump) - off;
43031da177e4SLinus Torvalds 		else
43041da177e4SLinus Torvalds 			len = count;
43051da177e4SLinus Torvalds 		src = (u8 *)&dump->driver_dump + off;
43061da177e4SLinus Torvalds 		memcpy(buf, src, len);
43071da177e4SLinus Torvalds 		buf += len;
43081da177e4SLinus Torvalds 		off += len;
43091da177e4SLinus Torvalds 		count -= len;
43101da177e4SLinus Torvalds 	}
43111da177e4SLinus Torvalds 
43121da177e4SLinus Torvalds 	off -= sizeof(dump->driver_dump);
43131da177e4SLinus Torvalds 
43144d4dd706SKleber Sacilotto de Souza 	if (ioa_cfg->sis64)
43154d4dd706SKleber Sacilotto de Souza 		sdt_end = offsetof(struct ipr_ioa_dump, sdt.entry) +
43164d4dd706SKleber Sacilotto de Souza 			  (be32_to_cpu(dump->ioa_dump.sdt.hdr.num_entries_used) *
43174d4dd706SKleber Sacilotto de Souza 			   sizeof(struct ipr_sdt_entry));
43184d4dd706SKleber Sacilotto de Souza 	else
43194d4dd706SKleber Sacilotto de Souza 		sdt_end = offsetof(struct ipr_ioa_dump, sdt.entry) +
43204d4dd706SKleber Sacilotto de Souza 			  (IPR_FMT2_NUM_SDT_ENTRIES * sizeof(struct ipr_sdt_entry));
43214d4dd706SKleber Sacilotto de Souza 
43224d4dd706SKleber Sacilotto de Souza 	if (count && off < sdt_end) {
43234d4dd706SKleber Sacilotto de Souza 		if (off + count > sdt_end)
43244d4dd706SKleber Sacilotto de Souza 			len = sdt_end - off;
43251da177e4SLinus Torvalds 		else
43261da177e4SLinus Torvalds 			len = count;
43271da177e4SLinus Torvalds 		src = (u8 *)&dump->ioa_dump + off;
43281da177e4SLinus Torvalds 		memcpy(buf, src, len);
43291da177e4SLinus Torvalds 		buf += len;
43301da177e4SLinus Torvalds 		off += len;
43311da177e4SLinus Torvalds 		count -= len;
43321da177e4SLinus Torvalds 	}
43331da177e4SLinus Torvalds 
43344d4dd706SKleber Sacilotto de Souza 	off -= sdt_end;
43351da177e4SLinus Torvalds 
43361da177e4SLinus Torvalds 	while (count) {
43371da177e4SLinus Torvalds 		if ((off & PAGE_MASK) != ((off + count) & PAGE_MASK))
43381da177e4SLinus Torvalds 			len = PAGE_ALIGN(off) - off;
43391da177e4SLinus Torvalds 		else
43401da177e4SLinus Torvalds 			len = count;
43411da177e4SLinus Torvalds 		src = (u8 *)dump->ioa_dump.ioa_data[(off & PAGE_MASK) >> PAGE_SHIFT];
43421da177e4SLinus Torvalds 		src += off & ~PAGE_MASK;
43431da177e4SLinus Torvalds 		memcpy(buf, src, len);
43441da177e4SLinus Torvalds 		buf += len;
43451da177e4SLinus Torvalds 		off += len;
43461da177e4SLinus Torvalds 		count -= len;
43471da177e4SLinus Torvalds 	}
43481da177e4SLinus Torvalds 
43491da177e4SLinus Torvalds 	kref_put(&dump->kref, ipr_release_dump);
43501da177e4SLinus Torvalds 	return rc;
43511da177e4SLinus Torvalds }
43521da177e4SLinus Torvalds 
43531da177e4SLinus Torvalds /**
43541da177e4SLinus Torvalds  * ipr_alloc_dump - Prepare for adapter dump
43551da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
43561da177e4SLinus Torvalds  *
43571da177e4SLinus Torvalds  * Return value:
43581da177e4SLinus Torvalds  *	0 on success / other on failure
43591da177e4SLinus Torvalds  **/
43601da177e4SLinus Torvalds static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
43611da177e4SLinus Torvalds {
43621da177e4SLinus Torvalds 	struct ipr_dump *dump;
43634d4dd706SKleber Sacilotto de Souza 	__be32 **ioa_data;
43641da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
43651da177e4SLinus Torvalds 
43660bc42e35Sbrking@us.ibm.com 	dump = kzalloc(sizeof(struct ipr_dump), GFP_KERNEL);
43671da177e4SLinus Torvalds 
43681da177e4SLinus Torvalds 	if (!dump) {
43691da177e4SLinus Torvalds 		ipr_err("Dump memory allocation failed\n");
43701da177e4SLinus Torvalds 		return -ENOMEM;
43711da177e4SLinus Torvalds 	}
43721da177e4SLinus Torvalds 
43734d4dd706SKleber Sacilotto de Souza 	if (ioa_cfg->sis64)
437442bc47b3SKees Cook 		ioa_data = vmalloc(array_size(IPR_FMT3_MAX_NUM_DUMP_PAGES,
437542bc47b3SKees Cook 					      sizeof(__be32 *)));
43764d4dd706SKleber Sacilotto de Souza 	else
437742bc47b3SKees Cook 		ioa_data = vmalloc(array_size(IPR_FMT2_MAX_NUM_DUMP_PAGES,
437842bc47b3SKees Cook 					      sizeof(__be32 *)));
43794d4dd706SKleber Sacilotto de Souza 
43804d4dd706SKleber Sacilotto de Souza 	if (!ioa_data) {
43814d4dd706SKleber Sacilotto de Souza 		ipr_err("Dump memory allocation failed\n");
43824d4dd706SKleber Sacilotto de Souza 		kfree(dump);
43834d4dd706SKleber Sacilotto de Souza 		return -ENOMEM;
43844d4dd706SKleber Sacilotto de Souza 	}
43854d4dd706SKleber Sacilotto de Souza 
43864d4dd706SKleber Sacilotto de Souza 	dump->ioa_dump.ioa_data = ioa_data;
43874d4dd706SKleber Sacilotto de Souza 
43881da177e4SLinus Torvalds 	kref_init(&dump->kref);
43891da177e4SLinus Torvalds 	dump->ioa_cfg = ioa_cfg;
43901da177e4SLinus Torvalds 
43911da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
43921da177e4SLinus Torvalds 
43931da177e4SLinus Torvalds 	if (INACTIVE != ioa_cfg->sdt_state) {
43941da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
43954d4dd706SKleber Sacilotto de Souza 		vfree(dump->ioa_dump.ioa_data);
43961da177e4SLinus Torvalds 		kfree(dump);
43971da177e4SLinus Torvalds 		return 0;
43981da177e4SLinus Torvalds 	}
43991da177e4SLinus Torvalds 
44001da177e4SLinus Torvalds 	ioa_cfg->dump = dump;
44011da177e4SLinus Torvalds 	ioa_cfg->sdt_state = WAIT_FOR_DUMP;
440256d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead && !ioa_cfg->dump_taken) {
44031da177e4SLinus Torvalds 		ioa_cfg->dump_taken = 1;
44041da177e4SLinus Torvalds 		schedule_work(&ioa_cfg->work_q);
44051da177e4SLinus Torvalds 	}
44061da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
44071da177e4SLinus Torvalds 
44081da177e4SLinus Torvalds 	return 0;
44091da177e4SLinus Torvalds }
44101da177e4SLinus Torvalds 
44111da177e4SLinus Torvalds /**
44121da177e4SLinus Torvalds  * ipr_free_dump - Free adapter dump memory
44131da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
44141da177e4SLinus Torvalds  *
44151da177e4SLinus Torvalds  * Return value:
44161da177e4SLinus Torvalds  *	0 on success / other on failure
44171da177e4SLinus Torvalds  **/
44181da177e4SLinus Torvalds static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg)
44191da177e4SLinus Torvalds {
44201da177e4SLinus Torvalds 	struct ipr_dump *dump;
44211da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
44221da177e4SLinus Torvalds 
44231da177e4SLinus Torvalds 	ENTER;
44241da177e4SLinus Torvalds 
44251da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
44261da177e4SLinus Torvalds 	dump = ioa_cfg->dump;
44271da177e4SLinus Torvalds 	if (!dump) {
44281da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
44291da177e4SLinus Torvalds 		return 0;
44301da177e4SLinus Torvalds 	}
44311da177e4SLinus Torvalds 
44321da177e4SLinus Torvalds 	ioa_cfg->dump = NULL;
44331da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
44341da177e4SLinus Torvalds 
44351da177e4SLinus Torvalds 	kref_put(&dump->kref, ipr_release_dump);
44361da177e4SLinus Torvalds 
44371da177e4SLinus Torvalds 	LEAVE;
44381da177e4SLinus Torvalds 	return 0;
44391da177e4SLinus Torvalds }
44401da177e4SLinus Torvalds 
44411da177e4SLinus Torvalds /**
44421da177e4SLinus Torvalds  * ipr_write_dump - Setup dump state of adapter
44432c3c8beaSChris Wright  * @filp:		open sysfs file
44441da177e4SLinus Torvalds  * @kobj:		kobject struct
444591a69029SZhang Rui  * @bin_attr:		bin_attribute struct
44461da177e4SLinus Torvalds  * @buf:		buffer
44471da177e4SLinus Torvalds  * @off:		offset
44481da177e4SLinus Torvalds  * @count:		buffer size
44491da177e4SLinus Torvalds  *
44501da177e4SLinus Torvalds  * Return value:
44511da177e4SLinus Torvalds  *	number of bytes printed to buffer
44521da177e4SLinus Torvalds  **/
44532c3c8beaSChris Wright static ssize_t ipr_write_dump(struct file *filp, struct kobject *kobj,
445491a69029SZhang Rui 			      struct bin_attribute *bin_attr,
445591a69029SZhang Rui 			      char *buf, loff_t off, size_t count)
44561da177e4SLinus Torvalds {
4457ee959b00STony Jones 	struct device *cdev = container_of(kobj, struct device, kobj);
44581da177e4SLinus Torvalds 	struct Scsi_Host *shost = class_to_shost(cdev);
44591da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
44601da177e4SLinus Torvalds 	int rc;
44611da177e4SLinus Torvalds 
44621da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
44631da177e4SLinus Torvalds 		return -EACCES;
44641da177e4SLinus Torvalds 
44651da177e4SLinus Torvalds 	if (buf[0] == '1')
44661da177e4SLinus Torvalds 		rc = ipr_alloc_dump(ioa_cfg);
44671da177e4SLinus Torvalds 	else if (buf[0] == '0')
44681da177e4SLinus Torvalds 		rc = ipr_free_dump(ioa_cfg);
44691da177e4SLinus Torvalds 	else
44701da177e4SLinus Torvalds 		return -EINVAL;
44711da177e4SLinus Torvalds 
44721da177e4SLinus Torvalds 	if (rc)
44731da177e4SLinus Torvalds 		return rc;
44741da177e4SLinus Torvalds 	else
44751da177e4SLinus Torvalds 		return count;
44761da177e4SLinus Torvalds }
44771da177e4SLinus Torvalds 
44781da177e4SLinus Torvalds static struct bin_attribute ipr_dump_attr = {
44791da177e4SLinus Torvalds 	.attr =	{
44801da177e4SLinus Torvalds 		.name = "dump",
44811da177e4SLinus Torvalds 		.mode = S_IRUSR | S_IWUSR,
44821da177e4SLinus Torvalds 	},
44831da177e4SLinus Torvalds 	.size = 0,
44841da177e4SLinus Torvalds 	.read = ipr_read_dump,
44851da177e4SLinus Torvalds 	.write = ipr_write_dump
44861da177e4SLinus Torvalds };
44871da177e4SLinus Torvalds #else
44881da177e4SLinus Torvalds static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; };
44891da177e4SLinus Torvalds #endif
44901da177e4SLinus Torvalds 
44911da177e4SLinus Torvalds /**
44921da177e4SLinus Torvalds  * ipr_change_queue_depth - Change the device's queue depth
44931da177e4SLinus Torvalds  * @sdev:	scsi device struct
44941da177e4SLinus Torvalds  * @qdepth:	depth to set
44951da177e4SLinus Torvalds  *
44961da177e4SLinus Torvalds  * Return value:
44971da177e4SLinus Torvalds  * 	actual depth set
44981da177e4SLinus Torvalds  **/
4499db5ed4dfSChristoph Hellwig static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
45001da177e4SLinus Torvalds {
450135a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
450235a39691SBrian King 	struct ipr_resource_entry *res;
450335a39691SBrian King 	unsigned long lock_flags = 0;
450435a39691SBrian King 
450535a39691SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
450635a39691SBrian King 	res = (struct ipr_resource_entry *)sdev->hostdata;
450735a39691SBrian King 
450835a39691SBrian King 	if (res && ipr_is_gata(res) && qdepth > IPR_MAX_CMD_PER_ATA_LUN)
450935a39691SBrian King 		qdepth = IPR_MAX_CMD_PER_ATA_LUN;
451035a39691SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
451135a39691SBrian King 
4512db5ed4dfSChristoph Hellwig 	scsi_change_queue_depth(sdev, qdepth);
45131da177e4SLinus Torvalds 	return sdev->queue_depth;
45141da177e4SLinus Torvalds }
45151da177e4SLinus Torvalds 
45161da177e4SLinus Torvalds /**
45171da177e4SLinus Torvalds  * ipr_show_adapter_handle - Show the adapter's resource handle for this device
45181da177e4SLinus Torvalds  * @dev:	device struct
451946d74563SWayne Boyer  * @attr:	device attribute structure
45201da177e4SLinus Torvalds  * @buf:	buffer
45211da177e4SLinus Torvalds  *
45221da177e4SLinus Torvalds  * Return value:
45231da177e4SLinus Torvalds  * 	number of bytes printed to buffer
45241da177e4SLinus Torvalds  **/
452510523b3bSYani Ioannou static ssize_t ipr_show_adapter_handle(struct device *dev, struct device_attribute *attr, char *buf)
45261da177e4SLinus Torvalds {
45271da177e4SLinus Torvalds 	struct scsi_device *sdev = to_scsi_device(dev);
45281da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
45291da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
45301da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
45311da177e4SLinus Torvalds 	ssize_t len = -ENXIO;
45321da177e4SLinus Torvalds 
45331da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
45341da177e4SLinus Torvalds 	res = (struct ipr_resource_entry *)sdev->hostdata;
45351da177e4SLinus Torvalds 	if (res)
45363e7ebdfaSWayne Boyer 		len = snprintf(buf, PAGE_SIZE, "%08X\n", res->res_handle);
45371da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
45381da177e4SLinus Torvalds 	return len;
45391da177e4SLinus Torvalds }
45401da177e4SLinus Torvalds 
45411da177e4SLinus Torvalds static struct device_attribute ipr_adapter_handle_attr = {
45421da177e4SLinus Torvalds 	.attr = {
45431da177e4SLinus Torvalds 		.name = 	"adapter_handle",
45441da177e4SLinus Torvalds 		.mode =		S_IRUSR,
45451da177e4SLinus Torvalds 	},
45461da177e4SLinus Torvalds 	.show = ipr_show_adapter_handle
45471da177e4SLinus Torvalds };
45481da177e4SLinus Torvalds 
45493e7ebdfaSWayne Boyer /**
45505adcbeb3SWayne Boyer  * ipr_show_resource_path - Show the resource path or the resource address for
45515adcbeb3SWayne Boyer  *			    this device.
45523e7ebdfaSWayne Boyer  * @dev:	device struct
455346d74563SWayne Boyer  * @attr:	device attribute structure
45543e7ebdfaSWayne Boyer  * @buf:	buffer
45553e7ebdfaSWayne Boyer  *
45563e7ebdfaSWayne Boyer  * Return value:
45573e7ebdfaSWayne Boyer  * 	number of bytes printed to buffer
45583e7ebdfaSWayne Boyer  **/
45593e7ebdfaSWayne Boyer static ssize_t ipr_show_resource_path(struct device *dev, struct device_attribute *attr, char *buf)
45603e7ebdfaSWayne Boyer {
45613e7ebdfaSWayne Boyer 	struct scsi_device *sdev = to_scsi_device(dev);
45623e7ebdfaSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
45633e7ebdfaSWayne Boyer 	struct ipr_resource_entry *res;
45643e7ebdfaSWayne Boyer 	unsigned long lock_flags = 0;
45653e7ebdfaSWayne Boyer 	ssize_t len = -ENXIO;
45663e7ebdfaSWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
45673e7ebdfaSWayne Boyer 
45683e7ebdfaSWayne Boyer 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
45693e7ebdfaSWayne Boyer 	res = (struct ipr_resource_entry *)sdev->hostdata;
45705adcbeb3SWayne Boyer 	if (res && ioa_cfg->sis64)
45713e7ebdfaSWayne Boyer 		len = snprintf(buf, PAGE_SIZE, "%s\n",
4572b3b3b407SBrian King 			       __ipr_format_res_path(res->res_path, buffer,
45735adcbeb3SWayne Boyer 						     sizeof(buffer)));
45745adcbeb3SWayne Boyer 	else if (res)
45755adcbeb3SWayne Boyer 		len = snprintf(buf, PAGE_SIZE, "%d:%d:%d:%d\n", ioa_cfg->host->host_no,
45765adcbeb3SWayne Boyer 			       res->bus, res->target, res->lun);
45775adcbeb3SWayne Boyer 
45783e7ebdfaSWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
45793e7ebdfaSWayne Boyer 	return len;
45803e7ebdfaSWayne Boyer }
45813e7ebdfaSWayne Boyer 
45823e7ebdfaSWayne Boyer static struct device_attribute ipr_resource_path_attr = {
45833e7ebdfaSWayne Boyer 	.attr = {
45843e7ebdfaSWayne Boyer 		.name = 	"resource_path",
458575576bb9SWayne Boyer 		.mode =		S_IRUGO,
45863e7ebdfaSWayne Boyer 	},
45873e7ebdfaSWayne Boyer 	.show = ipr_show_resource_path
45883e7ebdfaSWayne Boyer };
45893e7ebdfaSWayne Boyer 
459075576bb9SWayne Boyer /**
459146d74563SWayne Boyer  * ipr_show_device_id - Show the device_id for this device.
459246d74563SWayne Boyer  * @dev:	device struct
459346d74563SWayne Boyer  * @attr:	device attribute structure
459446d74563SWayne Boyer  * @buf:	buffer
459546d74563SWayne Boyer  *
459646d74563SWayne Boyer  * Return value:
459746d74563SWayne Boyer  *	number of bytes printed to buffer
459846d74563SWayne Boyer  **/
459946d74563SWayne Boyer static ssize_t ipr_show_device_id(struct device *dev, struct device_attribute *attr, char *buf)
460046d74563SWayne Boyer {
460146d74563SWayne Boyer 	struct scsi_device *sdev = to_scsi_device(dev);
460246d74563SWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
460346d74563SWayne Boyer 	struct ipr_resource_entry *res;
460446d74563SWayne Boyer 	unsigned long lock_flags = 0;
460546d74563SWayne Boyer 	ssize_t len = -ENXIO;
460646d74563SWayne Boyer 
460746d74563SWayne Boyer 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
460846d74563SWayne Boyer 	res = (struct ipr_resource_entry *)sdev->hostdata;
460946d74563SWayne Boyer 	if (res && ioa_cfg->sis64)
4610bb8647e8SWen Xiong 		len = snprintf(buf, PAGE_SIZE, "0x%llx\n", be64_to_cpu(res->dev_id));
461146d74563SWayne Boyer 	else if (res)
461246d74563SWayne Boyer 		len = snprintf(buf, PAGE_SIZE, "0x%llx\n", res->lun_wwn);
461346d74563SWayne Boyer 
461446d74563SWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
461546d74563SWayne Boyer 	return len;
461646d74563SWayne Boyer }
461746d74563SWayne Boyer 
461846d74563SWayne Boyer static struct device_attribute ipr_device_id_attr = {
461946d74563SWayne Boyer 	.attr = {
462046d74563SWayne Boyer 		.name =		"device_id",
462146d74563SWayne Boyer 		.mode =		S_IRUGO,
462246d74563SWayne Boyer 	},
462346d74563SWayne Boyer 	.show = ipr_show_device_id
462446d74563SWayne Boyer };
462546d74563SWayne Boyer 
462646d74563SWayne Boyer /**
462775576bb9SWayne Boyer  * ipr_show_resource_type - Show the resource type for this device.
462875576bb9SWayne Boyer  * @dev:	device struct
462946d74563SWayne Boyer  * @attr:	device attribute structure
463075576bb9SWayne Boyer  * @buf:	buffer
463175576bb9SWayne Boyer  *
463275576bb9SWayne Boyer  * Return value:
463375576bb9SWayne Boyer  *	number of bytes printed to buffer
463475576bb9SWayne Boyer  **/
463575576bb9SWayne Boyer static ssize_t ipr_show_resource_type(struct device *dev, struct device_attribute *attr, char *buf)
463675576bb9SWayne Boyer {
463775576bb9SWayne Boyer 	struct scsi_device *sdev = to_scsi_device(dev);
463875576bb9SWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
463975576bb9SWayne Boyer 	struct ipr_resource_entry *res;
464075576bb9SWayne Boyer 	unsigned long lock_flags = 0;
464175576bb9SWayne Boyer 	ssize_t len = -ENXIO;
464275576bb9SWayne Boyer 
464375576bb9SWayne Boyer 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
464475576bb9SWayne Boyer 	res = (struct ipr_resource_entry *)sdev->hostdata;
464575576bb9SWayne Boyer 
464675576bb9SWayne Boyer 	if (res)
464775576bb9SWayne Boyer 		len = snprintf(buf, PAGE_SIZE, "%x\n", res->type);
464875576bb9SWayne Boyer 
464975576bb9SWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
465075576bb9SWayne Boyer 	return len;
465175576bb9SWayne Boyer }
465275576bb9SWayne Boyer 
465375576bb9SWayne Boyer static struct device_attribute ipr_resource_type_attr = {
465475576bb9SWayne Boyer 	.attr = {
465575576bb9SWayne Boyer 		.name =		"resource_type",
465675576bb9SWayne Boyer 		.mode =		S_IRUGO,
465775576bb9SWayne Boyer 	},
465875576bb9SWayne Boyer 	.show = ipr_show_resource_type
465975576bb9SWayne Boyer };
466075576bb9SWayne Boyer 
4661f8ee25d7SWen Xiong /**
4662f8ee25d7SWen Xiong  * ipr_show_raw_mode - Show the adapter's raw mode
4663f8ee25d7SWen Xiong  * @dev:	class device struct
4664a96099e2SLee Jones  * @attr:	device attribute (unused)
4665f8ee25d7SWen Xiong  * @buf:	buffer
4666f8ee25d7SWen Xiong  *
4667f8ee25d7SWen Xiong  * Return value:
4668f8ee25d7SWen Xiong  * 	number of bytes printed to buffer
4669f8ee25d7SWen Xiong  **/
4670f8ee25d7SWen Xiong static ssize_t ipr_show_raw_mode(struct device *dev,
4671f8ee25d7SWen Xiong 				 struct device_attribute *attr, char *buf)
4672f8ee25d7SWen Xiong {
4673f8ee25d7SWen Xiong 	struct scsi_device *sdev = to_scsi_device(dev);
4674f8ee25d7SWen Xiong 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
4675f8ee25d7SWen Xiong 	struct ipr_resource_entry *res;
4676f8ee25d7SWen Xiong 	unsigned long lock_flags = 0;
4677f8ee25d7SWen Xiong 	ssize_t len;
4678f8ee25d7SWen Xiong 
4679f8ee25d7SWen Xiong 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
4680f8ee25d7SWen Xiong 	res = (struct ipr_resource_entry *)sdev->hostdata;
4681f8ee25d7SWen Xiong 	if (res)
4682f8ee25d7SWen Xiong 		len = snprintf(buf, PAGE_SIZE, "%d\n", res->raw_mode);
4683f8ee25d7SWen Xiong 	else
4684f8ee25d7SWen Xiong 		len = -ENXIO;
4685f8ee25d7SWen Xiong 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4686f8ee25d7SWen Xiong 	return len;
4687f8ee25d7SWen Xiong }
4688f8ee25d7SWen Xiong 
4689f8ee25d7SWen Xiong /**
4690f8ee25d7SWen Xiong  * ipr_store_raw_mode - Change the adapter's raw mode
4691f8ee25d7SWen Xiong  * @dev:	class device struct
4692a96099e2SLee Jones  * @attr:	device attribute (unused)
4693f8ee25d7SWen Xiong  * @buf:	buffer
4694a96099e2SLee Jones  * @count:		buffer size
4695f8ee25d7SWen Xiong  *
4696f8ee25d7SWen Xiong  * Return value:
4697f8ee25d7SWen Xiong  * 	number of bytes printed to buffer
4698f8ee25d7SWen Xiong  **/
4699f8ee25d7SWen Xiong static ssize_t ipr_store_raw_mode(struct device *dev,
4700f8ee25d7SWen Xiong 				  struct device_attribute *attr,
4701f8ee25d7SWen Xiong 				  const char *buf, size_t count)
4702f8ee25d7SWen Xiong {
4703f8ee25d7SWen Xiong 	struct scsi_device *sdev = to_scsi_device(dev);
4704f8ee25d7SWen Xiong 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
4705f8ee25d7SWen Xiong 	struct ipr_resource_entry *res;
4706f8ee25d7SWen Xiong 	unsigned long lock_flags = 0;
4707f8ee25d7SWen Xiong 	ssize_t len;
4708f8ee25d7SWen Xiong 
4709f8ee25d7SWen Xiong 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
4710f8ee25d7SWen Xiong 	res = (struct ipr_resource_entry *)sdev->hostdata;
4711f8ee25d7SWen Xiong 	if (res) {
4712e35d7f27SGabriel Krisman Bertazi 		if (ipr_is_af_dasd_device(res)) {
4713f8ee25d7SWen Xiong 			res->raw_mode = simple_strtoul(buf, NULL, 10);
4714f8ee25d7SWen Xiong 			len = strlen(buf);
4715f8ee25d7SWen Xiong 			if (res->sdev)
4716f8ee25d7SWen Xiong 				sdev_printk(KERN_INFO, res->sdev, "raw mode is %s\n",
4717f8ee25d7SWen Xiong 					res->raw_mode ? "enabled" : "disabled");
4718f8ee25d7SWen Xiong 		} else
4719f8ee25d7SWen Xiong 			len = -EINVAL;
4720f8ee25d7SWen Xiong 	} else
4721f8ee25d7SWen Xiong 		len = -ENXIO;
4722f8ee25d7SWen Xiong 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4723f8ee25d7SWen Xiong 	return len;
4724f8ee25d7SWen Xiong }
4725f8ee25d7SWen Xiong 
4726f8ee25d7SWen Xiong static struct device_attribute ipr_raw_mode_attr = {
4727f8ee25d7SWen Xiong 	.attr = {
4728f8ee25d7SWen Xiong 		.name =		"raw_mode",
4729f8ee25d7SWen Xiong 		.mode =		S_IRUGO | S_IWUSR,
4730f8ee25d7SWen Xiong 	},
4731f8ee25d7SWen Xiong 	.show = ipr_show_raw_mode,
4732f8ee25d7SWen Xiong 	.store = ipr_store_raw_mode
4733f8ee25d7SWen Xiong };
4734f8ee25d7SWen Xiong 
47351da177e4SLinus Torvalds static struct device_attribute *ipr_dev_attrs[] = {
47361da177e4SLinus Torvalds 	&ipr_adapter_handle_attr,
47373e7ebdfaSWayne Boyer 	&ipr_resource_path_attr,
473846d74563SWayne Boyer 	&ipr_device_id_attr,
473975576bb9SWayne Boyer 	&ipr_resource_type_attr,
4740f8ee25d7SWen Xiong 	&ipr_raw_mode_attr,
47411da177e4SLinus Torvalds 	NULL,
47421da177e4SLinus Torvalds };
47431da177e4SLinus Torvalds 
47441da177e4SLinus Torvalds /**
47451da177e4SLinus Torvalds  * ipr_biosparam - Return the HSC mapping
47461da177e4SLinus Torvalds  * @sdev:			scsi device struct
47471da177e4SLinus Torvalds  * @block_device:	block device pointer
47481da177e4SLinus Torvalds  * @capacity:		capacity of the device
47491da177e4SLinus Torvalds  * @parm:			Array containing returned HSC values.
47501da177e4SLinus Torvalds  *
47511da177e4SLinus Torvalds  * This function generates the HSC parms that fdisk uses.
47521da177e4SLinus Torvalds  * We want to make sure we return something that places partitions
47531da177e4SLinus Torvalds  * on 4k boundaries for best performance with the IOA.
47541da177e4SLinus Torvalds  *
47551da177e4SLinus Torvalds  * Return value:
47561da177e4SLinus Torvalds  * 	0 on success
47571da177e4SLinus Torvalds  **/
47581da177e4SLinus Torvalds static int ipr_biosparam(struct scsi_device *sdev,
47591da177e4SLinus Torvalds 			 struct block_device *block_device,
47601da177e4SLinus Torvalds 			 sector_t capacity, int *parm)
47611da177e4SLinus Torvalds {
47621da177e4SLinus Torvalds 	int heads, sectors;
47631da177e4SLinus Torvalds 	sector_t cylinders;
47641da177e4SLinus Torvalds 
47651da177e4SLinus Torvalds 	heads = 128;
47661da177e4SLinus Torvalds 	sectors = 32;
47671da177e4SLinus Torvalds 
47681da177e4SLinus Torvalds 	cylinders = capacity;
47691da177e4SLinus Torvalds 	sector_div(cylinders, (128 * 32));
47701da177e4SLinus Torvalds 
47711da177e4SLinus Torvalds 	/* return result */
47721da177e4SLinus Torvalds 	parm[0] = heads;
47731da177e4SLinus Torvalds 	parm[1] = sectors;
47741da177e4SLinus Torvalds 	parm[2] = cylinders;
47751da177e4SLinus Torvalds 
47761da177e4SLinus Torvalds 	return 0;
47771da177e4SLinus Torvalds }
47781da177e4SLinus Torvalds 
47791da177e4SLinus Torvalds /**
478035a39691SBrian King  * ipr_find_starget - Find target based on bus/target.
478135a39691SBrian King  * @starget:	scsi target struct
478235a39691SBrian King  *
478335a39691SBrian King  * Return value:
478435a39691SBrian King  * 	resource entry pointer if found / NULL if not found
478535a39691SBrian King  **/
478635a39691SBrian King static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget)
478735a39691SBrian King {
478835a39691SBrian King 	struct Scsi_Host *shost = dev_to_shost(&starget->dev);
478935a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
479035a39691SBrian King 	struct ipr_resource_entry *res;
479135a39691SBrian King 
479235a39691SBrian King 	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
47933e7ebdfaSWayne Boyer 		if ((res->bus == starget->channel) &&
47940ee1d714SBrian King 		    (res->target == starget->id)) {
479535a39691SBrian King 			return res;
479635a39691SBrian King 		}
479735a39691SBrian King 	}
479835a39691SBrian King 
479935a39691SBrian King 	return NULL;
480035a39691SBrian King }
480135a39691SBrian King 
480235a39691SBrian King static struct ata_port_info sata_port_info;
480335a39691SBrian King 
480435a39691SBrian King /**
480535a39691SBrian King  * ipr_target_alloc - Prepare for commands to a SCSI target
480635a39691SBrian King  * @starget:	scsi target struct
480735a39691SBrian King  *
480835a39691SBrian King  * If the device is a SATA device, this function allocates an
480935a39691SBrian King  * ATA port with libata, else it does nothing.
481035a39691SBrian King  *
481135a39691SBrian King  * Return value:
481235a39691SBrian King  * 	0 on success / non-0 on failure
481335a39691SBrian King  **/
481435a39691SBrian King static int ipr_target_alloc(struct scsi_target *starget)
481535a39691SBrian King {
481635a39691SBrian King 	struct Scsi_Host *shost = dev_to_shost(&starget->dev);
481735a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
481835a39691SBrian King 	struct ipr_sata_port *sata_port;
481935a39691SBrian King 	struct ata_port *ap;
482035a39691SBrian King 	struct ipr_resource_entry *res;
482135a39691SBrian King 	unsigned long lock_flags;
482235a39691SBrian King 
482335a39691SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
482435a39691SBrian King 	res = ipr_find_starget(starget);
482535a39691SBrian King 	starget->hostdata = NULL;
482635a39691SBrian King 
482735a39691SBrian King 	if (res && ipr_is_gata(res)) {
482835a39691SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
482935a39691SBrian King 		sata_port = kzalloc(sizeof(*sata_port), GFP_KERNEL);
483035a39691SBrian King 		if (!sata_port)
483135a39691SBrian King 			return -ENOMEM;
483235a39691SBrian King 
483335a39691SBrian King 		ap = ata_sas_port_alloc(&ioa_cfg->ata_host, &sata_port_info, shost);
483435a39691SBrian King 		if (ap) {
483535a39691SBrian King 			spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
483635a39691SBrian King 			sata_port->ioa_cfg = ioa_cfg;
483735a39691SBrian King 			sata_port->ap = ap;
483835a39691SBrian King 			sata_port->res = res;
483935a39691SBrian King 
484035a39691SBrian King 			res->sata_port = sata_port;
484135a39691SBrian King 			ap->private_data = sata_port;
484235a39691SBrian King 			starget->hostdata = sata_port;
484335a39691SBrian King 		} else {
484435a39691SBrian King 			kfree(sata_port);
484535a39691SBrian King 			return -ENOMEM;
484635a39691SBrian King 		}
484735a39691SBrian King 	}
484835a39691SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
484935a39691SBrian King 
485035a39691SBrian King 	return 0;
485135a39691SBrian King }
485235a39691SBrian King 
485335a39691SBrian King /**
485435a39691SBrian King  * ipr_target_destroy - Destroy a SCSI target
485535a39691SBrian King  * @starget:	scsi target struct
485635a39691SBrian King  *
485735a39691SBrian King  * If the device was a SATA device, this function frees the libata
485835a39691SBrian King  * ATA port, else it does nothing.
485935a39691SBrian King  *
486035a39691SBrian King  **/
486135a39691SBrian King static void ipr_target_destroy(struct scsi_target *starget)
486235a39691SBrian King {
486335a39691SBrian King 	struct ipr_sata_port *sata_port = starget->hostdata;
48643e7ebdfaSWayne Boyer 	struct Scsi_Host *shost = dev_to_shost(&starget->dev);
48653e7ebdfaSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
48663e7ebdfaSWayne Boyer 
48673e7ebdfaSWayne Boyer 	if (ioa_cfg->sis64) {
48680ee1d714SBrian King 		if (!ipr_find_starget(starget)) {
48693e7ebdfaSWayne Boyer 			if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
48703e7ebdfaSWayne Boyer 				clear_bit(starget->id, ioa_cfg->array_ids);
48713e7ebdfaSWayne Boyer 			else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
48723e7ebdfaSWayne Boyer 				clear_bit(starget->id, ioa_cfg->vset_ids);
48733e7ebdfaSWayne Boyer 			else if (starget->channel == 0)
48743e7ebdfaSWayne Boyer 				clear_bit(starget->id, ioa_cfg->target_ids);
48753e7ebdfaSWayne Boyer 		}
48760ee1d714SBrian King 	}
487735a39691SBrian King 
487835a39691SBrian King 	if (sata_port) {
487935a39691SBrian King 		starget->hostdata = NULL;
488035a39691SBrian King 		ata_sas_port_destroy(sata_port->ap);
488135a39691SBrian King 		kfree(sata_port);
488235a39691SBrian King 	}
488335a39691SBrian King }
488435a39691SBrian King 
488535a39691SBrian King /**
488635a39691SBrian King  * ipr_find_sdev - Find device based on bus/target/lun.
488735a39691SBrian King  * @sdev:	scsi device struct
488835a39691SBrian King  *
488935a39691SBrian King  * Return value:
489035a39691SBrian King  * 	resource entry pointer if found / NULL if not found
489135a39691SBrian King  **/
489235a39691SBrian King static struct ipr_resource_entry *ipr_find_sdev(struct scsi_device *sdev)
489335a39691SBrian King {
489435a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
489535a39691SBrian King 	struct ipr_resource_entry *res;
489635a39691SBrian King 
489735a39691SBrian King 	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
48983e7ebdfaSWayne Boyer 		if ((res->bus == sdev->channel) &&
48993e7ebdfaSWayne Boyer 		    (res->target == sdev->id) &&
49003e7ebdfaSWayne Boyer 		    (res->lun == sdev->lun))
490135a39691SBrian King 			return res;
490235a39691SBrian King 	}
490335a39691SBrian King 
490435a39691SBrian King 	return NULL;
490535a39691SBrian King }
490635a39691SBrian King 
490735a39691SBrian King /**
49081da177e4SLinus Torvalds  * ipr_slave_destroy - Unconfigure a SCSI device
49091da177e4SLinus Torvalds  * @sdev:	scsi device struct
49101da177e4SLinus Torvalds  *
49111da177e4SLinus Torvalds  * Return value:
49121da177e4SLinus Torvalds  * 	nothing
49131da177e4SLinus Torvalds  **/
49141da177e4SLinus Torvalds static void ipr_slave_destroy(struct scsi_device *sdev)
49151da177e4SLinus Torvalds {
49161da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
49171da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
49181da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
49191da177e4SLinus Torvalds 
49201da177e4SLinus Torvalds 	ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
49211da177e4SLinus Torvalds 
49221da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
49231da177e4SLinus Torvalds 	res = (struct ipr_resource_entry *) sdev->hostdata;
49241da177e4SLinus Torvalds 	if (res) {
492535a39691SBrian King 		if (res->sata_port)
49263e4ec344STejun Heo 			res->sata_port->ap->link.device[0].class = ATA_DEV_NONE;
49271da177e4SLinus Torvalds 		sdev->hostdata = NULL;
49281da177e4SLinus Torvalds 		res->sdev = NULL;
492935a39691SBrian King 		res->sata_port = NULL;
49301da177e4SLinus Torvalds 	}
49311da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
49321da177e4SLinus Torvalds }
49331da177e4SLinus Torvalds 
49341da177e4SLinus Torvalds /**
49351da177e4SLinus Torvalds  * ipr_slave_configure - Configure a SCSI device
49361da177e4SLinus Torvalds  * @sdev:	scsi device struct
49371da177e4SLinus Torvalds  *
49381da177e4SLinus Torvalds  * This function configures the specified scsi device.
49391da177e4SLinus Torvalds  *
49401da177e4SLinus Torvalds  * Return value:
49411da177e4SLinus Torvalds  * 	0 on success
49421da177e4SLinus Torvalds  **/
49431da177e4SLinus Torvalds static int ipr_slave_configure(struct scsi_device *sdev)
49441da177e4SLinus Torvalds {
49451da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
49461da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
4947dd406ef8SBrian King 	struct ata_port *ap = NULL;
49481da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
49493e7ebdfaSWayne Boyer 	char buffer[IPR_MAX_RES_PATH_LENGTH];
49501da177e4SLinus Torvalds 
49511da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
49521da177e4SLinus Torvalds 	res = sdev->hostdata;
49531da177e4SLinus Torvalds 	if (res) {
49541da177e4SLinus Torvalds 		if (ipr_is_af_dasd_device(res))
49551da177e4SLinus Torvalds 			sdev->type = TYPE_RAID;
49560726ce26Sbrking@us.ibm.com 		if (ipr_is_af_dasd_device(res) || ipr_is_ioa_resource(res)) {
49571da177e4SLinus Torvalds 			sdev->scsi_level = 4;
49580726ce26Sbrking@us.ibm.com 			sdev->no_uld_attach = 1;
49590726ce26Sbrking@us.ibm.com 		}
49601da177e4SLinus Torvalds 		if (ipr_is_vset_device(res)) {
496160654e25SBrian King 			sdev->scsi_level = SCSI_SPC_3;
4962723cd772SBrian King 			sdev->no_report_opcodes = 1;
4963242f9dcbSJens Axboe 			blk_queue_rq_timeout(sdev->request_queue,
4964242f9dcbSJens Axboe 					     IPR_VSET_RW_TIMEOUT);
4965086fa5ffSMartin K. Petersen 			blk_queue_max_hw_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
49661da177e4SLinus Torvalds 		}
4967dd406ef8SBrian King 		if (ipr_is_gata(res) && res->sata_port)
4968dd406ef8SBrian King 			ap = res->sata_port->ap;
4969dd406ef8SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
4970dd406ef8SBrian King 
4971dd406ef8SBrian King 		if (ap) {
4972db5ed4dfSChristoph Hellwig 			scsi_change_queue_depth(sdev, IPR_MAX_CMD_PER_ATA_LUN);
4973dd406ef8SBrian King 			ata_sas_slave_configure(sdev, ap);
4974c8b09f6fSChristoph Hellwig 		}
4975c8b09f6fSChristoph Hellwig 
49763e7ebdfaSWayne Boyer 		if (ioa_cfg->sis64)
49773e7ebdfaSWayne Boyer 			sdev_printk(KERN_INFO, sdev, "Resource path: %s\n",
4978b3b3b407SBrian King 				    ipr_format_res_path(ioa_cfg,
4979b3b3b407SBrian King 				res->res_path, buffer, sizeof(buffer)));
4980dd406ef8SBrian King 		return 0;
498135a39691SBrian King 	}
49821da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
49831da177e4SLinus Torvalds 	return 0;
49841da177e4SLinus Torvalds }
49851da177e4SLinus Torvalds 
49861da177e4SLinus Torvalds /**
498735a39691SBrian King  * ipr_ata_slave_alloc - Prepare for commands to a SATA device
498835a39691SBrian King  * @sdev:	scsi device struct
498935a39691SBrian King  *
499035a39691SBrian King  * This function initializes an ATA port so that future commands
499135a39691SBrian King  * sent through queuecommand will work.
499235a39691SBrian King  *
499335a39691SBrian King  * Return value:
499435a39691SBrian King  * 	0 on success
499535a39691SBrian King  **/
499635a39691SBrian King static int ipr_ata_slave_alloc(struct scsi_device *sdev)
499735a39691SBrian King {
499835a39691SBrian King 	struct ipr_sata_port *sata_port = NULL;
499935a39691SBrian King 	int rc = -ENXIO;
500035a39691SBrian King 
500135a39691SBrian King 	ENTER;
500235a39691SBrian King 	if (sdev->sdev_target)
500335a39691SBrian King 		sata_port = sdev->sdev_target->hostdata;
5004b2024459SDan Williams 	if (sata_port) {
500535a39691SBrian King 		rc = ata_sas_port_init(sata_port->ap);
5006b2024459SDan Williams 		if (rc == 0)
5007b2024459SDan Williams 			rc = ata_sas_sync_probe(sata_port->ap);
5008b2024459SDan Williams 	}
5009b2024459SDan Williams 
501035a39691SBrian King 	if (rc)
501135a39691SBrian King 		ipr_slave_destroy(sdev);
501235a39691SBrian King 
501335a39691SBrian King 	LEAVE;
501435a39691SBrian King 	return rc;
501535a39691SBrian King }
501635a39691SBrian King 
501735a39691SBrian King /**
50181da177e4SLinus Torvalds  * ipr_slave_alloc - Prepare for commands to a device.
50191da177e4SLinus Torvalds  * @sdev:	scsi device struct
50201da177e4SLinus Torvalds  *
50211da177e4SLinus Torvalds  * This function saves a pointer to the resource entry
50221da177e4SLinus Torvalds  * in the scsi device struct if the device exists. We
50231da177e4SLinus Torvalds  * can then use this pointer in ipr_queuecommand when
50241da177e4SLinus Torvalds  * handling new commands.
50251da177e4SLinus Torvalds  *
50261da177e4SLinus Torvalds  * Return value:
5027692aebfcSbrking@us.ibm.com  * 	0 on success / -ENXIO if device does not exist
50281da177e4SLinus Torvalds  **/
50291da177e4SLinus Torvalds static int ipr_slave_alloc(struct scsi_device *sdev)
50301da177e4SLinus Torvalds {
50311da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
50321da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
50331da177e4SLinus Torvalds 	unsigned long lock_flags;
5034692aebfcSbrking@us.ibm.com 	int rc = -ENXIO;
50351da177e4SLinus Torvalds 
50361da177e4SLinus Torvalds 	sdev->hostdata = NULL;
50371da177e4SLinus Torvalds 
50381da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
50391da177e4SLinus Torvalds 
504035a39691SBrian King 	res = ipr_find_sdev(sdev);
504135a39691SBrian King 	if (res) {
50421da177e4SLinus Torvalds 		res->sdev = sdev;
50431da177e4SLinus Torvalds 		res->add_to_ml = 0;
50441da177e4SLinus Torvalds 		res->in_erp = 0;
50451da177e4SLinus Torvalds 		sdev->hostdata = res;
5046ee0a90faSbrking@us.ibm.com 		if (!ipr_is_naca_model(res))
50471da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
5048692aebfcSbrking@us.ibm.com 		rc = 0;
504935a39691SBrian King 		if (ipr_is_gata(res)) {
505035a39691SBrian King 			spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
505135a39691SBrian King 			return ipr_ata_slave_alloc(sdev);
50521da177e4SLinus Torvalds 		}
50531da177e4SLinus Torvalds 	}
50541da177e4SLinus Torvalds 
50551da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
50561da177e4SLinus Torvalds 
5057692aebfcSbrking@us.ibm.com 	return rc;
50581da177e4SLinus Torvalds }
50591da177e4SLinus Torvalds 
50606cdb0817SBrian King /**
50616cdb0817SBrian King  * ipr_match_lun - Match function for specified LUN
50626cdb0817SBrian King  * @ipr_cmd:	ipr command struct
50636cdb0817SBrian King  * @device:		device to match (sdev)
50646cdb0817SBrian King  *
50656cdb0817SBrian King  * Returns:
50666cdb0817SBrian King  *	1 if command matches sdev / 0 if command does not match sdev
50676cdb0817SBrian King  **/
50686cdb0817SBrian King static int ipr_match_lun(struct ipr_cmnd *ipr_cmd, void *device)
50696cdb0817SBrian King {
50706cdb0817SBrian King 	if (ipr_cmd->scsi_cmd && ipr_cmd->scsi_cmd->device == device)
50716cdb0817SBrian King 		return 1;
50726cdb0817SBrian King 	return 0;
50736cdb0817SBrian King }
50746cdb0817SBrian King 
50756cdb0817SBrian King /**
5076439ae285SBrian King  * ipr_cmnd_is_free - Check if a command is free or not
5077a96099e2SLee Jones  * @ipr_cmd:	ipr command struct
5078439ae285SBrian King  *
5079439ae285SBrian King  * Returns:
5080439ae285SBrian King  *	true / false
5081439ae285SBrian King  **/
5082439ae285SBrian King static bool ipr_cmnd_is_free(struct ipr_cmnd *ipr_cmd)
5083439ae285SBrian King {
5084439ae285SBrian King 	struct ipr_cmnd *loop_cmd;
5085439ae285SBrian King 
5086439ae285SBrian King 	list_for_each_entry(loop_cmd, &ipr_cmd->hrrq->hrrq_free_q, queue) {
5087439ae285SBrian King 		if (loop_cmd == ipr_cmd)
5088439ae285SBrian King 			return true;
5089439ae285SBrian King 	}
5090439ae285SBrian King 
5091439ae285SBrian King 	return false;
5092439ae285SBrian King }
5093439ae285SBrian King 
5094439ae285SBrian King /**
5095ef97d8aeSBrian King  * ipr_match_res - Match function for specified resource entry
5096ef97d8aeSBrian King  * @ipr_cmd:	ipr command struct
5097ef97d8aeSBrian King  * @resource:	resource entry to match
5098ef97d8aeSBrian King  *
5099ef97d8aeSBrian King  * Returns:
5100ef97d8aeSBrian King  *	1 if command matches sdev / 0 if command does not match sdev
5101ef97d8aeSBrian King  **/
5102ef97d8aeSBrian King static int ipr_match_res(struct ipr_cmnd *ipr_cmd, void *resource)
5103ef97d8aeSBrian King {
5104ef97d8aeSBrian King 	struct ipr_resource_entry *res = resource;
5105ef97d8aeSBrian King 
5106ef97d8aeSBrian King 	if (res && ipr_cmd->ioarcb.res_handle == res->res_handle)
5107ef97d8aeSBrian King 		return 1;
5108ef97d8aeSBrian King 	return 0;
5109ef97d8aeSBrian King }
5110ef97d8aeSBrian King 
5111ef97d8aeSBrian King /**
51126cdb0817SBrian King  * ipr_wait_for_ops - Wait for matching commands to complete
5113a96099e2SLee Jones  * @ioa_cfg:	ioa config struct
51146cdb0817SBrian King  * @device:		device to match (sdev)
51156cdb0817SBrian King  * @match:		match function to use
51166cdb0817SBrian King  *
51176cdb0817SBrian King  * Returns:
51186cdb0817SBrian King  *	SUCCESS / FAILED
51196cdb0817SBrian King  **/
51206cdb0817SBrian King static int ipr_wait_for_ops(struct ipr_ioa_cfg *ioa_cfg, void *device,
51216cdb0817SBrian King 			    int (*match)(struct ipr_cmnd *, void *))
51226cdb0817SBrian King {
51236cdb0817SBrian King 	struct ipr_cmnd *ipr_cmd;
5124439ae285SBrian King 	int wait, i;
51256cdb0817SBrian King 	unsigned long flags;
51266cdb0817SBrian King 	struct ipr_hrr_queue *hrrq;
51276cdb0817SBrian King 	signed long timeout = IPR_ABORT_TASK_TIMEOUT;
51286cdb0817SBrian King 	DECLARE_COMPLETION_ONSTACK(comp);
51296cdb0817SBrian King 
51306cdb0817SBrian King 	ENTER;
51316cdb0817SBrian King 	do {
51326cdb0817SBrian King 		wait = 0;
51336cdb0817SBrian King 
51346cdb0817SBrian King 		for_each_hrrq(hrrq, ioa_cfg) {
51356cdb0817SBrian King 			spin_lock_irqsave(hrrq->lock, flags);
5136439ae285SBrian King 			for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
5137439ae285SBrian King 				ipr_cmd = ioa_cfg->ipr_cmnd_list[i];
5138439ae285SBrian King 				if (!ipr_cmnd_is_free(ipr_cmd)) {
51396cdb0817SBrian King 					if (match(ipr_cmd, device)) {
51406cdb0817SBrian King 						ipr_cmd->eh_comp = &comp;
51416cdb0817SBrian King 						wait++;
51426cdb0817SBrian King 					}
51436cdb0817SBrian King 				}
5144439ae285SBrian King 			}
51456cdb0817SBrian King 			spin_unlock_irqrestore(hrrq->lock, flags);
51466cdb0817SBrian King 		}
51476cdb0817SBrian King 
51486cdb0817SBrian King 		if (wait) {
51496cdb0817SBrian King 			timeout = wait_for_completion_timeout(&comp, timeout);
51506cdb0817SBrian King 
51516cdb0817SBrian King 			if (!timeout) {
51526cdb0817SBrian King 				wait = 0;
51536cdb0817SBrian King 
51546cdb0817SBrian King 				for_each_hrrq(hrrq, ioa_cfg) {
51556cdb0817SBrian King 					spin_lock_irqsave(hrrq->lock, flags);
5156439ae285SBrian King 					for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
5157439ae285SBrian King 						ipr_cmd = ioa_cfg->ipr_cmnd_list[i];
5158439ae285SBrian King 						if (!ipr_cmnd_is_free(ipr_cmd)) {
51596cdb0817SBrian King 							if (match(ipr_cmd, device)) {
51606cdb0817SBrian King 								ipr_cmd->eh_comp = NULL;
51616cdb0817SBrian King 								wait++;
51626cdb0817SBrian King 							}
51636cdb0817SBrian King 						}
5164439ae285SBrian King 					}
51656cdb0817SBrian King 					spin_unlock_irqrestore(hrrq->lock, flags);
51666cdb0817SBrian King 				}
51676cdb0817SBrian King 
51686cdb0817SBrian King 				if (wait)
51696cdb0817SBrian King 					dev_err(&ioa_cfg->pdev->dev, "Timed out waiting for aborted commands\n");
51706cdb0817SBrian King 				LEAVE;
51716cdb0817SBrian King 				return wait ? FAILED : SUCCESS;
51726cdb0817SBrian King 			}
51736cdb0817SBrian King 		}
51746cdb0817SBrian King 	} while (wait);
51756cdb0817SBrian King 
51766cdb0817SBrian King 	LEAVE;
51776cdb0817SBrian King 	return SUCCESS;
51786cdb0817SBrian King }
51796cdb0817SBrian King 
518070233ac5Swenxiong@linux.vnet.ibm.com static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
51811da177e4SLinus Torvalds {
51821da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
518370233ac5Swenxiong@linux.vnet.ibm.com 	unsigned long lock_flags = 0;
518470233ac5Swenxiong@linux.vnet.ibm.com 	int rc = SUCCESS;
51851da177e4SLinus Torvalds 
51861da177e4SLinus Torvalds 	ENTER;
518770233ac5Swenxiong@linux.vnet.ibm.com 	ioa_cfg = (struct ipr_ioa_cfg *) cmd->device->host->hostdata;
518870233ac5Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
51891da177e4SLinus Torvalds 
519096b04db9Swenxiong@linux.vnet.ibm.com 	if (!ioa_cfg->in_reset_reload && !ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead) {
519170233ac5Swenxiong@linux.vnet.ibm.com 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_ABBREV);
51921da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
51931da177e4SLinus Torvalds 			"Adapter being reset as a result of error recovery.\n");
51941da177e4SLinus Torvalds 
51951da177e4SLinus Torvalds 		if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
51961da177e4SLinus Torvalds 			ioa_cfg->sdt_state = GET_DUMP;
5197a92fa25cSKleber Sacilotto de Souza 	}
51981da177e4SLinus Torvalds 
519970233ac5Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
520070233ac5Swenxiong@linux.vnet.ibm.com 	wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
520170233ac5Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
52021da177e4SLinus Torvalds 
520370233ac5Swenxiong@linux.vnet.ibm.com 	/* If we got hit with a host reset while we were already resetting
520470233ac5Swenxiong@linux.vnet.ibm.com 	 the adapter for some reason, and the reset failed. */
520570233ac5Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead) {
520670233ac5Swenxiong@linux.vnet.ibm.com 		ipr_trace;
520770233ac5Swenxiong@linux.vnet.ibm.com 		rc = FAILED;
52081da177e4SLinus Torvalds 	}
52091da177e4SLinus Torvalds 
521070233ac5Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
521170233ac5Swenxiong@linux.vnet.ibm.com 	LEAVE;
5212df0ae249SJeff Garzik  	return rc;
5213df0ae249SJeff Garzik  }
5214df0ae249SJeff Garzik  
52151da177e4SLinus Torvalds /**
5216c6513096SBrian King  * ipr_device_reset - Reset the device
5217c6513096SBrian King  * @ioa_cfg:	ioa config struct
5218c6513096SBrian King  * @res:		resource entry struct
5219c6513096SBrian King  *
5220c6513096SBrian King  * This function issues a device reset to the affected device.
5221c6513096SBrian King  * If the device is a SCSI device, a LUN reset will be sent
5222c6513096SBrian King  * to the device first. If that does not work, a target reset
522335a39691SBrian King  * will be sent. If the device is a SATA device, a PHY reset will
522435a39691SBrian King  * be sent.
5225c6513096SBrian King  *
5226c6513096SBrian King  * Return value:
5227c6513096SBrian King  *	0 on success / non-zero on failure
5228c6513096SBrian King  **/
5229c6513096SBrian King static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
5230c6513096SBrian King 			    struct ipr_resource_entry *res)
5231c6513096SBrian King {
5232c6513096SBrian King 	struct ipr_cmnd *ipr_cmd;
5233c6513096SBrian King 	struct ipr_ioarcb *ioarcb;
5234c6513096SBrian King 	struct ipr_cmd_pkt *cmd_pkt;
523535a39691SBrian King 	struct ipr_ioarcb_ata_regs *regs;
5236c6513096SBrian King 	u32 ioasc;
5237c6513096SBrian King 
5238c6513096SBrian King 	ENTER;
5239c6513096SBrian King 	ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
5240c6513096SBrian King 	ioarcb = &ipr_cmd->ioarcb;
5241c6513096SBrian King 	cmd_pkt = &ioarcb->cmd_pkt;
5242a32c055fSWayne Boyer 
5243a32c055fSWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64) {
5244a32c055fSWayne Boyer 		regs = &ipr_cmd->i.ata_ioadl.regs;
5245a32c055fSWayne Boyer 		ioarcb->add_cmd_parms_offset = cpu_to_be16(sizeof(*ioarcb));
5246a32c055fSWayne Boyer 	} else
5247a32c055fSWayne Boyer 		regs = &ioarcb->u.add_data.u.regs;
5248c6513096SBrian King 
52493e7ebdfaSWayne Boyer 	ioarcb->res_handle = res->res_handle;
5250c6513096SBrian King 	cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
5251c6513096SBrian King 	cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
525235a39691SBrian King 	if (ipr_is_gata(res)) {
525335a39691SBrian King 		cmd_pkt->cdb[2] = IPR_ATA_PHY_RESET;
5254a32c055fSWayne Boyer 		ioarcb->add_cmd_parms_len = cpu_to_be16(sizeof(regs->flags));
525535a39691SBrian King 		regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION;
525635a39691SBrian King 	}
5257c6513096SBrian King 
5258c6513096SBrian King 	ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
525996d21f00SWayne Boyer 	ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
526005a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
526196d21f00SWayne Boyer 	if (ipr_is_gata(res) && res->sata_port && ioasc != IPR_IOASC_IOA_WAS_RESET) {
526296d21f00SWayne Boyer 		if (ipr_cmd->ioa_cfg->sis64)
526396d21f00SWayne Boyer 			memcpy(&res->sata_port->ioasa, &ipr_cmd->s.ioasa64.u.gata,
526435a39691SBrian King 			       sizeof(struct ipr_ioasa_gata));
526596d21f00SWayne Boyer 		else
526696d21f00SWayne Boyer 			memcpy(&res->sata_port->ioasa, &ipr_cmd->s.ioasa.u.gata,
526796d21f00SWayne Boyer 			       sizeof(struct ipr_ioasa_gata));
526896d21f00SWayne Boyer 	}
5269c6513096SBrian King 
5270c6513096SBrian King 	LEAVE;
5271203fa3feSKleber Sacilotto de Souza 	return IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0;
5272c6513096SBrian King }
5273c6513096SBrian King 
5274c6513096SBrian King /**
527535a39691SBrian King  * ipr_sata_reset - Reset the SATA port
5276cc0680a5STejun Heo  * @link:	SATA link to reset
527735a39691SBrian King  * @classes:	class of the attached device
5278a96099e2SLee Jones  * @deadline:	unused
527935a39691SBrian King  *
5280cc0680a5STejun Heo  * This function issues a SATA phy reset to the affected ATA link.
528135a39691SBrian King  *
528235a39691SBrian King  * Return value:
528335a39691SBrian King  *	0 on success / non-zero on failure
528435a39691SBrian King  **/
5285cc0680a5STejun Heo static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
5286120bda35SAndrew Morton 				unsigned long deadline)
528735a39691SBrian King {
5288cc0680a5STejun Heo 	struct ipr_sata_port *sata_port = link->ap->private_data;
528935a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
529035a39691SBrian King 	struct ipr_resource_entry *res;
529135a39691SBrian King 	unsigned long lock_flags = 0;
5292ef97d8aeSBrian King 	int rc = -ENXIO, ret;
529335a39691SBrian King 
529435a39691SBrian King 	ENTER;
529535a39691SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
529673d98ff0SBrian King 	while (ioa_cfg->in_reset_reload) {
529773d98ff0SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
529873d98ff0SBrian King 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
529973d98ff0SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
530073d98ff0SBrian King 	}
530173d98ff0SBrian King 
530235a39691SBrian King 	res = sata_port->res;
530335a39691SBrian King 	if (res) {
530435a39691SBrian King 		rc = ipr_device_reset(ioa_cfg, res);
53053e7ebdfaSWayne Boyer 		*classes = res->ata_class;
530635a39691SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
5307ef97d8aeSBrian King 
5308ef97d8aeSBrian King 		ret = ipr_wait_for_ops(ioa_cfg, res, ipr_match_res);
5309ef97d8aeSBrian King 		if (ret != SUCCESS) {
5310ef97d8aeSBrian King 			spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
5311ef97d8aeSBrian King 			ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_ABBREV);
5312ef97d8aeSBrian King 			spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
5313ef97d8aeSBrian King 
5314ef97d8aeSBrian King 			wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
5315ef97d8aeSBrian King 		}
5316ef97d8aeSBrian King 	} else
5317ef97d8aeSBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
5318ef97d8aeSBrian King 
531935a39691SBrian King 	LEAVE;
532035a39691SBrian King 	return rc;
532135a39691SBrian King }
532235a39691SBrian King 
532335a39691SBrian King /**
5324*637b5c3eSLee Jones  * __ipr_eh_dev_reset - Reset the device
53251da177e4SLinus Torvalds  * @scsi_cmd:	scsi command struct
53261da177e4SLinus Torvalds  *
53271da177e4SLinus Torvalds  * This function issues a device reset to the affected device.
53281da177e4SLinus Torvalds  * A LUN reset will be sent to the device first. If that does
53291da177e4SLinus Torvalds  * not work, a target reset will be sent.
53301da177e4SLinus Torvalds  *
53311da177e4SLinus Torvalds  * Return value:
53321da177e4SLinus Torvalds  *	SUCCESS / FAILED
53331da177e4SLinus Torvalds  **/
533494d0e7b8SJeff Garzik  static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
53351da177e4SLinus Torvalds {
53361da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd;
53371da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
53381da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
533935a39691SBrian King 	struct ata_port *ap;
5340439ae285SBrian King 	int rc = 0, i;
534105a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
53421da177e4SLinus Torvalds 
53431da177e4SLinus Torvalds 	ENTER;
53441da177e4SLinus Torvalds 	ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
53451da177e4SLinus Torvalds 	res = scsi_cmd->device->hostdata;
53461da177e4SLinus Torvalds 
53471da177e4SLinus Torvalds 	/*
53481da177e4SLinus Torvalds 	 * If we are currently going through reset/reload, return failed. This will force the
53491da177e4SLinus Torvalds 	 * mid-layer to call ipr_eh_host_reset, which will then go to sleep and wait for the
53501da177e4SLinus Torvalds 	 * reset to complete
53511da177e4SLinus Torvalds 	 */
53521da177e4SLinus Torvalds 	if (ioa_cfg->in_reset_reload)
53531da177e4SLinus Torvalds 		return FAILED;
535456d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
53551da177e4SLinus Torvalds 		return FAILED;
53561da177e4SLinus Torvalds 
535705a6538aSwenxiong@linux.vnet.ibm.com 	for_each_hrrq(hrrq, ioa_cfg) {
535856d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&hrrq->_lock);
5359439ae285SBrian King 		for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
5360439ae285SBrian King 			ipr_cmd = ioa_cfg->ipr_cmnd_list[i];
5361439ae285SBrian King 
53623e7ebdfaSWayne Boyer 			if (ipr_cmd->ioarcb.res_handle == res->res_handle) {
5363960e9648SBrian King 				if (!ipr_cmd->qc)
5364960e9648SBrian King 					continue;
5365439ae285SBrian King 				if (ipr_cmnd_is_free(ipr_cmd))
5366439ae285SBrian King 					continue;
5367960e9648SBrian King 
536824d6f2b5SBrian King 				ipr_cmd->done = ipr_sata_eh_done;
5369960e9648SBrian King 				if (!(ipr_cmd->qc->flags & ATA_QCFLAG_FAILED)) {
53707402ecefSBrian King 					ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
53717402ecefSBrian King 					ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED;
53727402ecefSBrian King 				}
53731da177e4SLinus Torvalds 			}
53741da177e4SLinus Torvalds 		}
537556d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
537605a6538aSwenxiong@linux.vnet.ibm.com 	}
53771da177e4SLinus Torvalds 	res->resetting_device = 1;
5378fb3ed3cbSBrian King 	scmd_printk(KERN_ERR, scsi_cmd, "Resetting device\n");
537935a39691SBrian King 
538035a39691SBrian King 	if (ipr_is_gata(res) && res->sata_port) {
538135a39691SBrian King 		ap = res->sata_port->ap;
538235a39691SBrian King 		spin_unlock_irq(scsi_cmd->device->host->host_lock);
5383a1efdabaSTejun Heo 		ata_std_error_handler(ap);
538435a39691SBrian King 		spin_lock_irq(scsi_cmd->device->host->host_lock);
538535a39691SBrian King 	} else
5386c6513096SBrian King 		rc = ipr_device_reset(ioa_cfg, res);
53871da177e4SLinus Torvalds 	res->resetting_device = 0;
53880b1f8d44SWendy Xiong 	res->reset_occurred = 1;
53891da177e4SLinus Torvalds 
53901da177e4SLinus Torvalds 	LEAVE;
5391203fa3feSKleber Sacilotto de Souza 	return rc ? FAILED : SUCCESS;
53921da177e4SLinus Torvalds }
53931da177e4SLinus Torvalds 
539494d0e7b8SJeff Garzik  static int ipr_eh_dev_reset(struct scsi_cmnd *cmd)
539594d0e7b8SJeff Garzik  {
539694d0e7b8SJeff Garzik  	int rc;
53976cdb0817SBrian King 	struct ipr_ioa_cfg *ioa_cfg;
5398ef97d8aeSBrian King 	struct ipr_resource_entry *res;
53996cdb0817SBrian King 
54006cdb0817SBrian King 	ioa_cfg = (struct ipr_ioa_cfg *) cmd->device->host->hostdata;
5401ef97d8aeSBrian King 	res = cmd->device->hostdata;
5402ef97d8aeSBrian King 
5403ef97d8aeSBrian King 	if (!res)
5404ef97d8aeSBrian King 		return FAILED;
540594d0e7b8SJeff Garzik  
540694d0e7b8SJeff Garzik  	spin_lock_irq(cmd->device->host->host_lock);
540794d0e7b8SJeff Garzik  	rc = __ipr_eh_dev_reset(cmd);
540894d0e7b8SJeff Garzik  	spin_unlock_irq(cmd->device->host->host_lock);
540994d0e7b8SJeff Garzik  
5410ef97d8aeSBrian King 	if (rc == SUCCESS) {
5411ef97d8aeSBrian King 		if (ipr_is_gata(res) && res->sata_port)
5412ef97d8aeSBrian King 			rc = ipr_wait_for_ops(ioa_cfg, res, ipr_match_res);
5413ef97d8aeSBrian King 		else
54146cdb0817SBrian King 			rc = ipr_wait_for_ops(ioa_cfg, cmd->device, ipr_match_lun);
5415ef97d8aeSBrian King 	}
54166cdb0817SBrian King 
541794d0e7b8SJeff Garzik  	return rc;
541894d0e7b8SJeff Garzik  }
541994d0e7b8SJeff Garzik  
54201da177e4SLinus Torvalds /**
54211da177e4SLinus Torvalds  * ipr_bus_reset_done - Op done function for bus reset.
54221da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
54231da177e4SLinus Torvalds  *
54241da177e4SLinus Torvalds  * This function is the op done function for a bus reset
54251da177e4SLinus Torvalds  *
54261da177e4SLinus Torvalds  * Return value:
54271da177e4SLinus Torvalds  * 	none
54281da177e4SLinus Torvalds  **/
54291da177e4SLinus Torvalds static void ipr_bus_reset_done(struct ipr_cmnd *ipr_cmd)
54301da177e4SLinus Torvalds {
54311da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
54321da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
54331da177e4SLinus Torvalds 
54341da177e4SLinus Torvalds 	ENTER;
54353e7ebdfaSWayne Boyer 	if (!ioa_cfg->sis64)
54361da177e4SLinus Torvalds 		list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
54373e7ebdfaSWayne Boyer 			if (res->res_handle == ipr_cmd->ioarcb.res_handle) {
54383e7ebdfaSWayne Boyer 				scsi_report_bus_reset(ioa_cfg->host, res->bus);
54391da177e4SLinus Torvalds 				break;
54401da177e4SLinus Torvalds 			}
54411da177e4SLinus Torvalds 		}
54421da177e4SLinus Torvalds 
54431da177e4SLinus Torvalds 	/*
54441da177e4SLinus Torvalds 	 * If abort has not completed, indicate the reset has, else call the
54451da177e4SLinus Torvalds 	 * abort's done function to wake the sleeping eh thread
54461da177e4SLinus Torvalds 	 */
54471da177e4SLinus Torvalds 	if (ipr_cmd->sibling->sibling)
54481da177e4SLinus Torvalds 		ipr_cmd->sibling->sibling = NULL;
54491da177e4SLinus Torvalds 	else
54501da177e4SLinus Torvalds 		ipr_cmd->sibling->done(ipr_cmd->sibling);
54511da177e4SLinus Torvalds 
545205a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
54531da177e4SLinus Torvalds 	LEAVE;
54541da177e4SLinus Torvalds }
54551da177e4SLinus Torvalds 
54561da177e4SLinus Torvalds /**
54571da177e4SLinus Torvalds  * ipr_abort_timeout - An abort task has timed out
5458a96099e2SLee Jones  * @t: Timer context used to fetch ipr command struct
54591da177e4SLinus Torvalds  *
54601da177e4SLinus Torvalds  * This function handles when an abort task times out. If this
54611da177e4SLinus Torvalds  * happens we issue a bus reset since we have resources tied
54621da177e4SLinus Torvalds  * up that must be freed before returning to the midlayer.
54631da177e4SLinus Torvalds  *
54641da177e4SLinus Torvalds  * Return value:
54651da177e4SLinus Torvalds  *	none
54661da177e4SLinus Torvalds  **/
5467738c6ec5SKees Cook static void ipr_abort_timeout(struct timer_list *t)
54681da177e4SLinus Torvalds {
5469738c6ec5SKees Cook 	struct ipr_cmnd *ipr_cmd = from_timer(ipr_cmd, t, timer);
54701da177e4SLinus Torvalds 	struct ipr_cmnd *reset_cmd;
54711da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
54721da177e4SLinus Torvalds 	struct ipr_cmd_pkt *cmd_pkt;
54731da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
54741da177e4SLinus Torvalds 
54751da177e4SLinus Torvalds 	ENTER;
54761da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
54771da177e4SLinus Torvalds 	if (ipr_cmd->completion.done || ioa_cfg->in_reset_reload) {
54781da177e4SLinus Torvalds 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
54791da177e4SLinus Torvalds 		return;
54801da177e4SLinus Torvalds 	}
54811da177e4SLinus Torvalds 
5482fb3ed3cbSBrian King 	sdev_printk(KERN_ERR, ipr_cmd->u.sdev, "Abort timed out. Resetting bus.\n");
54831da177e4SLinus Torvalds 	reset_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
54841da177e4SLinus Torvalds 	ipr_cmd->sibling = reset_cmd;
54851da177e4SLinus Torvalds 	reset_cmd->sibling = ipr_cmd;
54861da177e4SLinus Torvalds 	reset_cmd->ioarcb.res_handle = ipr_cmd->ioarcb.res_handle;
54871da177e4SLinus Torvalds 	cmd_pkt = &reset_cmd->ioarcb.cmd_pkt;
54881da177e4SLinus Torvalds 	cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
54891da177e4SLinus Torvalds 	cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
54901da177e4SLinus Torvalds 	cmd_pkt->cdb[2] = IPR_RESET_TYPE_SELECT | IPR_BUS_RESET;
54911da177e4SLinus Torvalds 
54921da177e4SLinus Torvalds 	ipr_do_req(reset_cmd, ipr_bus_reset_done, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
54931da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
54941da177e4SLinus Torvalds 	LEAVE;
54951da177e4SLinus Torvalds }
54961da177e4SLinus Torvalds 
54971da177e4SLinus Torvalds /**
54981da177e4SLinus Torvalds  * ipr_cancel_op - Cancel specified op
54991da177e4SLinus Torvalds  * @scsi_cmd:	scsi command struct
55001da177e4SLinus Torvalds  *
55011da177e4SLinus Torvalds  * This function cancels specified op.
55021da177e4SLinus Torvalds  *
55031da177e4SLinus Torvalds  * Return value:
55041da177e4SLinus Torvalds  *	SUCCESS / FAILED
55051da177e4SLinus Torvalds  **/
55061da177e4SLinus Torvalds static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
55071da177e4SLinus Torvalds {
55081da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd;
55091da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
55101da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
55111da177e4SLinus Torvalds 	struct ipr_cmd_pkt *cmd_pkt;
55124dc83399SLee Jones 	u32 ioasc;
5513439ae285SBrian King 	int i, op_found = 0;
551405a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
55151da177e4SLinus Torvalds 
55161da177e4SLinus Torvalds 	ENTER;
55171da177e4SLinus Torvalds 	ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata;
55181da177e4SLinus Torvalds 	res = scsi_cmd->device->hostdata;
55191da177e4SLinus Torvalds 
55208fa728a2SJeff Garzik  	/* If we are currently going through reset/reload, return failed.
55218fa728a2SJeff Garzik  	 * This will force the mid-layer to call ipr_eh_host_reset,
55228fa728a2SJeff Garzik  	 * which will then go to sleep and wait for the reset to complete
55238fa728a2SJeff Garzik  	 */
552456d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->in_reset_reload ||
552556d6aa33Swenxiong@linux.vnet.ibm.com 	    ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
55268fa728a2SJeff Garzik  		return FAILED;
5527a92fa25cSKleber Sacilotto de Souza 	if (!res)
5528a92fa25cSKleber Sacilotto de Souza 		return FAILED;
5529a92fa25cSKleber Sacilotto de Souza 
5530a92fa25cSKleber Sacilotto de Souza 	/*
5531a92fa25cSKleber Sacilotto de Souza 	 * If we are aborting a timed out op, chances are that the timeout was caused
5532a92fa25cSKleber Sacilotto de Souza 	 * by a still not detected EEH error. In such cases, reading a register will
5533a92fa25cSKleber Sacilotto de Souza 	 * trigger the EEH recovery infrastructure.
5534a92fa25cSKleber Sacilotto de Souza 	 */
55354dc83399SLee Jones 	readl(ioa_cfg->regs.sense_interrupt_reg);
5536a92fa25cSKleber Sacilotto de Souza 
5537a92fa25cSKleber Sacilotto de Souza 	if (!ipr_is_gscsi(res))
55381da177e4SLinus Torvalds 		return FAILED;
55391da177e4SLinus Torvalds 
554005a6538aSwenxiong@linux.vnet.ibm.com 	for_each_hrrq(hrrq, ioa_cfg) {
554156d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&hrrq->_lock);
5542439ae285SBrian King 		for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
5543439ae285SBrian King 			if (ioa_cfg->ipr_cmnd_list[i]->scsi_cmd == scsi_cmd) {
5544439ae285SBrian King 				if (!ipr_cmnd_is_free(ioa_cfg->ipr_cmnd_list[i])) {
55451da177e4SLinus Torvalds 					op_found = 1;
55461da177e4SLinus Torvalds 					break;
55471da177e4SLinus Torvalds 				}
55481da177e4SLinus Torvalds 			}
5549439ae285SBrian King 		}
555056d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
555105a6538aSwenxiong@linux.vnet.ibm.com 	}
55521da177e4SLinus Torvalds 
55531da177e4SLinus Torvalds 	if (!op_found)
55541da177e4SLinus Torvalds 		return SUCCESS;
55551da177e4SLinus Torvalds 
55561da177e4SLinus Torvalds 	ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
55573e7ebdfaSWayne Boyer 	ipr_cmd->ioarcb.res_handle = res->res_handle;
55581da177e4SLinus Torvalds 	cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
55591da177e4SLinus Torvalds 	cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
55601da177e4SLinus Torvalds 	cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS;
55611da177e4SLinus Torvalds 	ipr_cmd->u.sdev = scsi_cmd->device;
55621da177e4SLinus Torvalds 
5563fb3ed3cbSBrian King 	scmd_printk(KERN_ERR, scsi_cmd, "Aborting command: %02X\n",
5564fb3ed3cbSBrian King 		    scsi_cmd->cmnd[0]);
55651da177e4SLinus Torvalds 	ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_CANCEL_ALL_TIMEOUT);
556696d21f00SWayne Boyer 	ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
55671da177e4SLinus Torvalds 
55681da177e4SLinus Torvalds 	/*
55691da177e4SLinus Torvalds 	 * If the abort task timed out and we sent a bus reset, we will get
55701da177e4SLinus Torvalds 	 * one the following responses to the abort
55711da177e4SLinus Torvalds 	 */
55721da177e4SLinus Torvalds 	if (ioasc == IPR_IOASC_BUS_WAS_RESET || ioasc == IPR_IOASC_SYNC_REQUIRED) {
55731da177e4SLinus Torvalds 		ioasc = 0;
55741da177e4SLinus Torvalds 		ipr_trace;
55751da177e4SLinus Torvalds 	}
55761da177e4SLinus Torvalds 
5577c4ee22a3SKleber Sacilotto de Souza 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
5578ee0a90faSbrking@us.ibm.com 	if (!ipr_is_naca_model(res))
55791da177e4SLinus Torvalds 		res->needs_sync_complete = 1;
55801da177e4SLinus Torvalds 
55811da177e4SLinus Torvalds 	LEAVE;
5582203fa3feSKleber Sacilotto de Souza 	return IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
55831da177e4SLinus Torvalds }
55841da177e4SLinus Torvalds 
55851da177e4SLinus Torvalds /**
5586*637b5c3eSLee Jones  * ipr_scan_finished - Report whether scan is done
5587a96099e2SLee Jones  * @shost:           scsi host struct
5588a96099e2SLee Jones  * @elapsed_time:    elapsed time
55891da177e4SLinus Torvalds  *
55901da177e4SLinus Torvalds  * Return value:
5591f688f96dSBrian King  *	0 if scan in progress / 1 if scan is complete
5592f688f96dSBrian King  **/
5593f688f96dSBrian King static int ipr_scan_finished(struct Scsi_Host *shost, unsigned long elapsed_time)
5594f688f96dSBrian King {
5595f688f96dSBrian King 	unsigned long lock_flags;
5596f688f96dSBrian King 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
5597f688f96dSBrian King 	int rc = 0;
5598f688f96dSBrian King 
5599f688f96dSBrian King 	spin_lock_irqsave(shost->host_lock, lock_flags);
5600f688f96dSBrian King 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead || ioa_cfg->scan_done)
5601f688f96dSBrian King 		rc = 1;
5602f688f96dSBrian King 	if ((elapsed_time/HZ) > (ioa_cfg->transop_timeout * 2))
5603f688f96dSBrian King 		rc = 1;
5604f688f96dSBrian King 	spin_unlock_irqrestore(shost->host_lock, lock_flags);
5605f688f96dSBrian King 	return rc;
5606f688f96dSBrian King }
5607f688f96dSBrian King 
5608f688f96dSBrian King /**
5609*637b5c3eSLee Jones  * ipr_eh_abort - Reset the host adapter
5610f688f96dSBrian King  * @scsi_cmd:	scsi command struct
5611f688f96dSBrian King  *
5612f688f96dSBrian King  * Return value:
56131da177e4SLinus Torvalds  * 	SUCCESS / FAILED
56141da177e4SLinus Torvalds  **/
56151da177e4SLinus Torvalds static int ipr_eh_abort(struct scsi_cmnd *scsi_cmd)
56161da177e4SLinus Torvalds {
56178fa728a2SJeff Garzik  	unsigned long flags;
56188fa728a2SJeff Garzik  	int rc;
56196cdb0817SBrian King 	struct ipr_ioa_cfg *ioa_cfg;
56201da177e4SLinus Torvalds 
56211da177e4SLinus Torvalds 	ENTER;
56221da177e4SLinus Torvalds 
56236cdb0817SBrian King 	ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
56246cdb0817SBrian King 
56258fa728a2SJeff Garzik  	spin_lock_irqsave(scsi_cmd->device->host->host_lock, flags);
56268fa728a2SJeff Garzik  	rc = ipr_cancel_op(scsi_cmd);
56278fa728a2SJeff Garzik  	spin_unlock_irqrestore(scsi_cmd->device->host->host_lock, flags);
56281da177e4SLinus Torvalds 
56296cdb0817SBrian King 	if (rc == SUCCESS)
56306cdb0817SBrian King 		rc = ipr_wait_for_ops(ioa_cfg, scsi_cmd->device, ipr_match_lun);
56311da177e4SLinus Torvalds 	LEAVE;
56328fa728a2SJeff Garzik  	return rc;
56331da177e4SLinus Torvalds }
56341da177e4SLinus Torvalds 
56351da177e4SLinus Torvalds /**
56361da177e4SLinus Torvalds  * ipr_handle_other_interrupt - Handle "other" interrupts
56371da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
5638634651faSWayne Boyer  * @int_reg:	interrupt register
56391da177e4SLinus Torvalds  *
56401da177e4SLinus Torvalds  * Return value:
56411da177e4SLinus Torvalds  * 	IRQ_NONE / IRQ_HANDLED
56421da177e4SLinus Torvalds  **/
5643634651faSWayne Boyer static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
5644630ad831SWayne Boyer 					      u32 int_reg)
56451da177e4SLinus Torvalds {
56461da177e4SLinus Torvalds 	irqreturn_t rc = IRQ_HANDLED;
56477dacb64fSWayne Boyer 	u32 int_mask_reg;
564856d6aa33Swenxiong@linux.vnet.ibm.com 
56497dacb64fSWayne Boyer 	int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg32);
56507dacb64fSWayne Boyer 	int_reg &= ~int_mask_reg;
56517dacb64fSWayne Boyer 
56527dacb64fSWayne Boyer 	/* If an interrupt on the adapter did not occur, ignore it.
56537dacb64fSWayne Boyer 	 * Or in the case of SIS 64, check for a stage change interrupt.
56547dacb64fSWayne Boyer 	 */
56557dacb64fSWayne Boyer 	if ((int_reg & IPR_PCII_OPER_INTERRUPTS) == 0) {
56567dacb64fSWayne Boyer 		if (ioa_cfg->sis64) {
56577dacb64fSWayne Boyer 			int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
56587dacb64fSWayne Boyer 			int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
56597dacb64fSWayne Boyer 			if (int_reg & IPR_PCII_IPL_STAGE_CHANGE) {
56607dacb64fSWayne Boyer 
56617dacb64fSWayne Boyer 				/* clear stage change */
56627dacb64fSWayne Boyer 				writel(IPR_PCII_IPL_STAGE_CHANGE, ioa_cfg->regs.clr_interrupt_reg);
56637dacb64fSWayne Boyer 				int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
56647dacb64fSWayne Boyer 				list_del(&ioa_cfg->reset_cmd->queue);
56657dacb64fSWayne Boyer 				del_timer(&ioa_cfg->reset_cmd->timer);
56667dacb64fSWayne Boyer 				ipr_reset_ioa_job(ioa_cfg->reset_cmd);
56677dacb64fSWayne Boyer 				return IRQ_HANDLED;
56687dacb64fSWayne Boyer 			}
56697dacb64fSWayne Boyer 		}
56707dacb64fSWayne Boyer 
56717dacb64fSWayne Boyer 		return IRQ_NONE;
56727dacb64fSWayne Boyer 	}
56731da177e4SLinus Torvalds 
56741da177e4SLinus Torvalds 	if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
56751da177e4SLinus Torvalds 		/* Mask the interrupt */
56761da177e4SLinus Torvalds 		writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.set_interrupt_mask_reg);
56771da177e4SLinus Torvalds 		int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
56781da177e4SLinus Torvalds 
56791da177e4SLinus Torvalds 		list_del(&ioa_cfg->reset_cmd->queue);
56801da177e4SLinus Torvalds 		del_timer(&ioa_cfg->reset_cmd->timer);
56811da177e4SLinus Torvalds 		ipr_reset_ioa_job(ioa_cfg->reset_cmd);
56827dacb64fSWayne Boyer 	} else if ((int_reg & IPR_PCII_HRRQ_UPDATED) == int_reg) {
56837dd21308SBrian King 		if (ioa_cfg->clear_isr) {
56847dacb64fSWayne Boyer 			if (ipr_debug && printk_ratelimit())
56857dacb64fSWayne Boyer 				dev_err(&ioa_cfg->pdev->dev,
56867dacb64fSWayne Boyer 					"Spurious interrupt detected. 0x%08X\n", int_reg);
56877dacb64fSWayne Boyer 			writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
56887dacb64fSWayne Boyer 			int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
56897dacb64fSWayne Boyer 			return IRQ_NONE;
56907dd21308SBrian King 		}
56911da177e4SLinus Torvalds 	} else {
56921da177e4SLinus Torvalds 		if (int_reg & IPR_PCII_IOA_UNIT_CHECKED)
56931da177e4SLinus Torvalds 			ioa_cfg->ioa_unit_checked = 1;
569405a6538aSwenxiong@linux.vnet.ibm.com 		else if (int_reg & IPR_PCII_NO_HOST_RRQ)
569505a6538aSwenxiong@linux.vnet.ibm.com 			dev_err(&ioa_cfg->pdev->dev,
569605a6538aSwenxiong@linux.vnet.ibm.com 				"No Host RRQ. 0x%08X\n", int_reg);
56971da177e4SLinus Torvalds 		else
56981da177e4SLinus Torvalds 			dev_err(&ioa_cfg->pdev->dev,
56991da177e4SLinus Torvalds 				"Permanent IOA failure. 0x%08X\n", int_reg);
57001da177e4SLinus Torvalds 
57011da177e4SLinus Torvalds 		if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
57021da177e4SLinus Torvalds 			ioa_cfg->sdt_state = GET_DUMP;
57031da177e4SLinus Torvalds 
57041da177e4SLinus Torvalds 		ipr_mask_and_clear_interrupts(ioa_cfg, ~0);
57051da177e4SLinus Torvalds 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
57061da177e4SLinus Torvalds 	}
570756d6aa33Swenxiong@linux.vnet.ibm.com 
57081da177e4SLinus Torvalds 	return rc;
57091da177e4SLinus Torvalds }
57101da177e4SLinus Torvalds 
57111da177e4SLinus Torvalds /**
57123feeb89dSWayne Boyer  * ipr_isr_eh - Interrupt service routine error handler
57133feeb89dSWayne Boyer  * @ioa_cfg:	ioa config struct
57143feeb89dSWayne Boyer  * @msg:	message to log
5715a96099e2SLee Jones  * @number:	various meanings depending on the caller/message
57163feeb89dSWayne Boyer  *
57173feeb89dSWayne Boyer  * Return value:
57183feeb89dSWayne Boyer  * 	none
57193feeb89dSWayne Boyer  **/
572005a6538aSwenxiong@linux.vnet.ibm.com static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg, u16 number)
57213feeb89dSWayne Boyer {
57223feeb89dSWayne Boyer 	ioa_cfg->errors_logged++;
572305a6538aSwenxiong@linux.vnet.ibm.com 	dev_err(&ioa_cfg->pdev->dev, "%s %d\n", msg, number);
57243feeb89dSWayne Boyer 
57253feeb89dSWayne Boyer 	if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
57263feeb89dSWayne Boyer 		ioa_cfg->sdt_state = GET_DUMP;
57273feeb89dSWayne Boyer 
57283feeb89dSWayne Boyer 	ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
57293feeb89dSWayne Boyer }
57303feeb89dSWayne Boyer 
5731b53d124aSwenxiong@linux.vnet.ibm.com static int ipr_process_hrrq(struct ipr_hrr_queue *hrr_queue, int budget,
573205a6538aSwenxiong@linux.vnet.ibm.com 						struct list_head *doneq)
573305a6538aSwenxiong@linux.vnet.ibm.com {
573405a6538aSwenxiong@linux.vnet.ibm.com 	u32 ioasc;
573505a6538aSwenxiong@linux.vnet.ibm.com 	u16 cmd_index;
573605a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_cmnd *ipr_cmd;
573705a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = hrr_queue->ioa_cfg;
573805a6538aSwenxiong@linux.vnet.ibm.com 	int num_hrrq = 0;
573905a6538aSwenxiong@linux.vnet.ibm.com 
574005a6538aSwenxiong@linux.vnet.ibm.com 	/* If interrupts are disabled, ignore the interrupt */
574156d6aa33Swenxiong@linux.vnet.ibm.com 	if (!hrr_queue->allow_interrupts)
574205a6538aSwenxiong@linux.vnet.ibm.com 		return 0;
574305a6538aSwenxiong@linux.vnet.ibm.com 
574405a6538aSwenxiong@linux.vnet.ibm.com 	while ((be32_to_cpu(*hrr_queue->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
574505a6538aSwenxiong@linux.vnet.ibm.com 	       hrr_queue->toggle_bit) {
574605a6538aSwenxiong@linux.vnet.ibm.com 
574705a6538aSwenxiong@linux.vnet.ibm.com 		cmd_index = (be32_to_cpu(*hrr_queue->hrrq_curr) &
574805a6538aSwenxiong@linux.vnet.ibm.com 			     IPR_HRRQ_REQ_RESP_HANDLE_MASK) >>
574905a6538aSwenxiong@linux.vnet.ibm.com 			     IPR_HRRQ_REQ_RESP_HANDLE_SHIFT;
575005a6538aSwenxiong@linux.vnet.ibm.com 
575105a6538aSwenxiong@linux.vnet.ibm.com 		if (unlikely(cmd_index > hrr_queue->max_cmd_id ||
575205a6538aSwenxiong@linux.vnet.ibm.com 			     cmd_index < hrr_queue->min_cmd_id)) {
575305a6538aSwenxiong@linux.vnet.ibm.com 			ipr_isr_eh(ioa_cfg,
575405a6538aSwenxiong@linux.vnet.ibm.com 				"Invalid response handle from IOA: ",
575505a6538aSwenxiong@linux.vnet.ibm.com 				cmd_index);
575605a6538aSwenxiong@linux.vnet.ibm.com 			break;
575705a6538aSwenxiong@linux.vnet.ibm.com 		}
575805a6538aSwenxiong@linux.vnet.ibm.com 
575905a6538aSwenxiong@linux.vnet.ibm.com 		ipr_cmd = ioa_cfg->ipr_cmnd_list[cmd_index];
576005a6538aSwenxiong@linux.vnet.ibm.com 		ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
576105a6538aSwenxiong@linux.vnet.ibm.com 
576205a6538aSwenxiong@linux.vnet.ibm.com 		ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, ioasc);
576305a6538aSwenxiong@linux.vnet.ibm.com 
576405a6538aSwenxiong@linux.vnet.ibm.com 		list_move_tail(&ipr_cmd->queue, doneq);
576505a6538aSwenxiong@linux.vnet.ibm.com 
576605a6538aSwenxiong@linux.vnet.ibm.com 		if (hrr_queue->hrrq_curr < hrr_queue->hrrq_end) {
576705a6538aSwenxiong@linux.vnet.ibm.com 			hrr_queue->hrrq_curr++;
576805a6538aSwenxiong@linux.vnet.ibm.com 		} else {
576905a6538aSwenxiong@linux.vnet.ibm.com 			hrr_queue->hrrq_curr = hrr_queue->hrrq_start;
577005a6538aSwenxiong@linux.vnet.ibm.com 			hrr_queue->toggle_bit ^= 1u;
577105a6538aSwenxiong@linux.vnet.ibm.com 		}
577205a6538aSwenxiong@linux.vnet.ibm.com 		num_hrrq++;
5773b53d124aSwenxiong@linux.vnet.ibm.com 		if (budget > 0 && num_hrrq >= budget)
5774b53d124aSwenxiong@linux.vnet.ibm.com 			break;
577505a6538aSwenxiong@linux.vnet.ibm.com 	}
5776b53d124aSwenxiong@linux.vnet.ibm.com 
577705a6538aSwenxiong@linux.vnet.ibm.com 	return num_hrrq;
577805a6538aSwenxiong@linux.vnet.ibm.com }
5779b53d124aSwenxiong@linux.vnet.ibm.com 
5780511cbce2SChristoph Hellwig static int ipr_iopoll(struct irq_poll *iop, int budget)
5781b53d124aSwenxiong@linux.vnet.ibm.com {
5782b53d124aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
5783b53d124aSwenxiong@linux.vnet.ibm.com 	struct ipr_cmnd *ipr_cmd, *temp;
5784b53d124aSwenxiong@linux.vnet.ibm.com 	unsigned long hrrq_flags;
5785b53d124aSwenxiong@linux.vnet.ibm.com 	int completed_ops;
5786b53d124aSwenxiong@linux.vnet.ibm.com 	LIST_HEAD(doneq);
5787b53d124aSwenxiong@linux.vnet.ibm.com 
5788b53d124aSwenxiong@linux.vnet.ibm.com 	hrrq = container_of(iop, struct ipr_hrr_queue, iopoll);
5789b53d124aSwenxiong@linux.vnet.ibm.com 
5790b53d124aSwenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(hrrq->lock, hrrq_flags);
5791b53d124aSwenxiong@linux.vnet.ibm.com 	completed_ops = ipr_process_hrrq(hrrq, budget, &doneq);
5792b53d124aSwenxiong@linux.vnet.ibm.com 
5793b53d124aSwenxiong@linux.vnet.ibm.com 	if (completed_ops < budget)
5794511cbce2SChristoph Hellwig 		irq_poll_complete(iop);
5795b53d124aSwenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
5796b53d124aSwenxiong@linux.vnet.ibm.com 
5797b53d124aSwenxiong@linux.vnet.ibm.com 	list_for_each_entry_safe(ipr_cmd, temp, &doneq, queue) {
5798b53d124aSwenxiong@linux.vnet.ibm.com 		list_del(&ipr_cmd->queue);
5799b53d124aSwenxiong@linux.vnet.ibm.com 		del_timer(&ipr_cmd->timer);
5800b53d124aSwenxiong@linux.vnet.ibm.com 		ipr_cmd->fast_done(ipr_cmd);
5801b53d124aSwenxiong@linux.vnet.ibm.com 	}
5802b53d124aSwenxiong@linux.vnet.ibm.com 
5803b53d124aSwenxiong@linux.vnet.ibm.com 	return completed_ops;
5804b53d124aSwenxiong@linux.vnet.ibm.com }
5805b53d124aSwenxiong@linux.vnet.ibm.com 
58063feeb89dSWayne Boyer /**
58071da177e4SLinus Torvalds  * ipr_isr - Interrupt service routine
58081da177e4SLinus Torvalds  * @irq:	irq number
58091da177e4SLinus Torvalds  * @devp:	pointer to ioa config struct
58101da177e4SLinus Torvalds  *
58111da177e4SLinus Torvalds  * Return value:
58121da177e4SLinus Torvalds  * 	IRQ_NONE / IRQ_HANDLED
58131da177e4SLinus Torvalds  **/
58147d12e780SDavid Howells static irqreturn_t ipr_isr(int irq, void *devp)
58151da177e4SLinus Torvalds {
581605a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq = (struct ipr_hrr_queue *)devp;
581705a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = hrrq->ioa_cfg;
581856d6aa33Swenxiong@linux.vnet.ibm.com 	unsigned long hrrq_flags = 0;
58197dacb64fSWayne Boyer 	u32 int_reg = 0;
58203feeb89dSWayne Boyer 	int num_hrrq = 0;
58217dacb64fSWayne Boyer 	int irq_none = 0;
5822172cd6e1SBrian King 	struct ipr_cmnd *ipr_cmd, *temp;
58231da177e4SLinus Torvalds 	irqreturn_t rc = IRQ_NONE;
5824172cd6e1SBrian King 	LIST_HEAD(doneq);
58251da177e4SLinus Torvalds 
582656d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(hrrq->lock, hrrq_flags);
58271da177e4SLinus Torvalds 	/* If interrupts are disabled, ignore the interrupt */
582856d6aa33Swenxiong@linux.vnet.ibm.com 	if (!hrrq->allow_interrupts) {
582956d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
58301da177e4SLinus Torvalds 		return IRQ_NONE;
58311da177e4SLinus Torvalds 	}
58321da177e4SLinus Torvalds 
58331da177e4SLinus Torvalds 	while (1) {
5834b53d124aSwenxiong@linux.vnet.ibm.com 		if (ipr_process_hrrq(hrrq, -1, &doneq)) {
58351da177e4SLinus Torvalds 			rc =  IRQ_HANDLED;
58361da177e4SLinus Torvalds 
5837b53d124aSwenxiong@linux.vnet.ibm.com 			if (!ioa_cfg->clear_isr)
58387dd21308SBrian King 				break;
58397dd21308SBrian King 
58401da177e4SLinus Torvalds 			/* Clear the PCI interrupt */
5841a5442ba4SWayne Boyer 			num_hrrq = 0;
58423feeb89dSWayne Boyer 			do {
5843b53d124aSwenxiong@linux.vnet.ibm.com 				writel(IPR_PCII_HRRQ_UPDATED,
5844b53d124aSwenxiong@linux.vnet.ibm.com 				     ioa_cfg->regs.clr_interrupt_reg32);
58457dacb64fSWayne Boyer 				int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
58463feeb89dSWayne Boyer 			} while (int_reg & IPR_PCII_HRRQ_UPDATED &&
58473feeb89dSWayne Boyer 				num_hrrq++ < IPR_MAX_HRRQ_RETRIES);
58483feeb89dSWayne Boyer 
58497dacb64fSWayne Boyer 		} else if (rc == IRQ_NONE && irq_none == 0) {
58507dacb64fSWayne Boyer 			int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
58517dacb64fSWayne Boyer 			irq_none++;
5852a5442ba4SWayne Boyer 		} else if (num_hrrq == IPR_MAX_HRRQ_RETRIES &&
5853a5442ba4SWayne Boyer 			   int_reg & IPR_PCII_HRRQ_UPDATED) {
5854b53d124aSwenxiong@linux.vnet.ibm.com 			ipr_isr_eh(ioa_cfg,
5855b53d124aSwenxiong@linux.vnet.ibm.com 				"Error clearing HRRQ: ", num_hrrq);
5856172cd6e1SBrian King 			rc = IRQ_HANDLED;
5857b53d124aSwenxiong@linux.vnet.ibm.com 			break;
58581da177e4SLinus Torvalds 		} else
58591da177e4SLinus Torvalds 			break;
58601da177e4SLinus Torvalds 	}
58611da177e4SLinus Torvalds 
58621da177e4SLinus Torvalds 	if (unlikely(rc == IRQ_NONE))
5863634651faSWayne Boyer 		rc = ipr_handle_other_interrupt(ioa_cfg, int_reg);
58641da177e4SLinus Torvalds 
586556d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
5866172cd6e1SBrian King 	list_for_each_entry_safe(ipr_cmd, temp, &doneq, queue) {
5867172cd6e1SBrian King 		list_del(&ipr_cmd->queue);
5868172cd6e1SBrian King 		del_timer(&ipr_cmd->timer);
5869172cd6e1SBrian King 		ipr_cmd->fast_done(ipr_cmd);
5870172cd6e1SBrian King 	}
587105a6538aSwenxiong@linux.vnet.ibm.com 	return rc;
587205a6538aSwenxiong@linux.vnet.ibm.com }
5873172cd6e1SBrian King 
587405a6538aSwenxiong@linux.vnet.ibm.com /**
587505a6538aSwenxiong@linux.vnet.ibm.com  * ipr_isr_mhrrq - Interrupt service routine
587605a6538aSwenxiong@linux.vnet.ibm.com  * @irq:	irq number
587705a6538aSwenxiong@linux.vnet.ibm.com  * @devp:	pointer to ioa config struct
587805a6538aSwenxiong@linux.vnet.ibm.com  *
587905a6538aSwenxiong@linux.vnet.ibm.com  * Return value:
588005a6538aSwenxiong@linux.vnet.ibm.com  *	IRQ_NONE / IRQ_HANDLED
588105a6538aSwenxiong@linux.vnet.ibm.com  **/
588205a6538aSwenxiong@linux.vnet.ibm.com static irqreturn_t ipr_isr_mhrrq(int irq, void *devp)
588305a6538aSwenxiong@linux.vnet.ibm.com {
588405a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq = (struct ipr_hrr_queue *)devp;
5885b53d124aSwenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = hrrq->ioa_cfg;
588656d6aa33Swenxiong@linux.vnet.ibm.com 	unsigned long hrrq_flags = 0;
588705a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_cmnd *ipr_cmd, *temp;
588805a6538aSwenxiong@linux.vnet.ibm.com 	irqreturn_t rc = IRQ_NONE;
588905a6538aSwenxiong@linux.vnet.ibm.com 	LIST_HEAD(doneq);
589005a6538aSwenxiong@linux.vnet.ibm.com 
589156d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(hrrq->lock, hrrq_flags);
589205a6538aSwenxiong@linux.vnet.ibm.com 
589305a6538aSwenxiong@linux.vnet.ibm.com 	/* If interrupts are disabled, ignore the interrupt */
589456d6aa33Swenxiong@linux.vnet.ibm.com 	if (!hrrq->allow_interrupts) {
589556d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
589605a6538aSwenxiong@linux.vnet.ibm.com 		return IRQ_NONE;
589705a6538aSwenxiong@linux.vnet.ibm.com 	}
589805a6538aSwenxiong@linux.vnet.ibm.com 
589989f8b33cSJens Axboe 	if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
5900b53d124aSwenxiong@linux.vnet.ibm.com 		if ((be32_to_cpu(*hrrq->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
5901b53d124aSwenxiong@linux.vnet.ibm.com 		       hrrq->toggle_bit) {
5902511cbce2SChristoph Hellwig 			irq_poll_sched(&hrrq->iopoll);
5903b53d124aSwenxiong@linux.vnet.ibm.com 			spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
5904b53d124aSwenxiong@linux.vnet.ibm.com 			return IRQ_HANDLED;
5905b53d124aSwenxiong@linux.vnet.ibm.com 		}
5906b53d124aSwenxiong@linux.vnet.ibm.com 	} else {
590705a6538aSwenxiong@linux.vnet.ibm.com 		if ((be32_to_cpu(*hrrq->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
590805a6538aSwenxiong@linux.vnet.ibm.com 			hrrq->toggle_bit)
590905a6538aSwenxiong@linux.vnet.ibm.com 
5910b53d124aSwenxiong@linux.vnet.ibm.com 			if (ipr_process_hrrq(hrrq, -1, &doneq))
591105a6538aSwenxiong@linux.vnet.ibm.com 				rc =  IRQ_HANDLED;
5912b53d124aSwenxiong@linux.vnet.ibm.com 	}
591305a6538aSwenxiong@linux.vnet.ibm.com 
591456d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
591505a6538aSwenxiong@linux.vnet.ibm.com 
591605a6538aSwenxiong@linux.vnet.ibm.com 	list_for_each_entry_safe(ipr_cmd, temp, &doneq, queue) {
591705a6538aSwenxiong@linux.vnet.ibm.com 		list_del(&ipr_cmd->queue);
591805a6538aSwenxiong@linux.vnet.ibm.com 		del_timer(&ipr_cmd->timer);
591905a6538aSwenxiong@linux.vnet.ibm.com 		ipr_cmd->fast_done(ipr_cmd);
592005a6538aSwenxiong@linux.vnet.ibm.com 	}
59211da177e4SLinus Torvalds 	return rc;
59221da177e4SLinus Torvalds }
59231da177e4SLinus Torvalds 
59241da177e4SLinus Torvalds /**
5925a32c055fSWayne Boyer  * ipr_build_ioadl64 - Build a scatter/gather list and map the buffer
59261da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
59271da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
59281da177e4SLinus Torvalds  *
59291da177e4SLinus Torvalds  * Return value:
59301da177e4SLinus Torvalds  * 	0 on success / -1 on failure
59311da177e4SLinus Torvalds  **/
5932a32c055fSWayne Boyer static int ipr_build_ioadl64(struct ipr_ioa_cfg *ioa_cfg,
59331da177e4SLinus Torvalds 			     struct ipr_cmnd *ipr_cmd)
59341da177e4SLinus Torvalds {
593563015bc9SFUJITA Tomonori 	int i, nseg;
593663015bc9SFUJITA Tomonori 	struct scatterlist *sg;
59371da177e4SLinus Torvalds 	u32 length;
59381da177e4SLinus Torvalds 	u32 ioadl_flags = 0;
59391da177e4SLinus Torvalds 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
59401da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
5941a32c055fSWayne Boyer 	struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64;
59421da177e4SLinus Torvalds 
594363015bc9SFUJITA Tomonori 	length = scsi_bufflen(scsi_cmd);
594463015bc9SFUJITA Tomonori 	if (!length)
59451da177e4SLinus Torvalds 		return 0;
59461da177e4SLinus Torvalds 
594763015bc9SFUJITA Tomonori 	nseg = scsi_dma_map(scsi_cmd);
594863015bc9SFUJITA Tomonori 	if (nseg < 0) {
594951f52a47SAnton Blanchard 		if (printk_ratelimit())
5950d73341bfSAnton Blanchard 			dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n");
595163015bc9SFUJITA Tomonori 		return -1;
595263015bc9SFUJITA Tomonori 	}
595363015bc9SFUJITA Tomonori 
595463015bc9SFUJITA Tomonori 	ipr_cmd->dma_use_sg = nseg;
59551da177e4SLinus Torvalds 
5956438b0331SWayne Boyer 	ioarcb->data_transfer_length = cpu_to_be32(length);
5957b8803b1cSWayne Boyer 	ioarcb->ioadl_len =
5958b8803b1cSWayne Boyer 		cpu_to_be32(sizeof(struct ipr_ioadl64_desc) * ipr_cmd->dma_use_sg);
5959438b0331SWayne Boyer 
59601da177e4SLinus Torvalds 	if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) {
59611da177e4SLinus Torvalds 		ioadl_flags = IPR_IOADL_FLAGS_WRITE;
59621da177e4SLinus Torvalds 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
5963a32c055fSWayne Boyer 	} else if (scsi_cmd->sc_data_direction == DMA_FROM_DEVICE)
5964a32c055fSWayne Boyer 		ioadl_flags = IPR_IOADL_FLAGS_READ;
5965a32c055fSWayne Boyer 
5966a32c055fSWayne Boyer 	scsi_for_each_sg(scsi_cmd, sg, ipr_cmd->dma_use_sg, i) {
5967a32c055fSWayne Boyer 		ioadl64[i].flags = cpu_to_be32(ioadl_flags);
5968a32c055fSWayne Boyer 		ioadl64[i].data_len = cpu_to_be32(sg_dma_len(sg));
5969a32c055fSWayne Boyer 		ioadl64[i].address = cpu_to_be64(sg_dma_address(sg));
5970a32c055fSWayne Boyer 	}
5971a32c055fSWayne Boyer 
5972a32c055fSWayne Boyer 	ioadl64[i-1].flags |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
5973a32c055fSWayne Boyer 	return 0;
5974a32c055fSWayne Boyer }
5975a32c055fSWayne Boyer 
5976a32c055fSWayne Boyer /**
5977a32c055fSWayne Boyer  * ipr_build_ioadl - Build a scatter/gather list and map the buffer
5978a32c055fSWayne Boyer  * @ioa_cfg:	ioa config struct
5979a32c055fSWayne Boyer  * @ipr_cmd:	ipr command struct
5980a32c055fSWayne Boyer  *
5981a32c055fSWayne Boyer  * Return value:
5982a32c055fSWayne Boyer  * 	0 on success / -1 on failure
5983a32c055fSWayne Boyer  **/
5984a32c055fSWayne Boyer static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg,
5985a32c055fSWayne Boyer 			   struct ipr_cmnd *ipr_cmd)
5986a32c055fSWayne Boyer {
5987a32c055fSWayne Boyer 	int i, nseg;
5988a32c055fSWayne Boyer 	struct scatterlist *sg;
5989a32c055fSWayne Boyer 	u32 length;
5990a32c055fSWayne Boyer 	u32 ioadl_flags = 0;
5991a32c055fSWayne Boyer 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
5992a32c055fSWayne Boyer 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
5993a32c055fSWayne Boyer 	struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl;
5994a32c055fSWayne Boyer 
5995a32c055fSWayne Boyer 	length = scsi_bufflen(scsi_cmd);
5996a32c055fSWayne Boyer 	if (!length)
5997a32c055fSWayne Boyer 		return 0;
5998a32c055fSWayne Boyer 
5999a32c055fSWayne Boyer 	nseg = scsi_dma_map(scsi_cmd);
6000a32c055fSWayne Boyer 	if (nseg < 0) {
6001d73341bfSAnton Blanchard 		dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n");
6002a32c055fSWayne Boyer 		return -1;
6003a32c055fSWayne Boyer 	}
6004a32c055fSWayne Boyer 
6005a32c055fSWayne Boyer 	ipr_cmd->dma_use_sg = nseg;
6006a32c055fSWayne Boyer 
6007a32c055fSWayne Boyer 	if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) {
6008a32c055fSWayne Boyer 		ioadl_flags = IPR_IOADL_FLAGS_WRITE;
6009a32c055fSWayne Boyer 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
6010a32c055fSWayne Boyer 		ioarcb->data_transfer_length = cpu_to_be32(length);
6011a32c055fSWayne Boyer 		ioarcb->ioadl_len =
60121da177e4SLinus Torvalds 			cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
60131da177e4SLinus Torvalds 	} else if (scsi_cmd->sc_data_direction == DMA_FROM_DEVICE) {
60141da177e4SLinus Torvalds 		ioadl_flags = IPR_IOADL_FLAGS_READ;
60151da177e4SLinus Torvalds 		ioarcb->read_data_transfer_length = cpu_to_be32(length);
60161da177e4SLinus Torvalds 		ioarcb->read_ioadl_len =
60171da177e4SLinus Torvalds 			cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
60181da177e4SLinus Torvalds 	}
60191da177e4SLinus Torvalds 
6020a32c055fSWayne Boyer 	if (ipr_cmd->dma_use_sg <= ARRAY_SIZE(ioarcb->u.add_data.u.ioadl)) {
6021a32c055fSWayne Boyer 		ioadl = ioarcb->u.add_data.u.ioadl;
6022a32c055fSWayne Boyer 		ioarcb->write_ioadl_addr = cpu_to_be32((ipr_cmd->dma_addr) +
6023a32c055fSWayne Boyer 				    offsetof(struct ipr_ioarcb, u.add_data));
602451b1c7e1SBrian King 		ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
602551b1c7e1SBrian King 	}
602651b1c7e1SBrian King 
602763015bc9SFUJITA Tomonori 	scsi_for_each_sg(scsi_cmd, sg, ipr_cmd->dma_use_sg, i) {
60281da177e4SLinus Torvalds 		ioadl[i].flags_and_data_len =
602963015bc9SFUJITA Tomonori 			cpu_to_be32(ioadl_flags | sg_dma_len(sg));
603063015bc9SFUJITA Tomonori 		ioadl[i].address = cpu_to_be32(sg_dma_address(sg));
60311da177e4SLinus Torvalds 	}
60321da177e4SLinus Torvalds 
603363015bc9SFUJITA Tomonori 	ioadl[i-1].flags_and_data_len |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
60341da177e4SLinus Torvalds 	return 0;
60351da177e4SLinus Torvalds }
60361da177e4SLinus Torvalds 
60371da177e4SLinus Torvalds /**
6038f646f325SBrian King  * __ipr_erp_done - Process completion of ERP for a device
60391da177e4SLinus Torvalds  * @ipr_cmd:		ipr command struct
60401da177e4SLinus Torvalds  *
60411da177e4SLinus Torvalds  * This function copies the sense buffer into the scsi_cmd
60421da177e4SLinus Torvalds  * struct and pushes the scsi_done function.
60431da177e4SLinus Torvalds  *
60441da177e4SLinus Torvalds  * Return value:
60451da177e4SLinus Torvalds  * 	nothing
60461da177e4SLinus Torvalds  **/
6047f646f325SBrian King static void __ipr_erp_done(struct ipr_cmnd *ipr_cmd)
60481da177e4SLinus Torvalds {
60491da177e4SLinus Torvalds 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
60501da177e4SLinus Torvalds 	struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
605196d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
60521da177e4SLinus Torvalds 
60531da177e4SLinus Torvalds 	if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
60541da177e4SLinus Torvalds 		scsi_cmd->result |= (DID_ERROR << 16);
6055fb3ed3cbSBrian King 		scmd_printk(KERN_ERR, scsi_cmd,
60561da177e4SLinus Torvalds 			    "Request Sense failed with IOASC: 0x%08X\n", ioasc);
60571da177e4SLinus Torvalds 	} else {
60581da177e4SLinus Torvalds 		memcpy(scsi_cmd->sense_buffer, ipr_cmd->sense_buffer,
60591da177e4SLinus Torvalds 		       SCSI_SENSE_BUFFERSIZE);
60601da177e4SLinus Torvalds 	}
60611da177e4SLinus Torvalds 
60621da177e4SLinus Torvalds 	if (res) {
6063ee0a90faSbrking@us.ibm.com 		if (!ipr_is_naca_model(res))
60641da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
60651da177e4SLinus Torvalds 		res->in_erp = 0;
60661da177e4SLinus Torvalds 	}
606763015bc9SFUJITA Tomonori 	scsi_dma_unmap(ipr_cmd->scsi_cmd);
60681da177e4SLinus Torvalds 	scsi_cmd->scsi_done(scsi_cmd);
606966a0d59cSBrian King 	if (ipr_cmd->eh_comp)
607066a0d59cSBrian King 		complete(ipr_cmd->eh_comp);
607166a0d59cSBrian King 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
60721da177e4SLinus Torvalds }
60731da177e4SLinus Torvalds 
60741da177e4SLinus Torvalds /**
6075f646f325SBrian King  * ipr_erp_done - Process completion of ERP for a device
6076f646f325SBrian King  * @ipr_cmd:		ipr command struct
6077f646f325SBrian King  *
6078f646f325SBrian King  * This function copies the sense buffer into the scsi_cmd
6079f646f325SBrian King  * struct and pushes the scsi_done function.
6080f646f325SBrian King  *
6081f646f325SBrian King  * Return value:
6082f646f325SBrian King  * 	nothing
6083f646f325SBrian King  **/
6084f646f325SBrian King static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
6085f646f325SBrian King {
6086f646f325SBrian King 	struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
6087f646f325SBrian King 	unsigned long hrrq_flags;
6088f646f325SBrian King 
6089f646f325SBrian King 	spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
6090f646f325SBrian King 	__ipr_erp_done(ipr_cmd);
6091f646f325SBrian King 	spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
60921da177e4SLinus Torvalds }
60931da177e4SLinus Torvalds 
60941da177e4SLinus Torvalds /**
60951da177e4SLinus Torvalds  * ipr_reinit_ipr_cmnd_for_erp - Re-initialize a cmnd block to be used for ERP
60961da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
60971da177e4SLinus Torvalds  *
60981da177e4SLinus Torvalds  * Return value:
60991da177e4SLinus Torvalds  * 	none
61001da177e4SLinus Torvalds  **/
61011da177e4SLinus Torvalds static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd)
61021da177e4SLinus Torvalds {
610351b1c7e1SBrian King 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
610496d21f00SWayne Boyer 	struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa;
6105a32c055fSWayne Boyer 	dma_addr_t dma_addr = ipr_cmd->dma_addr;
61061da177e4SLinus Torvalds 
61071da177e4SLinus Torvalds 	memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt));
6108a32c055fSWayne Boyer 	ioarcb->data_transfer_length = 0;
61091da177e4SLinus Torvalds 	ioarcb->read_data_transfer_length = 0;
6110a32c055fSWayne Boyer 	ioarcb->ioadl_len = 0;
61111da177e4SLinus Torvalds 	ioarcb->read_ioadl_len = 0;
611296d21f00SWayne Boyer 	ioasa->hdr.ioasc = 0;
611396d21f00SWayne Boyer 	ioasa->hdr.residual_data_len = 0;
6114a32c055fSWayne Boyer 
6115a32c055fSWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64)
6116a32c055fSWayne Boyer 		ioarcb->u.sis64_addr_data.data_ioadl_addr =
6117a32c055fSWayne Boyer 			cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ioadl64));
6118a32c055fSWayne Boyer 	else {
611951b1c7e1SBrian King 		ioarcb->write_ioadl_addr =
6120a32c055fSWayne Boyer 			cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, i.ioadl));
612151b1c7e1SBrian King 		ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
61221da177e4SLinus Torvalds 	}
6123a32c055fSWayne Boyer }
61241da177e4SLinus Torvalds 
61251da177e4SLinus Torvalds /**
6126f646f325SBrian King  * __ipr_erp_request_sense - Send request sense to a device
61271da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
61281da177e4SLinus Torvalds  *
61291da177e4SLinus Torvalds  * This function sends a request sense to a device as a result
61301da177e4SLinus Torvalds  * of a check condition.
61311da177e4SLinus Torvalds  *
61321da177e4SLinus Torvalds  * Return value:
61331da177e4SLinus Torvalds  * 	nothing
61341da177e4SLinus Torvalds  **/
6135f646f325SBrian King static void __ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
61361da177e4SLinus Torvalds {
61371da177e4SLinus Torvalds 	struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
613896d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
61391da177e4SLinus Torvalds 
61401da177e4SLinus Torvalds 	if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
6141f646f325SBrian King 		__ipr_erp_done(ipr_cmd);
61421da177e4SLinus Torvalds 		return;
61431da177e4SLinus Torvalds 	}
61441da177e4SLinus Torvalds 
61451da177e4SLinus Torvalds 	ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
61461da177e4SLinus Torvalds 
61471da177e4SLinus Torvalds 	cmd_pkt->request_type = IPR_RQTYPE_SCSICDB;
61481da177e4SLinus Torvalds 	cmd_pkt->cdb[0] = REQUEST_SENSE;
61491da177e4SLinus Torvalds 	cmd_pkt->cdb[4] = SCSI_SENSE_BUFFERSIZE;
61501da177e4SLinus Torvalds 	cmd_pkt->flags_hi |= IPR_FLAGS_HI_SYNC_OVERRIDE;
61511da177e4SLinus Torvalds 	cmd_pkt->flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
61521da177e4SLinus Torvalds 	cmd_pkt->timeout = cpu_to_be16(IPR_REQUEST_SENSE_TIMEOUT / HZ);
61531da177e4SLinus Torvalds 
6154a32c055fSWayne Boyer 	ipr_init_ioadl(ipr_cmd, ipr_cmd->sense_buffer_dma,
6155a32c055fSWayne Boyer 		       SCSI_SENSE_BUFFERSIZE, IPR_IOADL_FLAGS_READ_LAST);
61561da177e4SLinus Torvalds 
61571da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_erp_done, ipr_timeout,
61581da177e4SLinus Torvalds 		   IPR_REQUEST_SENSE_TIMEOUT * 2);
61591da177e4SLinus Torvalds }
61601da177e4SLinus Torvalds 
61611da177e4SLinus Torvalds /**
6162f646f325SBrian King  * ipr_erp_request_sense - Send request sense to a device
6163f646f325SBrian King  * @ipr_cmd:	ipr command struct
6164f646f325SBrian King  *
6165f646f325SBrian King  * This function sends a request sense to a device as a result
6166f646f325SBrian King  * of a check condition.
6167f646f325SBrian King  *
6168f646f325SBrian King  * Return value:
6169f646f325SBrian King  * 	nothing
6170f646f325SBrian King  **/
6171f646f325SBrian King static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
6172f646f325SBrian King {
6173f646f325SBrian King 	struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
6174f646f325SBrian King 	unsigned long hrrq_flags;
6175f646f325SBrian King 
6176f646f325SBrian King 	spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
6177f646f325SBrian King 	__ipr_erp_request_sense(ipr_cmd);
6178f646f325SBrian King 	spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
6179f646f325SBrian King }
6180f646f325SBrian King 
6181f646f325SBrian King /**
61821da177e4SLinus Torvalds  * ipr_erp_cancel_all - Send cancel all to a device
61831da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
61841da177e4SLinus Torvalds  *
61851da177e4SLinus Torvalds  * This function sends a cancel all to a device to clear the
61861da177e4SLinus Torvalds  * queue. If we are running TCQ on the device, QERR is set to 1,
61871da177e4SLinus Torvalds  * which means all outstanding ops have been dropped on the floor.
61881da177e4SLinus Torvalds  * Cancel all will return them to us.
61891da177e4SLinus Torvalds  *
61901da177e4SLinus Torvalds  * Return value:
61911da177e4SLinus Torvalds  * 	nothing
61921da177e4SLinus Torvalds  **/
61931da177e4SLinus Torvalds static void ipr_erp_cancel_all(struct ipr_cmnd *ipr_cmd)
61941da177e4SLinus Torvalds {
61951da177e4SLinus Torvalds 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
61961da177e4SLinus Torvalds 	struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
61971da177e4SLinus Torvalds 	struct ipr_cmd_pkt *cmd_pkt;
61981da177e4SLinus Torvalds 
61991da177e4SLinus Torvalds 	res->in_erp = 1;
62001da177e4SLinus Torvalds 
62011da177e4SLinus Torvalds 	ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
62021da177e4SLinus Torvalds 
620317ea0126SChristoph Hellwig 	if (!scsi_cmd->device->simple_tags) {
6204f646f325SBrian King 		__ipr_erp_request_sense(ipr_cmd);
62051da177e4SLinus Torvalds 		return;
62061da177e4SLinus Torvalds 	}
62071da177e4SLinus Torvalds 
62081da177e4SLinus Torvalds 	cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
62091da177e4SLinus Torvalds 	cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
62101da177e4SLinus Torvalds 	cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS;
62111da177e4SLinus Torvalds 
62121da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_erp_request_sense, ipr_timeout,
62131da177e4SLinus Torvalds 		   IPR_CANCEL_ALL_TIMEOUT);
62141da177e4SLinus Torvalds }
62151da177e4SLinus Torvalds 
62161da177e4SLinus Torvalds /**
62171da177e4SLinus Torvalds  * ipr_dump_ioasa - Dump contents of IOASA
62181da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
62191da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
6220fe964d0aSBrian King  * @res:		resource entry struct
62211da177e4SLinus Torvalds  *
62221da177e4SLinus Torvalds  * This function is invoked by the interrupt handler when ops
62231da177e4SLinus Torvalds  * fail. It will log the IOASA if appropriate. Only called
62241da177e4SLinus Torvalds  * for GPDD ops.
62251da177e4SLinus Torvalds  *
62261da177e4SLinus Torvalds  * Return value:
62271da177e4SLinus Torvalds  * 	none
62281da177e4SLinus Torvalds  **/
62291da177e4SLinus Torvalds static void ipr_dump_ioasa(struct ipr_ioa_cfg *ioa_cfg,
6230fe964d0aSBrian King 			   struct ipr_cmnd *ipr_cmd, struct ipr_resource_entry *res)
62311da177e4SLinus Torvalds {
62321da177e4SLinus Torvalds 	int i;
62331da177e4SLinus Torvalds 	u16 data_len;
6234b0692dd4SBrian King 	u32 ioasc, fd_ioasc;
623596d21f00SWayne Boyer 	struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa;
62361da177e4SLinus Torvalds 	__be32 *ioasa_data = (__be32 *)ioasa;
62371da177e4SLinus Torvalds 	int error_index;
62381da177e4SLinus Torvalds 
623996d21f00SWayne Boyer 	ioasc = be32_to_cpu(ioasa->hdr.ioasc) & IPR_IOASC_IOASC_MASK;
624096d21f00SWayne Boyer 	fd_ioasc = be32_to_cpu(ioasa->hdr.fd_ioasc) & IPR_IOASC_IOASC_MASK;
62411da177e4SLinus Torvalds 
62421da177e4SLinus Torvalds 	if (0 == ioasc)
62431da177e4SLinus Torvalds 		return;
62441da177e4SLinus Torvalds 
62451da177e4SLinus Torvalds 	if (ioa_cfg->log_level < IPR_DEFAULT_LOG_LEVEL)
62461da177e4SLinus Torvalds 		return;
62471da177e4SLinus Torvalds 
6248b0692dd4SBrian King 	if (ioasc == IPR_IOASC_BUS_WAS_RESET && fd_ioasc)
6249b0692dd4SBrian King 		error_index = ipr_get_error(fd_ioasc);
6250b0692dd4SBrian King 	else
62511da177e4SLinus Torvalds 		error_index = ipr_get_error(ioasc);
62521da177e4SLinus Torvalds 
62531da177e4SLinus Torvalds 	if (ioa_cfg->log_level < IPR_MAX_LOG_LEVEL) {
62541da177e4SLinus Torvalds 		/* Don't log an error if the IOA already logged one */
625596d21f00SWayne Boyer 		if (ioasa->hdr.ilid != 0)
62561da177e4SLinus Torvalds 			return;
62571da177e4SLinus Torvalds 
6258cc9bd5d4SBrian King 		if (!ipr_is_gscsi(res))
6259cc9bd5d4SBrian King 			return;
6260cc9bd5d4SBrian King 
62611da177e4SLinus Torvalds 		if (ipr_error_table[error_index].log_ioasa == 0)
62621da177e4SLinus Torvalds 			return;
62631da177e4SLinus Torvalds 	}
62641da177e4SLinus Torvalds 
6265fe964d0aSBrian King 	ipr_res_err(ioa_cfg, res, "%s\n", ipr_error_table[error_index].error);
62661da177e4SLinus Torvalds 
626796d21f00SWayne Boyer 	data_len = be16_to_cpu(ioasa->hdr.ret_stat_len);
626896d21f00SWayne Boyer 	if (ioa_cfg->sis64 && sizeof(struct ipr_ioasa64) < data_len)
626996d21f00SWayne Boyer 		data_len = sizeof(struct ipr_ioasa64);
627096d21f00SWayne Boyer 	else if (!ioa_cfg->sis64 && sizeof(struct ipr_ioasa) < data_len)
62711da177e4SLinus Torvalds 		data_len = sizeof(struct ipr_ioasa);
62721da177e4SLinus Torvalds 
62731da177e4SLinus Torvalds 	ipr_err("IOASA Dump:\n");
62741da177e4SLinus Torvalds 
62751da177e4SLinus Torvalds 	for (i = 0; i < data_len / 4; i += 4) {
62761da177e4SLinus Torvalds 		ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
62771da177e4SLinus Torvalds 			be32_to_cpu(ioasa_data[i]),
62781da177e4SLinus Torvalds 			be32_to_cpu(ioasa_data[i+1]),
62791da177e4SLinus Torvalds 			be32_to_cpu(ioasa_data[i+2]),
62801da177e4SLinus Torvalds 			be32_to_cpu(ioasa_data[i+3]));
62811da177e4SLinus Torvalds 	}
62821da177e4SLinus Torvalds }
62831da177e4SLinus Torvalds 
62841da177e4SLinus Torvalds /**
62851da177e4SLinus Torvalds  * ipr_gen_sense - Generate SCSI sense data from an IOASA
6286a96099e2SLee Jones  * @ipr_cmd:	ipr command struct
62871da177e4SLinus Torvalds  *
62881da177e4SLinus Torvalds  * Return value:
62891da177e4SLinus Torvalds  * 	none
62901da177e4SLinus Torvalds  **/
62911da177e4SLinus Torvalds static void ipr_gen_sense(struct ipr_cmnd *ipr_cmd)
62921da177e4SLinus Torvalds {
62931da177e4SLinus Torvalds 	u32 failing_lba;
62941da177e4SLinus Torvalds 	u8 *sense_buf = ipr_cmd->scsi_cmd->sense_buffer;
62951da177e4SLinus Torvalds 	struct ipr_resource_entry *res = ipr_cmd->scsi_cmd->device->hostdata;
629696d21f00SWayne Boyer 	struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa;
629796d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ioasa->hdr.ioasc);
62981da177e4SLinus Torvalds 
62991da177e4SLinus Torvalds 	memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
63001da177e4SLinus Torvalds 
63011da177e4SLinus Torvalds 	if (ioasc >= IPR_FIRST_DRIVER_IOASC)
63021da177e4SLinus Torvalds 		return;
63031da177e4SLinus Torvalds 
63041da177e4SLinus Torvalds 	ipr_cmd->scsi_cmd->result = SAM_STAT_CHECK_CONDITION;
63051da177e4SLinus Torvalds 
63061da177e4SLinus Torvalds 	if (ipr_is_vset_device(res) &&
63071da177e4SLinus Torvalds 	    ioasc == IPR_IOASC_MED_DO_NOT_REALLOC &&
63081da177e4SLinus Torvalds 	    ioasa->u.vset.failing_lba_hi != 0) {
63091da177e4SLinus Torvalds 		sense_buf[0] = 0x72;
63101da177e4SLinus Torvalds 		sense_buf[1] = IPR_IOASC_SENSE_KEY(ioasc);
63111da177e4SLinus Torvalds 		sense_buf[2] = IPR_IOASC_SENSE_CODE(ioasc);
63121da177e4SLinus Torvalds 		sense_buf[3] = IPR_IOASC_SENSE_QUAL(ioasc);
63131da177e4SLinus Torvalds 
63141da177e4SLinus Torvalds 		sense_buf[7] = 12;
63151da177e4SLinus Torvalds 		sense_buf[8] = 0;
63161da177e4SLinus Torvalds 		sense_buf[9] = 0x0A;
63171da177e4SLinus Torvalds 		sense_buf[10] = 0x80;
63181da177e4SLinus Torvalds 
63191da177e4SLinus Torvalds 		failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_hi);
63201da177e4SLinus Torvalds 
63211da177e4SLinus Torvalds 		sense_buf[12] = (failing_lba & 0xff000000) >> 24;
63221da177e4SLinus Torvalds 		sense_buf[13] = (failing_lba & 0x00ff0000) >> 16;
63231da177e4SLinus Torvalds 		sense_buf[14] = (failing_lba & 0x0000ff00) >> 8;
63241da177e4SLinus Torvalds 		sense_buf[15] = failing_lba & 0x000000ff;
63251da177e4SLinus Torvalds 
63261da177e4SLinus Torvalds 		failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_lo);
63271da177e4SLinus Torvalds 
63281da177e4SLinus Torvalds 		sense_buf[16] = (failing_lba & 0xff000000) >> 24;
63291da177e4SLinus Torvalds 		sense_buf[17] = (failing_lba & 0x00ff0000) >> 16;
63301da177e4SLinus Torvalds 		sense_buf[18] = (failing_lba & 0x0000ff00) >> 8;
63311da177e4SLinus Torvalds 		sense_buf[19] = failing_lba & 0x000000ff;
63321da177e4SLinus Torvalds 	} else {
63331da177e4SLinus Torvalds 		sense_buf[0] = 0x70;
63341da177e4SLinus Torvalds 		sense_buf[2] = IPR_IOASC_SENSE_KEY(ioasc);
63351da177e4SLinus Torvalds 		sense_buf[12] = IPR_IOASC_SENSE_CODE(ioasc);
63361da177e4SLinus Torvalds 		sense_buf[13] = IPR_IOASC_SENSE_QUAL(ioasc);
63371da177e4SLinus Torvalds 
63381da177e4SLinus Torvalds 		/* Illegal request */
63391da177e4SLinus Torvalds 		if ((IPR_IOASC_SENSE_KEY(ioasc) == 0x05) &&
634096d21f00SWayne Boyer 		    (be32_to_cpu(ioasa->hdr.ioasc_specific) & IPR_FIELD_POINTER_VALID)) {
63411da177e4SLinus Torvalds 			sense_buf[7] = 10;	/* additional length */
63421da177e4SLinus Torvalds 
63431da177e4SLinus Torvalds 			/* IOARCB was in error */
63441da177e4SLinus Torvalds 			if (IPR_IOASC_SENSE_CODE(ioasc) == 0x24)
63451da177e4SLinus Torvalds 				sense_buf[15] = 0xC0;
63461da177e4SLinus Torvalds 			else	/* Parameter data was invalid */
63471da177e4SLinus Torvalds 				sense_buf[15] = 0x80;
63481da177e4SLinus Torvalds 
63491da177e4SLinus Torvalds 			sense_buf[16] =
63501da177e4SLinus Torvalds 			    ((IPR_FIELD_POINTER_MASK &
635196d21f00SWayne Boyer 			      be32_to_cpu(ioasa->hdr.ioasc_specific)) >> 8) & 0xff;
63521da177e4SLinus Torvalds 			sense_buf[17] =
63531da177e4SLinus Torvalds 			    (IPR_FIELD_POINTER_MASK &
635496d21f00SWayne Boyer 			     be32_to_cpu(ioasa->hdr.ioasc_specific)) & 0xff;
63551da177e4SLinus Torvalds 		} else {
63561da177e4SLinus Torvalds 			if (ioasc == IPR_IOASC_MED_DO_NOT_REALLOC) {
63571da177e4SLinus Torvalds 				if (ipr_is_vset_device(res))
63581da177e4SLinus Torvalds 					failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_lo);
63591da177e4SLinus Torvalds 				else
63601da177e4SLinus Torvalds 					failing_lba = be32_to_cpu(ioasa->u.dasd.failing_lba);
63611da177e4SLinus Torvalds 
63621da177e4SLinus Torvalds 				sense_buf[0] |= 0x80;	/* Or in the Valid bit */
63631da177e4SLinus Torvalds 				sense_buf[3] = (failing_lba & 0xff000000) >> 24;
63641da177e4SLinus Torvalds 				sense_buf[4] = (failing_lba & 0x00ff0000) >> 16;
63651da177e4SLinus Torvalds 				sense_buf[5] = (failing_lba & 0x0000ff00) >> 8;
63661da177e4SLinus Torvalds 				sense_buf[6] = failing_lba & 0x000000ff;
63671da177e4SLinus Torvalds 			}
63681da177e4SLinus Torvalds 
63691da177e4SLinus Torvalds 			sense_buf[7] = 6;	/* additional length */
63701da177e4SLinus Torvalds 		}
63711da177e4SLinus Torvalds 	}
63721da177e4SLinus Torvalds }
63731da177e4SLinus Torvalds 
63741da177e4SLinus Torvalds /**
6375ee0a90faSbrking@us.ibm.com  * ipr_get_autosense - Copy autosense data to sense buffer
6376ee0a90faSbrking@us.ibm.com  * @ipr_cmd:	ipr command struct
6377ee0a90faSbrking@us.ibm.com  *
6378ee0a90faSbrking@us.ibm.com  * This function copies the autosense buffer to the buffer
6379ee0a90faSbrking@us.ibm.com  * in the scsi_cmd, if there is autosense available.
6380ee0a90faSbrking@us.ibm.com  *
6381ee0a90faSbrking@us.ibm.com  * Return value:
6382ee0a90faSbrking@us.ibm.com  *	1 if autosense was available / 0 if not
6383ee0a90faSbrking@us.ibm.com  **/
6384ee0a90faSbrking@us.ibm.com static int ipr_get_autosense(struct ipr_cmnd *ipr_cmd)
6385ee0a90faSbrking@us.ibm.com {
638696d21f00SWayne Boyer 	struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa;
638796d21f00SWayne Boyer 	struct ipr_ioasa64 *ioasa64 = &ipr_cmd->s.ioasa64;
6388ee0a90faSbrking@us.ibm.com 
638996d21f00SWayne Boyer 	if ((be32_to_cpu(ioasa->hdr.ioasc_specific) & IPR_AUTOSENSE_VALID) == 0)
6390ee0a90faSbrking@us.ibm.com 		return 0;
6391ee0a90faSbrking@us.ibm.com 
639296d21f00SWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64)
639396d21f00SWayne Boyer 		memcpy(ipr_cmd->scsi_cmd->sense_buffer, ioasa64->auto_sense.data,
639496d21f00SWayne Boyer 		       min_t(u16, be16_to_cpu(ioasa64->auto_sense.auto_sense_len),
639596d21f00SWayne Boyer 			   SCSI_SENSE_BUFFERSIZE));
639696d21f00SWayne Boyer 	else
6397ee0a90faSbrking@us.ibm.com 		memcpy(ipr_cmd->scsi_cmd->sense_buffer, ioasa->auto_sense.data,
6398ee0a90faSbrking@us.ibm.com 		       min_t(u16, be16_to_cpu(ioasa->auto_sense.auto_sense_len),
6399ee0a90faSbrking@us.ibm.com 			   SCSI_SENSE_BUFFERSIZE));
6400ee0a90faSbrking@us.ibm.com 	return 1;
6401ee0a90faSbrking@us.ibm.com }
6402ee0a90faSbrking@us.ibm.com 
6403ee0a90faSbrking@us.ibm.com /**
64041da177e4SLinus Torvalds  * ipr_erp_start - Process an error response for a SCSI op
64051da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
64061da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
64071da177e4SLinus Torvalds  *
64081da177e4SLinus Torvalds  * This function determines whether or not to initiate ERP
64091da177e4SLinus Torvalds  * on the affected device.
64101da177e4SLinus Torvalds  *
64111da177e4SLinus Torvalds  * Return value:
64121da177e4SLinus Torvalds  * 	nothing
64131da177e4SLinus Torvalds  **/
64141da177e4SLinus Torvalds static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
64151da177e4SLinus Torvalds 			      struct ipr_cmnd *ipr_cmd)
64161da177e4SLinus Torvalds {
64171da177e4SLinus Torvalds 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
64181da177e4SLinus Torvalds 	struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
641996d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
64208a048994SBrian King 	u32 masked_ioasc = ioasc & IPR_IOASC_IOASC_MASK;
64211da177e4SLinus Torvalds 
64221da177e4SLinus Torvalds 	if (!res) {
6423f646f325SBrian King 		__ipr_scsi_eh_done(ipr_cmd);
64241da177e4SLinus Torvalds 		return;
64251da177e4SLinus Torvalds 	}
64261da177e4SLinus Torvalds 
64278a048994SBrian King 	if (!ipr_is_gscsi(res) && masked_ioasc != IPR_IOASC_HW_DEV_BUS_STATUS)
64281da177e4SLinus Torvalds 		ipr_gen_sense(ipr_cmd);
64291da177e4SLinus Torvalds 
6430cc9bd5d4SBrian King 	ipr_dump_ioasa(ioa_cfg, ipr_cmd, res);
6431cc9bd5d4SBrian King 
64328a048994SBrian King 	switch (masked_ioasc) {
64331da177e4SLinus Torvalds 	case IPR_IOASC_ABORTED_CMD_TERM_BY_HOST:
6434ee0a90faSbrking@us.ibm.com 		if (ipr_is_naca_model(res))
6435ee0a90faSbrking@us.ibm.com 			scsi_cmd->result |= (DID_ABORT << 16);
6436ee0a90faSbrking@us.ibm.com 		else
64371da177e4SLinus Torvalds 			scsi_cmd->result |= (DID_IMM_RETRY << 16);
64381da177e4SLinus Torvalds 		break;
64391da177e4SLinus Torvalds 	case IPR_IOASC_IR_RESOURCE_HANDLE:
6440b0df54bbSbrking@us.ibm.com 	case IPR_IOASC_IR_NO_CMDS_TO_2ND_IOA:
64411da177e4SLinus Torvalds 		scsi_cmd->result |= (DID_NO_CONNECT << 16);
64421da177e4SLinus Torvalds 		break;
64431da177e4SLinus Torvalds 	case IPR_IOASC_HW_SEL_TIMEOUT:
64441da177e4SLinus Torvalds 		scsi_cmd->result |= (DID_NO_CONNECT << 16);
6445ee0a90faSbrking@us.ibm.com 		if (!ipr_is_naca_model(res))
64461da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
64471da177e4SLinus Torvalds 		break;
64481da177e4SLinus Torvalds 	case IPR_IOASC_SYNC_REQUIRED:
64491da177e4SLinus Torvalds 		if (!res->in_erp)
64501da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
64511da177e4SLinus Torvalds 		scsi_cmd->result |= (DID_IMM_RETRY << 16);
64521da177e4SLinus Torvalds 		break;
64531da177e4SLinus Torvalds 	case IPR_IOASC_MED_DO_NOT_REALLOC: /* prevent retries */
6454b0df54bbSbrking@us.ibm.com 	case IPR_IOASA_IR_DUAL_IOA_DISABLED:
6455785a4704SMauricio Faria de Oliveira 		/*
6456785a4704SMauricio Faria de Oliveira 		 * exception: do not set DID_PASSTHROUGH on CHECK CONDITION
6457785a4704SMauricio Faria de Oliveira 		 * so SCSI mid-layer and upper layers handle it accordingly.
6458785a4704SMauricio Faria de Oliveira 		 */
6459785a4704SMauricio Faria de Oliveira 		if (scsi_cmd->result != SAM_STAT_CHECK_CONDITION)
64601da177e4SLinus Torvalds 			scsi_cmd->result |= (DID_PASSTHROUGH << 16);
64611da177e4SLinus Torvalds 		break;
64621da177e4SLinus Torvalds 	case IPR_IOASC_BUS_WAS_RESET:
64631da177e4SLinus Torvalds 	case IPR_IOASC_BUS_WAS_RESET_BY_OTHER:
64641da177e4SLinus Torvalds 		/*
64651da177e4SLinus Torvalds 		 * Report the bus reset and ask for a retry. The device
64661da177e4SLinus Torvalds 		 * will give CC/UA the next command.
64671da177e4SLinus Torvalds 		 */
64681da177e4SLinus Torvalds 		if (!res->resetting_device)
64691da177e4SLinus Torvalds 			scsi_report_bus_reset(ioa_cfg->host, scsi_cmd->device->channel);
64701da177e4SLinus Torvalds 		scsi_cmd->result |= (DID_ERROR << 16);
6471ee0a90faSbrking@us.ibm.com 		if (!ipr_is_naca_model(res))
64721da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
64731da177e4SLinus Torvalds 		break;
64741da177e4SLinus Torvalds 	case IPR_IOASC_HW_DEV_BUS_STATUS:
64751da177e4SLinus Torvalds 		scsi_cmd->result |= IPR_IOASC_SENSE_STATUS(ioasc);
64761da177e4SLinus Torvalds 		if (IPR_IOASC_SENSE_STATUS(ioasc) == SAM_STAT_CHECK_CONDITION) {
6477ee0a90faSbrking@us.ibm.com 			if (!ipr_get_autosense(ipr_cmd)) {
6478ee0a90faSbrking@us.ibm.com 				if (!ipr_is_naca_model(res)) {
64791da177e4SLinus Torvalds 					ipr_erp_cancel_all(ipr_cmd);
64801da177e4SLinus Torvalds 					return;
64811da177e4SLinus Torvalds 				}
6482ee0a90faSbrking@us.ibm.com 			}
6483ee0a90faSbrking@us.ibm.com 		}
6484ee0a90faSbrking@us.ibm.com 		if (!ipr_is_naca_model(res))
64851da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
64861da177e4SLinus Torvalds 		break;
64871da177e4SLinus Torvalds 	case IPR_IOASC_NR_INIT_CMD_REQUIRED:
64881da177e4SLinus Torvalds 		break;
6489f8ee25d7SWen Xiong 	case IPR_IOASC_IR_NON_OPTIMIZED:
6490f8ee25d7SWen Xiong 		if (res->raw_mode) {
6491f8ee25d7SWen Xiong 			res->raw_mode = 0;
6492f8ee25d7SWen Xiong 			scsi_cmd->result |= (DID_IMM_RETRY << 16);
6493f8ee25d7SWen Xiong 		} else
6494f8ee25d7SWen Xiong 			scsi_cmd->result |= (DID_ERROR << 16);
6495f8ee25d7SWen Xiong 		break;
64961da177e4SLinus Torvalds 	default:
64975b7304fbSBrian King 		if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
64981da177e4SLinus Torvalds 			scsi_cmd->result |= (DID_ERROR << 16);
6499ee0a90faSbrking@us.ibm.com 		if (!ipr_is_vset_device(res) && !ipr_is_naca_model(res))
65001da177e4SLinus Torvalds 			res->needs_sync_complete = 1;
65011da177e4SLinus Torvalds 		break;
65021da177e4SLinus Torvalds 	}
65031da177e4SLinus Torvalds 
650463015bc9SFUJITA Tomonori 	scsi_dma_unmap(ipr_cmd->scsi_cmd);
65051da177e4SLinus Torvalds 	scsi_cmd->scsi_done(scsi_cmd);
650666a0d59cSBrian King 	if (ipr_cmd->eh_comp)
650766a0d59cSBrian King 		complete(ipr_cmd->eh_comp);
650866a0d59cSBrian King 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
65091da177e4SLinus Torvalds }
65101da177e4SLinus Torvalds 
65111da177e4SLinus Torvalds /**
65121da177e4SLinus Torvalds  * ipr_scsi_done - mid-layer done function
65131da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
65141da177e4SLinus Torvalds  *
65151da177e4SLinus Torvalds  * This function is invoked by the interrupt handler for
65161da177e4SLinus Torvalds  * ops generated by the SCSI mid-layer
65171da177e4SLinus Torvalds  *
65181da177e4SLinus Torvalds  * Return value:
65191da177e4SLinus Torvalds  * 	none
65201da177e4SLinus Torvalds  **/
65211da177e4SLinus Torvalds static void ipr_scsi_done(struct ipr_cmnd *ipr_cmd)
65221da177e4SLinus Torvalds {
65231da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
65241da177e4SLinus Torvalds 	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
652596d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
652636b8e180SBrian King 	unsigned long lock_flags;
65271da177e4SLinus Torvalds 
652896d21f00SWayne Boyer 	scsi_set_resid(scsi_cmd, be32_to_cpu(ipr_cmd->s.ioasa.hdr.residual_data_len));
65291da177e4SLinus Torvalds 
65301da177e4SLinus Torvalds 	if (likely(IPR_IOASC_SENSE_KEY(ioasc) == 0)) {
6531172cd6e1SBrian King 		scsi_dma_unmap(scsi_cmd);
6532172cd6e1SBrian King 
653336b8e180SBrian King 		spin_lock_irqsave(ipr_cmd->hrrq->lock, lock_flags);
65341da177e4SLinus Torvalds 		scsi_cmd->scsi_done(scsi_cmd);
653566a0d59cSBrian King 		if (ipr_cmd->eh_comp)
653666a0d59cSBrian King 			complete(ipr_cmd->eh_comp);
653766a0d59cSBrian King 		list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
653836b8e180SBrian King 		spin_unlock_irqrestore(ipr_cmd->hrrq->lock, lock_flags);
6539172cd6e1SBrian King 	} else {
654036b8e180SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
654136b8e180SBrian King 		spin_lock(&ipr_cmd->hrrq->_lock);
65421da177e4SLinus Torvalds 		ipr_erp_start(ioa_cfg, ipr_cmd);
654336b8e180SBrian King 		spin_unlock(&ipr_cmd->hrrq->_lock);
654436b8e180SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
6545172cd6e1SBrian King 	}
65461da177e4SLinus Torvalds }
65471da177e4SLinus Torvalds 
65481da177e4SLinus Torvalds /**
65491da177e4SLinus Torvalds  * ipr_queuecommand - Queue a mid-layer request
655000bfef2cSBrian King  * @shost:		scsi host struct
65511da177e4SLinus Torvalds  * @scsi_cmd:	scsi command struct
65521da177e4SLinus Torvalds  *
65531da177e4SLinus Torvalds  * This function queues a request generated by the mid-layer.
65541da177e4SLinus Torvalds  *
65551da177e4SLinus Torvalds  * Return value:
65561da177e4SLinus Torvalds  *	0 on success
65571da177e4SLinus Torvalds  *	SCSI_MLQUEUE_DEVICE_BUSY if device is busy
65581da177e4SLinus Torvalds  *	SCSI_MLQUEUE_HOST_BUSY if host is busy
65591da177e4SLinus Torvalds  **/
656000bfef2cSBrian King static int ipr_queuecommand(struct Scsi_Host *shost,
656100bfef2cSBrian King 			    struct scsi_cmnd *scsi_cmd)
65621da177e4SLinus Torvalds {
65631da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
65641da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
65651da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb;
65661da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd;
656756d6aa33Swenxiong@linux.vnet.ibm.com 	unsigned long hrrq_flags, lock_flags;
6568d12f1576SDan Carpenter 	int rc;
656905a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
657005a6538aSwenxiong@linux.vnet.ibm.com 	int hrrq_id;
65711da177e4SLinus Torvalds 
657200bfef2cSBrian King 	ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
657300bfef2cSBrian King 
65741da177e4SLinus Torvalds 	scsi_cmd->result = (DID_OK << 16);
657500bfef2cSBrian King 	res = scsi_cmd->device->hostdata;
657656d6aa33Swenxiong@linux.vnet.ibm.com 
657756d6aa33Swenxiong@linux.vnet.ibm.com 	if (ipr_is_gata(res) && res->sata_port) {
657856d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
657956d6aa33Swenxiong@linux.vnet.ibm.com 		rc = ata_sas_queuecmd(scsi_cmd, res->sata_port->ap);
658056d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
658156d6aa33Swenxiong@linux.vnet.ibm.com 		return rc;
658256d6aa33Swenxiong@linux.vnet.ibm.com 	}
658356d6aa33Swenxiong@linux.vnet.ibm.com 
658405a6538aSwenxiong@linux.vnet.ibm.com 	hrrq_id = ipr_get_hrrq_index(ioa_cfg);
658505a6538aSwenxiong@linux.vnet.ibm.com 	hrrq = &ioa_cfg->hrrq[hrrq_id];
65861da177e4SLinus Torvalds 
658756d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(hrrq->lock, hrrq_flags);
65881da177e4SLinus Torvalds 	/*
65891da177e4SLinus Torvalds 	 * We are currently blocking all devices due to a host reset
65901da177e4SLinus Torvalds 	 * We have told the host to stop giving us new requests, but
65911da177e4SLinus Torvalds 	 * ERP ops don't count. FIXME
65921da177e4SLinus Torvalds 	 */
6593bfae7820SBrian King 	if (unlikely(!hrrq->allow_cmds && !hrrq->ioa_is_dead && !hrrq->removing_ioa)) {
659456d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
65951da177e4SLinus Torvalds 		return SCSI_MLQUEUE_HOST_BUSY;
659600bfef2cSBrian King 	}
65971da177e4SLinus Torvalds 
65981da177e4SLinus Torvalds 	/*
65991da177e4SLinus Torvalds 	 * FIXME - Create scsi_set_host_offline interface
66001da177e4SLinus Torvalds 	 *  and the ioa_is_dead check can be removed
66011da177e4SLinus Torvalds 	 */
6602bfae7820SBrian King 	if (unlikely(hrrq->ioa_is_dead || hrrq->removing_ioa || !res)) {
660356d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
660400bfef2cSBrian King 		goto err_nodev;
66051da177e4SLinus Torvalds 	}
66061da177e4SLinus Torvalds 
660705a6538aSwenxiong@linux.vnet.ibm.com 	ipr_cmd = __ipr_get_free_ipr_cmnd(hrrq);
660805a6538aSwenxiong@linux.vnet.ibm.com 	if (ipr_cmd == NULL) {
660956d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
661005a6538aSwenxiong@linux.vnet.ibm.com 		return SCSI_MLQUEUE_HOST_BUSY;
661105a6538aSwenxiong@linux.vnet.ibm.com 	}
661256d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
661300bfef2cSBrian King 
6614172cd6e1SBrian King 	ipr_init_ipr_cmnd(ipr_cmd, ipr_scsi_done);
66151da177e4SLinus Torvalds 	ioarcb = &ipr_cmd->ioarcb;
66161da177e4SLinus Torvalds 
66171da177e4SLinus Torvalds 	memcpy(ioarcb->cmd_pkt.cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len);
66181da177e4SLinus Torvalds 	ipr_cmd->scsi_cmd = scsi_cmd;
6619172cd6e1SBrian King 	ipr_cmd->done = ipr_scsi_eh_done;
66201da177e4SLinus Torvalds 
66214f92d01aSGabriel Krisman Bertazi 	if (ipr_is_gscsi(res)) {
66221da177e4SLinus Torvalds 		if (scsi_cmd->underflow == 0)
66231da177e4SLinus Torvalds 			ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
66241da177e4SLinus Torvalds 
66254f92d01aSGabriel Krisman Bertazi 		if (res->reset_occurred) {
66260b1f8d44SWendy Xiong 			res->reset_occurred = 0;
66271da177e4SLinus Torvalds 			ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
66280b1f8d44SWendy Xiong 		}
66294f92d01aSGabriel Krisman Bertazi 	}
66304f92d01aSGabriel Krisman Bertazi 
66314f92d01aSGabriel Krisman Bertazi 	if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) {
66324f92d01aSGabriel Krisman Bertazi 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
66334f92d01aSGabriel Krisman Bertazi 
66341da177e4SLinus Torvalds 		ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR;
663550668633SChristoph Hellwig 		if (scsi_cmd->flags & SCMD_TAGGED)
663650668633SChristoph Hellwig 			ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_SIMPLE_TASK;
663750668633SChristoph Hellwig 		else
663850668633SChristoph Hellwig 			ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_UNTAGGED_TASK;
66391da177e4SLinus Torvalds 	}
66401da177e4SLinus Torvalds 
66411da177e4SLinus Torvalds 	if (scsi_cmd->cmnd[0] >= 0xC0 &&
664205a6538aSwenxiong@linux.vnet.ibm.com 	    (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE)) {
66431da177e4SLinus Torvalds 		ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
664405a6538aSwenxiong@linux.vnet.ibm.com 	}
66453cb4fc1fSGabriel Krisman Bertazi 	if (res->raw_mode && ipr_is_af_dasd_device(res)) {
6646f8ee25d7SWen Xiong 		ioarcb->cmd_pkt.request_type = IPR_RQTYPE_PIPE;
66471da177e4SLinus Torvalds 
66483cb4fc1fSGabriel Krisman Bertazi 		if (scsi_cmd->underflow == 0)
66493cb4fc1fSGabriel Krisman Bertazi 			ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
66503cb4fc1fSGabriel Krisman Bertazi 	}
66513cb4fc1fSGabriel Krisman Bertazi 
6652a32c055fSWayne Boyer 	if (ioa_cfg->sis64)
6653a32c055fSWayne Boyer 		rc = ipr_build_ioadl64(ioa_cfg, ipr_cmd);
6654a32c055fSWayne Boyer 	else
66551da177e4SLinus Torvalds 		rc = ipr_build_ioadl(ioa_cfg, ipr_cmd);
66561da177e4SLinus Torvalds 
665756d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(hrrq->lock, hrrq_flags);
665856d6aa33Swenxiong@linux.vnet.ibm.com 	if (unlikely(rc || (!hrrq->allow_cmds && !hrrq->ioa_is_dead))) {
665905a6538aSwenxiong@linux.vnet.ibm.com 		list_add_tail(&ipr_cmd->queue, &hrrq->hrrq_free_q);
666056d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
666100bfef2cSBrian King 		if (!rc)
666200bfef2cSBrian King 			scsi_dma_unmap(scsi_cmd);
66631da177e4SLinus Torvalds 		return SCSI_MLQUEUE_HOST_BUSY;
66641da177e4SLinus Torvalds 	}
66651da177e4SLinus Torvalds 
666656d6aa33Swenxiong@linux.vnet.ibm.com 	if (unlikely(hrrq->ioa_is_dead)) {
666705a6538aSwenxiong@linux.vnet.ibm.com 		list_add_tail(&ipr_cmd->queue, &hrrq->hrrq_free_q);
666856d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
666900bfef2cSBrian King 		scsi_dma_unmap(scsi_cmd);
667000bfef2cSBrian King 		goto err_nodev;
66711da177e4SLinus Torvalds 	}
66721da177e4SLinus Torvalds 
667300bfef2cSBrian King 	ioarcb->res_handle = res->res_handle;
667400bfef2cSBrian King 	if (res->needs_sync_complete) {
667500bfef2cSBrian King 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_SYNC_COMPLETE;
667600bfef2cSBrian King 		res->needs_sync_complete = 0;
667700bfef2cSBrian King 	}
667805a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &hrrq->hrrq_pending_q);
667900bfef2cSBrian King 	ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_RES_PHYS_LOC(res));
668000bfef2cSBrian King 	ipr_send_command(ipr_cmd);
668156d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
668200bfef2cSBrian King 	return 0;
668300bfef2cSBrian King 
668400bfef2cSBrian King err_nodev:
668556d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(hrrq->lock, hrrq_flags);
668600bfef2cSBrian King 	memset(scsi_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
668700bfef2cSBrian King 	scsi_cmd->result = (DID_NO_CONNECT << 16);
668800bfef2cSBrian King 	scsi_cmd->scsi_done(scsi_cmd);
668956d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
669000bfef2cSBrian King 	return 0;
669100bfef2cSBrian King }
6692f281233dSJeff Garzik 
66931da177e4SLinus Torvalds /**
669435a39691SBrian King  * ipr_ioctl - IOCTL handler
669535a39691SBrian King  * @sdev:	scsi device struct
669635a39691SBrian King  * @cmd:	IOCTL cmd
669735a39691SBrian King  * @arg:	IOCTL arg
669835a39691SBrian King  *
669935a39691SBrian King  * Return value:
670035a39691SBrian King  * 	0 on success / other on failure
670135a39691SBrian King  **/
67026f4e626fSNathan Chancellor static int ipr_ioctl(struct scsi_device *sdev, unsigned int cmd,
67036f4e626fSNathan Chancellor 		     void __user *arg)
670435a39691SBrian King {
670535a39691SBrian King 	struct ipr_resource_entry *res;
670635a39691SBrian King 
670735a39691SBrian King 	res = (struct ipr_resource_entry *)sdev->hostdata;
67080ce3a7e5SBrian King 	if (res && ipr_is_gata(res)) {
67090ce3a7e5SBrian King 		if (cmd == HDIO_GET_IDENTITY)
67100ce3a7e5SBrian King 			return -ENOTTY;
671194be9a58SJeff Garzik 		return ata_sas_scsi_ioctl(res->sata_port->ap, sdev, cmd, arg);
67120ce3a7e5SBrian King 	}
671335a39691SBrian King 
671435a39691SBrian King 	return -EINVAL;
671535a39691SBrian King }
671635a39691SBrian King 
671735a39691SBrian King /**
6718*637b5c3eSLee Jones  * ipr_ioa_info - Get information about the card/driver
6719a96099e2SLee Jones  * @host:	scsi host struct
67201da177e4SLinus Torvalds  *
67211da177e4SLinus Torvalds  * Return value:
67221da177e4SLinus Torvalds  * 	pointer to buffer with description string
67231da177e4SLinus Torvalds  **/
67241da177e4SLinus Torvalds static const char *ipr_ioa_info(struct Scsi_Host *host)
67251da177e4SLinus Torvalds {
67261da177e4SLinus Torvalds 	static char buffer[512];
67271da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
67281da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
67291da177e4SLinus Torvalds 
67301da177e4SLinus Torvalds 	ioa_cfg = (struct ipr_ioa_cfg *) host->hostdata;
67311da177e4SLinus Torvalds 
67321da177e4SLinus Torvalds 	spin_lock_irqsave(host->host_lock, lock_flags);
67331da177e4SLinus Torvalds 	sprintf(buffer, "IBM %X Storage Adapter", ioa_cfg->type);
67341da177e4SLinus Torvalds 	spin_unlock_irqrestore(host->host_lock, lock_flags);
67351da177e4SLinus Torvalds 
67361da177e4SLinus Torvalds 	return buffer;
67371da177e4SLinus Torvalds }
67381da177e4SLinus Torvalds 
67391da177e4SLinus Torvalds static struct scsi_host_template driver_template = {
67401da177e4SLinus Torvalds 	.module = THIS_MODULE,
67411da177e4SLinus Torvalds 	.name = "IPR",
67421da177e4SLinus Torvalds 	.info = ipr_ioa_info,
674335a39691SBrian King 	.ioctl = ipr_ioctl,
674475c0b0e1SArnd Bergmann #ifdef CONFIG_COMPAT
674575c0b0e1SArnd Bergmann 	.compat_ioctl = ipr_ioctl,
674675c0b0e1SArnd Bergmann #endif
67471da177e4SLinus Torvalds 	.queuecommand = ipr_queuecommand,
6748b8f1d1e0SChristoph Hellwig 	.dma_need_drain = ata_scsi_dma_need_drain,
67491da177e4SLinus Torvalds 	.eh_abort_handler = ipr_eh_abort,
67501da177e4SLinus Torvalds 	.eh_device_reset_handler = ipr_eh_dev_reset,
67511da177e4SLinus Torvalds 	.eh_host_reset_handler = ipr_eh_host_reset,
67521da177e4SLinus Torvalds 	.slave_alloc = ipr_slave_alloc,
67531da177e4SLinus Torvalds 	.slave_configure = ipr_slave_configure,
67541da177e4SLinus Torvalds 	.slave_destroy = ipr_slave_destroy,
6755f688f96dSBrian King 	.scan_finished = ipr_scan_finished,
675635a39691SBrian King 	.target_alloc = ipr_target_alloc,
675735a39691SBrian King 	.target_destroy = ipr_target_destroy,
67581da177e4SLinus Torvalds 	.change_queue_depth = ipr_change_queue_depth,
67591da177e4SLinus Torvalds 	.bios_param = ipr_biosparam,
67601da177e4SLinus Torvalds 	.can_queue = IPR_MAX_COMMANDS,
67611da177e4SLinus Torvalds 	.this_id = -1,
67621da177e4SLinus Torvalds 	.sg_tablesize = IPR_MAX_SGLIST,
67631da177e4SLinus Torvalds 	.max_sectors = IPR_IOA_MAX_SECTORS,
67641da177e4SLinus Torvalds 	.cmd_per_lun = IPR_MAX_CMD_PER_LUN,
67651da177e4SLinus Torvalds 	.shost_attrs = ipr_ioa_attrs,
67661da177e4SLinus Torvalds 	.sdev_attrs = ipr_dev_attrs,
676754b2b50cSMartin K. Petersen 	.proc_name = IPR_NAME,
67681da177e4SLinus Torvalds };
67691da177e4SLinus Torvalds 
677035a39691SBrian King /**
677135a39691SBrian King  * ipr_ata_phy_reset - libata phy_reset handler
677235a39691SBrian King  * @ap:		ata port to reset
677335a39691SBrian King  *
677435a39691SBrian King  **/
677535a39691SBrian King static void ipr_ata_phy_reset(struct ata_port *ap)
677635a39691SBrian King {
677735a39691SBrian King 	unsigned long flags;
677835a39691SBrian King 	struct ipr_sata_port *sata_port = ap->private_data;
677935a39691SBrian King 	struct ipr_resource_entry *res = sata_port->res;
678035a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
678135a39691SBrian King 	int rc;
678235a39691SBrian King 
678335a39691SBrian King 	ENTER;
678435a39691SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
678535a39691SBrian King 	while (ioa_cfg->in_reset_reload) {
678635a39691SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
678735a39691SBrian King 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
678835a39691SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
678935a39691SBrian King 	}
679035a39691SBrian King 
679156d6aa33Swenxiong@linux.vnet.ibm.com 	if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds)
679235a39691SBrian King 		goto out_unlock;
679335a39691SBrian King 
679435a39691SBrian King 	rc = ipr_device_reset(ioa_cfg, res);
679535a39691SBrian King 
679635a39691SBrian King 	if (rc) {
67973e4ec344STejun Heo 		ap->link.device[0].class = ATA_DEV_NONE;
679835a39691SBrian King 		goto out_unlock;
679935a39691SBrian King 	}
680035a39691SBrian King 
68013e7ebdfaSWayne Boyer 	ap->link.device[0].class = res->ata_class;
68023e7ebdfaSWayne Boyer 	if (ap->link.device[0].class == ATA_DEV_UNKNOWN)
68033e4ec344STejun Heo 		ap->link.device[0].class = ATA_DEV_NONE;
680435a39691SBrian King 
680535a39691SBrian King out_unlock:
680635a39691SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
680735a39691SBrian King 	LEAVE;
680835a39691SBrian King }
680935a39691SBrian King 
681035a39691SBrian King /**
681135a39691SBrian King  * ipr_ata_post_internal - Cleanup after an internal command
681235a39691SBrian King  * @qc:	ATA queued command
681335a39691SBrian King  *
681435a39691SBrian King  * Return value:
681535a39691SBrian King  * 	none
681635a39691SBrian King  **/
681735a39691SBrian King static void ipr_ata_post_internal(struct ata_queued_cmd *qc)
681835a39691SBrian King {
681935a39691SBrian King 	struct ipr_sata_port *sata_port = qc->ap->private_data;
682035a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
682135a39691SBrian King 	struct ipr_cmnd *ipr_cmd;
682205a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
682335a39691SBrian King 	unsigned long flags;
682435a39691SBrian King 
682535a39691SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
682673d98ff0SBrian King 	while (ioa_cfg->in_reset_reload) {
682773d98ff0SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
682873d98ff0SBrian King 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
682973d98ff0SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
683073d98ff0SBrian King 	}
683173d98ff0SBrian King 
683205a6538aSwenxiong@linux.vnet.ibm.com 	for_each_hrrq(hrrq, ioa_cfg) {
683356d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&hrrq->_lock);
683405a6538aSwenxiong@linux.vnet.ibm.com 		list_for_each_entry(ipr_cmd, &hrrq->hrrq_pending_q, queue) {
683535a39691SBrian King 			if (ipr_cmd->qc == qc) {
683635a39691SBrian King 				ipr_device_reset(ioa_cfg, sata_port->res);
683735a39691SBrian King 				break;
683835a39691SBrian King 			}
683935a39691SBrian King 		}
684056d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
684105a6538aSwenxiong@linux.vnet.ibm.com 	}
684235a39691SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
684335a39691SBrian King }
684435a39691SBrian King 
684535a39691SBrian King /**
684635a39691SBrian King  * ipr_copy_sata_tf - Copy a SATA taskfile to an IOA data structure
684735a39691SBrian King  * @regs:	destination
684835a39691SBrian King  * @tf:	source ATA taskfile
684935a39691SBrian King  *
685035a39691SBrian King  * Return value:
685135a39691SBrian King  * 	none
685235a39691SBrian King  **/
685335a39691SBrian King static void ipr_copy_sata_tf(struct ipr_ioarcb_ata_regs *regs,
685435a39691SBrian King 			     struct ata_taskfile *tf)
685535a39691SBrian King {
685635a39691SBrian King 	regs->feature = tf->feature;
685735a39691SBrian King 	regs->nsect = tf->nsect;
685835a39691SBrian King 	regs->lbal = tf->lbal;
685935a39691SBrian King 	regs->lbam = tf->lbam;
686035a39691SBrian King 	regs->lbah = tf->lbah;
686135a39691SBrian King 	regs->device = tf->device;
686235a39691SBrian King 	regs->command = tf->command;
686335a39691SBrian King 	regs->hob_feature = tf->hob_feature;
686435a39691SBrian King 	regs->hob_nsect = tf->hob_nsect;
686535a39691SBrian King 	regs->hob_lbal = tf->hob_lbal;
686635a39691SBrian King 	regs->hob_lbam = tf->hob_lbam;
686735a39691SBrian King 	regs->hob_lbah = tf->hob_lbah;
686835a39691SBrian King 	regs->ctl = tf->ctl;
686935a39691SBrian King }
687035a39691SBrian King 
687135a39691SBrian King /**
687235a39691SBrian King  * ipr_sata_done - done function for SATA commands
687335a39691SBrian King  * @ipr_cmd:	ipr command struct
687435a39691SBrian King  *
687535a39691SBrian King  * This function is invoked by the interrupt handler for
687635a39691SBrian King  * ops generated by the SCSI mid-layer to SATA devices
687735a39691SBrian King  *
687835a39691SBrian King  * Return value:
687935a39691SBrian King  * 	none
688035a39691SBrian King  **/
688135a39691SBrian King static void ipr_sata_done(struct ipr_cmnd *ipr_cmd)
688235a39691SBrian King {
688335a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
688435a39691SBrian King 	struct ata_queued_cmd *qc = ipr_cmd->qc;
688535a39691SBrian King 	struct ipr_sata_port *sata_port = qc->ap->private_data;
688635a39691SBrian King 	struct ipr_resource_entry *res = sata_port->res;
688796d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
688835a39691SBrian King 
688956d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock(&ipr_cmd->hrrq->_lock);
689096d21f00SWayne Boyer 	if (ipr_cmd->ioa_cfg->sis64)
689196d21f00SWayne Boyer 		memcpy(&sata_port->ioasa, &ipr_cmd->s.ioasa64.u.gata,
689296d21f00SWayne Boyer 		       sizeof(struct ipr_ioasa_gata));
689396d21f00SWayne Boyer 	else
689496d21f00SWayne Boyer 		memcpy(&sata_port->ioasa, &ipr_cmd->s.ioasa.u.gata,
689535a39691SBrian King 		       sizeof(struct ipr_ioasa_gata));
689635a39691SBrian King 	ipr_dump_ioasa(ioa_cfg, ipr_cmd, res);
689735a39691SBrian King 
689896d21f00SWayne Boyer 	if (be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc_specific) & IPR_ATA_DEVICE_WAS_RESET)
68993e7ebdfaSWayne Boyer 		scsi_report_device_reset(ioa_cfg->host, res->bus, res->target);
690035a39691SBrian King 
690135a39691SBrian King 	if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
690296d21f00SWayne Boyer 		qc->err_mask |= __ac_err_mask(sata_port->ioasa.status);
690335a39691SBrian King 	else
690496d21f00SWayne Boyer 		qc->err_mask |= ac_err_mask(sata_port->ioasa.status);
690505a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
690656d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock(&ipr_cmd->hrrq->_lock);
690735a39691SBrian King 	ata_qc_complete(qc);
690835a39691SBrian King }
690935a39691SBrian King 
691035a39691SBrian King /**
6911a32c055fSWayne Boyer  * ipr_build_ata_ioadl64 - Build an ATA scatter/gather list
6912a32c055fSWayne Boyer  * @ipr_cmd:	ipr command struct
6913a32c055fSWayne Boyer  * @qc:		ATA queued command
6914a32c055fSWayne Boyer  *
6915a32c055fSWayne Boyer  **/
6916a32c055fSWayne Boyer static void ipr_build_ata_ioadl64(struct ipr_cmnd *ipr_cmd,
6917a32c055fSWayne Boyer 				  struct ata_queued_cmd *qc)
6918a32c055fSWayne Boyer {
6919a32c055fSWayne Boyer 	u32 ioadl_flags = 0;
6920a32c055fSWayne Boyer 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
69211ac7c26dSwenxiong@linux.vnet.ibm.com 	struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ata_ioadl.ioadl64;
6922a32c055fSWayne Boyer 	struct ipr_ioadl64_desc *last_ioadl64 = NULL;
6923a32c055fSWayne Boyer 	int len = qc->nbytes;
6924a32c055fSWayne Boyer 	struct scatterlist *sg;
6925a32c055fSWayne Boyer 	unsigned int si;
6926a32c055fSWayne Boyer 	dma_addr_t dma_addr = ipr_cmd->dma_addr;
6927a32c055fSWayne Boyer 
6928a32c055fSWayne Boyer 	if (len == 0)
6929a32c055fSWayne Boyer 		return;
6930a32c055fSWayne Boyer 
6931a32c055fSWayne Boyer 	if (qc->dma_dir == DMA_TO_DEVICE) {
6932a32c055fSWayne Boyer 		ioadl_flags = IPR_IOADL_FLAGS_WRITE;
6933a32c055fSWayne Boyer 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
6934a32c055fSWayne Boyer 	} else if (qc->dma_dir == DMA_FROM_DEVICE)
6935a32c055fSWayne Boyer 		ioadl_flags = IPR_IOADL_FLAGS_READ;
6936a32c055fSWayne Boyer 
6937a32c055fSWayne Boyer 	ioarcb->data_transfer_length = cpu_to_be32(len);
6938a32c055fSWayne Boyer 	ioarcb->ioadl_len =
6939a32c055fSWayne Boyer 		cpu_to_be32(sizeof(struct ipr_ioadl64_desc) * ipr_cmd->dma_use_sg);
6940a32c055fSWayne Boyer 	ioarcb->u.sis64_addr_data.data_ioadl_addr =
69411ac7c26dSwenxiong@linux.vnet.ibm.com 		cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ata_ioadl.ioadl64));
6942a32c055fSWayne Boyer 
6943a32c055fSWayne Boyer 	for_each_sg(qc->sg, sg, qc->n_elem, si) {
6944a32c055fSWayne Boyer 		ioadl64->flags = cpu_to_be32(ioadl_flags);
6945a32c055fSWayne Boyer 		ioadl64->data_len = cpu_to_be32(sg_dma_len(sg));
6946a32c055fSWayne Boyer 		ioadl64->address = cpu_to_be64(sg_dma_address(sg));
6947a32c055fSWayne Boyer 
6948a32c055fSWayne Boyer 		last_ioadl64 = ioadl64;
6949a32c055fSWayne Boyer 		ioadl64++;
6950a32c055fSWayne Boyer 	}
6951a32c055fSWayne Boyer 
6952a32c055fSWayne Boyer 	if (likely(last_ioadl64))
6953a32c055fSWayne Boyer 		last_ioadl64->flags |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
6954a32c055fSWayne Boyer }
6955a32c055fSWayne Boyer 
6956a32c055fSWayne Boyer /**
695735a39691SBrian King  * ipr_build_ata_ioadl - Build an ATA scatter/gather list
695835a39691SBrian King  * @ipr_cmd:	ipr command struct
695935a39691SBrian King  * @qc:		ATA queued command
696035a39691SBrian King  *
696135a39691SBrian King  **/
696235a39691SBrian King static void ipr_build_ata_ioadl(struct ipr_cmnd *ipr_cmd,
696335a39691SBrian King 				struct ata_queued_cmd *qc)
696435a39691SBrian King {
696535a39691SBrian King 	u32 ioadl_flags = 0;
696635a39691SBrian King 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
6967a32c055fSWayne Boyer 	struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl;
69683be6cbd7SJeff Garzik 	struct ipr_ioadl_desc *last_ioadl = NULL;
6969dde20207SJames Bottomley 	int len = qc->nbytes;
697035a39691SBrian King 	struct scatterlist *sg;
6971ff2aeb1eSTejun Heo 	unsigned int si;
697235a39691SBrian King 
697335a39691SBrian King 	if (len == 0)
697435a39691SBrian King 		return;
697535a39691SBrian King 
697635a39691SBrian King 	if (qc->dma_dir == DMA_TO_DEVICE) {
697735a39691SBrian King 		ioadl_flags = IPR_IOADL_FLAGS_WRITE;
697835a39691SBrian King 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
6979a32c055fSWayne Boyer 		ioarcb->data_transfer_length = cpu_to_be32(len);
6980a32c055fSWayne Boyer 		ioarcb->ioadl_len =
698135a39691SBrian King 			cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
698235a39691SBrian King 	} else if (qc->dma_dir == DMA_FROM_DEVICE) {
698335a39691SBrian King 		ioadl_flags = IPR_IOADL_FLAGS_READ;
698435a39691SBrian King 		ioarcb->read_data_transfer_length = cpu_to_be32(len);
698535a39691SBrian King 		ioarcb->read_ioadl_len =
698635a39691SBrian King 			cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
698735a39691SBrian King 	}
698835a39691SBrian King 
6989ff2aeb1eSTejun Heo 	for_each_sg(qc->sg, sg, qc->n_elem, si) {
699035a39691SBrian King 		ioadl->flags_and_data_len = cpu_to_be32(ioadl_flags | sg_dma_len(sg));
699135a39691SBrian King 		ioadl->address = cpu_to_be32(sg_dma_address(sg));
69923be6cbd7SJeff Garzik 
69933be6cbd7SJeff Garzik 		last_ioadl = ioadl;
699435a39691SBrian King 		ioadl++;
699535a39691SBrian King 	}
69963be6cbd7SJeff Garzik 
69973be6cbd7SJeff Garzik 	if (likely(last_ioadl))
69983be6cbd7SJeff Garzik 		last_ioadl->flags_and_data_len |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
699935a39691SBrian King }
700035a39691SBrian King 
700135a39691SBrian King /**
700256d6aa33Swenxiong@linux.vnet.ibm.com  * ipr_qc_defer - Get a free ipr_cmd
700356d6aa33Swenxiong@linux.vnet.ibm.com  * @qc:	queued command
700456d6aa33Swenxiong@linux.vnet.ibm.com  *
700556d6aa33Swenxiong@linux.vnet.ibm.com  * Return value:
700656d6aa33Swenxiong@linux.vnet.ibm.com  *	0 if success
700756d6aa33Swenxiong@linux.vnet.ibm.com  **/
700856d6aa33Swenxiong@linux.vnet.ibm.com static int ipr_qc_defer(struct ata_queued_cmd *qc)
700956d6aa33Swenxiong@linux.vnet.ibm.com {
701056d6aa33Swenxiong@linux.vnet.ibm.com 	struct ata_port *ap = qc->ap;
701156d6aa33Swenxiong@linux.vnet.ibm.com 	struct ipr_sata_port *sata_port = ap->private_data;
701256d6aa33Swenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
701356d6aa33Swenxiong@linux.vnet.ibm.com 	struct ipr_cmnd *ipr_cmd;
701456d6aa33Swenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
701556d6aa33Swenxiong@linux.vnet.ibm.com 	int hrrq_id;
701656d6aa33Swenxiong@linux.vnet.ibm.com 
701756d6aa33Swenxiong@linux.vnet.ibm.com 	hrrq_id = ipr_get_hrrq_index(ioa_cfg);
701856d6aa33Swenxiong@linux.vnet.ibm.com 	hrrq = &ioa_cfg->hrrq[hrrq_id];
701956d6aa33Swenxiong@linux.vnet.ibm.com 
702056d6aa33Swenxiong@linux.vnet.ibm.com 	qc->lldd_task = NULL;
702156d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock(&hrrq->_lock);
702256d6aa33Swenxiong@linux.vnet.ibm.com 	if (unlikely(hrrq->ioa_is_dead)) {
702356d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
702456d6aa33Swenxiong@linux.vnet.ibm.com 		return 0;
702556d6aa33Swenxiong@linux.vnet.ibm.com 	}
702656d6aa33Swenxiong@linux.vnet.ibm.com 
702756d6aa33Swenxiong@linux.vnet.ibm.com 	if (unlikely(!hrrq->allow_cmds)) {
702856d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
702956d6aa33Swenxiong@linux.vnet.ibm.com 		return ATA_DEFER_LINK;
703056d6aa33Swenxiong@linux.vnet.ibm.com 	}
703156d6aa33Swenxiong@linux.vnet.ibm.com 
703256d6aa33Swenxiong@linux.vnet.ibm.com 	ipr_cmd = __ipr_get_free_ipr_cmnd(hrrq);
703356d6aa33Swenxiong@linux.vnet.ibm.com 	if (ipr_cmd == NULL) {
703456d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
703556d6aa33Swenxiong@linux.vnet.ibm.com 		return ATA_DEFER_LINK;
703656d6aa33Swenxiong@linux.vnet.ibm.com 	}
703756d6aa33Swenxiong@linux.vnet.ibm.com 
703856d6aa33Swenxiong@linux.vnet.ibm.com 	qc->lldd_task = ipr_cmd;
703956d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock(&hrrq->_lock);
704056d6aa33Swenxiong@linux.vnet.ibm.com 	return 0;
704156d6aa33Swenxiong@linux.vnet.ibm.com }
704256d6aa33Swenxiong@linux.vnet.ibm.com 
704356d6aa33Swenxiong@linux.vnet.ibm.com /**
704435a39691SBrian King  * ipr_qc_issue - Issue a SATA qc to a device
704535a39691SBrian King  * @qc:	queued command
704635a39691SBrian King  *
704735a39691SBrian King  * Return value:
704835a39691SBrian King  * 	0 if success
704935a39691SBrian King  **/
705035a39691SBrian King static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc)
705135a39691SBrian King {
705235a39691SBrian King 	struct ata_port *ap = qc->ap;
705335a39691SBrian King 	struct ipr_sata_port *sata_port = ap->private_data;
705435a39691SBrian King 	struct ipr_resource_entry *res = sata_port->res;
705535a39691SBrian King 	struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
705635a39691SBrian King 	struct ipr_cmnd *ipr_cmd;
705735a39691SBrian King 	struct ipr_ioarcb *ioarcb;
705835a39691SBrian King 	struct ipr_ioarcb_ata_regs *regs;
705935a39691SBrian King 
706056d6aa33Swenxiong@linux.vnet.ibm.com 	if (qc->lldd_task == NULL)
706156d6aa33Swenxiong@linux.vnet.ibm.com 		ipr_qc_defer(qc);
706256d6aa33Swenxiong@linux.vnet.ibm.com 
706356d6aa33Swenxiong@linux.vnet.ibm.com 	ipr_cmd = qc->lldd_task;
706456d6aa33Swenxiong@linux.vnet.ibm.com 	if (ipr_cmd == NULL)
70650feeed82SBrian King 		return AC_ERR_SYSTEM;
706635a39691SBrian King 
706756d6aa33Swenxiong@linux.vnet.ibm.com 	qc->lldd_task = NULL;
706856d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock(&ipr_cmd->hrrq->_lock);
706956d6aa33Swenxiong@linux.vnet.ibm.com 	if (unlikely(!ipr_cmd->hrrq->allow_cmds ||
707056d6aa33Swenxiong@linux.vnet.ibm.com 			ipr_cmd->hrrq->ioa_is_dead)) {
707156d6aa33Swenxiong@linux.vnet.ibm.com 		list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
707256d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ipr_cmd->hrrq->_lock);
707356d6aa33Swenxiong@linux.vnet.ibm.com 		return AC_ERR_SYSTEM;
707456d6aa33Swenxiong@linux.vnet.ibm.com 	}
707556d6aa33Swenxiong@linux.vnet.ibm.com 
707605a6538aSwenxiong@linux.vnet.ibm.com 	ipr_init_ipr_cmnd(ipr_cmd, ipr_lock_and_done);
707735a39691SBrian King 	ioarcb = &ipr_cmd->ioarcb;
707835a39691SBrian King 
7079a32c055fSWayne Boyer 	if (ioa_cfg->sis64) {
7080a32c055fSWayne Boyer 		regs = &ipr_cmd->i.ata_ioadl.regs;
7081a32c055fSWayne Boyer 		ioarcb->add_cmd_parms_offset = cpu_to_be16(sizeof(*ioarcb));
7082a32c055fSWayne Boyer 	} else
7083a32c055fSWayne Boyer 		regs = &ioarcb->u.add_data.u.regs;
7084a32c055fSWayne Boyer 
7085a32c055fSWayne Boyer 	memset(regs, 0, sizeof(*regs));
7086a32c055fSWayne Boyer 	ioarcb->add_cmd_parms_len = cpu_to_be16(sizeof(*regs));
708735a39691SBrian King 
708856d6aa33Swenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
708935a39691SBrian King 	ipr_cmd->qc = qc;
709035a39691SBrian King 	ipr_cmd->done = ipr_sata_done;
70913e7ebdfaSWayne Boyer 	ipr_cmd->ioarcb.res_handle = res->res_handle;
709235a39691SBrian King 	ioarcb->cmd_pkt.request_type = IPR_RQTYPE_ATA_PASSTHRU;
709335a39691SBrian King 	ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
709435a39691SBrian King 	ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
7095dde20207SJames Bottomley 	ipr_cmd->dma_use_sg = qc->n_elem;
709635a39691SBrian King 
7097a32c055fSWayne Boyer 	if (ioa_cfg->sis64)
7098a32c055fSWayne Boyer 		ipr_build_ata_ioadl64(ipr_cmd, qc);
7099a32c055fSWayne Boyer 	else
710035a39691SBrian King 		ipr_build_ata_ioadl(ipr_cmd, qc);
7101a32c055fSWayne Boyer 
710235a39691SBrian King 	regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION;
710335a39691SBrian King 	ipr_copy_sata_tf(regs, &qc->tf);
710435a39691SBrian King 	memcpy(ioarcb->cmd_pkt.cdb, qc->cdb, IPR_MAX_CDB_LEN);
71053e7ebdfaSWayne Boyer 	ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_RES_PHYS_LOC(res));
710635a39691SBrian King 
710735a39691SBrian King 	switch (qc->tf.protocol) {
710835a39691SBrian King 	case ATA_PROT_NODATA:
710935a39691SBrian King 	case ATA_PROT_PIO:
711035a39691SBrian King 		break;
711135a39691SBrian King 
711235a39691SBrian King 	case ATA_PROT_DMA:
711335a39691SBrian King 		regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA;
711435a39691SBrian King 		break;
711535a39691SBrian King 
71160dc36888STejun Heo 	case ATAPI_PROT_PIO:
71170dc36888STejun Heo 	case ATAPI_PROT_NODATA:
711835a39691SBrian King 		regs->flags |= IPR_ATA_FLAG_PACKET_CMD;
711935a39691SBrian King 		break;
712035a39691SBrian King 
71210dc36888STejun Heo 	case ATAPI_PROT_DMA:
712235a39691SBrian King 		regs->flags |= IPR_ATA_FLAG_PACKET_CMD;
712335a39691SBrian King 		regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA;
712435a39691SBrian King 		break;
712535a39691SBrian King 
712635a39691SBrian King 	default:
712735a39691SBrian King 		WARN_ON(1);
712856d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ipr_cmd->hrrq->_lock);
71290feeed82SBrian King 		return AC_ERR_INVALID;
713035a39691SBrian King 	}
713135a39691SBrian King 
7132a32c055fSWayne Boyer 	ipr_send_command(ipr_cmd);
713356d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock(&ipr_cmd->hrrq->_lock);
7134a32c055fSWayne Boyer 
713535a39691SBrian King 	return 0;
713635a39691SBrian King }
713735a39691SBrian King 
713835a39691SBrian King /**
71394c9bf4e7STejun Heo  * ipr_qc_fill_rtf - Read result TF
71404c9bf4e7STejun Heo  * @qc: ATA queued command
71414c9bf4e7STejun Heo  *
71424c9bf4e7STejun Heo  * Return value:
71434c9bf4e7STejun Heo  * 	true
71444c9bf4e7STejun Heo  **/
71454c9bf4e7STejun Heo static bool ipr_qc_fill_rtf(struct ata_queued_cmd *qc)
71464c9bf4e7STejun Heo {
71474c9bf4e7STejun Heo 	struct ipr_sata_port *sata_port = qc->ap->private_data;
71484c9bf4e7STejun Heo 	struct ipr_ioasa_gata *g = &sata_port->ioasa;
71494c9bf4e7STejun Heo 	struct ata_taskfile *tf = &qc->result_tf;
71504c9bf4e7STejun Heo 
71514c9bf4e7STejun Heo 	tf->feature = g->error;
71524c9bf4e7STejun Heo 	tf->nsect = g->nsect;
71534c9bf4e7STejun Heo 	tf->lbal = g->lbal;
71544c9bf4e7STejun Heo 	tf->lbam = g->lbam;
71554c9bf4e7STejun Heo 	tf->lbah = g->lbah;
71564c9bf4e7STejun Heo 	tf->device = g->device;
71574c9bf4e7STejun Heo 	tf->command = g->status;
71584c9bf4e7STejun Heo 	tf->hob_nsect = g->hob_nsect;
71594c9bf4e7STejun Heo 	tf->hob_lbal = g->hob_lbal;
71604c9bf4e7STejun Heo 	tf->hob_lbam = g->hob_lbam;
71614c9bf4e7STejun Heo 	tf->hob_lbah = g->hob_lbah;
71624c9bf4e7STejun Heo 
71634c9bf4e7STejun Heo 	return true;
71644c9bf4e7STejun Heo }
71654c9bf4e7STejun Heo 
716635a39691SBrian King static struct ata_port_operations ipr_sata_ops = {
716735a39691SBrian King 	.phy_reset = ipr_ata_phy_reset,
7168a1efdabaSTejun Heo 	.hardreset = ipr_sata_reset,
716935a39691SBrian King 	.post_internal_cmd = ipr_ata_post_internal,
717035a39691SBrian King 	.qc_prep = ata_noop_qc_prep,
717156d6aa33Swenxiong@linux.vnet.ibm.com 	.qc_defer = ipr_qc_defer,
717235a39691SBrian King 	.qc_issue = ipr_qc_issue,
71734c9bf4e7STejun Heo 	.qc_fill_rtf = ipr_qc_fill_rtf,
717435a39691SBrian King 	.port_start = ata_sas_port_start,
717535a39691SBrian King 	.port_stop = ata_sas_port_stop
717635a39691SBrian King };
717735a39691SBrian King 
717835a39691SBrian King static struct ata_port_info sata_port_info = {
71795067c046SShaohua Li 	.flags		= ATA_FLAG_SATA | ATA_FLAG_PIO_DMA |
71805067c046SShaohua Li 			  ATA_FLAG_SAS_HOST,
71810f2e0330SSergei Shtylyov 	.pio_mask	= ATA_PIO4_ONLY,
71820f2e0330SSergei Shtylyov 	.mwdma_mask	= ATA_MWDMA2,
71830f2e0330SSergei Shtylyov 	.udma_mask	= ATA_UDMA6,
718435a39691SBrian King 	.port_ops	= &ipr_sata_ops
718535a39691SBrian King };
718635a39691SBrian King 
71871da177e4SLinus Torvalds #ifdef CONFIG_PPC_PSERIES
71881da177e4SLinus Torvalds static const u16 ipr_blocked_processors[] = {
7189d3dbeef6SMichael Ellerman 	PVR_NORTHSTAR,
7190d3dbeef6SMichael Ellerman 	PVR_PULSAR,
7191d3dbeef6SMichael Ellerman 	PVR_POWER4,
7192d3dbeef6SMichael Ellerman 	PVR_ICESTAR,
7193d3dbeef6SMichael Ellerman 	PVR_SSTAR,
7194d3dbeef6SMichael Ellerman 	PVR_POWER4p,
7195d3dbeef6SMichael Ellerman 	PVR_630,
7196d3dbeef6SMichael Ellerman 	PVR_630p
71971da177e4SLinus Torvalds };
71981da177e4SLinus Torvalds 
71991da177e4SLinus Torvalds /**
72001da177e4SLinus Torvalds  * ipr_invalid_adapter - Determine if this adapter is supported on this hardware
72011da177e4SLinus Torvalds  * @ioa_cfg:	ioa cfg struct
72021da177e4SLinus Torvalds  *
72031da177e4SLinus Torvalds  * Adapters that use Gemstone revision < 3.1 do not work reliably on
72041da177e4SLinus Torvalds  * certain pSeries hardware. This function determines if the given
72051da177e4SLinus Torvalds  * adapter is in one of these confgurations or not.
72061da177e4SLinus Torvalds  *
72071da177e4SLinus Torvalds  * Return value:
72081da177e4SLinus Torvalds  * 	1 if adapter is not supported / 0 if adapter is supported
72091da177e4SLinus Torvalds  **/
72101da177e4SLinus Torvalds static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
72111da177e4SLinus Torvalds {
72121da177e4SLinus Torvalds 	int i;
72131da177e4SLinus Torvalds 
721444c10138SAuke Kok 	if ((ioa_cfg->type == 0x5702) && (ioa_cfg->pdev->revision < 4)) {
72151da177e4SLinus Torvalds 		for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++) {
7216d3dbeef6SMichael Ellerman 			if (pvr_version_is(ipr_blocked_processors[i]))
72171da177e4SLinus Torvalds 				return 1;
72181da177e4SLinus Torvalds 		}
72191da177e4SLinus Torvalds 	}
72201da177e4SLinus Torvalds 	return 0;
72211da177e4SLinus Torvalds }
72221da177e4SLinus Torvalds #else
72231da177e4SLinus Torvalds #define ipr_invalid_adapter(ioa_cfg) 0
72241da177e4SLinus Torvalds #endif
72251da177e4SLinus Torvalds 
72261da177e4SLinus Torvalds /**
72271da177e4SLinus Torvalds  * ipr_ioa_bringdown_done - IOA bring down completion.
72281da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
72291da177e4SLinus Torvalds  *
72301da177e4SLinus Torvalds  * This function processes the completion of an adapter bring down.
72311da177e4SLinus Torvalds  * It wakes any reset sleepers.
72321da177e4SLinus Torvalds  *
72331da177e4SLinus Torvalds  * Return value:
72341da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
72351da177e4SLinus Torvalds  **/
72361da177e4SLinus Torvalds static int ipr_ioa_bringdown_done(struct ipr_cmnd *ipr_cmd)
72371da177e4SLinus Torvalds {
72381da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
723996b04db9Swenxiong@linux.vnet.ibm.com 	int i;
72401da177e4SLinus Torvalds 
72411da177e4SLinus Torvalds 	ENTER;
7242bfae7820SBrian King 	if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) {
7243bfae7820SBrian King 		ipr_trace;
7244b0e17a9bSBrian King 		ioa_cfg->scsi_unblock = 1;
7245b0e17a9bSBrian King 		schedule_work(&ioa_cfg->work_q);
7246bfae7820SBrian King 	}
7247bfae7820SBrian King 
72481da177e4SLinus Torvalds 	ioa_cfg->in_reset_reload = 0;
72491da177e4SLinus Torvalds 	ioa_cfg->reset_retries = 0;
725096b04db9Swenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
725196b04db9Swenxiong@linux.vnet.ibm.com 		spin_lock(&ioa_cfg->hrrq[i]._lock);
725296b04db9Swenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].ioa_is_dead = 1;
725396b04db9Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ioa_cfg->hrrq[i]._lock);
725496b04db9Swenxiong@linux.vnet.ibm.com 	}
725596b04db9Swenxiong@linux.vnet.ibm.com 	wmb();
725696b04db9Swenxiong@linux.vnet.ibm.com 
725705a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
72581da177e4SLinus Torvalds 	wake_up_all(&ioa_cfg->reset_wait_q);
72591da177e4SLinus Torvalds 	LEAVE;
72601da177e4SLinus Torvalds 
72611da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
72621da177e4SLinus Torvalds }
72631da177e4SLinus Torvalds 
72641da177e4SLinus Torvalds /**
72651da177e4SLinus Torvalds  * ipr_ioa_reset_done - IOA reset completion.
72661da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
72671da177e4SLinus Torvalds  *
72681da177e4SLinus Torvalds  * This function processes the completion of an adapter reset.
72691da177e4SLinus Torvalds  * It schedules any necessary mid-layer add/removes and
72701da177e4SLinus Torvalds  * wakes any reset sleepers.
72711da177e4SLinus Torvalds  *
72721da177e4SLinus Torvalds  * Return value:
72731da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
72741da177e4SLinus Torvalds  **/
72751da177e4SLinus Torvalds static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
72761da177e4SLinus Torvalds {
72771da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
72781da177e4SLinus Torvalds 	struct ipr_resource_entry *res;
7279afc3f83cSBrian King 	int j;
72801da177e4SLinus Torvalds 
72811da177e4SLinus Torvalds 	ENTER;
72821da177e4SLinus Torvalds 	ioa_cfg->in_reset_reload = 0;
728356d6aa33Swenxiong@linux.vnet.ibm.com 	for (j = 0; j < ioa_cfg->hrrq_num; j++) {
728456d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&ioa_cfg->hrrq[j]._lock);
728556d6aa33Swenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[j].allow_cmds = 1;
728656d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ioa_cfg->hrrq[j]._lock);
728756d6aa33Swenxiong@linux.vnet.ibm.com 	}
728856d6aa33Swenxiong@linux.vnet.ibm.com 	wmb();
72891da177e4SLinus Torvalds 	ioa_cfg->reset_cmd = NULL;
72903d1d0da6Sbrking@us.ibm.com 	ioa_cfg->doorbell |= IPR_RUNTIME_RESET;
72911da177e4SLinus Torvalds 
72921da177e4SLinus Torvalds 	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
7293f688f96dSBrian King 		if (res->add_to_ml || res->del_from_ml) {
72941da177e4SLinus Torvalds 			ipr_trace;
72951da177e4SLinus Torvalds 			break;
72961da177e4SLinus Torvalds 		}
72971da177e4SLinus Torvalds 	}
72981da177e4SLinus Torvalds 	schedule_work(&ioa_cfg->work_q);
72991da177e4SLinus Torvalds 
7300afc3f83cSBrian King 	for (j = 0; j < IPR_NUM_HCAMS; j++) {
7301afc3f83cSBrian King 		list_del_init(&ioa_cfg->hostrcb[j]->queue);
7302afc3f83cSBrian King 		if (j < IPR_NUM_LOG_HCAMS)
7303afc3f83cSBrian King 			ipr_send_hcam(ioa_cfg,
7304afc3f83cSBrian King 				IPR_HCAM_CDB_OP_CODE_LOG_DATA,
7305afc3f83cSBrian King 				ioa_cfg->hostrcb[j]);
73061da177e4SLinus Torvalds 		else
7307afc3f83cSBrian King 			ipr_send_hcam(ioa_cfg,
7308afc3f83cSBrian King 				IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE,
7309afc3f83cSBrian King 				ioa_cfg->hostrcb[j]);
73101da177e4SLinus Torvalds 	}
73111da177e4SLinus Torvalds 
73126bb04170SBrian King 	scsi_report_bus_reset(ioa_cfg->host, IPR_VSET_BUS);
73131da177e4SLinus Torvalds 	dev_info(&ioa_cfg->pdev->dev, "IOA initialized.\n");
73141da177e4SLinus Torvalds 
73151da177e4SLinus Torvalds 	ioa_cfg->reset_retries = 0;
731605a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
73171da177e4SLinus Torvalds 	wake_up_all(&ioa_cfg->reset_wait_q);
73181da177e4SLinus Torvalds 
7319b0e17a9bSBrian King 	ioa_cfg->scsi_unblock = 1;
7320f688f96dSBrian King 	schedule_work(&ioa_cfg->work_q);
73211da177e4SLinus Torvalds 	LEAVE;
73221da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
73231da177e4SLinus Torvalds }
73241da177e4SLinus Torvalds 
73251da177e4SLinus Torvalds /**
73261da177e4SLinus Torvalds  * ipr_set_sup_dev_dflt - Initialize a Set Supported Device buffer
73271da177e4SLinus Torvalds  * @supported_dev:	supported device struct
73281da177e4SLinus Torvalds  * @vpids:			vendor product id struct
73291da177e4SLinus Torvalds  *
73301da177e4SLinus Torvalds  * Return value:
73311da177e4SLinus Torvalds  * 	none
73321da177e4SLinus Torvalds  **/
73331da177e4SLinus Torvalds static void ipr_set_sup_dev_dflt(struct ipr_supported_device *supported_dev,
73341da177e4SLinus Torvalds 				 struct ipr_std_inq_vpids *vpids)
73351da177e4SLinus Torvalds {
73361da177e4SLinus Torvalds 	memset(supported_dev, 0, sizeof(struct ipr_supported_device));
73371da177e4SLinus Torvalds 	memcpy(&supported_dev->vpids, vpids, sizeof(struct ipr_std_inq_vpids));
73381da177e4SLinus Torvalds 	supported_dev->num_records = 1;
73391da177e4SLinus Torvalds 	supported_dev->data_length =
73401da177e4SLinus Torvalds 		cpu_to_be16(sizeof(struct ipr_supported_device));
73411da177e4SLinus Torvalds 	supported_dev->reserved = 0;
73421da177e4SLinus Torvalds }
73431da177e4SLinus Torvalds 
73441da177e4SLinus Torvalds /**
73451da177e4SLinus Torvalds  * ipr_set_supported_devs - Send Set Supported Devices for a device
73461da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
73471da177e4SLinus Torvalds  *
7348a32c055fSWayne Boyer  * This function sends a Set Supported Devices to the adapter
73491da177e4SLinus Torvalds  *
73501da177e4SLinus Torvalds  * Return value:
73511da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
73521da177e4SLinus Torvalds  **/
73531da177e4SLinus Torvalds static int ipr_set_supported_devs(struct ipr_cmnd *ipr_cmd)
73541da177e4SLinus Torvalds {
73551da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
73561da177e4SLinus Torvalds 	struct ipr_supported_device *supp_dev = &ioa_cfg->vpd_cbs->supp_dev;
73571da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
73581da177e4SLinus Torvalds 	struct ipr_resource_entry *res = ipr_cmd->u.res;
73591da177e4SLinus Torvalds 
73601da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_ioa_reset_done;
73611da177e4SLinus Torvalds 
73621da177e4SLinus Torvalds 	list_for_each_entry_continue(res, &ioa_cfg->used_res_q, queue) {
7363e4fbf44eSBrian King 		if (!ipr_is_scsi_disk(res))
73641da177e4SLinus Torvalds 			continue;
73651da177e4SLinus Torvalds 
73661da177e4SLinus Torvalds 		ipr_cmd->u.res = res;
73673e7ebdfaSWayne Boyer 		ipr_set_sup_dev_dflt(supp_dev, &res->std_inq_data.vpids);
73681da177e4SLinus Torvalds 
73691da177e4SLinus Torvalds 		ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
73701da177e4SLinus Torvalds 		ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
73711da177e4SLinus Torvalds 		ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
73721da177e4SLinus Torvalds 
73731da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[0] = IPR_SET_SUPPORTED_DEVICES;
73743e7ebdfaSWayne Boyer 		ioarcb->cmd_pkt.cdb[1] = IPR_SET_ALL_SUPPORTED_DEVICES;
73751da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[7] = (sizeof(struct ipr_supported_device) >> 8) & 0xff;
73761da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[8] = sizeof(struct ipr_supported_device) & 0xff;
73771da177e4SLinus Torvalds 
7378a32c055fSWayne Boyer 		ipr_init_ioadl(ipr_cmd,
7379a32c055fSWayne Boyer 			       ioa_cfg->vpd_cbs_dma +
7380a32c055fSWayne Boyer 				 offsetof(struct ipr_misc_cbs, supp_dev),
7381a32c055fSWayne Boyer 			       sizeof(struct ipr_supported_device),
7382a32c055fSWayne Boyer 			       IPR_IOADL_FLAGS_WRITE_LAST);
73831da177e4SLinus Torvalds 
73841da177e4SLinus Torvalds 		ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
73851da177e4SLinus Torvalds 			   IPR_SET_SUP_DEVICE_TIMEOUT);
73861da177e4SLinus Torvalds 
73873e7ebdfaSWayne Boyer 		if (!ioa_cfg->sis64)
73881da177e4SLinus Torvalds 			ipr_cmd->job_step = ipr_set_supported_devs;
738905a6538aSwenxiong@linux.vnet.ibm.com 		LEAVE;
73901da177e4SLinus Torvalds 		return IPR_RC_JOB_RETURN;
73911da177e4SLinus Torvalds 	}
73921da177e4SLinus Torvalds 
739305a6538aSwenxiong@linux.vnet.ibm.com 	LEAVE;
73941da177e4SLinus Torvalds 	return IPR_RC_JOB_CONTINUE;
73951da177e4SLinus Torvalds }
73961da177e4SLinus Torvalds 
73971da177e4SLinus Torvalds /**
73981da177e4SLinus Torvalds  * ipr_get_mode_page - Locate specified mode page
73991da177e4SLinus Torvalds  * @mode_pages:	mode page buffer
74001da177e4SLinus Torvalds  * @page_code:	page code to find
74011da177e4SLinus Torvalds  * @len:		minimum required length for mode page
74021da177e4SLinus Torvalds  *
74031da177e4SLinus Torvalds  * Return value:
74041da177e4SLinus Torvalds  * 	pointer to mode page / NULL on failure
74051da177e4SLinus Torvalds  **/
74061da177e4SLinus Torvalds static void *ipr_get_mode_page(struct ipr_mode_pages *mode_pages,
74071da177e4SLinus Torvalds 			       u32 page_code, u32 len)
74081da177e4SLinus Torvalds {
74091da177e4SLinus Torvalds 	struct ipr_mode_page_hdr *mode_hdr;
74101da177e4SLinus Torvalds 	u32 page_length;
74111da177e4SLinus Torvalds 	u32 length;
74121da177e4SLinus Torvalds 
74131da177e4SLinus Torvalds 	if (!mode_pages || (mode_pages->hdr.length == 0))
74141da177e4SLinus Torvalds 		return NULL;
74151da177e4SLinus Torvalds 
74161da177e4SLinus Torvalds 	length = (mode_pages->hdr.length + 1) - 4 - mode_pages->hdr.block_desc_len;
74171da177e4SLinus Torvalds 	mode_hdr = (struct ipr_mode_page_hdr *)
74181da177e4SLinus Torvalds 		(mode_pages->data + mode_pages->hdr.block_desc_len);
74191da177e4SLinus Torvalds 
74201da177e4SLinus Torvalds 	while (length) {
74211da177e4SLinus Torvalds 		if (IPR_GET_MODE_PAGE_CODE(mode_hdr) == page_code) {
74221da177e4SLinus Torvalds 			if (mode_hdr->page_length >= (len - sizeof(struct ipr_mode_page_hdr)))
74231da177e4SLinus Torvalds 				return mode_hdr;
74241da177e4SLinus Torvalds 			break;
74251da177e4SLinus Torvalds 		} else {
74261da177e4SLinus Torvalds 			page_length = (sizeof(struct ipr_mode_page_hdr) +
74271da177e4SLinus Torvalds 				       mode_hdr->page_length);
74281da177e4SLinus Torvalds 			length -= page_length;
74291da177e4SLinus Torvalds 			mode_hdr = (struct ipr_mode_page_hdr *)
74301da177e4SLinus Torvalds 				((unsigned long)mode_hdr + page_length);
74311da177e4SLinus Torvalds 		}
74321da177e4SLinus Torvalds 	}
74331da177e4SLinus Torvalds 	return NULL;
74341da177e4SLinus Torvalds }
74351da177e4SLinus Torvalds 
74361da177e4SLinus Torvalds /**
74371da177e4SLinus Torvalds  * ipr_check_term_power - Check for term power errors
74381da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
74391da177e4SLinus Torvalds  * @mode_pages:	IOAFP mode pages buffer
74401da177e4SLinus Torvalds  *
74411da177e4SLinus Torvalds  * Check the IOAFP's mode page 28 for term power errors
74421da177e4SLinus Torvalds  *
74431da177e4SLinus Torvalds  * Return value:
74441da177e4SLinus Torvalds  * 	nothing
74451da177e4SLinus Torvalds  **/
74461da177e4SLinus Torvalds static void ipr_check_term_power(struct ipr_ioa_cfg *ioa_cfg,
74471da177e4SLinus Torvalds 				 struct ipr_mode_pages *mode_pages)
74481da177e4SLinus Torvalds {
74491da177e4SLinus Torvalds 	int i;
74501da177e4SLinus Torvalds 	int entry_length;
74511da177e4SLinus Torvalds 	struct ipr_dev_bus_entry *bus;
74521da177e4SLinus Torvalds 	struct ipr_mode_page28 *mode_page;
74531da177e4SLinus Torvalds 
74541da177e4SLinus Torvalds 	mode_page = ipr_get_mode_page(mode_pages, 0x28,
74551da177e4SLinus Torvalds 				      sizeof(struct ipr_mode_page28));
74561da177e4SLinus Torvalds 
74571da177e4SLinus Torvalds 	entry_length = mode_page->entry_length;
74581da177e4SLinus Torvalds 
74591da177e4SLinus Torvalds 	bus = mode_page->bus;
74601da177e4SLinus Torvalds 
74611da177e4SLinus Torvalds 	for (i = 0; i < mode_page->num_entries; i++) {
74621da177e4SLinus Torvalds 		if (bus->flags & IPR_SCSI_ATTR_NO_TERM_PWR) {
74631da177e4SLinus Torvalds 			dev_err(&ioa_cfg->pdev->dev,
74641da177e4SLinus Torvalds 				"Term power is absent on scsi bus %d\n",
74651da177e4SLinus Torvalds 				bus->res_addr.bus);
74661da177e4SLinus Torvalds 		}
74671da177e4SLinus Torvalds 
74681da177e4SLinus Torvalds 		bus = (struct ipr_dev_bus_entry *)((char *)bus + entry_length);
74691da177e4SLinus Torvalds 	}
74701da177e4SLinus Torvalds }
74711da177e4SLinus Torvalds 
74721da177e4SLinus Torvalds /**
74731da177e4SLinus Torvalds  * ipr_scsi_bus_speed_limit - Limit the SCSI speed based on SES table
74741da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
74751da177e4SLinus Torvalds  *
74761da177e4SLinus Torvalds  * Looks through the config table checking for SES devices. If
74771da177e4SLinus Torvalds  * the SES device is in the SES table indicating a maximum SCSI
74781da177e4SLinus Torvalds  * bus speed, the speed is limited for the bus.
74791da177e4SLinus Torvalds  *
74801da177e4SLinus Torvalds  * Return value:
74811da177e4SLinus Torvalds  * 	none
74821da177e4SLinus Torvalds  **/
74831da177e4SLinus Torvalds static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg)
74841da177e4SLinus Torvalds {
74851da177e4SLinus Torvalds 	u32 max_xfer_rate;
74861da177e4SLinus Torvalds 	int i;
74871da177e4SLinus Torvalds 
74881da177e4SLinus Torvalds 	for (i = 0; i < IPR_MAX_NUM_BUSES; i++) {
74891da177e4SLinus Torvalds 		max_xfer_rate = ipr_get_max_scsi_speed(ioa_cfg, i,
74901da177e4SLinus Torvalds 						       ioa_cfg->bus_attr[i].bus_width);
74911da177e4SLinus Torvalds 
74921da177e4SLinus Torvalds 		if (max_xfer_rate < ioa_cfg->bus_attr[i].max_xfer_rate)
74931da177e4SLinus Torvalds 			ioa_cfg->bus_attr[i].max_xfer_rate = max_xfer_rate;
74941da177e4SLinus Torvalds 	}
74951da177e4SLinus Torvalds }
74961da177e4SLinus Torvalds 
74971da177e4SLinus Torvalds /**
74981da177e4SLinus Torvalds  * ipr_modify_ioafp_mode_page_28 - Modify IOAFP Mode Page 28
74991da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
75001da177e4SLinus Torvalds  * @mode_pages:	mode page 28 buffer
75011da177e4SLinus Torvalds  *
75021da177e4SLinus Torvalds  * Updates mode page 28 based on driver configuration
75031da177e4SLinus Torvalds  *
75041da177e4SLinus Torvalds  * Return value:
75051da177e4SLinus Torvalds  * 	none
75061da177e4SLinus Torvalds  **/
75071da177e4SLinus Torvalds static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg,
75081da177e4SLinus Torvalds 					  struct ipr_mode_pages *mode_pages)
75091da177e4SLinus Torvalds {
75101da177e4SLinus Torvalds 	int i, entry_length;
75111da177e4SLinus Torvalds 	struct ipr_dev_bus_entry *bus;
75121da177e4SLinus Torvalds 	struct ipr_bus_attributes *bus_attr;
75131da177e4SLinus Torvalds 	struct ipr_mode_page28 *mode_page;
75141da177e4SLinus Torvalds 
75151da177e4SLinus Torvalds 	mode_page = ipr_get_mode_page(mode_pages, 0x28,
75161da177e4SLinus Torvalds 				      sizeof(struct ipr_mode_page28));
75171da177e4SLinus Torvalds 
75181da177e4SLinus Torvalds 	entry_length = mode_page->entry_length;
75191da177e4SLinus Torvalds 
75201da177e4SLinus Torvalds 	/* Loop for each device bus entry */
75211da177e4SLinus Torvalds 	for (i = 0, bus = mode_page->bus;
75221da177e4SLinus Torvalds 	     i < mode_page->num_entries;
75231da177e4SLinus Torvalds 	     i++, bus = (struct ipr_dev_bus_entry *)((u8 *)bus + entry_length)) {
75241da177e4SLinus Torvalds 		if (bus->res_addr.bus > IPR_MAX_NUM_BUSES) {
75251da177e4SLinus Torvalds 			dev_err(&ioa_cfg->pdev->dev,
75261da177e4SLinus Torvalds 				"Invalid resource address reported: 0x%08X\n",
75271da177e4SLinus Torvalds 				IPR_GET_PHYS_LOC(bus->res_addr));
75281da177e4SLinus Torvalds 			continue;
75291da177e4SLinus Torvalds 		}
75301da177e4SLinus Torvalds 
75311da177e4SLinus Torvalds 		bus_attr = &ioa_cfg->bus_attr[i];
75321da177e4SLinus Torvalds 		bus->extended_reset_delay = IPR_EXTENDED_RESET_DELAY;
75331da177e4SLinus Torvalds 		bus->bus_width = bus_attr->bus_width;
75341da177e4SLinus Torvalds 		bus->max_xfer_rate = cpu_to_be32(bus_attr->max_xfer_rate);
75351da177e4SLinus Torvalds 		bus->flags &= ~IPR_SCSI_ATTR_QAS_MASK;
75361da177e4SLinus Torvalds 		if (bus_attr->qas_enabled)
75371da177e4SLinus Torvalds 			bus->flags |= IPR_SCSI_ATTR_ENABLE_QAS;
75381da177e4SLinus Torvalds 		else
75391da177e4SLinus Torvalds 			bus->flags |= IPR_SCSI_ATTR_DISABLE_QAS;
75401da177e4SLinus Torvalds 	}
75411da177e4SLinus Torvalds }
75421da177e4SLinus Torvalds 
75431da177e4SLinus Torvalds /**
75441da177e4SLinus Torvalds  * ipr_build_mode_select - Build a mode select command
75451da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
75461da177e4SLinus Torvalds  * @res_handle:	resource handle to send command to
75471da177e4SLinus Torvalds  * @parm:		Byte 2 of Mode Sense command
75481da177e4SLinus Torvalds  * @dma_addr:	DMA buffer address
75491da177e4SLinus Torvalds  * @xfer_len:	data transfer length
75501da177e4SLinus Torvalds  *
75511da177e4SLinus Torvalds  * Return value:
75521da177e4SLinus Torvalds  * 	none
75531da177e4SLinus Torvalds  **/
75541da177e4SLinus Torvalds static void ipr_build_mode_select(struct ipr_cmnd *ipr_cmd,
7555a32c055fSWayne Boyer 				  __be32 res_handle, u8 parm,
7556a32c055fSWayne Boyer 				  dma_addr_t dma_addr, u8 xfer_len)
75571da177e4SLinus Torvalds {
75581da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
75591da177e4SLinus Torvalds 
75601da177e4SLinus Torvalds 	ioarcb->res_handle = res_handle;
75611da177e4SLinus Torvalds 	ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
75621da177e4SLinus Torvalds 	ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
75631da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[0] = MODE_SELECT;
75641da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[1] = parm;
75651da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[4] = xfer_len;
75661da177e4SLinus Torvalds 
7567a32c055fSWayne Boyer 	ipr_init_ioadl(ipr_cmd, dma_addr, xfer_len, IPR_IOADL_FLAGS_WRITE_LAST);
75681da177e4SLinus Torvalds }
75691da177e4SLinus Torvalds 
75701da177e4SLinus Torvalds /**
75711da177e4SLinus Torvalds  * ipr_ioafp_mode_select_page28 - Issue Mode Select Page 28 to IOA
75721da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
75731da177e4SLinus Torvalds  *
75741da177e4SLinus Torvalds  * This function sets up the SCSI bus attributes and sends
75751da177e4SLinus Torvalds  * a Mode Select for Page 28 to activate them.
75761da177e4SLinus Torvalds  *
75771da177e4SLinus Torvalds  * Return value:
75781da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
75791da177e4SLinus Torvalds  **/
75801da177e4SLinus Torvalds static int ipr_ioafp_mode_select_page28(struct ipr_cmnd *ipr_cmd)
75811da177e4SLinus Torvalds {
75821da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
75831da177e4SLinus Torvalds 	struct ipr_mode_pages *mode_pages = &ioa_cfg->vpd_cbs->mode_pages;
75841da177e4SLinus Torvalds 	int length;
75851da177e4SLinus Torvalds 
75861da177e4SLinus Torvalds 	ENTER;
75871da177e4SLinus Torvalds 	ipr_scsi_bus_speed_limit(ioa_cfg);
75881da177e4SLinus Torvalds 	ipr_check_term_power(ioa_cfg, mode_pages);
75891da177e4SLinus Torvalds 	ipr_modify_ioafp_mode_page_28(ioa_cfg, mode_pages);
75901da177e4SLinus Torvalds 	length = mode_pages->hdr.length + 1;
75911da177e4SLinus Torvalds 	mode_pages->hdr.length = 0;
75921da177e4SLinus Torvalds 
75931da177e4SLinus Torvalds 	ipr_build_mode_select(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE), 0x11,
75941da177e4SLinus Torvalds 			      ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, mode_pages),
75951da177e4SLinus Torvalds 			      length);
75961da177e4SLinus Torvalds 
7597f72919ecSWayne Boyer 	ipr_cmd->job_step = ipr_set_supported_devs;
7598f72919ecSWayne Boyer 	ipr_cmd->u.res = list_entry(ioa_cfg->used_res_q.next,
7599f72919ecSWayne Boyer 				    struct ipr_resource_entry, queue);
76001da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
76011da177e4SLinus Torvalds 
76021da177e4SLinus Torvalds 	LEAVE;
76031da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
76041da177e4SLinus Torvalds }
76051da177e4SLinus Torvalds 
76061da177e4SLinus Torvalds /**
76071da177e4SLinus Torvalds  * ipr_build_mode_sense - Builds a mode sense command
76081da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
7609a96099e2SLee Jones  * @res_handle:		resource entry struct
76101da177e4SLinus Torvalds  * @parm:		Byte 2 of mode sense command
76111da177e4SLinus Torvalds  * @dma_addr:	DMA address of mode sense buffer
76121da177e4SLinus Torvalds  * @xfer_len:	Size of DMA buffer
76131da177e4SLinus Torvalds  *
76141da177e4SLinus Torvalds  * Return value:
76151da177e4SLinus Torvalds  * 	none
76161da177e4SLinus Torvalds  **/
76171da177e4SLinus Torvalds static void ipr_build_mode_sense(struct ipr_cmnd *ipr_cmd,
76181da177e4SLinus Torvalds 				 __be32 res_handle,
7619a32c055fSWayne Boyer 				 u8 parm, dma_addr_t dma_addr, u8 xfer_len)
76201da177e4SLinus Torvalds {
76211da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
76221da177e4SLinus Torvalds 
76231da177e4SLinus Torvalds 	ioarcb->res_handle = res_handle;
76241da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[0] = MODE_SENSE;
76251da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[2] = parm;
76261da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[4] = xfer_len;
76271da177e4SLinus Torvalds 	ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
76281da177e4SLinus Torvalds 
7629a32c055fSWayne Boyer 	ipr_init_ioadl(ipr_cmd, dma_addr, xfer_len, IPR_IOADL_FLAGS_READ_LAST);
76301da177e4SLinus Torvalds }
76311da177e4SLinus Torvalds 
76321da177e4SLinus Torvalds /**
7633dfed823eSbrking@us.ibm.com  * ipr_reset_cmd_failed - Handle failure of IOA reset command
7634dfed823eSbrking@us.ibm.com  * @ipr_cmd:	ipr command struct
7635dfed823eSbrking@us.ibm.com  *
7636dfed823eSbrking@us.ibm.com  * This function handles the failure of an IOA bringup command.
7637dfed823eSbrking@us.ibm.com  *
7638dfed823eSbrking@us.ibm.com  * Return value:
7639dfed823eSbrking@us.ibm.com  * 	IPR_RC_JOB_RETURN
7640dfed823eSbrking@us.ibm.com  **/
7641dfed823eSbrking@us.ibm.com static int ipr_reset_cmd_failed(struct ipr_cmnd *ipr_cmd)
7642dfed823eSbrking@us.ibm.com {
7643dfed823eSbrking@us.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
764496d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
7645dfed823eSbrking@us.ibm.com 
7646dfed823eSbrking@us.ibm.com 	dev_err(&ioa_cfg->pdev->dev,
7647dfed823eSbrking@us.ibm.com 		"0x%02X failed with IOASC: 0x%08X\n",
7648dfed823eSbrking@us.ibm.com 		ipr_cmd->ioarcb.cmd_pkt.cdb[0], ioasc);
7649dfed823eSbrking@us.ibm.com 
7650dfed823eSbrking@us.ibm.com 	ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
765105a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
7652dfed823eSbrking@us.ibm.com 	return IPR_RC_JOB_RETURN;
7653dfed823eSbrking@us.ibm.com }
7654dfed823eSbrking@us.ibm.com 
7655dfed823eSbrking@us.ibm.com /**
7656dfed823eSbrking@us.ibm.com  * ipr_reset_mode_sense_failed - Handle failure of IOAFP mode sense
7657dfed823eSbrking@us.ibm.com  * @ipr_cmd:	ipr command struct
7658dfed823eSbrking@us.ibm.com  *
7659dfed823eSbrking@us.ibm.com  * This function handles the failure of a Mode Sense to the IOAFP.
7660dfed823eSbrking@us.ibm.com  * Some adapters do not handle all mode pages.
7661dfed823eSbrking@us.ibm.com  *
7662dfed823eSbrking@us.ibm.com  * Return value:
7663dfed823eSbrking@us.ibm.com  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
7664dfed823eSbrking@us.ibm.com  **/
7665dfed823eSbrking@us.ibm.com static int ipr_reset_mode_sense_failed(struct ipr_cmnd *ipr_cmd)
7666dfed823eSbrking@us.ibm.com {
7667f72919ecSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
766896d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
7669dfed823eSbrking@us.ibm.com 
7670dfed823eSbrking@us.ibm.com 	if (ioasc == IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT) {
7671f72919ecSWayne Boyer 		ipr_cmd->job_step = ipr_set_supported_devs;
7672f72919ecSWayne Boyer 		ipr_cmd->u.res = list_entry(ioa_cfg->used_res_q.next,
7673f72919ecSWayne Boyer 					    struct ipr_resource_entry, queue);
7674dfed823eSbrking@us.ibm.com 		return IPR_RC_JOB_CONTINUE;
7675dfed823eSbrking@us.ibm.com 	}
7676dfed823eSbrking@us.ibm.com 
7677dfed823eSbrking@us.ibm.com 	return ipr_reset_cmd_failed(ipr_cmd);
7678dfed823eSbrking@us.ibm.com }
7679dfed823eSbrking@us.ibm.com 
7680dfed823eSbrking@us.ibm.com /**
76811da177e4SLinus Torvalds  * ipr_ioafp_mode_sense_page28 - Issue Mode Sense Page 28 to IOA
76821da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
76831da177e4SLinus Torvalds  *
76841da177e4SLinus Torvalds  * This function send a Page 28 mode sense to the IOA to
76851da177e4SLinus Torvalds  * retrieve SCSI bus attributes.
76861da177e4SLinus Torvalds  *
76871da177e4SLinus Torvalds  * Return value:
76881da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
76891da177e4SLinus Torvalds  **/
76901da177e4SLinus Torvalds static int ipr_ioafp_mode_sense_page28(struct ipr_cmnd *ipr_cmd)
76911da177e4SLinus Torvalds {
76921da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
76931da177e4SLinus Torvalds 
76941da177e4SLinus Torvalds 	ENTER;
76951da177e4SLinus Torvalds 	ipr_build_mode_sense(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE),
76961da177e4SLinus Torvalds 			     0x28, ioa_cfg->vpd_cbs_dma +
76971da177e4SLinus Torvalds 			     offsetof(struct ipr_misc_cbs, mode_pages),
76981da177e4SLinus Torvalds 			     sizeof(struct ipr_mode_pages));
76991da177e4SLinus Torvalds 
77001da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_ioafp_mode_select_page28;
7701dfed823eSbrking@us.ibm.com 	ipr_cmd->job_step_failed = ipr_reset_mode_sense_failed;
77021da177e4SLinus Torvalds 
77031da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
77041da177e4SLinus Torvalds 
77051da177e4SLinus Torvalds 	LEAVE;
77061da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
77071da177e4SLinus Torvalds }
77081da177e4SLinus Torvalds 
77091da177e4SLinus Torvalds /**
7710ac09c349SBrian King  * ipr_ioafp_mode_select_page24 - Issue Mode Select to IOA
7711ac09c349SBrian King  * @ipr_cmd:	ipr command struct
7712ac09c349SBrian King  *
7713ac09c349SBrian King  * This function enables dual IOA RAID support if possible.
7714ac09c349SBrian King  *
7715ac09c349SBrian King  * Return value:
7716ac09c349SBrian King  * 	IPR_RC_JOB_RETURN
7717ac09c349SBrian King  **/
7718ac09c349SBrian King static int ipr_ioafp_mode_select_page24(struct ipr_cmnd *ipr_cmd)
7719ac09c349SBrian King {
7720ac09c349SBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
7721ac09c349SBrian King 	struct ipr_mode_pages *mode_pages = &ioa_cfg->vpd_cbs->mode_pages;
7722ac09c349SBrian King 	struct ipr_mode_page24 *mode_page;
7723ac09c349SBrian King 	int length;
7724ac09c349SBrian King 
7725ac09c349SBrian King 	ENTER;
7726ac09c349SBrian King 	mode_page = ipr_get_mode_page(mode_pages, 0x24,
7727ac09c349SBrian King 				      sizeof(struct ipr_mode_page24));
7728ac09c349SBrian King 
7729ac09c349SBrian King 	if (mode_page)
7730ac09c349SBrian King 		mode_page->flags |= IPR_ENABLE_DUAL_IOA_AF;
7731ac09c349SBrian King 
7732ac09c349SBrian King 	length = mode_pages->hdr.length + 1;
7733ac09c349SBrian King 	mode_pages->hdr.length = 0;
7734ac09c349SBrian King 
7735ac09c349SBrian King 	ipr_build_mode_select(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE), 0x11,
7736ac09c349SBrian King 			      ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, mode_pages),
7737ac09c349SBrian King 			      length);
7738ac09c349SBrian King 
7739ac09c349SBrian King 	ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
7740ac09c349SBrian King 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
7741ac09c349SBrian King 
7742ac09c349SBrian King 	LEAVE;
7743ac09c349SBrian King 	return IPR_RC_JOB_RETURN;
7744ac09c349SBrian King }
7745ac09c349SBrian King 
7746ac09c349SBrian King /**
7747ac09c349SBrian King  * ipr_reset_mode_sense_page24_failed - Handle failure of IOAFP mode sense
7748ac09c349SBrian King  * @ipr_cmd:	ipr command struct
7749ac09c349SBrian King  *
7750ac09c349SBrian King  * This function handles the failure of a Mode Sense to the IOAFP.
7751ac09c349SBrian King  * Some adapters do not handle all mode pages.
7752ac09c349SBrian King  *
7753ac09c349SBrian King  * Return value:
7754ac09c349SBrian King  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
7755ac09c349SBrian King  **/
7756ac09c349SBrian King static int ipr_reset_mode_sense_page24_failed(struct ipr_cmnd *ipr_cmd)
7757ac09c349SBrian King {
775896d21f00SWayne Boyer 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
7759ac09c349SBrian King 
7760ac09c349SBrian King 	if (ioasc == IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT) {
7761ac09c349SBrian King 		ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
7762ac09c349SBrian King 		return IPR_RC_JOB_CONTINUE;
7763ac09c349SBrian King 	}
7764ac09c349SBrian King 
7765ac09c349SBrian King 	return ipr_reset_cmd_failed(ipr_cmd);
7766ac09c349SBrian King }
7767ac09c349SBrian King 
7768ac09c349SBrian King /**
7769ac09c349SBrian King  * ipr_ioafp_mode_sense_page24 - Issue Page 24 Mode Sense to IOA
7770ac09c349SBrian King  * @ipr_cmd:	ipr command struct
7771ac09c349SBrian King  *
7772ac09c349SBrian King  * This function send a mode sense to the IOA to retrieve
7773ac09c349SBrian King  * the IOA Advanced Function Control mode page.
7774ac09c349SBrian King  *
7775ac09c349SBrian King  * Return value:
7776ac09c349SBrian King  * 	IPR_RC_JOB_RETURN
7777ac09c349SBrian King  **/
7778ac09c349SBrian King static int ipr_ioafp_mode_sense_page24(struct ipr_cmnd *ipr_cmd)
7779ac09c349SBrian King {
7780ac09c349SBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
7781ac09c349SBrian King 
7782ac09c349SBrian King 	ENTER;
7783ac09c349SBrian King 	ipr_build_mode_sense(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE),
7784ac09c349SBrian King 			     0x24, ioa_cfg->vpd_cbs_dma +
7785ac09c349SBrian King 			     offsetof(struct ipr_misc_cbs, mode_pages),
7786ac09c349SBrian King 			     sizeof(struct ipr_mode_pages));
7787ac09c349SBrian King 
7788ac09c349SBrian King 	ipr_cmd->job_step = ipr_ioafp_mode_select_page24;
7789ac09c349SBrian King 	ipr_cmd->job_step_failed = ipr_reset_mode_sense_page24_failed;
7790ac09c349SBrian King 
7791ac09c349SBrian King 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
7792ac09c349SBrian King 
7793ac09c349SBrian King 	LEAVE;
7794ac09c349SBrian King 	return IPR_RC_JOB_RETURN;
7795ac09c349SBrian King }
7796ac09c349SBrian King 
7797ac09c349SBrian King /**
77981da177e4SLinus Torvalds  * ipr_init_res_table - Initialize the resource table
77991da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
78001da177e4SLinus Torvalds  *
78011da177e4SLinus Torvalds  * This function looks through the existing resource table, comparing
78021da177e4SLinus Torvalds  * it with the config table. This function will take care of old/new
78031da177e4SLinus Torvalds  * devices and schedule adding/removing them from the mid-layer
78041da177e4SLinus Torvalds  * as appropriate.
78051da177e4SLinus Torvalds  *
78061da177e4SLinus Torvalds  * Return value:
78071da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE
78081da177e4SLinus Torvalds  **/
78091da177e4SLinus Torvalds static int ipr_init_res_table(struct ipr_cmnd *ipr_cmd)
78101da177e4SLinus Torvalds {
78111da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
78121da177e4SLinus Torvalds 	struct ipr_resource_entry *res, *temp;
78133e7ebdfaSWayne Boyer 	struct ipr_config_table_entry_wrapper cfgtew;
78143e7ebdfaSWayne Boyer 	int entries, found, flag, i;
78151da177e4SLinus Torvalds 	LIST_HEAD(old_res);
78161da177e4SLinus Torvalds 
78171da177e4SLinus Torvalds 	ENTER;
78183e7ebdfaSWayne Boyer 	if (ioa_cfg->sis64)
78193e7ebdfaSWayne Boyer 		flag = ioa_cfg->u.cfg_table64->hdr64.flags;
78203e7ebdfaSWayne Boyer 	else
78213e7ebdfaSWayne Boyer 		flag = ioa_cfg->u.cfg_table->hdr.flags;
78223e7ebdfaSWayne Boyer 
78233e7ebdfaSWayne Boyer 	if (flag & IPR_UCODE_DOWNLOAD_REQ)
78241da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev, "Microcode download required\n");
78251da177e4SLinus Torvalds 
78261da177e4SLinus Torvalds 	list_for_each_entry_safe(res, temp, &ioa_cfg->used_res_q, queue)
78271da177e4SLinus Torvalds 		list_move_tail(&res->queue, &old_res);
78281da177e4SLinus Torvalds 
78293e7ebdfaSWayne Boyer 	if (ioa_cfg->sis64)
7830438b0331SWayne Boyer 		entries = be16_to_cpu(ioa_cfg->u.cfg_table64->hdr64.num_entries);
78313e7ebdfaSWayne Boyer 	else
78323e7ebdfaSWayne Boyer 		entries = ioa_cfg->u.cfg_table->hdr.num_entries;
78333e7ebdfaSWayne Boyer 
78343e7ebdfaSWayne Boyer 	for (i = 0; i < entries; i++) {
78353e7ebdfaSWayne Boyer 		if (ioa_cfg->sis64)
78363e7ebdfaSWayne Boyer 			cfgtew.u.cfgte64 = &ioa_cfg->u.cfg_table64->dev[i];
78373e7ebdfaSWayne Boyer 		else
78383e7ebdfaSWayne Boyer 			cfgtew.u.cfgte = &ioa_cfg->u.cfg_table->dev[i];
78391da177e4SLinus Torvalds 		found = 0;
78401da177e4SLinus Torvalds 
78411da177e4SLinus Torvalds 		list_for_each_entry_safe(res, temp, &old_res, queue) {
78423e7ebdfaSWayne Boyer 			if (ipr_is_same_device(res, &cfgtew)) {
78431da177e4SLinus Torvalds 				list_move_tail(&res->queue, &ioa_cfg->used_res_q);
78441da177e4SLinus Torvalds 				found = 1;
78451da177e4SLinus Torvalds 				break;
78461da177e4SLinus Torvalds 			}
78471da177e4SLinus Torvalds 		}
78481da177e4SLinus Torvalds 
78491da177e4SLinus Torvalds 		if (!found) {
78501da177e4SLinus Torvalds 			if (list_empty(&ioa_cfg->free_res_q)) {
78511da177e4SLinus Torvalds 				dev_err(&ioa_cfg->pdev->dev, "Too many devices attached\n");
78521da177e4SLinus Torvalds 				break;
78531da177e4SLinus Torvalds 			}
78541da177e4SLinus Torvalds 
78551da177e4SLinus Torvalds 			found = 1;
78561da177e4SLinus Torvalds 			res = list_entry(ioa_cfg->free_res_q.next,
78571da177e4SLinus Torvalds 					 struct ipr_resource_entry, queue);
78581da177e4SLinus Torvalds 			list_move_tail(&res->queue, &ioa_cfg->used_res_q);
78593e7ebdfaSWayne Boyer 			ipr_init_res_entry(res, &cfgtew);
78601da177e4SLinus Torvalds 			res->add_to_ml = 1;
786156115598SWayne Boyer 		} else if (res->sdev && (ipr_is_vset_device(res) || ipr_is_scsi_disk(res)))
786256115598SWayne Boyer 			res->sdev->allow_restart = 1;
78631da177e4SLinus Torvalds 
78641da177e4SLinus Torvalds 		if (found)
78653e7ebdfaSWayne Boyer 			ipr_update_res_entry(res, &cfgtew);
78661da177e4SLinus Torvalds 	}
78671da177e4SLinus Torvalds 
78681da177e4SLinus Torvalds 	list_for_each_entry_safe(res, temp, &old_res, queue) {
78691da177e4SLinus Torvalds 		if (res->sdev) {
78701da177e4SLinus Torvalds 			res->del_from_ml = 1;
78713e7ebdfaSWayne Boyer 			res->res_handle = IPR_INVALID_RES_HANDLE;
78721da177e4SLinus Torvalds 			list_move_tail(&res->queue, &ioa_cfg->used_res_q);
78731da177e4SLinus Torvalds 		}
78741da177e4SLinus Torvalds 	}
78751da177e4SLinus Torvalds 
78763e7ebdfaSWayne Boyer 	list_for_each_entry_safe(res, temp, &old_res, queue) {
78773e7ebdfaSWayne Boyer 		ipr_clear_res_target(res);
78783e7ebdfaSWayne Boyer 		list_move_tail(&res->queue, &ioa_cfg->free_res_q);
78793e7ebdfaSWayne Boyer 	}
78803e7ebdfaSWayne Boyer 
7881ac09c349SBrian King 	if (ioa_cfg->dual_raid && ipr_dual_ioa_raid)
7882ac09c349SBrian King 		ipr_cmd->job_step = ipr_ioafp_mode_sense_page24;
7883ac09c349SBrian King 	else
78841da177e4SLinus Torvalds 		ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
78851da177e4SLinus Torvalds 
78861da177e4SLinus Torvalds 	LEAVE;
78871da177e4SLinus Torvalds 	return IPR_RC_JOB_CONTINUE;
78881da177e4SLinus Torvalds }
78891da177e4SLinus Torvalds 
78901da177e4SLinus Torvalds /**
78911da177e4SLinus Torvalds  * ipr_ioafp_query_ioa_cfg - Send a Query IOA Config to the adapter.
78921da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
78931da177e4SLinus Torvalds  *
78941da177e4SLinus Torvalds  * This function sends a Query IOA Configuration command
78951da177e4SLinus Torvalds  * to the adapter to retrieve the IOA configuration table.
78961da177e4SLinus Torvalds  *
78971da177e4SLinus Torvalds  * Return value:
78981da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
78991da177e4SLinus Torvalds  **/
79001da177e4SLinus Torvalds static int ipr_ioafp_query_ioa_cfg(struct ipr_cmnd *ipr_cmd)
79011da177e4SLinus Torvalds {
79021da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
79031da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
79041da177e4SLinus Torvalds 	struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
7905ac09c349SBrian King 	struct ipr_inquiry_cap *cap = &ioa_cfg->vpd_cbs->cap;
79061da177e4SLinus Torvalds 
79071da177e4SLinus Torvalds 	ENTER;
7908ac09c349SBrian King 	if (cap->cap & IPR_CAP_DUAL_IOA_RAID)
7909ac09c349SBrian King 		ioa_cfg->dual_raid = 1;
79101da177e4SLinus Torvalds 	dev_info(&ioa_cfg->pdev->dev, "Adapter firmware version: %02X%02X%02X%02X\n",
79111da177e4SLinus Torvalds 		 ucode_vpd->major_release, ucode_vpd->card_type,
79121da177e4SLinus Torvalds 		 ucode_vpd->minor_release[0], ucode_vpd->minor_release[1]);
79131da177e4SLinus Torvalds 	ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
79141da177e4SLinus Torvalds 	ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
79151da177e4SLinus Torvalds 
79161da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[0] = IPR_QUERY_IOA_CONFIG;
7917438b0331SWayne Boyer 	ioarcb->cmd_pkt.cdb[6] = (ioa_cfg->cfg_table_size >> 16) & 0xff;
79183e7ebdfaSWayne Boyer 	ioarcb->cmd_pkt.cdb[7] = (ioa_cfg->cfg_table_size >> 8) & 0xff;
79193e7ebdfaSWayne Boyer 	ioarcb->cmd_pkt.cdb[8] = ioa_cfg->cfg_table_size & 0xff;
79201da177e4SLinus Torvalds 
79213e7ebdfaSWayne Boyer 	ipr_init_ioadl(ipr_cmd, ioa_cfg->cfg_table_dma, ioa_cfg->cfg_table_size,
7922a32c055fSWayne Boyer 		       IPR_IOADL_FLAGS_READ_LAST);
79231da177e4SLinus Torvalds 
79241da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_init_res_table;
79251da177e4SLinus Torvalds 
79261da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
79271da177e4SLinus Torvalds 
79281da177e4SLinus Torvalds 	LEAVE;
79291da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
79301da177e4SLinus Torvalds }
79311da177e4SLinus Torvalds 
79321a47af26SGabriel Krisman Bertazi static int ipr_ioa_service_action_failed(struct ipr_cmnd *ipr_cmd)
79331a47af26SGabriel Krisman Bertazi {
79341a47af26SGabriel Krisman Bertazi 	u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
79351a47af26SGabriel Krisman Bertazi 
79361a47af26SGabriel Krisman Bertazi 	if (ioasc == IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT)
79371a47af26SGabriel Krisman Bertazi 		return IPR_RC_JOB_CONTINUE;
79381a47af26SGabriel Krisman Bertazi 
79391a47af26SGabriel Krisman Bertazi 	return ipr_reset_cmd_failed(ipr_cmd);
79401a47af26SGabriel Krisman Bertazi }
79411a47af26SGabriel Krisman Bertazi 
79421a47af26SGabriel Krisman Bertazi static void ipr_build_ioa_service_action(struct ipr_cmnd *ipr_cmd,
79431a47af26SGabriel Krisman Bertazi 					 __be32 res_handle, u8 sa_code)
79441a47af26SGabriel Krisman Bertazi {
79451a47af26SGabriel Krisman Bertazi 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
79461a47af26SGabriel Krisman Bertazi 
79471a47af26SGabriel Krisman Bertazi 	ioarcb->res_handle = res_handle;
79481a47af26SGabriel Krisman Bertazi 	ioarcb->cmd_pkt.cdb[0] = IPR_IOA_SERVICE_ACTION;
79491a47af26SGabriel Krisman Bertazi 	ioarcb->cmd_pkt.cdb[1] = sa_code;
79501a47af26SGabriel Krisman Bertazi 	ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
79511a47af26SGabriel Krisman Bertazi }
79521a47af26SGabriel Krisman Bertazi 
79531a47af26SGabriel Krisman Bertazi /**
79541a47af26SGabriel Krisman Bertazi  * ipr_ioafp_set_caching_parameters - Issue Set Cache parameters service
79551a47af26SGabriel Krisman Bertazi  * action
7956a96099e2SLee Jones  * @ipr_cmd:	ipr command struct
79571a47af26SGabriel Krisman Bertazi  *
79581a47af26SGabriel Krisman Bertazi  * Return value:
79591a47af26SGabriel Krisman Bertazi  *	none
79601a47af26SGabriel Krisman Bertazi  **/
79611a47af26SGabriel Krisman Bertazi static int ipr_ioafp_set_caching_parameters(struct ipr_cmnd *ipr_cmd)
79621a47af26SGabriel Krisman Bertazi {
79631a47af26SGabriel Krisman Bertazi 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
79641a47af26SGabriel Krisman Bertazi 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
79651a47af26SGabriel Krisman Bertazi 	struct ipr_inquiry_pageC4 *pageC4 = &ioa_cfg->vpd_cbs->pageC4_data;
79661a47af26SGabriel Krisman Bertazi 
79671a47af26SGabriel Krisman Bertazi 	ENTER;
79681a47af26SGabriel Krisman Bertazi 
79691a47af26SGabriel Krisman Bertazi 	ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg;
79701a47af26SGabriel Krisman Bertazi 
79711a47af26SGabriel Krisman Bertazi 	if (pageC4->cache_cap[0] & IPR_CAP_SYNC_CACHE) {
79721a47af26SGabriel Krisman Bertazi 		ipr_build_ioa_service_action(ipr_cmd,
79731a47af26SGabriel Krisman Bertazi 					     cpu_to_be32(IPR_IOA_RES_HANDLE),
79741a47af26SGabriel Krisman Bertazi 					     IPR_IOA_SA_CHANGE_CACHE_PARAMS);
79751a47af26SGabriel Krisman Bertazi 
79761a47af26SGabriel Krisman Bertazi 		ioarcb->cmd_pkt.cdb[2] = 0x40;
79771a47af26SGabriel Krisman Bertazi 
79781a47af26SGabriel Krisman Bertazi 		ipr_cmd->job_step_failed = ipr_ioa_service_action_failed;
79791a47af26SGabriel Krisman Bertazi 		ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
79801a47af26SGabriel Krisman Bertazi 			   IPR_SET_SUP_DEVICE_TIMEOUT);
79811a47af26SGabriel Krisman Bertazi 
79821a47af26SGabriel Krisman Bertazi 		LEAVE;
79831a47af26SGabriel Krisman Bertazi 		return IPR_RC_JOB_RETURN;
79841a47af26SGabriel Krisman Bertazi 	}
79851a47af26SGabriel Krisman Bertazi 
79861a47af26SGabriel Krisman Bertazi 	LEAVE;
79871a47af26SGabriel Krisman Bertazi 	return IPR_RC_JOB_CONTINUE;
79881a47af26SGabriel Krisman Bertazi }
79891a47af26SGabriel Krisman Bertazi 
79901da177e4SLinus Torvalds /**
79911da177e4SLinus Torvalds  * ipr_ioafp_inquiry - Send an Inquiry to the adapter.
79921da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
7993a96099e2SLee Jones  * @flags:	flags to send
7994a96099e2SLee Jones  * @page:	page to inquire
7995a96099e2SLee Jones  * @dma_addr:	DMA address
7996a96099e2SLee Jones  * @xfer_len:	transfer data length
79971da177e4SLinus Torvalds  *
79981da177e4SLinus Torvalds  * This utility function sends an inquiry to the adapter.
79991da177e4SLinus Torvalds  *
80001da177e4SLinus Torvalds  * Return value:
80011da177e4SLinus Torvalds  * 	none
80021da177e4SLinus Torvalds  **/
80031da177e4SLinus Torvalds static void ipr_ioafp_inquiry(struct ipr_cmnd *ipr_cmd, u8 flags, u8 page,
8004a32c055fSWayne Boyer 			      dma_addr_t dma_addr, u8 xfer_len)
80051da177e4SLinus Torvalds {
80061da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
80071da177e4SLinus Torvalds 
80081da177e4SLinus Torvalds 	ENTER;
80091da177e4SLinus Torvalds 	ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
80101da177e4SLinus Torvalds 	ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
80111da177e4SLinus Torvalds 
80121da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[0] = INQUIRY;
80131da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[1] = flags;
80141da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[2] = page;
80151da177e4SLinus Torvalds 	ioarcb->cmd_pkt.cdb[4] = xfer_len;
80161da177e4SLinus Torvalds 
8017a32c055fSWayne Boyer 	ipr_init_ioadl(ipr_cmd, dma_addr, xfer_len, IPR_IOADL_FLAGS_READ_LAST);
80181da177e4SLinus Torvalds 
80191da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
80201da177e4SLinus Torvalds 	LEAVE;
80211da177e4SLinus Torvalds }
80221da177e4SLinus Torvalds 
80231da177e4SLinus Torvalds /**
802462275040Sbrking@us.ibm.com  * ipr_inquiry_page_supported - Is the given inquiry page supported
802562275040Sbrking@us.ibm.com  * @page0:		inquiry page 0 buffer
802662275040Sbrking@us.ibm.com  * @page:		page code.
802762275040Sbrking@us.ibm.com  *
802862275040Sbrking@us.ibm.com  * This function determines if the specified inquiry page is supported.
802962275040Sbrking@us.ibm.com  *
803062275040Sbrking@us.ibm.com  * Return value:
803162275040Sbrking@us.ibm.com  *	1 if page is supported / 0 if not
803262275040Sbrking@us.ibm.com  **/
803362275040Sbrking@us.ibm.com static int ipr_inquiry_page_supported(struct ipr_inquiry_page0 *page0, u8 page)
803462275040Sbrking@us.ibm.com {
803562275040Sbrking@us.ibm.com 	int i;
803662275040Sbrking@us.ibm.com 
803762275040Sbrking@us.ibm.com 	for (i = 0; i < min_t(u8, page0->len, IPR_INQUIRY_PAGE0_ENTRIES); i++)
803862275040Sbrking@us.ibm.com 		if (page0->page[i] == page)
803962275040Sbrking@us.ibm.com 			return 1;
804062275040Sbrking@us.ibm.com 
804162275040Sbrking@us.ibm.com 	return 0;
804262275040Sbrking@us.ibm.com }
804362275040Sbrking@us.ibm.com 
804462275040Sbrking@us.ibm.com /**
80451021b3ffSGabriel Krisman Bertazi  * ipr_ioafp_pageC4_inquiry - Send a Page 0xC4 Inquiry to the adapter.
80461021b3ffSGabriel Krisman Bertazi  * @ipr_cmd:	ipr command struct
80471021b3ffSGabriel Krisman Bertazi  *
80481021b3ffSGabriel Krisman Bertazi  * This function sends a Page 0xC4 inquiry to the adapter
80491021b3ffSGabriel Krisman Bertazi  * to retrieve software VPD information.
80501021b3ffSGabriel Krisman Bertazi  *
80511021b3ffSGabriel Krisman Bertazi  * Return value:
80521021b3ffSGabriel Krisman Bertazi  *	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
80531021b3ffSGabriel Krisman Bertazi  **/
80541021b3ffSGabriel Krisman Bertazi static int ipr_ioafp_pageC4_inquiry(struct ipr_cmnd *ipr_cmd)
80551021b3ffSGabriel Krisman Bertazi {
80561021b3ffSGabriel Krisman Bertazi 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
80571021b3ffSGabriel Krisman Bertazi 	struct ipr_inquiry_page0 *page0 = &ioa_cfg->vpd_cbs->page0_data;
80581021b3ffSGabriel Krisman Bertazi 	struct ipr_inquiry_pageC4 *pageC4 = &ioa_cfg->vpd_cbs->pageC4_data;
80591021b3ffSGabriel Krisman Bertazi 
80601021b3ffSGabriel Krisman Bertazi 	ENTER;
80611a47af26SGabriel Krisman Bertazi 	ipr_cmd->job_step = ipr_ioafp_set_caching_parameters;
80621021b3ffSGabriel Krisman Bertazi 	memset(pageC4, 0, sizeof(*pageC4));
80631021b3ffSGabriel Krisman Bertazi 
80641021b3ffSGabriel Krisman Bertazi 	if (ipr_inquiry_page_supported(page0, 0xC4)) {
80651021b3ffSGabriel Krisman Bertazi 		ipr_ioafp_inquiry(ipr_cmd, 1, 0xC4,
80661021b3ffSGabriel Krisman Bertazi 				  (ioa_cfg->vpd_cbs_dma
80671021b3ffSGabriel Krisman Bertazi 				   + offsetof(struct ipr_misc_cbs,
80681021b3ffSGabriel Krisman Bertazi 					      pageC4_data)),
80691021b3ffSGabriel Krisman Bertazi 				  sizeof(struct ipr_inquiry_pageC4));
80701021b3ffSGabriel Krisman Bertazi 		return IPR_RC_JOB_RETURN;
80711021b3ffSGabriel Krisman Bertazi 	}
80721021b3ffSGabriel Krisman Bertazi 
80731021b3ffSGabriel Krisman Bertazi 	LEAVE;
80741021b3ffSGabriel Krisman Bertazi 	return IPR_RC_JOB_CONTINUE;
80751021b3ffSGabriel Krisman Bertazi }
80761021b3ffSGabriel Krisman Bertazi 
80771021b3ffSGabriel Krisman Bertazi /**
8078ac09c349SBrian King  * ipr_ioafp_cap_inquiry - Send a Page 0xD0 Inquiry to the adapter.
8079ac09c349SBrian King  * @ipr_cmd:	ipr command struct
8080ac09c349SBrian King  *
8081ac09c349SBrian King  * This function sends a Page 0xD0 inquiry to the adapter
8082ac09c349SBrian King  * to retrieve adapter capabilities.
8083ac09c349SBrian King  *
8084ac09c349SBrian King  * Return value:
8085ac09c349SBrian King  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
8086ac09c349SBrian King  **/
8087ac09c349SBrian King static int ipr_ioafp_cap_inquiry(struct ipr_cmnd *ipr_cmd)
8088ac09c349SBrian King {
8089ac09c349SBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8090ac09c349SBrian King 	struct ipr_inquiry_page0 *page0 = &ioa_cfg->vpd_cbs->page0_data;
8091ac09c349SBrian King 	struct ipr_inquiry_cap *cap = &ioa_cfg->vpd_cbs->cap;
8092ac09c349SBrian King 
8093ac09c349SBrian King 	ENTER;
80941021b3ffSGabriel Krisman Bertazi 	ipr_cmd->job_step = ipr_ioafp_pageC4_inquiry;
8095ac09c349SBrian King 	memset(cap, 0, sizeof(*cap));
8096ac09c349SBrian King 
8097ac09c349SBrian King 	if (ipr_inquiry_page_supported(page0, 0xD0)) {
8098ac09c349SBrian King 		ipr_ioafp_inquiry(ipr_cmd, 1, 0xD0,
8099ac09c349SBrian King 				  ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, cap),
8100ac09c349SBrian King 				  sizeof(struct ipr_inquiry_cap));
8101ac09c349SBrian King 		return IPR_RC_JOB_RETURN;
8102ac09c349SBrian King 	}
8103ac09c349SBrian King 
8104ac09c349SBrian King 	LEAVE;
8105ac09c349SBrian King 	return IPR_RC_JOB_CONTINUE;
8106ac09c349SBrian King }
8107ac09c349SBrian King 
8108ac09c349SBrian King /**
81091da177e4SLinus Torvalds  * ipr_ioafp_page3_inquiry - Send a Page 3 Inquiry to the adapter.
81101da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
81111da177e4SLinus Torvalds  *
81121da177e4SLinus Torvalds  * This function sends a Page 3 inquiry to the adapter
81131da177e4SLinus Torvalds  * to retrieve software VPD information.
81141da177e4SLinus Torvalds  *
81151da177e4SLinus Torvalds  * Return value:
81161da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
81171da177e4SLinus Torvalds  **/
81181da177e4SLinus Torvalds static int ipr_ioafp_page3_inquiry(struct ipr_cmnd *ipr_cmd)
81191da177e4SLinus Torvalds {
81201da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
812162275040Sbrking@us.ibm.com 
812262275040Sbrking@us.ibm.com 	ENTER;
812362275040Sbrking@us.ibm.com 
8124ac09c349SBrian King 	ipr_cmd->job_step = ipr_ioafp_cap_inquiry;
812562275040Sbrking@us.ibm.com 
812662275040Sbrking@us.ibm.com 	ipr_ioafp_inquiry(ipr_cmd, 1, 3,
812762275040Sbrking@us.ibm.com 			  ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, page3_data),
812862275040Sbrking@us.ibm.com 			  sizeof(struct ipr_inquiry_page3));
812962275040Sbrking@us.ibm.com 
813062275040Sbrking@us.ibm.com 	LEAVE;
813162275040Sbrking@us.ibm.com 	return IPR_RC_JOB_RETURN;
813262275040Sbrking@us.ibm.com }
813362275040Sbrking@us.ibm.com 
813462275040Sbrking@us.ibm.com /**
813562275040Sbrking@us.ibm.com  * ipr_ioafp_page0_inquiry - Send a Page 0 Inquiry to the adapter.
813662275040Sbrking@us.ibm.com  * @ipr_cmd:	ipr command struct
813762275040Sbrking@us.ibm.com  *
813862275040Sbrking@us.ibm.com  * This function sends a Page 0 inquiry to the adapter
813962275040Sbrking@us.ibm.com  * to retrieve supported inquiry pages.
814062275040Sbrking@us.ibm.com  *
814162275040Sbrking@us.ibm.com  * Return value:
814262275040Sbrking@us.ibm.com  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
814362275040Sbrking@us.ibm.com  **/
814462275040Sbrking@us.ibm.com static int ipr_ioafp_page0_inquiry(struct ipr_cmnd *ipr_cmd)
814562275040Sbrking@us.ibm.com {
814662275040Sbrking@us.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
81471da177e4SLinus Torvalds 	char type[5];
81481da177e4SLinus Torvalds 
81491da177e4SLinus Torvalds 	ENTER;
81501da177e4SLinus Torvalds 
81511da177e4SLinus Torvalds 	/* Grab the type out of the VPD and store it away */
81521da177e4SLinus Torvalds 	memcpy(type, ioa_cfg->vpd_cbs->ioa_vpd.std_inq_data.vpids.product_id, 4);
81531da177e4SLinus Torvalds 	type[4] = '\0';
81541da177e4SLinus Torvalds 	ioa_cfg->type = simple_strtoul((char *)type, NULL, 16);
81551da177e4SLinus Torvalds 
8156f688f96dSBrian King 	if (ipr_invalid_adapter(ioa_cfg)) {
8157f688f96dSBrian King 		dev_err(&ioa_cfg->pdev->dev,
8158f688f96dSBrian King 			"Adapter not supported in this hardware configuration.\n");
8159f688f96dSBrian King 
8160f688f96dSBrian King 		if (!ipr_testmode) {
8161f688f96dSBrian King 			ioa_cfg->reset_retries += IPR_NUM_RESET_RELOAD_RETRIES;
8162f688f96dSBrian King 			ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
8163f688f96dSBrian King 			list_add_tail(&ipr_cmd->queue,
8164f688f96dSBrian King 					&ioa_cfg->hrrq->hrrq_free_q);
8165f688f96dSBrian King 			return IPR_RC_JOB_RETURN;
8166f688f96dSBrian King 		}
8167f688f96dSBrian King 	}
8168f688f96dSBrian King 
816962275040Sbrking@us.ibm.com 	ipr_cmd->job_step = ipr_ioafp_page3_inquiry;
81701da177e4SLinus Torvalds 
817162275040Sbrking@us.ibm.com 	ipr_ioafp_inquiry(ipr_cmd, 1, 0,
817262275040Sbrking@us.ibm.com 			  ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, page0_data),
817362275040Sbrking@us.ibm.com 			  sizeof(struct ipr_inquiry_page0));
81741da177e4SLinus Torvalds 
81751da177e4SLinus Torvalds 	LEAVE;
81761da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
81771da177e4SLinus Torvalds }
81781da177e4SLinus Torvalds 
81791da177e4SLinus Torvalds /**
81801da177e4SLinus Torvalds  * ipr_ioafp_std_inquiry - Send a Standard Inquiry to the adapter.
81811da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
81821da177e4SLinus Torvalds  *
81831da177e4SLinus Torvalds  * This function sends a standard inquiry to the adapter.
81841da177e4SLinus Torvalds  *
81851da177e4SLinus Torvalds  * Return value:
81861da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
81871da177e4SLinus Torvalds  **/
81881da177e4SLinus Torvalds static int ipr_ioafp_std_inquiry(struct ipr_cmnd *ipr_cmd)
81891da177e4SLinus Torvalds {
81901da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
81911da177e4SLinus Torvalds 
81921da177e4SLinus Torvalds 	ENTER;
819362275040Sbrking@us.ibm.com 	ipr_cmd->job_step = ipr_ioafp_page0_inquiry;
81941da177e4SLinus Torvalds 
81951da177e4SLinus Torvalds 	ipr_ioafp_inquiry(ipr_cmd, 0, 0,
81961da177e4SLinus Torvalds 			  ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, ioa_vpd),
81971da177e4SLinus Torvalds 			  sizeof(struct ipr_ioa_vpd));
81981da177e4SLinus Torvalds 
81991da177e4SLinus Torvalds 	LEAVE;
82001da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
82011da177e4SLinus Torvalds }
82021da177e4SLinus Torvalds 
82031da177e4SLinus Torvalds /**
8204214777baSWayne Boyer  * ipr_ioafp_identify_hrrq - Send Identify Host RRQ.
82051da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
82061da177e4SLinus Torvalds  *
82071da177e4SLinus Torvalds  * This function send an Identify Host Request Response Queue
82081da177e4SLinus Torvalds  * command to establish the HRRQ with the adapter.
82091da177e4SLinus Torvalds  *
82101da177e4SLinus Torvalds  * Return value:
82111da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
82121da177e4SLinus Torvalds  **/
8213214777baSWayne Boyer static int ipr_ioafp_identify_hrrq(struct ipr_cmnd *ipr_cmd)
82141da177e4SLinus Torvalds {
82151da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
82161da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
821705a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
82181da177e4SLinus Torvalds 
82191da177e4SLinus Torvalds 	ENTER;
822005a6538aSwenxiong@linux.vnet.ibm.com 	ipr_cmd->job_step = ipr_ioafp_std_inquiry;
822187adbe08SBrian King 	if (ioa_cfg->identify_hrrq_index == 0)
82221da177e4SLinus Torvalds 		dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n");
82231da177e4SLinus Torvalds 
822456d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->identify_hrrq_index < ioa_cfg->hrrq_num) {
822556d6aa33Swenxiong@linux.vnet.ibm.com 		hrrq = &ioa_cfg->hrrq[ioa_cfg->identify_hrrq_index];
822605a6538aSwenxiong@linux.vnet.ibm.com 
82271da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[0] = IPR_ID_HOST_RR_Q;
82281da177e4SLinus Torvalds 		ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
82291da177e4SLinus Torvalds 
82301da177e4SLinus Torvalds 		ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
8231214777baSWayne Boyer 		if (ioa_cfg->sis64)
8232214777baSWayne Boyer 			ioarcb->cmd_pkt.cdb[1] = 0x1;
823305a6538aSwenxiong@linux.vnet.ibm.com 
823405a6538aSwenxiong@linux.vnet.ibm.com 		if (ioa_cfg->nvectors == 1)
823505a6538aSwenxiong@linux.vnet.ibm.com 			ioarcb->cmd_pkt.cdb[1] &= ~IPR_ID_HRRQ_SELE_ENABLE;
823605a6538aSwenxiong@linux.vnet.ibm.com 		else
823705a6538aSwenxiong@linux.vnet.ibm.com 			ioarcb->cmd_pkt.cdb[1] |= IPR_ID_HRRQ_SELE_ENABLE;
823805a6538aSwenxiong@linux.vnet.ibm.com 
82391da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[2] =
824005a6538aSwenxiong@linux.vnet.ibm.com 			((u64) hrrq->host_rrq_dma >> 24) & 0xff;
82411da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[3] =
824205a6538aSwenxiong@linux.vnet.ibm.com 			((u64) hrrq->host_rrq_dma >> 16) & 0xff;
82431da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[4] =
824405a6538aSwenxiong@linux.vnet.ibm.com 			((u64) hrrq->host_rrq_dma >> 8) & 0xff;
82451da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[5] =
824605a6538aSwenxiong@linux.vnet.ibm.com 			((u64) hrrq->host_rrq_dma) & 0xff;
82471da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[7] =
824805a6538aSwenxiong@linux.vnet.ibm.com 			((sizeof(u32) * hrrq->size) >> 8) & 0xff;
82491da177e4SLinus Torvalds 		ioarcb->cmd_pkt.cdb[8] =
825005a6538aSwenxiong@linux.vnet.ibm.com 			(sizeof(u32) * hrrq->size) & 0xff;
825105a6538aSwenxiong@linux.vnet.ibm.com 
825205a6538aSwenxiong@linux.vnet.ibm.com 		if (ioarcb->cmd_pkt.cdb[1] & IPR_ID_HRRQ_SELE_ENABLE)
825356d6aa33Swenxiong@linux.vnet.ibm.com 			ioarcb->cmd_pkt.cdb[9] =
825456d6aa33Swenxiong@linux.vnet.ibm.com 					ioa_cfg->identify_hrrq_index;
82551da177e4SLinus Torvalds 
8256214777baSWayne Boyer 		if (ioa_cfg->sis64) {
8257214777baSWayne Boyer 			ioarcb->cmd_pkt.cdb[10] =
825805a6538aSwenxiong@linux.vnet.ibm.com 				((u64) hrrq->host_rrq_dma >> 56) & 0xff;
8259214777baSWayne Boyer 			ioarcb->cmd_pkt.cdb[11] =
826005a6538aSwenxiong@linux.vnet.ibm.com 				((u64) hrrq->host_rrq_dma >> 48) & 0xff;
8261214777baSWayne Boyer 			ioarcb->cmd_pkt.cdb[12] =
826205a6538aSwenxiong@linux.vnet.ibm.com 				((u64) hrrq->host_rrq_dma >> 40) & 0xff;
8263214777baSWayne Boyer 			ioarcb->cmd_pkt.cdb[13] =
826405a6538aSwenxiong@linux.vnet.ibm.com 				((u64) hrrq->host_rrq_dma >> 32) & 0xff;
8265214777baSWayne Boyer 		}
8266214777baSWayne Boyer 
826705a6538aSwenxiong@linux.vnet.ibm.com 		if (ioarcb->cmd_pkt.cdb[1] & IPR_ID_HRRQ_SELE_ENABLE)
826856d6aa33Swenxiong@linux.vnet.ibm.com 			ioarcb->cmd_pkt.cdb[14] =
826956d6aa33Swenxiong@linux.vnet.ibm.com 					ioa_cfg->identify_hrrq_index;
82701da177e4SLinus Torvalds 
827105a6538aSwenxiong@linux.vnet.ibm.com 		ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
827205a6538aSwenxiong@linux.vnet.ibm.com 			   IPR_INTERNAL_TIMEOUT);
827305a6538aSwenxiong@linux.vnet.ibm.com 
827456d6aa33Swenxiong@linux.vnet.ibm.com 		if (++ioa_cfg->identify_hrrq_index < ioa_cfg->hrrq_num)
827505a6538aSwenxiong@linux.vnet.ibm.com 			ipr_cmd->job_step = ipr_ioafp_identify_hrrq;
82761da177e4SLinus Torvalds 
82771da177e4SLinus Torvalds 		LEAVE;
82781da177e4SLinus Torvalds 		return IPR_RC_JOB_RETURN;
827905a6538aSwenxiong@linux.vnet.ibm.com 	}
828005a6538aSwenxiong@linux.vnet.ibm.com 
828105a6538aSwenxiong@linux.vnet.ibm.com 	LEAVE;
828205a6538aSwenxiong@linux.vnet.ibm.com 	return IPR_RC_JOB_CONTINUE;
82831da177e4SLinus Torvalds }
82841da177e4SLinus Torvalds 
82851da177e4SLinus Torvalds /**
82861da177e4SLinus Torvalds  * ipr_reset_timer_done - Adapter reset timer function
8287a96099e2SLee Jones  * @t: Timer context used to fetch ipr command struct
82881da177e4SLinus Torvalds  *
82891da177e4SLinus Torvalds  * Description: This function is used in adapter reset processing
82901da177e4SLinus Torvalds  * for timing events. If the reset_cmd pointer in the IOA
82911da177e4SLinus Torvalds  * config struct is not this adapter's we are doing nested
82921da177e4SLinus Torvalds  * resets and fail_all_ops will take care of freeing the
82931da177e4SLinus Torvalds  * command block.
82941da177e4SLinus Torvalds  *
82951da177e4SLinus Torvalds  * Return value:
82961da177e4SLinus Torvalds  * 	none
82971da177e4SLinus Torvalds  **/
8298738c6ec5SKees Cook static void ipr_reset_timer_done(struct timer_list *t)
82991da177e4SLinus Torvalds {
8300738c6ec5SKees Cook 	struct ipr_cmnd *ipr_cmd = from_timer(ipr_cmd, t, timer);
83011da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
83021da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
83031da177e4SLinus Torvalds 
83041da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
83051da177e4SLinus Torvalds 
83061da177e4SLinus Torvalds 	if (ioa_cfg->reset_cmd == ipr_cmd) {
83071da177e4SLinus Torvalds 		list_del(&ipr_cmd->queue);
83081da177e4SLinus Torvalds 		ipr_cmd->done(ipr_cmd);
83091da177e4SLinus Torvalds 	}
83101da177e4SLinus Torvalds 
83111da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
83121da177e4SLinus Torvalds }
83131da177e4SLinus Torvalds 
83141da177e4SLinus Torvalds /**
83151da177e4SLinus Torvalds  * ipr_reset_start_timer - Start a timer for adapter reset job
83161da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
83171da177e4SLinus Torvalds  * @timeout:	timeout value
83181da177e4SLinus Torvalds  *
83191da177e4SLinus Torvalds  * Description: This function is used in adapter reset processing
83201da177e4SLinus Torvalds  * for timing events. If the reset_cmd pointer in the IOA
83211da177e4SLinus Torvalds  * config struct is not this adapter's we are doing nested
83221da177e4SLinus Torvalds  * resets and fail_all_ops will take care of freeing the
83231da177e4SLinus Torvalds  * command block.
83241da177e4SLinus Torvalds  *
83251da177e4SLinus Torvalds  * Return value:
83261da177e4SLinus Torvalds  * 	none
83271da177e4SLinus Torvalds  **/
83281da177e4SLinus Torvalds static void ipr_reset_start_timer(struct ipr_cmnd *ipr_cmd,
83291da177e4SLinus Torvalds 				  unsigned long timeout)
83301da177e4SLinus Torvalds {
833105a6538aSwenxiong@linux.vnet.ibm.com 
833205a6538aSwenxiong@linux.vnet.ibm.com 	ENTER;
833305a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
83341da177e4SLinus Torvalds 	ipr_cmd->done = ipr_reset_ioa_job;
83351da177e4SLinus Torvalds 
83361da177e4SLinus Torvalds 	ipr_cmd->timer.expires = jiffies + timeout;
8337841b86f3SKees Cook 	ipr_cmd->timer.function = ipr_reset_timer_done;
83381da177e4SLinus Torvalds 	add_timer(&ipr_cmd->timer);
83391da177e4SLinus Torvalds }
83401da177e4SLinus Torvalds 
83411da177e4SLinus Torvalds /**
83421da177e4SLinus Torvalds  * ipr_init_ioa_mem - Initialize ioa_cfg control block
83431da177e4SLinus Torvalds  * @ioa_cfg:	ioa cfg struct
83441da177e4SLinus Torvalds  *
83451da177e4SLinus Torvalds  * Return value:
83461da177e4SLinus Torvalds  * 	nothing
83471da177e4SLinus Torvalds  **/
83481da177e4SLinus Torvalds static void ipr_init_ioa_mem(struct ipr_ioa_cfg *ioa_cfg)
83491da177e4SLinus Torvalds {
835005a6538aSwenxiong@linux.vnet.ibm.com 	struct ipr_hrr_queue *hrrq;
835105a6538aSwenxiong@linux.vnet.ibm.com 
835205a6538aSwenxiong@linux.vnet.ibm.com 	for_each_hrrq(hrrq, ioa_cfg) {
835356d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&hrrq->_lock);
835405a6538aSwenxiong@linux.vnet.ibm.com 		memset(hrrq->host_rrq, 0, sizeof(u32) * hrrq->size);
83551da177e4SLinus Torvalds 
83561da177e4SLinus Torvalds 		/* Initialize Host RRQ pointers */
835705a6538aSwenxiong@linux.vnet.ibm.com 		hrrq->hrrq_start = hrrq->host_rrq;
835805a6538aSwenxiong@linux.vnet.ibm.com 		hrrq->hrrq_end = &hrrq->host_rrq[hrrq->size - 1];
835905a6538aSwenxiong@linux.vnet.ibm.com 		hrrq->hrrq_curr = hrrq->hrrq_start;
836005a6538aSwenxiong@linux.vnet.ibm.com 		hrrq->toggle_bit = 1;
836156d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&hrrq->_lock);
836205a6538aSwenxiong@linux.vnet.ibm.com 	}
836356d6aa33Swenxiong@linux.vnet.ibm.com 	wmb();
836405a6538aSwenxiong@linux.vnet.ibm.com 
836556d6aa33Swenxiong@linux.vnet.ibm.com 	ioa_cfg->identify_hrrq_index = 0;
836656d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq_num == 1)
836756d6aa33Swenxiong@linux.vnet.ibm.com 		atomic_set(&ioa_cfg->hrrq_index, 0);
836856d6aa33Swenxiong@linux.vnet.ibm.com 	else
836956d6aa33Swenxiong@linux.vnet.ibm.com 		atomic_set(&ioa_cfg->hrrq_index, 1);
83701da177e4SLinus Torvalds 
83711da177e4SLinus Torvalds 	/* Zero out config table */
83723e7ebdfaSWayne Boyer 	memset(ioa_cfg->u.cfg_table, 0, ioa_cfg->cfg_table_size);
83731da177e4SLinus Torvalds }
83741da177e4SLinus Torvalds 
83751da177e4SLinus Torvalds /**
8376214777baSWayne Boyer  * ipr_reset_next_stage - Process IPL stage change based on feedback register.
8377214777baSWayne Boyer  * @ipr_cmd:	ipr command struct
8378214777baSWayne Boyer  *
8379214777baSWayne Boyer  * Return value:
8380214777baSWayne Boyer  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
8381214777baSWayne Boyer  **/
8382214777baSWayne Boyer static int ipr_reset_next_stage(struct ipr_cmnd *ipr_cmd)
8383214777baSWayne Boyer {
8384214777baSWayne Boyer 	unsigned long stage, stage_time;
8385214777baSWayne Boyer 	u32 feedback;
8386214777baSWayne Boyer 	volatile u32 int_reg;
8387214777baSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8388214777baSWayne Boyer 	u64 maskval = 0;
8389214777baSWayne Boyer 
8390214777baSWayne Boyer 	feedback = readl(ioa_cfg->regs.init_feedback_reg);
8391214777baSWayne Boyer 	stage = feedback & IPR_IPL_INIT_STAGE_MASK;
8392214777baSWayne Boyer 	stage_time = feedback & IPR_IPL_INIT_STAGE_TIME_MASK;
8393214777baSWayne Boyer 
8394214777baSWayne Boyer 	ipr_dbg("IPL stage = 0x%lx, IPL stage time = %ld\n", stage, stage_time);
8395214777baSWayne Boyer 
8396214777baSWayne Boyer 	/* sanity check the stage_time value */
8397438b0331SWayne Boyer 	if (stage_time == 0)
8398438b0331SWayne Boyer 		stage_time = IPR_IPL_INIT_DEFAULT_STAGE_TIME;
8399438b0331SWayne Boyer 	else if (stage_time < IPR_IPL_INIT_MIN_STAGE_TIME)
8400214777baSWayne Boyer 		stage_time = IPR_IPL_INIT_MIN_STAGE_TIME;
8401214777baSWayne Boyer 	else if (stage_time > IPR_LONG_OPERATIONAL_TIMEOUT)
8402214777baSWayne Boyer 		stage_time = IPR_LONG_OPERATIONAL_TIMEOUT;
8403214777baSWayne Boyer 
8404214777baSWayne Boyer 	if (stage == IPR_IPL_INIT_STAGE_UNKNOWN) {
8405214777baSWayne Boyer 		writel(IPR_PCII_IPL_STAGE_CHANGE, ioa_cfg->regs.set_interrupt_mask_reg);
8406214777baSWayne Boyer 		int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
8407214777baSWayne Boyer 		stage_time = ioa_cfg->transop_timeout;
8408214777baSWayne Boyer 		ipr_cmd->job_step = ipr_ioafp_identify_hrrq;
8409214777baSWayne Boyer 	} else if (stage == IPR_IPL_INIT_STAGE_TRANSOP) {
84101df79ca4SWayne Boyer 		int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
84111df79ca4SWayne Boyer 		if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
8412214777baSWayne Boyer 			ipr_cmd->job_step = ipr_ioafp_identify_hrrq;
8413214777baSWayne Boyer 			maskval = IPR_PCII_IPL_STAGE_CHANGE;
8414214777baSWayne Boyer 			maskval = (maskval << 32) | IPR_PCII_IOA_TRANS_TO_OPER;
8415214777baSWayne Boyer 			writeq(maskval, ioa_cfg->regs.set_interrupt_mask_reg);
8416214777baSWayne Boyer 			int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
8417214777baSWayne Boyer 			return IPR_RC_JOB_CONTINUE;
8418214777baSWayne Boyer 		}
84191df79ca4SWayne Boyer 	}
8420214777baSWayne Boyer 
8421214777baSWayne Boyer 	ipr_cmd->timer.expires = jiffies + stage_time * HZ;
8422841b86f3SKees Cook 	ipr_cmd->timer.function = ipr_oper_timeout;
8423214777baSWayne Boyer 	ipr_cmd->done = ipr_reset_ioa_job;
8424214777baSWayne Boyer 	add_timer(&ipr_cmd->timer);
842505a6538aSwenxiong@linux.vnet.ibm.com 
842605a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
8427214777baSWayne Boyer 
8428214777baSWayne Boyer 	return IPR_RC_JOB_RETURN;
8429214777baSWayne Boyer }
8430214777baSWayne Boyer 
8431214777baSWayne Boyer /**
84321da177e4SLinus Torvalds  * ipr_reset_enable_ioa - Enable the IOA following a reset.
84331da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
84341da177e4SLinus Torvalds  *
84351da177e4SLinus Torvalds  * This function reinitializes some control blocks and
84361da177e4SLinus Torvalds  * enables destructive diagnostics on the adapter.
84371da177e4SLinus Torvalds  *
84381da177e4SLinus Torvalds  * Return value:
84391da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
84401da177e4SLinus Torvalds  **/
84411da177e4SLinus Torvalds static int ipr_reset_enable_ioa(struct ipr_cmnd *ipr_cmd)
84421da177e4SLinus Torvalds {
84431da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
84441da177e4SLinus Torvalds 	volatile u32 int_reg;
84457be96900SWayne Boyer 	volatile u64 maskval;
844656d6aa33Swenxiong@linux.vnet.ibm.com 	int i;
84471da177e4SLinus Torvalds 
84481da177e4SLinus Torvalds 	ENTER;
8449214777baSWayne Boyer 	ipr_cmd->job_step = ipr_ioafp_identify_hrrq;
84501da177e4SLinus Torvalds 	ipr_init_ioa_mem(ioa_cfg);
84511da177e4SLinus Torvalds 
845256d6aa33Swenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
845356d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&ioa_cfg->hrrq[i]._lock);
845456d6aa33Swenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].allow_interrupts = 1;
845556d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ioa_cfg->hrrq[i]._lock);
845656d6aa33Swenxiong@linux.vnet.ibm.com 	}
84578701f185SWayne Boyer 	if (ioa_cfg->sis64) {
84588701f185SWayne Boyer 		/* Set the adapter to the correct endian mode. */
84598701f185SWayne Boyer 		writel(IPR_ENDIAN_SWAP_KEY, ioa_cfg->regs.endian_swap_reg);
84608701f185SWayne Boyer 		int_reg = readl(ioa_cfg->regs.endian_swap_reg);
84618701f185SWayne Boyer 	}
84628701f185SWayne Boyer 
84637be96900SWayne Boyer 	int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
84641da177e4SLinus Torvalds 
84651da177e4SLinus Torvalds 	if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
84661da177e4SLinus Torvalds 		writel((IPR_PCII_ERROR_INTERRUPTS | IPR_PCII_HRRQ_UPDATED),
8467214777baSWayne Boyer 		       ioa_cfg->regs.clr_interrupt_mask_reg32);
84681da177e4SLinus Torvalds 		int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
84691da177e4SLinus Torvalds 		return IPR_RC_JOB_CONTINUE;
84701da177e4SLinus Torvalds 	}
84711da177e4SLinus Torvalds 
84721da177e4SLinus Torvalds 	/* Enable destructive diagnostics on IOA */
8473214777baSWayne Boyer 	writel(ioa_cfg->doorbell, ioa_cfg->regs.set_uproc_interrupt_reg32);
84741da177e4SLinus Torvalds 
84757be96900SWayne Boyer 	if (ioa_cfg->sis64) {
84767be96900SWayne Boyer 		maskval = IPR_PCII_IPL_STAGE_CHANGE;
84777be96900SWayne Boyer 		maskval = (maskval << 32) | IPR_PCII_OPER_INTERRUPTS;
84787be96900SWayne Boyer 		writeq(maskval, ioa_cfg->regs.clr_interrupt_mask_reg);
84797be96900SWayne Boyer 	} else
8480214777baSWayne Boyer 		writel(IPR_PCII_OPER_INTERRUPTS, ioa_cfg->regs.clr_interrupt_mask_reg32);
8481214777baSWayne Boyer 
84821da177e4SLinus Torvalds 	int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
84831da177e4SLinus Torvalds 
84841da177e4SLinus Torvalds 	dev_info(&ioa_cfg->pdev->dev, "Initializing IOA.\n");
84851da177e4SLinus Torvalds 
8486214777baSWayne Boyer 	if (ioa_cfg->sis64) {
8487214777baSWayne Boyer 		ipr_cmd->job_step = ipr_reset_next_stage;
8488214777baSWayne Boyer 		return IPR_RC_JOB_CONTINUE;
8489214777baSWayne Boyer 	}
8490214777baSWayne Boyer 
84915469cb5bSBrian King 	ipr_cmd->timer.expires = jiffies + (ioa_cfg->transop_timeout * HZ);
8492841b86f3SKees Cook 	ipr_cmd->timer.function = ipr_oper_timeout;
84931da177e4SLinus Torvalds 	ipr_cmd->done = ipr_reset_ioa_job;
84941da177e4SLinus Torvalds 	add_timer(&ipr_cmd->timer);
849505a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
84961da177e4SLinus Torvalds 
84971da177e4SLinus Torvalds 	LEAVE;
84981da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
84991da177e4SLinus Torvalds }
85001da177e4SLinus Torvalds 
85011da177e4SLinus Torvalds /**
85021da177e4SLinus Torvalds  * ipr_reset_wait_for_dump - Wait for a dump to timeout.
85031da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
85041da177e4SLinus Torvalds  *
85051da177e4SLinus Torvalds  * This function is invoked when an adapter dump has run out
85061da177e4SLinus Torvalds  * of processing time.
85071da177e4SLinus Torvalds  *
85081da177e4SLinus Torvalds  * Return value:
85091da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE
85101da177e4SLinus Torvalds  **/
85111da177e4SLinus Torvalds static int ipr_reset_wait_for_dump(struct ipr_cmnd *ipr_cmd)
85121da177e4SLinus Torvalds {
85131da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
85141da177e4SLinus Torvalds 
85151da177e4SLinus Torvalds 	if (ioa_cfg->sdt_state == GET_DUMP)
851641e9a696SBrian King 		ioa_cfg->sdt_state = WAIT_FOR_DUMP;
851741e9a696SBrian King 	else if (ioa_cfg->sdt_state == READ_DUMP)
85181da177e4SLinus Torvalds 		ioa_cfg->sdt_state = ABORT_DUMP;
85191da177e4SLinus Torvalds 
85204c647e90SBrian King 	ioa_cfg->dump_timeout = 1;
85211da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_reset_alert;
85221da177e4SLinus Torvalds 
85231da177e4SLinus Torvalds 	return IPR_RC_JOB_CONTINUE;
85241da177e4SLinus Torvalds }
85251da177e4SLinus Torvalds 
85261da177e4SLinus Torvalds /**
85271da177e4SLinus Torvalds  * ipr_unit_check_no_data - Log a unit check/no data error log
85281da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
85291da177e4SLinus Torvalds  *
85301da177e4SLinus Torvalds  * Logs an error indicating the adapter unit checked, but for some
85311da177e4SLinus Torvalds  * reason, we were unable to fetch the unit check buffer.
85321da177e4SLinus Torvalds  *
85331da177e4SLinus Torvalds  * Return value:
85341da177e4SLinus Torvalds  * 	nothing
85351da177e4SLinus Torvalds  **/
85361da177e4SLinus Torvalds static void ipr_unit_check_no_data(struct ipr_ioa_cfg *ioa_cfg)
85371da177e4SLinus Torvalds {
85381da177e4SLinus Torvalds 	ioa_cfg->errors_logged++;
85391da177e4SLinus Torvalds 	dev_err(&ioa_cfg->pdev->dev, "IOA unit check with no data\n");
85401da177e4SLinus Torvalds }
85411da177e4SLinus Torvalds 
85421da177e4SLinus Torvalds /**
85431da177e4SLinus Torvalds  * ipr_get_unit_check_buffer - Get the unit check buffer from the IOA
85441da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
85451da177e4SLinus Torvalds  *
85461da177e4SLinus Torvalds  * Fetches the unit check buffer from the adapter by clocking the data
85471da177e4SLinus Torvalds  * through the mailbox register.
85481da177e4SLinus Torvalds  *
85491da177e4SLinus Torvalds  * Return value:
85501da177e4SLinus Torvalds  * 	nothing
85511da177e4SLinus Torvalds  **/
85521da177e4SLinus Torvalds static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg)
85531da177e4SLinus Torvalds {
85541da177e4SLinus Torvalds 	unsigned long mailbox;
85551da177e4SLinus Torvalds 	struct ipr_hostrcb *hostrcb;
85561da177e4SLinus Torvalds 	struct ipr_uc_sdt sdt;
85571da177e4SLinus Torvalds 	int rc, length;
855865f56475SBrian King 	u32 ioasc;
85591da177e4SLinus Torvalds 
85601da177e4SLinus Torvalds 	mailbox = readl(ioa_cfg->ioa_mailbox);
85611da177e4SLinus Torvalds 
8562dcbad00eSWayne Boyer 	if (!ioa_cfg->sis64 && !ipr_sdt_is_fmt2(mailbox)) {
85631da177e4SLinus Torvalds 		ipr_unit_check_no_data(ioa_cfg);
85641da177e4SLinus Torvalds 		return;
85651da177e4SLinus Torvalds 	}
85661da177e4SLinus Torvalds 
85671da177e4SLinus Torvalds 	memset(&sdt, 0, sizeof(struct ipr_uc_sdt));
85681da177e4SLinus Torvalds 	rc = ipr_get_ldump_data_section(ioa_cfg, mailbox, (__be32 *) &sdt,
85691da177e4SLinus Torvalds 					(sizeof(struct ipr_uc_sdt)) / sizeof(__be32));
85701da177e4SLinus Torvalds 
8571dcbad00eSWayne Boyer 	if (rc || !(sdt.entry[0].flags & IPR_SDT_VALID_ENTRY) ||
8572dcbad00eSWayne Boyer 	    ((be32_to_cpu(sdt.hdr.state) != IPR_FMT3_SDT_READY_TO_USE) &&
8573dcbad00eSWayne Boyer 	    (be32_to_cpu(sdt.hdr.state) != IPR_FMT2_SDT_READY_TO_USE))) {
85741da177e4SLinus Torvalds 		ipr_unit_check_no_data(ioa_cfg);
85751da177e4SLinus Torvalds 		return;
85761da177e4SLinus Torvalds 	}
85771da177e4SLinus Torvalds 
85781da177e4SLinus Torvalds 	/* Find length of the first sdt entry (UC buffer) */
8579dcbad00eSWayne Boyer 	if (be32_to_cpu(sdt.hdr.state) == IPR_FMT3_SDT_READY_TO_USE)
8580dcbad00eSWayne Boyer 		length = be32_to_cpu(sdt.entry[0].end_token);
8581dcbad00eSWayne Boyer 	else
8582dcbad00eSWayne Boyer 		length = (be32_to_cpu(sdt.entry[0].end_token) -
8583dcbad00eSWayne Boyer 			  be32_to_cpu(sdt.entry[0].start_token)) &
8584dcbad00eSWayne Boyer 			  IPR_FMT2_MBX_ADDR_MASK;
85851da177e4SLinus Torvalds 
85861da177e4SLinus Torvalds 	hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next,
85871da177e4SLinus Torvalds 			     struct ipr_hostrcb, queue);
8588afc3f83cSBrian King 	list_del_init(&hostrcb->queue);
85891da177e4SLinus Torvalds 	memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam));
85901da177e4SLinus Torvalds 
85911da177e4SLinus Torvalds 	rc = ipr_get_ldump_data_section(ioa_cfg,
8592dcbad00eSWayne Boyer 					be32_to_cpu(sdt.entry[0].start_token),
85931da177e4SLinus Torvalds 					(__be32 *)&hostrcb->hcam,
85941da177e4SLinus Torvalds 					min(length, (int)sizeof(hostrcb->hcam)) / sizeof(__be32));
85951da177e4SLinus Torvalds 
859665f56475SBrian King 	if (!rc) {
85971da177e4SLinus Torvalds 		ipr_handle_log_data(ioa_cfg, hostrcb);
85984565e370SWayne Boyer 		ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc);
859965f56475SBrian King 		if (ioasc == IPR_IOASC_NR_IOA_RESET_REQUIRED &&
860065f56475SBrian King 		    ioa_cfg->sdt_state == GET_DUMP)
860165f56475SBrian King 			ioa_cfg->sdt_state = WAIT_FOR_DUMP;
860265f56475SBrian King 	} else
86031da177e4SLinus Torvalds 		ipr_unit_check_no_data(ioa_cfg);
86041da177e4SLinus Torvalds 
86051da177e4SLinus Torvalds 	list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
86061da177e4SLinus Torvalds }
86071da177e4SLinus Torvalds 
86081da177e4SLinus Torvalds /**
8609110def85SWayne Boyer  * ipr_reset_get_unit_check_job - Call to get the unit check buffer.
8610110def85SWayne Boyer  * @ipr_cmd:	ipr command struct
8611110def85SWayne Boyer  *
8612110def85SWayne Boyer  * Description: This function will call to get the unit check buffer.
8613110def85SWayne Boyer  *
8614110def85SWayne Boyer  * Return value:
8615110def85SWayne Boyer  *	IPR_RC_JOB_RETURN
8616110def85SWayne Boyer  **/
8617110def85SWayne Boyer static int ipr_reset_get_unit_check_job(struct ipr_cmnd *ipr_cmd)
8618110def85SWayne Boyer {
8619110def85SWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8620110def85SWayne Boyer 
8621110def85SWayne Boyer 	ENTER;
8622110def85SWayne Boyer 	ioa_cfg->ioa_unit_checked = 0;
8623110def85SWayne Boyer 	ipr_get_unit_check_buffer(ioa_cfg);
8624110def85SWayne Boyer 	ipr_cmd->job_step = ipr_reset_alert;
8625110def85SWayne Boyer 	ipr_reset_start_timer(ipr_cmd, 0);
8626110def85SWayne Boyer 
8627110def85SWayne Boyer 	LEAVE;
8628110def85SWayne Boyer 	return IPR_RC_JOB_RETURN;
8629110def85SWayne Boyer }
8630110def85SWayne Boyer 
8631f41f1d99SGabriel Krisman Bertazi static int ipr_dump_mailbox_wait(struct ipr_cmnd *ipr_cmd)
8632f41f1d99SGabriel Krisman Bertazi {
8633f41f1d99SGabriel Krisman Bertazi 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8634f41f1d99SGabriel Krisman Bertazi 
8635f41f1d99SGabriel Krisman Bertazi 	ENTER;
8636f41f1d99SGabriel Krisman Bertazi 
8637f41f1d99SGabriel Krisman Bertazi 	if (ioa_cfg->sdt_state != GET_DUMP)
8638f41f1d99SGabriel Krisman Bertazi 		return IPR_RC_JOB_RETURN;
8639f41f1d99SGabriel Krisman Bertazi 
8640f41f1d99SGabriel Krisman Bertazi 	if (!ioa_cfg->sis64 || !ipr_cmd->u.time_left ||
8641f41f1d99SGabriel Krisman Bertazi 	    (readl(ioa_cfg->regs.sense_interrupt_reg) &
8642f41f1d99SGabriel Krisman Bertazi 	     IPR_PCII_MAILBOX_STABLE)) {
8643f41f1d99SGabriel Krisman Bertazi 
8644f41f1d99SGabriel Krisman Bertazi 		if (!ipr_cmd->u.time_left)
8645f41f1d99SGabriel Krisman Bertazi 			dev_err(&ioa_cfg->pdev->dev,
8646f41f1d99SGabriel Krisman Bertazi 				"Timed out waiting for Mailbox register.\n");
8647f41f1d99SGabriel Krisman Bertazi 
8648f41f1d99SGabriel Krisman Bertazi 		ioa_cfg->sdt_state = READ_DUMP;
8649f41f1d99SGabriel Krisman Bertazi 		ioa_cfg->dump_timeout = 0;
8650f41f1d99SGabriel Krisman Bertazi 		if (ioa_cfg->sis64)
8651f41f1d99SGabriel Krisman Bertazi 			ipr_reset_start_timer(ipr_cmd, IPR_SIS64_DUMP_TIMEOUT);
8652f41f1d99SGabriel Krisman Bertazi 		else
8653f41f1d99SGabriel Krisman Bertazi 			ipr_reset_start_timer(ipr_cmd, IPR_SIS32_DUMP_TIMEOUT);
8654f41f1d99SGabriel Krisman Bertazi 		ipr_cmd->job_step = ipr_reset_wait_for_dump;
8655f41f1d99SGabriel Krisman Bertazi 		schedule_work(&ioa_cfg->work_q);
8656f41f1d99SGabriel Krisman Bertazi 
8657f41f1d99SGabriel Krisman Bertazi 	} else {
8658f41f1d99SGabriel Krisman Bertazi 		ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
8659f41f1d99SGabriel Krisman Bertazi 		ipr_reset_start_timer(ipr_cmd,
8660f41f1d99SGabriel Krisman Bertazi 				      IPR_CHECK_FOR_RESET_TIMEOUT);
8661f41f1d99SGabriel Krisman Bertazi 	}
8662f41f1d99SGabriel Krisman Bertazi 
8663f41f1d99SGabriel Krisman Bertazi 	LEAVE;
8664f41f1d99SGabriel Krisman Bertazi 	return IPR_RC_JOB_RETURN;
8665f41f1d99SGabriel Krisman Bertazi }
8666f41f1d99SGabriel Krisman Bertazi 
8667110def85SWayne Boyer /**
86681da177e4SLinus Torvalds  * ipr_reset_restore_cfg_space - Restore PCI config space.
86691da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
86701da177e4SLinus Torvalds  *
86711da177e4SLinus Torvalds  * Description: This function restores the saved PCI config space of
86721da177e4SLinus Torvalds  * the adapter, fails all outstanding ops back to the callers, and
86731da177e4SLinus Torvalds  * fetches the dump/unit check if applicable to this reset.
86741da177e4SLinus Torvalds  *
86751da177e4SLinus Torvalds  * Return value:
86761da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
86771da177e4SLinus Torvalds  **/
86781da177e4SLinus Torvalds static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
86791da177e4SLinus Torvalds {
86801da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
86811da177e4SLinus Torvalds 
86821da177e4SLinus Torvalds 	ENTER;
868399c965ddSKleber Sacilotto de Souza 	ioa_cfg->pdev->state_saved = true;
86841d3c16a8SJon Mason 	pci_restore_state(ioa_cfg->pdev);
86851da177e4SLinus Torvalds 
86861da177e4SLinus Torvalds 	if (ipr_set_pcix_cmd_reg(ioa_cfg)) {
868796d21f00SWayne Boyer 		ipr_cmd->s.ioasa.hdr.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR);
86881da177e4SLinus Torvalds 		return IPR_RC_JOB_CONTINUE;
86891da177e4SLinus Torvalds 	}
86901da177e4SLinus Torvalds 
86911da177e4SLinus Torvalds 	ipr_fail_all_ops(ioa_cfg);
86921da177e4SLinus Torvalds 
86938701f185SWayne Boyer 	if (ioa_cfg->sis64) {
86948701f185SWayne Boyer 		/* Set the adapter to the correct endian mode. */
86958701f185SWayne Boyer 		writel(IPR_ENDIAN_SWAP_KEY, ioa_cfg->regs.endian_swap_reg);
86964dc83399SLee Jones 		readl(ioa_cfg->regs.endian_swap_reg);
86978701f185SWayne Boyer 	}
86988701f185SWayne Boyer 
86991da177e4SLinus Torvalds 	if (ioa_cfg->ioa_unit_checked) {
8700110def85SWayne Boyer 		if (ioa_cfg->sis64) {
8701110def85SWayne Boyer 			ipr_cmd->job_step = ipr_reset_get_unit_check_job;
8702110def85SWayne Boyer 			ipr_reset_start_timer(ipr_cmd, IPR_DUMP_DELAY_TIMEOUT);
8703110def85SWayne Boyer 			return IPR_RC_JOB_RETURN;
8704110def85SWayne Boyer 		} else {
87051da177e4SLinus Torvalds 			ioa_cfg->ioa_unit_checked = 0;
87061da177e4SLinus Torvalds 			ipr_get_unit_check_buffer(ioa_cfg);
87071da177e4SLinus Torvalds 			ipr_cmd->job_step = ipr_reset_alert;
87081da177e4SLinus Torvalds 			ipr_reset_start_timer(ipr_cmd, 0);
87091da177e4SLinus Torvalds 			return IPR_RC_JOB_RETURN;
87101da177e4SLinus Torvalds 		}
8711110def85SWayne Boyer 	}
87121da177e4SLinus Torvalds 
87131da177e4SLinus Torvalds 	if (ioa_cfg->in_ioa_bringdown) {
87141da177e4SLinus Torvalds 		ipr_cmd->job_step = ipr_ioa_bringdown_done;
8715f41f1d99SGabriel Krisman Bertazi 	} else if (ioa_cfg->sdt_state == GET_DUMP) {
8716f41f1d99SGabriel Krisman Bertazi 		ipr_cmd->job_step = ipr_dump_mailbox_wait;
8717f41f1d99SGabriel Krisman Bertazi 		ipr_cmd->u.time_left = IPR_WAIT_FOR_MAILBOX;
87181da177e4SLinus Torvalds 	} else {
87191da177e4SLinus Torvalds 		ipr_cmd->job_step = ipr_reset_enable_ioa;
87201da177e4SLinus Torvalds 	}
87211da177e4SLinus Torvalds 
8722438b0331SWayne Boyer 	LEAVE;
87231da177e4SLinus Torvalds 	return IPR_RC_JOB_CONTINUE;
87241da177e4SLinus Torvalds }
87251da177e4SLinus Torvalds 
87261da177e4SLinus Torvalds /**
8727e619e1a7SBrian King  * ipr_reset_bist_done - BIST has completed on the adapter.
8728e619e1a7SBrian King  * @ipr_cmd:	ipr command struct
8729e619e1a7SBrian King  *
8730e619e1a7SBrian King  * Description: Unblock config space and resume the reset process.
8731e619e1a7SBrian King  *
8732e619e1a7SBrian King  * Return value:
8733e619e1a7SBrian King  * 	IPR_RC_JOB_CONTINUE
8734e619e1a7SBrian King  **/
8735e619e1a7SBrian King static int ipr_reset_bist_done(struct ipr_cmnd *ipr_cmd)
8736e619e1a7SBrian King {
8737fb51ccbfSJan Kiszka 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8738fb51ccbfSJan Kiszka 
8739e619e1a7SBrian King 	ENTER;
8740fb51ccbfSJan Kiszka 	if (ioa_cfg->cfg_locked)
8741fb51ccbfSJan Kiszka 		pci_cfg_access_unlock(ioa_cfg->pdev);
8742fb51ccbfSJan Kiszka 	ioa_cfg->cfg_locked = 0;
8743e619e1a7SBrian King 	ipr_cmd->job_step = ipr_reset_restore_cfg_space;
8744e619e1a7SBrian King 	LEAVE;
8745e619e1a7SBrian King 	return IPR_RC_JOB_CONTINUE;
8746e619e1a7SBrian King }
8747e619e1a7SBrian King 
8748e619e1a7SBrian King /**
87491da177e4SLinus Torvalds  * ipr_reset_start_bist - Run BIST on the adapter.
87501da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
87511da177e4SLinus Torvalds  *
87521da177e4SLinus Torvalds  * Description: This function runs BIST on the adapter, then delays 2 seconds.
87531da177e4SLinus Torvalds  *
87541da177e4SLinus Torvalds  * Return value:
87551da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
87561da177e4SLinus Torvalds  **/
87571da177e4SLinus Torvalds static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
87581da177e4SLinus Torvalds {
87591da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8760cb237ef7SWayne Boyer 	int rc = PCIBIOS_SUCCESSFUL;
87611da177e4SLinus Torvalds 
87621da177e4SLinus Torvalds 	ENTER;
8763cb237ef7SWayne Boyer 	if (ioa_cfg->ipr_chip->bist_method == IPR_MMIO)
8764cb237ef7SWayne Boyer 		writel(IPR_UPROCI_SIS64_START_BIST,
8765cb237ef7SWayne Boyer 		       ioa_cfg->regs.set_uproc_interrupt_reg32);
8766cb237ef7SWayne Boyer 	else
87671da177e4SLinus Torvalds 		rc = pci_write_config_byte(ioa_cfg->pdev, PCI_BIST, PCI_BIST_START);
87681da177e4SLinus Torvalds 
8769cb237ef7SWayne Boyer 	if (rc == PCIBIOS_SUCCESSFUL) {
8770e619e1a7SBrian King 		ipr_cmd->job_step = ipr_reset_bist_done;
87711da177e4SLinus Torvalds 		ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
87721da177e4SLinus Torvalds 		rc = IPR_RC_JOB_RETURN;
8773cb237ef7SWayne Boyer 	} else {
8774fb51ccbfSJan Kiszka 		if (ioa_cfg->cfg_locked)
8775fb51ccbfSJan Kiszka 			pci_cfg_access_unlock(ipr_cmd->ioa_cfg->pdev);
8776fb51ccbfSJan Kiszka 		ioa_cfg->cfg_locked = 0;
8777cb237ef7SWayne Boyer 		ipr_cmd->s.ioasa.hdr.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR);
8778cb237ef7SWayne Boyer 		rc = IPR_RC_JOB_CONTINUE;
87791da177e4SLinus Torvalds 	}
87801da177e4SLinus Torvalds 
87811da177e4SLinus Torvalds 	LEAVE;
87821da177e4SLinus Torvalds 	return rc;
87831da177e4SLinus Torvalds }
87841da177e4SLinus Torvalds 
87851da177e4SLinus Torvalds /**
8786463fc696SBrian King  * ipr_reset_slot_reset_done - Clear PCI reset to the adapter
8787463fc696SBrian King  * @ipr_cmd:	ipr command struct
8788463fc696SBrian King  *
8789463fc696SBrian King  * Description: This clears PCI reset to the adapter and delays two seconds.
8790463fc696SBrian King  *
8791463fc696SBrian King  * Return value:
8792463fc696SBrian King  * 	IPR_RC_JOB_RETURN
8793463fc696SBrian King  **/
8794463fc696SBrian King static int ipr_reset_slot_reset_done(struct ipr_cmnd *ipr_cmd)
8795463fc696SBrian King {
8796463fc696SBrian King 	ENTER;
8797463fc696SBrian King 	ipr_cmd->job_step = ipr_reset_bist_done;
8798463fc696SBrian King 	ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
8799463fc696SBrian King 	LEAVE;
8800463fc696SBrian King 	return IPR_RC_JOB_RETURN;
8801463fc696SBrian King }
8802463fc696SBrian King 
8803463fc696SBrian King /**
88042796ca5eSBrian King  * ipr_reset_reset_work - Pulse a PCIe fundamental reset
88052796ca5eSBrian King  * @work:	work struct
88062796ca5eSBrian King  *
88072796ca5eSBrian King  * Description: This pulses warm reset to a slot.
88082796ca5eSBrian King  *
88092796ca5eSBrian King  **/
88102796ca5eSBrian King static void ipr_reset_reset_work(struct work_struct *work)
88112796ca5eSBrian King {
88122796ca5eSBrian King 	struct ipr_cmnd *ipr_cmd = container_of(work, struct ipr_cmnd, work);
88132796ca5eSBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
88142796ca5eSBrian King 	struct pci_dev *pdev = ioa_cfg->pdev;
88152796ca5eSBrian King 	unsigned long lock_flags = 0;
88162796ca5eSBrian King 
88172796ca5eSBrian King 	ENTER;
88182796ca5eSBrian King 	pci_set_pcie_reset_state(pdev, pcie_warm_reset);
88192796ca5eSBrian King 	msleep(jiffies_to_msecs(IPR_PCI_RESET_TIMEOUT));
88202796ca5eSBrian King 	pci_set_pcie_reset_state(pdev, pcie_deassert_reset);
88212796ca5eSBrian King 
88222796ca5eSBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
88232796ca5eSBrian King 	if (ioa_cfg->reset_cmd == ipr_cmd)
88242796ca5eSBrian King 		ipr_reset_ioa_job(ipr_cmd);
88252796ca5eSBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
88262796ca5eSBrian King 	LEAVE;
88272796ca5eSBrian King }
88282796ca5eSBrian King 
88292796ca5eSBrian King /**
8830463fc696SBrian King  * ipr_reset_slot_reset - Reset the PCI slot of the adapter.
8831463fc696SBrian King  * @ipr_cmd:	ipr command struct
8832463fc696SBrian King  *
8833463fc696SBrian King  * Description: This asserts PCI reset to the adapter.
8834463fc696SBrian King  *
8835463fc696SBrian King  * Return value:
8836463fc696SBrian King  * 	IPR_RC_JOB_RETURN
8837463fc696SBrian King  **/
8838463fc696SBrian King static int ipr_reset_slot_reset(struct ipr_cmnd *ipr_cmd)
8839463fc696SBrian King {
8840463fc696SBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8841463fc696SBrian King 
8842463fc696SBrian King 	ENTER;
88432796ca5eSBrian King 	INIT_WORK(&ipr_cmd->work, ipr_reset_reset_work);
88442796ca5eSBrian King 	queue_work(ioa_cfg->reset_work_q, &ipr_cmd->work);
8845463fc696SBrian King 	ipr_cmd->job_step = ipr_reset_slot_reset_done;
8846463fc696SBrian King 	LEAVE;
8847463fc696SBrian King 	return IPR_RC_JOB_RETURN;
8848463fc696SBrian King }
8849463fc696SBrian King 
8850463fc696SBrian King /**
8851fb51ccbfSJan Kiszka  * ipr_reset_block_config_access_wait - Wait for permission to block config access
8852fb51ccbfSJan Kiszka  * @ipr_cmd:	ipr command struct
8853fb51ccbfSJan Kiszka  *
8854fb51ccbfSJan Kiszka  * Description: This attempts to block config access to the IOA.
8855fb51ccbfSJan Kiszka  *
8856fb51ccbfSJan Kiszka  * Return value:
8857fb51ccbfSJan Kiszka  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
8858fb51ccbfSJan Kiszka  **/
8859fb51ccbfSJan Kiszka static int ipr_reset_block_config_access_wait(struct ipr_cmnd *ipr_cmd)
8860fb51ccbfSJan Kiszka {
8861fb51ccbfSJan Kiszka 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
8862fb51ccbfSJan Kiszka 	int rc = IPR_RC_JOB_CONTINUE;
8863fb51ccbfSJan Kiszka 
8864fb51ccbfSJan Kiszka 	if (pci_cfg_access_trylock(ioa_cfg->pdev)) {
8865fb51ccbfSJan Kiszka 		ioa_cfg->cfg_locked = 1;
8866fb51ccbfSJan Kiszka 		ipr_cmd->job_step = ioa_cfg->reset;
8867fb51ccbfSJan Kiszka 	} else {
8868fb51ccbfSJan Kiszka 		if (ipr_cmd->u.time_left) {
8869fb51ccbfSJan Kiszka 			rc = IPR_RC_JOB_RETURN;
8870fb51ccbfSJan Kiszka 			ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
8871fb51ccbfSJan Kiszka 			ipr_reset_start_timer(ipr_cmd,
8872fb51ccbfSJan Kiszka 					      IPR_CHECK_FOR_RESET_TIMEOUT);
8873fb51ccbfSJan Kiszka 		} else {
8874fb51ccbfSJan Kiszka 			ipr_cmd->job_step = ioa_cfg->reset;
8875fb51ccbfSJan Kiszka 			dev_err(&ioa_cfg->pdev->dev,
8876fb51ccbfSJan Kiszka 				"Timed out waiting to lock config access. Resetting anyway.\n");
8877fb51ccbfSJan Kiszka 		}
8878fb51ccbfSJan Kiszka 	}
8879fb51ccbfSJan Kiszka 
8880fb51ccbfSJan Kiszka 	return rc;
8881fb51ccbfSJan Kiszka }
8882fb51ccbfSJan Kiszka 
8883fb51ccbfSJan Kiszka /**
8884fb51ccbfSJan Kiszka  * ipr_reset_block_config_access - Block config access to the IOA
8885fb51ccbfSJan Kiszka  * @ipr_cmd:	ipr command struct
8886fb51ccbfSJan Kiszka  *
8887fb51ccbfSJan Kiszka  * Description: This attempts to block config access to the IOA
8888fb51ccbfSJan Kiszka  *
8889fb51ccbfSJan Kiszka  * Return value:
8890fb51ccbfSJan Kiszka  * 	IPR_RC_JOB_CONTINUE
8891fb51ccbfSJan Kiszka  **/
8892fb51ccbfSJan Kiszka static int ipr_reset_block_config_access(struct ipr_cmnd *ipr_cmd)
8893fb51ccbfSJan Kiszka {
8894fb51ccbfSJan Kiszka 	ipr_cmd->ioa_cfg->cfg_locked = 0;
8895fb51ccbfSJan Kiszka 	ipr_cmd->job_step = ipr_reset_block_config_access_wait;
8896fb51ccbfSJan Kiszka 	ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT;
8897fb51ccbfSJan Kiszka 	return IPR_RC_JOB_CONTINUE;
8898fb51ccbfSJan Kiszka }
8899fb51ccbfSJan Kiszka 
8900fb51ccbfSJan Kiszka /**
89011da177e4SLinus Torvalds  * ipr_reset_allowed - Query whether or not IOA can be reset
89021da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
89031da177e4SLinus Torvalds  *
89041da177e4SLinus Torvalds  * Return value:
89051da177e4SLinus Torvalds  * 	0 if reset not allowed / non-zero if reset is allowed
89061da177e4SLinus Torvalds  **/
89071da177e4SLinus Torvalds static int ipr_reset_allowed(struct ipr_ioa_cfg *ioa_cfg)
89081da177e4SLinus Torvalds {
89091da177e4SLinus Torvalds 	volatile u32 temp_reg;
89101da177e4SLinus Torvalds 
89111da177e4SLinus Torvalds 	temp_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
89121da177e4SLinus Torvalds 	return ((temp_reg & IPR_PCII_CRITICAL_OPERATION) == 0);
89131da177e4SLinus Torvalds }
89141da177e4SLinus Torvalds 
89151da177e4SLinus Torvalds /**
89161da177e4SLinus Torvalds  * ipr_reset_wait_to_start_bist - Wait for permission to reset IOA.
89171da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
89181da177e4SLinus Torvalds  *
89191da177e4SLinus Torvalds  * Description: This function waits for adapter permission to run BIST,
89201da177e4SLinus Torvalds  * then runs BIST. If the adapter does not give permission after a
89211da177e4SLinus Torvalds  * reasonable time, we will reset the adapter anyway. The impact of
89221da177e4SLinus Torvalds  * resetting the adapter without warning the adapter is the risk of
89231da177e4SLinus Torvalds  * losing the persistent error log on the adapter. If the adapter is
89241da177e4SLinus Torvalds  * reset while it is writing to the flash on the adapter, the flash
89251da177e4SLinus Torvalds  * segment will have bad ECC and be zeroed.
89261da177e4SLinus Torvalds  *
89271da177e4SLinus Torvalds  * Return value:
89281da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
89291da177e4SLinus Torvalds  **/
89301da177e4SLinus Torvalds static int ipr_reset_wait_to_start_bist(struct ipr_cmnd *ipr_cmd)
89311da177e4SLinus Torvalds {
89321da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
89331da177e4SLinus Torvalds 	int rc = IPR_RC_JOB_RETURN;
89341da177e4SLinus Torvalds 
89351da177e4SLinus Torvalds 	if (!ipr_reset_allowed(ioa_cfg) && ipr_cmd->u.time_left) {
89361da177e4SLinus Torvalds 		ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
89371da177e4SLinus Torvalds 		ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT);
89381da177e4SLinus Torvalds 	} else {
8939fb51ccbfSJan Kiszka 		ipr_cmd->job_step = ipr_reset_block_config_access;
89401da177e4SLinus Torvalds 		rc = IPR_RC_JOB_CONTINUE;
89411da177e4SLinus Torvalds 	}
89421da177e4SLinus Torvalds 
89431da177e4SLinus Torvalds 	return rc;
89441da177e4SLinus Torvalds }
89451da177e4SLinus Torvalds 
89461da177e4SLinus Torvalds /**
89478701f185SWayne Boyer  * ipr_reset_alert - Alert the adapter of a pending reset
89481da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
89491da177e4SLinus Torvalds  *
89501da177e4SLinus Torvalds  * Description: This function alerts the adapter that it will be reset.
89511da177e4SLinus Torvalds  * If memory space is not currently enabled, proceed directly
89521da177e4SLinus Torvalds  * to running BIST on the adapter. The timer must always be started
89531da177e4SLinus Torvalds  * so we guarantee we do not run BIST from ipr_isr.
89541da177e4SLinus Torvalds  *
89551da177e4SLinus Torvalds  * Return value:
89561da177e4SLinus Torvalds  * 	IPR_RC_JOB_RETURN
89571da177e4SLinus Torvalds  **/
89581da177e4SLinus Torvalds static int ipr_reset_alert(struct ipr_cmnd *ipr_cmd)
89591da177e4SLinus Torvalds {
89601da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
89611da177e4SLinus Torvalds 	u16 cmd_reg;
89621da177e4SLinus Torvalds 	int rc;
89631da177e4SLinus Torvalds 
89641da177e4SLinus Torvalds 	ENTER;
89651da177e4SLinus Torvalds 	rc = pci_read_config_word(ioa_cfg->pdev, PCI_COMMAND, &cmd_reg);
89661da177e4SLinus Torvalds 
89671da177e4SLinus Torvalds 	if ((rc == PCIBIOS_SUCCESSFUL) && (cmd_reg & PCI_COMMAND_MEMORY)) {
89681da177e4SLinus Torvalds 		ipr_mask_and_clear_interrupts(ioa_cfg, ~0);
8969214777baSWayne Boyer 		writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.set_uproc_interrupt_reg32);
89701da177e4SLinus Torvalds 		ipr_cmd->job_step = ipr_reset_wait_to_start_bist;
89711da177e4SLinus Torvalds 	} else {
8972fb51ccbfSJan Kiszka 		ipr_cmd->job_step = ipr_reset_block_config_access;
89731da177e4SLinus Torvalds 	}
89741da177e4SLinus Torvalds 
89751da177e4SLinus Torvalds 	ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT;
89761da177e4SLinus Torvalds 	ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT);
89771da177e4SLinus Torvalds 
89781da177e4SLinus Torvalds 	LEAVE;
89791da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
89801da177e4SLinus Torvalds }
89811da177e4SLinus Torvalds 
89821da177e4SLinus Torvalds /**
89834fdd7c7aSBrian King  * ipr_reset_quiesce_done - Complete IOA disconnect
89844fdd7c7aSBrian King  * @ipr_cmd:	ipr command struct
89854fdd7c7aSBrian King  *
89864fdd7c7aSBrian King  * Description: Freeze the adapter to complete quiesce processing
89874fdd7c7aSBrian King  *
89884fdd7c7aSBrian King  * Return value:
89894fdd7c7aSBrian King  * 	IPR_RC_JOB_CONTINUE
89904fdd7c7aSBrian King  **/
89914fdd7c7aSBrian King static int ipr_reset_quiesce_done(struct ipr_cmnd *ipr_cmd)
89924fdd7c7aSBrian King {
89934fdd7c7aSBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
89944fdd7c7aSBrian King 
89954fdd7c7aSBrian King 	ENTER;
89964fdd7c7aSBrian King 	ipr_cmd->job_step = ipr_ioa_bringdown_done;
89974fdd7c7aSBrian King 	ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
89984fdd7c7aSBrian King 	LEAVE;
89994fdd7c7aSBrian King 	return IPR_RC_JOB_CONTINUE;
90004fdd7c7aSBrian King }
90014fdd7c7aSBrian King 
90024fdd7c7aSBrian King /**
90034fdd7c7aSBrian King  * ipr_reset_cancel_hcam_done - Check for outstanding commands
90044fdd7c7aSBrian King  * @ipr_cmd:	ipr command struct
90054fdd7c7aSBrian King  *
90064fdd7c7aSBrian King  * Description: Ensure nothing is outstanding to the IOA and
90074fdd7c7aSBrian King  *			proceed with IOA disconnect. Otherwise reset the IOA.
90084fdd7c7aSBrian King  *
90094fdd7c7aSBrian King  * Return value:
90104fdd7c7aSBrian King  * 	IPR_RC_JOB_RETURN / IPR_RC_JOB_CONTINUE
90114fdd7c7aSBrian King  **/
90124fdd7c7aSBrian King static int ipr_reset_cancel_hcam_done(struct ipr_cmnd *ipr_cmd)
90134fdd7c7aSBrian King {
90144fdd7c7aSBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
90154fdd7c7aSBrian King 	struct ipr_cmnd *loop_cmd;
90164fdd7c7aSBrian King 	struct ipr_hrr_queue *hrrq;
90174fdd7c7aSBrian King 	int rc = IPR_RC_JOB_CONTINUE;
90184fdd7c7aSBrian King 	int count = 0;
90194fdd7c7aSBrian King 
90204fdd7c7aSBrian King 	ENTER;
90214fdd7c7aSBrian King 	ipr_cmd->job_step = ipr_reset_quiesce_done;
90224fdd7c7aSBrian King 
90234fdd7c7aSBrian King 	for_each_hrrq(hrrq, ioa_cfg) {
90244fdd7c7aSBrian King 		spin_lock(&hrrq->_lock);
90254fdd7c7aSBrian King 		list_for_each_entry(loop_cmd, &hrrq->hrrq_pending_q, queue) {
90264fdd7c7aSBrian King 			count++;
90274fdd7c7aSBrian King 			ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
90284fdd7c7aSBrian King 			list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
90294fdd7c7aSBrian King 			rc = IPR_RC_JOB_RETURN;
90304fdd7c7aSBrian King 			break;
90314fdd7c7aSBrian King 		}
90324fdd7c7aSBrian King 		spin_unlock(&hrrq->_lock);
90334fdd7c7aSBrian King 
90344fdd7c7aSBrian King 		if (count)
90354fdd7c7aSBrian King 			break;
90364fdd7c7aSBrian King 	}
90374fdd7c7aSBrian King 
90384fdd7c7aSBrian King 	LEAVE;
90394fdd7c7aSBrian King 	return rc;
90404fdd7c7aSBrian King }
90414fdd7c7aSBrian King 
90424fdd7c7aSBrian King /**
90434fdd7c7aSBrian King  * ipr_reset_cancel_hcam - Cancel outstanding HCAMs
90444fdd7c7aSBrian King  * @ipr_cmd:	ipr command struct
90454fdd7c7aSBrian King  *
90464fdd7c7aSBrian King  * Description: Cancel any oustanding HCAMs to the IOA.
90474fdd7c7aSBrian King  *
90484fdd7c7aSBrian King  * Return value:
90494fdd7c7aSBrian King  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
90504fdd7c7aSBrian King  **/
90514fdd7c7aSBrian King static int ipr_reset_cancel_hcam(struct ipr_cmnd *ipr_cmd)
90524fdd7c7aSBrian King {
90534fdd7c7aSBrian King 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
90544fdd7c7aSBrian King 	int rc = IPR_RC_JOB_CONTINUE;
90554fdd7c7aSBrian King 	struct ipr_cmd_pkt *cmd_pkt;
90564fdd7c7aSBrian King 	struct ipr_cmnd *hcam_cmd;
90574fdd7c7aSBrian King 	struct ipr_hrr_queue *hrrq = &ioa_cfg->hrrq[IPR_INIT_HRRQ];
90584fdd7c7aSBrian King 
90594fdd7c7aSBrian King 	ENTER;
90604fdd7c7aSBrian King 	ipr_cmd->job_step = ipr_reset_cancel_hcam_done;
90614fdd7c7aSBrian King 
90624fdd7c7aSBrian King 	if (!hrrq->ioa_is_dead) {
90634fdd7c7aSBrian King 		if (!list_empty(&ioa_cfg->hostrcb_pending_q)) {
90644fdd7c7aSBrian King 			list_for_each_entry(hcam_cmd, &hrrq->hrrq_pending_q, queue) {
90654fdd7c7aSBrian King 				if (hcam_cmd->ioarcb.cmd_pkt.cdb[0] != IPR_HOST_CONTROLLED_ASYNC)
90664fdd7c7aSBrian King 					continue;
90674fdd7c7aSBrian King 
90684fdd7c7aSBrian King 				ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
90694fdd7c7aSBrian King 				ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
90704fdd7c7aSBrian King 				cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
90714fdd7c7aSBrian King 				cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
90724fdd7c7aSBrian King 				cmd_pkt->cdb[0] = IPR_CANCEL_REQUEST;
90734fdd7c7aSBrian King 				cmd_pkt->cdb[1] = IPR_CANCEL_64BIT_IOARCB;
90744fdd7c7aSBrian King 				cmd_pkt->cdb[10] = ((u64) hcam_cmd->dma_addr >> 56) & 0xff;
90754fdd7c7aSBrian King 				cmd_pkt->cdb[11] = ((u64) hcam_cmd->dma_addr >> 48) & 0xff;
90764fdd7c7aSBrian King 				cmd_pkt->cdb[12] = ((u64) hcam_cmd->dma_addr >> 40) & 0xff;
90774fdd7c7aSBrian King 				cmd_pkt->cdb[13] = ((u64) hcam_cmd->dma_addr >> 32) & 0xff;
90784fdd7c7aSBrian King 				cmd_pkt->cdb[2] = ((u64) hcam_cmd->dma_addr >> 24) & 0xff;
90794fdd7c7aSBrian King 				cmd_pkt->cdb[3] = ((u64) hcam_cmd->dma_addr >> 16) & 0xff;
90804fdd7c7aSBrian King 				cmd_pkt->cdb[4] = ((u64) hcam_cmd->dma_addr >> 8) & 0xff;
90814fdd7c7aSBrian King 				cmd_pkt->cdb[5] = ((u64) hcam_cmd->dma_addr) & 0xff;
90824fdd7c7aSBrian King 
90834fdd7c7aSBrian King 				ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
90844fdd7c7aSBrian King 					   IPR_CANCEL_TIMEOUT);
90854fdd7c7aSBrian King 
90864fdd7c7aSBrian King 				rc = IPR_RC_JOB_RETURN;
90874fdd7c7aSBrian King 				ipr_cmd->job_step = ipr_reset_cancel_hcam;
90884fdd7c7aSBrian King 				break;
90894fdd7c7aSBrian King 			}
90904fdd7c7aSBrian King 		}
90914fdd7c7aSBrian King 	} else
90924fdd7c7aSBrian King 		ipr_cmd->job_step = ipr_reset_alert;
90934fdd7c7aSBrian King 
90944fdd7c7aSBrian King 	LEAVE;
90954fdd7c7aSBrian King 	return rc;
90964fdd7c7aSBrian King }
90974fdd7c7aSBrian King 
90984fdd7c7aSBrian King /**
90991da177e4SLinus Torvalds  * ipr_reset_ucode_download_done - Microcode download completion
91001da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
91011da177e4SLinus Torvalds  *
91021da177e4SLinus Torvalds  * Description: This function unmaps the microcode download buffer.
91031da177e4SLinus Torvalds  *
91041da177e4SLinus Torvalds  * Return value:
91051da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE
91061da177e4SLinus Torvalds  **/
91071da177e4SLinus Torvalds static int ipr_reset_ucode_download_done(struct ipr_cmnd *ipr_cmd)
91081da177e4SLinus Torvalds {
91091da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
91101da177e4SLinus Torvalds 	struct ipr_sglist *sglist = ioa_cfg->ucode_sglist;
91111da177e4SLinus Torvalds 
9112d73341bfSAnton Blanchard 	dma_unmap_sg(&ioa_cfg->pdev->dev, sglist->scatterlist,
91131da177e4SLinus Torvalds 		     sglist->num_sg, DMA_TO_DEVICE);
91141da177e4SLinus Torvalds 
91151da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_reset_alert;
91161da177e4SLinus Torvalds 	return IPR_RC_JOB_CONTINUE;
91171da177e4SLinus Torvalds }
91181da177e4SLinus Torvalds 
91191da177e4SLinus Torvalds /**
91201da177e4SLinus Torvalds  * ipr_reset_ucode_download - Download microcode to the adapter
91211da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
91221da177e4SLinus Torvalds  *
91231da177e4SLinus Torvalds  * Description: This function checks to see if it there is microcode
91241da177e4SLinus Torvalds  * to download to the adapter. If there is, a download is performed.
91251da177e4SLinus Torvalds  *
91261da177e4SLinus Torvalds  * Return value:
91271da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
91281da177e4SLinus Torvalds  **/
91291da177e4SLinus Torvalds static int ipr_reset_ucode_download(struct ipr_cmnd *ipr_cmd)
91301da177e4SLinus Torvalds {
91311da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
91321da177e4SLinus Torvalds 	struct ipr_sglist *sglist = ioa_cfg->ucode_sglist;
91331da177e4SLinus Torvalds 
91341da177e4SLinus Torvalds 	ENTER;
91351da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_reset_alert;
91361da177e4SLinus Torvalds 
91371da177e4SLinus Torvalds 	if (!sglist)
91381da177e4SLinus Torvalds 		return IPR_RC_JOB_CONTINUE;
91391da177e4SLinus Torvalds 
91401da177e4SLinus Torvalds 	ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
91411da177e4SLinus Torvalds 	ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
91421da177e4SLinus Torvalds 	ipr_cmd->ioarcb.cmd_pkt.cdb[0] = WRITE_BUFFER;
91431da177e4SLinus Torvalds 	ipr_cmd->ioarcb.cmd_pkt.cdb[1] = IPR_WR_BUF_DOWNLOAD_AND_SAVE;
91441da177e4SLinus Torvalds 	ipr_cmd->ioarcb.cmd_pkt.cdb[6] = (sglist->buffer_len & 0xff0000) >> 16;
91451da177e4SLinus Torvalds 	ipr_cmd->ioarcb.cmd_pkt.cdb[7] = (sglist->buffer_len & 0x00ff00) >> 8;
91461da177e4SLinus Torvalds 	ipr_cmd->ioarcb.cmd_pkt.cdb[8] = sglist->buffer_len & 0x0000ff;
91471da177e4SLinus Torvalds 
9148a32c055fSWayne Boyer 	if (ioa_cfg->sis64)
9149a32c055fSWayne Boyer 		ipr_build_ucode_ioadl64(ipr_cmd, sglist);
9150a32c055fSWayne Boyer 	else
915112baa420Sbrking@us.ibm.com 		ipr_build_ucode_ioadl(ipr_cmd, sglist);
91521da177e4SLinus Torvalds 	ipr_cmd->job_step = ipr_reset_ucode_download_done;
91531da177e4SLinus Torvalds 
91541da177e4SLinus Torvalds 	ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
91551da177e4SLinus Torvalds 		   IPR_WRITE_BUFFER_TIMEOUT);
91561da177e4SLinus Torvalds 
91571da177e4SLinus Torvalds 	LEAVE;
91581da177e4SLinus Torvalds 	return IPR_RC_JOB_RETURN;
91591da177e4SLinus Torvalds }
91601da177e4SLinus Torvalds 
91611da177e4SLinus Torvalds /**
91621da177e4SLinus Torvalds  * ipr_reset_shutdown_ioa - Shutdown the adapter
91631da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
91641da177e4SLinus Torvalds  *
91651da177e4SLinus Torvalds  * Description: This function issues an adapter shutdown of the
91661da177e4SLinus Torvalds  * specified type to the specified adapter as part of the
91671da177e4SLinus Torvalds  * adapter reset job.
91681da177e4SLinus Torvalds  *
91691da177e4SLinus Torvalds  * Return value:
91701da177e4SLinus Torvalds  * 	IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
91711da177e4SLinus Torvalds  **/
91721da177e4SLinus Torvalds static int ipr_reset_shutdown_ioa(struct ipr_cmnd *ipr_cmd)
91731da177e4SLinus Torvalds {
91741da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
91751da177e4SLinus Torvalds 	enum ipr_shutdown_type shutdown_type = ipr_cmd->u.shutdown_type;
91761da177e4SLinus Torvalds 	unsigned long timeout;
91771da177e4SLinus Torvalds 	int rc = IPR_RC_JOB_CONTINUE;
91781da177e4SLinus Torvalds 
91791da177e4SLinus Torvalds 	ENTER;
91804fdd7c7aSBrian King 	if (shutdown_type == IPR_SHUTDOWN_QUIESCE)
91814fdd7c7aSBrian King 		ipr_cmd->job_step = ipr_reset_cancel_hcam;
91824fdd7c7aSBrian King 	else if (shutdown_type != IPR_SHUTDOWN_NONE &&
918356d6aa33Swenxiong@linux.vnet.ibm.com 			!ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead) {
91841da177e4SLinus Torvalds 		ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
91851da177e4SLinus Torvalds 		ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
91861da177e4SLinus Torvalds 		ipr_cmd->ioarcb.cmd_pkt.cdb[0] = IPR_IOA_SHUTDOWN;
91871da177e4SLinus Torvalds 		ipr_cmd->ioarcb.cmd_pkt.cdb[1] = shutdown_type;
91881da177e4SLinus Torvalds 
9189ac09c349SBrian King 		if (shutdown_type == IPR_SHUTDOWN_NORMAL)
9190ac09c349SBrian King 			timeout = IPR_SHUTDOWN_TIMEOUT;
91911da177e4SLinus Torvalds 		else if (shutdown_type == IPR_SHUTDOWN_PREPARE_FOR_NORMAL)
91921da177e4SLinus Torvalds 			timeout = IPR_INTERNAL_TIMEOUT;
9193ac09c349SBrian King 		else if (ioa_cfg->dual_raid && ipr_dual_ioa_raid)
9194ac09c349SBrian King 			timeout = IPR_DUAL_IOA_ABBR_SHUTDOWN_TO;
91951da177e4SLinus Torvalds 		else
9196ac09c349SBrian King 			timeout = IPR_ABBREV_SHUTDOWN_TIMEOUT;
91971da177e4SLinus Torvalds 
91981da177e4SLinus Torvalds 		ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, timeout);
91991da177e4SLinus Torvalds 
92001da177e4SLinus Torvalds 		rc = IPR_RC_JOB_RETURN;
92011da177e4SLinus Torvalds 		ipr_cmd->job_step = ipr_reset_ucode_download;
92021da177e4SLinus Torvalds 	} else
92031da177e4SLinus Torvalds 		ipr_cmd->job_step = ipr_reset_alert;
92041da177e4SLinus Torvalds 
92051da177e4SLinus Torvalds 	LEAVE;
92061da177e4SLinus Torvalds 	return rc;
92071da177e4SLinus Torvalds }
92081da177e4SLinus Torvalds 
92091da177e4SLinus Torvalds /**
92101da177e4SLinus Torvalds  * ipr_reset_ioa_job - Adapter reset job
92111da177e4SLinus Torvalds  * @ipr_cmd:	ipr command struct
92121da177e4SLinus Torvalds  *
92131da177e4SLinus Torvalds  * Description: This function is the job router for the adapter reset job.
92141da177e4SLinus Torvalds  *
92151da177e4SLinus Torvalds  * Return value:
92161da177e4SLinus Torvalds  * 	none
92171da177e4SLinus Torvalds  **/
92181da177e4SLinus Torvalds static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
92191da177e4SLinus Torvalds {
92201da177e4SLinus Torvalds 	u32 rc, ioasc;
92211da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
92221da177e4SLinus Torvalds 
92231da177e4SLinus Torvalds 	do {
922496d21f00SWayne Boyer 		ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
92251da177e4SLinus Torvalds 
92261da177e4SLinus Torvalds 		if (ioa_cfg->reset_cmd != ipr_cmd) {
92271da177e4SLinus Torvalds 			/*
92281da177e4SLinus Torvalds 			 * We are doing nested adapter resets and this is
92291da177e4SLinus Torvalds 			 * not the current reset job.
92301da177e4SLinus Torvalds 			 */
923105a6538aSwenxiong@linux.vnet.ibm.com 			list_add_tail(&ipr_cmd->queue,
923205a6538aSwenxiong@linux.vnet.ibm.com 					&ipr_cmd->hrrq->hrrq_free_q);
92331da177e4SLinus Torvalds 			return;
92341da177e4SLinus Torvalds 		}
92351da177e4SLinus Torvalds 
92361da177e4SLinus Torvalds 		if (IPR_IOASC_SENSE_KEY(ioasc)) {
9237dfed823eSbrking@us.ibm.com 			rc = ipr_cmd->job_step_failed(ipr_cmd);
9238dfed823eSbrking@us.ibm.com 			if (rc == IPR_RC_JOB_RETURN)
92391da177e4SLinus Torvalds 				return;
92401da177e4SLinus Torvalds 		}
92411da177e4SLinus Torvalds 
92421da177e4SLinus Torvalds 		ipr_reinit_ipr_cmnd(ipr_cmd);
9243dfed823eSbrking@us.ibm.com 		ipr_cmd->job_step_failed = ipr_reset_cmd_failed;
92441da177e4SLinus Torvalds 		rc = ipr_cmd->job_step(ipr_cmd);
92451da177e4SLinus Torvalds 	} while (rc == IPR_RC_JOB_CONTINUE);
92461da177e4SLinus Torvalds }
92471da177e4SLinus Torvalds 
92481da177e4SLinus Torvalds /**
92491da177e4SLinus Torvalds  * _ipr_initiate_ioa_reset - Initiate an adapter reset
92501da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
92511da177e4SLinus Torvalds  * @job_step:		first job step of reset job
92521da177e4SLinus Torvalds  * @shutdown_type:	shutdown type
92531da177e4SLinus Torvalds  *
92541da177e4SLinus Torvalds  * Description: This function will initiate the reset of the given adapter
92551da177e4SLinus Torvalds  * starting at the selected job step.
92561da177e4SLinus Torvalds  * If the caller needs to wait on the completion of the reset,
92571da177e4SLinus Torvalds  * the caller must sleep on the reset_wait_q.
92581da177e4SLinus Torvalds  *
92591da177e4SLinus Torvalds  * Return value:
92601da177e4SLinus Torvalds  * 	none
92611da177e4SLinus Torvalds  **/
92621da177e4SLinus Torvalds static void _ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
92631da177e4SLinus Torvalds 				    int (*job_step) (struct ipr_cmnd *),
92641da177e4SLinus Torvalds 				    enum ipr_shutdown_type shutdown_type)
92651da177e4SLinus Torvalds {
92661da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd;
926756d6aa33Swenxiong@linux.vnet.ibm.com 	int i;
92681da177e4SLinus Torvalds 
92691da177e4SLinus Torvalds 	ioa_cfg->in_reset_reload = 1;
927056d6aa33Swenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
927156d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&ioa_cfg->hrrq[i]._lock);
927256d6aa33Swenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].allow_cmds = 0;
927356d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ioa_cfg->hrrq[i]._lock);
927456d6aa33Swenxiong@linux.vnet.ibm.com 	}
927556d6aa33Swenxiong@linux.vnet.ibm.com 	wmb();
9276b0e17a9bSBrian King 	if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) {
9277b0e17a9bSBrian King 		ioa_cfg->scsi_unblock = 0;
9278b0e17a9bSBrian King 		ioa_cfg->scsi_blocked = 1;
92791da177e4SLinus Torvalds 		scsi_block_requests(ioa_cfg->host);
9280b0e17a9bSBrian King 	}
92811da177e4SLinus Torvalds 
92821da177e4SLinus Torvalds 	ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
92831da177e4SLinus Torvalds 	ioa_cfg->reset_cmd = ipr_cmd;
92841da177e4SLinus Torvalds 	ipr_cmd->job_step = job_step;
92851da177e4SLinus Torvalds 	ipr_cmd->u.shutdown_type = shutdown_type;
92861da177e4SLinus Torvalds 
92871da177e4SLinus Torvalds 	ipr_reset_ioa_job(ipr_cmd);
92881da177e4SLinus Torvalds }
92891da177e4SLinus Torvalds 
92901da177e4SLinus Torvalds /**
92911da177e4SLinus Torvalds  * ipr_initiate_ioa_reset - Initiate an adapter reset
92921da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
92931da177e4SLinus Torvalds  * @shutdown_type:	shutdown type
92941da177e4SLinus Torvalds  *
92951da177e4SLinus Torvalds  * Description: This function will initiate the reset of the given adapter.
92961da177e4SLinus Torvalds  * If the caller needs to wait on the completion of the reset,
92971da177e4SLinus Torvalds  * the caller must sleep on the reset_wait_q.
92981da177e4SLinus Torvalds  *
92991da177e4SLinus Torvalds  * Return value:
93001da177e4SLinus Torvalds  * 	none
93011da177e4SLinus Torvalds  **/
93021da177e4SLinus Torvalds static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
93031da177e4SLinus Torvalds 				   enum ipr_shutdown_type shutdown_type)
93041da177e4SLinus Torvalds {
930556d6aa33Swenxiong@linux.vnet.ibm.com 	int i;
930656d6aa33Swenxiong@linux.vnet.ibm.com 
930756d6aa33Swenxiong@linux.vnet.ibm.com 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
93081da177e4SLinus Torvalds 		return;
93091da177e4SLinus Torvalds 
931041e9a696SBrian King 	if (ioa_cfg->in_reset_reload) {
931141e9a696SBrian King 		if (ioa_cfg->sdt_state == GET_DUMP)
931241e9a696SBrian King 			ioa_cfg->sdt_state = WAIT_FOR_DUMP;
931341e9a696SBrian King 		else if (ioa_cfg->sdt_state == READ_DUMP)
93141da177e4SLinus Torvalds 			ioa_cfg->sdt_state = ABORT_DUMP;
931541e9a696SBrian King 	}
93161da177e4SLinus Torvalds 
93171da177e4SLinus Torvalds 	if (ioa_cfg->reset_retries++ >= IPR_NUM_RESET_RELOAD_RETRIES) {
93181da177e4SLinus Torvalds 		dev_err(&ioa_cfg->pdev->dev,
93191da177e4SLinus Torvalds 			"IOA taken offline - error recovery failed\n");
93201da177e4SLinus Torvalds 
93211da177e4SLinus Torvalds 		ioa_cfg->reset_retries = 0;
932256d6aa33Swenxiong@linux.vnet.ibm.com 		for (i = 0; i < ioa_cfg->hrrq_num; i++) {
932356d6aa33Swenxiong@linux.vnet.ibm.com 			spin_lock(&ioa_cfg->hrrq[i]._lock);
932456d6aa33Swenxiong@linux.vnet.ibm.com 			ioa_cfg->hrrq[i].ioa_is_dead = 1;
932556d6aa33Swenxiong@linux.vnet.ibm.com 			spin_unlock(&ioa_cfg->hrrq[i]._lock);
932656d6aa33Swenxiong@linux.vnet.ibm.com 		}
932756d6aa33Swenxiong@linux.vnet.ibm.com 		wmb();
93281da177e4SLinus Torvalds 
93291da177e4SLinus Torvalds 		if (ioa_cfg->in_ioa_bringdown) {
93301da177e4SLinus Torvalds 			ioa_cfg->reset_cmd = NULL;
93311da177e4SLinus Torvalds 			ioa_cfg->in_reset_reload = 0;
93321da177e4SLinus Torvalds 			ipr_fail_all_ops(ioa_cfg);
93331da177e4SLinus Torvalds 			wake_up_all(&ioa_cfg->reset_wait_q);
93341da177e4SLinus Torvalds 
9335bfae7820SBrian King 			if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) {
9336b0e17a9bSBrian King 				ioa_cfg->scsi_unblock = 1;
9337b0e17a9bSBrian King 				schedule_work(&ioa_cfg->work_q);
9338bfae7820SBrian King 			}
93391da177e4SLinus Torvalds 			return;
93401da177e4SLinus Torvalds 		} else {
93411da177e4SLinus Torvalds 			ioa_cfg->in_ioa_bringdown = 1;
93421da177e4SLinus Torvalds 			shutdown_type = IPR_SHUTDOWN_NONE;
93431da177e4SLinus Torvalds 		}
93441da177e4SLinus Torvalds 	}
93451da177e4SLinus Torvalds 
93461da177e4SLinus Torvalds 	_ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_shutdown_ioa,
93471da177e4SLinus Torvalds 				shutdown_type);
93481da177e4SLinus Torvalds }
93491da177e4SLinus Torvalds 
93501da177e4SLinus Torvalds /**
9351f8a88b19SLinas Vepstas  * ipr_reset_freeze - Hold off all I/O activity
9352f8a88b19SLinas Vepstas  * @ipr_cmd:	ipr command struct
9353f8a88b19SLinas Vepstas  *
9354f8a88b19SLinas Vepstas  * Description: If the PCI slot is frozen, hold off all I/O
9355f8a88b19SLinas Vepstas  * activity; then, as soon as the slot is available again,
9356f8a88b19SLinas Vepstas  * initiate an adapter reset.
9357f8a88b19SLinas Vepstas  */
9358f8a88b19SLinas Vepstas static int ipr_reset_freeze(struct ipr_cmnd *ipr_cmd)
9359f8a88b19SLinas Vepstas {
936056d6aa33Swenxiong@linux.vnet.ibm.com 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
936156d6aa33Swenxiong@linux.vnet.ibm.com 	int i;
936256d6aa33Swenxiong@linux.vnet.ibm.com 
9363f8a88b19SLinas Vepstas 	/* Disallow new interrupts, avoid loop */
936456d6aa33Swenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
936556d6aa33Swenxiong@linux.vnet.ibm.com 		spin_lock(&ioa_cfg->hrrq[i]._lock);
936656d6aa33Swenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].allow_interrupts = 0;
936756d6aa33Swenxiong@linux.vnet.ibm.com 		spin_unlock(&ioa_cfg->hrrq[i]._lock);
936856d6aa33Swenxiong@linux.vnet.ibm.com 	}
936956d6aa33Swenxiong@linux.vnet.ibm.com 	wmb();
937005a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
9371f8a88b19SLinas Vepstas 	ipr_cmd->done = ipr_reset_ioa_job;
9372f8a88b19SLinas Vepstas 	return IPR_RC_JOB_RETURN;
9373f8a88b19SLinas Vepstas }
9374f8a88b19SLinas Vepstas 
9375f8a88b19SLinas Vepstas /**
93766270e593SBrian King  * ipr_pci_mmio_enabled - Called when MMIO has been re-enabled
93776270e593SBrian King  * @pdev:	PCI device struct
93786270e593SBrian King  *
93796270e593SBrian King  * Description: This routine is called to tell us that the MMIO
93806270e593SBrian King  * access to the IOA has been restored
93816270e593SBrian King  */
93826270e593SBrian King static pci_ers_result_t ipr_pci_mmio_enabled(struct pci_dev *pdev)
93836270e593SBrian King {
93846270e593SBrian King 	unsigned long flags = 0;
93856270e593SBrian King 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
93866270e593SBrian King 
93876270e593SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
93886270e593SBrian King 	if (!ioa_cfg->probe_done)
93896270e593SBrian King 		pci_save_state(pdev);
93906270e593SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
93916270e593SBrian King 	return PCI_ERS_RESULT_NEED_RESET;
93926270e593SBrian King }
93936270e593SBrian King 
93946270e593SBrian King /**
9395f8a88b19SLinas Vepstas  * ipr_pci_frozen - Called when slot has experienced a PCI bus error.
9396f8a88b19SLinas Vepstas  * @pdev:	PCI device struct
9397f8a88b19SLinas Vepstas  *
9398f8a88b19SLinas Vepstas  * Description: This routine is called to tell us that the PCI bus
9399f8a88b19SLinas Vepstas  * is down. Can't do anything here, except put the device driver
9400f8a88b19SLinas Vepstas  * into a holding pattern, waiting for the PCI bus to come back.
9401f8a88b19SLinas Vepstas  */
9402f8a88b19SLinas Vepstas static void ipr_pci_frozen(struct pci_dev *pdev)
9403f8a88b19SLinas Vepstas {
9404f8a88b19SLinas Vepstas 	unsigned long flags = 0;
9405f8a88b19SLinas Vepstas 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
9406f8a88b19SLinas Vepstas 
9407f8a88b19SLinas Vepstas 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
94086270e593SBrian King 	if (ioa_cfg->probe_done)
9409f8a88b19SLinas Vepstas 		_ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_freeze, IPR_SHUTDOWN_NONE);
9410f8a88b19SLinas Vepstas 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
9411f8a88b19SLinas Vepstas }
9412f8a88b19SLinas Vepstas 
9413f8a88b19SLinas Vepstas /**
9414f8a88b19SLinas Vepstas  * ipr_pci_slot_reset - Called when PCI slot has been reset.
9415f8a88b19SLinas Vepstas  * @pdev:	PCI device struct
9416f8a88b19SLinas Vepstas  *
9417f8a88b19SLinas Vepstas  * Description: This routine is called by the pci error recovery
9418f8a88b19SLinas Vepstas  * code after the PCI slot has been reset, just before we
9419f8a88b19SLinas Vepstas  * should resume normal operations.
9420f8a88b19SLinas Vepstas  */
9421f8a88b19SLinas Vepstas static pci_ers_result_t ipr_pci_slot_reset(struct pci_dev *pdev)
9422f8a88b19SLinas Vepstas {
9423f8a88b19SLinas Vepstas 	unsigned long flags = 0;
9424f8a88b19SLinas Vepstas 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
9425f8a88b19SLinas Vepstas 
9426f8a88b19SLinas Vepstas 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
94276270e593SBrian King 	if (ioa_cfg->probe_done) {
9428463fc696SBrian King 		if (ioa_cfg->needs_warm_reset)
9429463fc696SBrian King 			ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
9430463fc696SBrian King 		else
9431f8a88b19SLinas Vepstas 			_ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_restore_cfg_space,
9432f8a88b19SLinas Vepstas 						IPR_SHUTDOWN_NONE);
94336270e593SBrian King 	} else
94346270e593SBrian King 		wake_up_all(&ioa_cfg->eeh_wait_q);
9435f8a88b19SLinas Vepstas 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
9436f8a88b19SLinas Vepstas 	return PCI_ERS_RESULT_RECOVERED;
9437f8a88b19SLinas Vepstas }
9438f8a88b19SLinas Vepstas 
9439f8a88b19SLinas Vepstas /**
9440f8a88b19SLinas Vepstas  * ipr_pci_perm_failure - Called when PCI slot is dead for good.
9441f8a88b19SLinas Vepstas  * @pdev:	PCI device struct
9442f8a88b19SLinas Vepstas  *
9443f8a88b19SLinas Vepstas  * Description: This routine is called when the PCI bus has
9444f8a88b19SLinas Vepstas  * permanently failed.
9445f8a88b19SLinas Vepstas  */
9446f8a88b19SLinas Vepstas static void ipr_pci_perm_failure(struct pci_dev *pdev)
9447f8a88b19SLinas Vepstas {
9448f8a88b19SLinas Vepstas 	unsigned long flags = 0;
9449f8a88b19SLinas Vepstas 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
945056d6aa33Swenxiong@linux.vnet.ibm.com 	int i;
9451f8a88b19SLinas Vepstas 
9452f8a88b19SLinas Vepstas 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
94536270e593SBrian King 	if (ioa_cfg->probe_done) {
9454f8a88b19SLinas Vepstas 		if (ioa_cfg->sdt_state == WAIT_FOR_DUMP)
9455f8a88b19SLinas Vepstas 			ioa_cfg->sdt_state = ABORT_DUMP;
945696b04db9Swenxiong@linux.vnet.ibm.com 		ioa_cfg->reset_retries = IPR_NUM_RESET_RELOAD_RETRIES - 1;
9457f8a88b19SLinas Vepstas 		ioa_cfg->in_ioa_bringdown = 1;
945856d6aa33Swenxiong@linux.vnet.ibm.com 		for (i = 0; i < ioa_cfg->hrrq_num; i++) {
945956d6aa33Swenxiong@linux.vnet.ibm.com 			spin_lock(&ioa_cfg->hrrq[i]._lock);
946056d6aa33Swenxiong@linux.vnet.ibm.com 			ioa_cfg->hrrq[i].allow_cmds = 0;
946156d6aa33Swenxiong@linux.vnet.ibm.com 			spin_unlock(&ioa_cfg->hrrq[i]._lock);
946256d6aa33Swenxiong@linux.vnet.ibm.com 		}
946356d6aa33Swenxiong@linux.vnet.ibm.com 		wmb();
9464f8a88b19SLinas Vepstas 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
94656270e593SBrian King 	} else
94666270e593SBrian King 		wake_up_all(&ioa_cfg->eeh_wait_q);
9467f8a88b19SLinas Vepstas 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
9468f8a88b19SLinas Vepstas }
9469f8a88b19SLinas Vepstas 
9470f8a88b19SLinas Vepstas /**
9471f8a88b19SLinas Vepstas  * ipr_pci_error_detected - Called when a PCI error is detected.
9472f8a88b19SLinas Vepstas  * @pdev:	PCI device struct
9473f8a88b19SLinas Vepstas  * @state:	PCI channel state
9474f8a88b19SLinas Vepstas  *
9475f8a88b19SLinas Vepstas  * Description: Called when a PCI error is detected.
9476f8a88b19SLinas Vepstas  *
9477f8a88b19SLinas Vepstas  * Return value:
9478f8a88b19SLinas Vepstas  * 	PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT
9479f8a88b19SLinas Vepstas  */
9480f8a88b19SLinas Vepstas static pci_ers_result_t ipr_pci_error_detected(struct pci_dev *pdev,
9481f8a88b19SLinas Vepstas 					       pci_channel_state_t state)
9482f8a88b19SLinas Vepstas {
9483f8a88b19SLinas Vepstas 	switch (state) {
9484f8a88b19SLinas Vepstas 	case pci_channel_io_frozen:
9485f8a88b19SLinas Vepstas 		ipr_pci_frozen(pdev);
94866270e593SBrian King 		return PCI_ERS_RESULT_CAN_RECOVER;
9487f8a88b19SLinas Vepstas 	case pci_channel_io_perm_failure:
9488f8a88b19SLinas Vepstas 		ipr_pci_perm_failure(pdev);
9489f8a88b19SLinas Vepstas 		return PCI_ERS_RESULT_DISCONNECT;
9490f8a88b19SLinas Vepstas 	default:
9491f8a88b19SLinas Vepstas 		break;
9492f8a88b19SLinas Vepstas 	}
9493f8a88b19SLinas Vepstas 	return PCI_ERS_RESULT_NEED_RESET;
9494f8a88b19SLinas Vepstas }
9495f8a88b19SLinas Vepstas 
9496f8a88b19SLinas Vepstas /**
94971da177e4SLinus Torvalds  * ipr_probe_ioa_part2 - Initializes IOAs found in ipr_probe_ioa(..)
94981da177e4SLinus Torvalds  * @ioa_cfg:	ioa cfg struct
94991da177e4SLinus Torvalds  *
9500183b8021SMasahiro Yamada  * Description: This is the second phase of adapter initialization
95011da177e4SLinus Torvalds  * This function takes care of initilizing the adapter to the point
95021da177e4SLinus Torvalds  * where it can accept new commands.
95031da177e4SLinus Torvalds  * Return value:
9504b1c11812SJoe Perches  * 	0 on success / -EIO on failure
95051da177e4SLinus Torvalds  **/
95066f039790SGreg Kroah-Hartman static int ipr_probe_ioa_part2(struct ipr_ioa_cfg *ioa_cfg)
95071da177e4SLinus Torvalds {
95081da177e4SLinus Torvalds 	int rc = 0;
95091da177e4SLinus Torvalds 	unsigned long host_lock_flags = 0;
95101da177e4SLinus Torvalds 
95111da177e4SLinus Torvalds 	ENTER;
95121da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
95131da177e4SLinus Torvalds 	dev_dbg(&ioa_cfg->pdev->dev, "ioa_cfg adx: 0x%p\n", ioa_cfg);
95146270e593SBrian King 	ioa_cfg->probe_done = 1;
9515ce155cceSbrking@us.ibm.com 	if (ioa_cfg->needs_hard_reset) {
9516ce155cceSbrking@us.ibm.com 		ioa_cfg->needs_hard_reset = 0;
9517ce155cceSbrking@us.ibm.com 		ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
9518ce155cceSbrking@us.ibm.com 	} else
9519ce155cceSbrking@us.ibm.com 		_ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_enable_ioa,
9520ce155cceSbrking@us.ibm.com 					IPR_SHUTDOWN_NONE);
95211da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
95221da177e4SLinus Torvalds 
95231da177e4SLinus Torvalds 	LEAVE;
95241da177e4SLinus Torvalds 	return rc;
95251da177e4SLinus Torvalds }
95261da177e4SLinus Torvalds 
95271da177e4SLinus Torvalds /**
95281da177e4SLinus Torvalds  * ipr_free_cmd_blks - Frees command blocks allocated for an adapter
95291da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
95301da177e4SLinus Torvalds  *
95311da177e4SLinus Torvalds  * Return value:
95321da177e4SLinus Torvalds  * 	none
95331da177e4SLinus Torvalds  **/
95341da177e4SLinus Torvalds static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
95351da177e4SLinus Torvalds {
95361da177e4SLinus Torvalds 	int i;
95371da177e4SLinus Torvalds 
9538a65e8f12SBrian King 	if (ioa_cfg->ipr_cmnd_list) {
95391da177e4SLinus Torvalds 		for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
95401da177e4SLinus Torvalds 			if (ioa_cfg->ipr_cmnd_list[i])
9541d73341bfSAnton Blanchard 				dma_pool_free(ioa_cfg->ipr_cmd_pool,
95421da177e4SLinus Torvalds 					      ioa_cfg->ipr_cmnd_list[i],
95431da177e4SLinus Torvalds 					      ioa_cfg->ipr_cmnd_list_dma[i]);
95441da177e4SLinus Torvalds 
95451da177e4SLinus Torvalds 			ioa_cfg->ipr_cmnd_list[i] = NULL;
95461da177e4SLinus Torvalds 		}
9547a65e8f12SBrian King 	}
95481da177e4SLinus Torvalds 
9549d73341bfSAnton Blanchard 	dma_pool_destroy(ioa_cfg->ipr_cmd_pool);
95501da177e4SLinus Torvalds 
955189aad428SBrian King 	kfree(ioa_cfg->ipr_cmnd_list);
955289aad428SBrian King 	kfree(ioa_cfg->ipr_cmnd_list_dma);
955389aad428SBrian King 	ioa_cfg->ipr_cmnd_list = NULL;
955489aad428SBrian King 	ioa_cfg->ipr_cmnd_list_dma = NULL;
95551da177e4SLinus Torvalds 	ioa_cfg->ipr_cmd_pool = NULL;
95561da177e4SLinus Torvalds }
95571da177e4SLinus Torvalds 
95581da177e4SLinus Torvalds /**
95591da177e4SLinus Torvalds  * ipr_free_mem - Frees memory allocated for an adapter
95601da177e4SLinus Torvalds  * @ioa_cfg:	ioa cfg struct
95611da177e4SLinus Torvalds  *
95621da177e4SLinus Torvalds  * Return value:
95631da177e4SLinus Torvalds  * 	nothing
95641da177e4SLinus Torvalds  **/
95651da177e4SLinus Torvalds static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg)
95661da177e4SLinus Torvalds {
95671da177e4SLinus Torvalds 	int i;
95681da177e4SLinus Torvalds 
95691da177e4SLinus Torvalds 	kfree(ioa_cfg->res_entries);
9570d73341bfSAnton Blanchard 	dma_free_coherent(&ioa_cfg->pdev->dev, sizeof(struct ipr_misc_cbs),
95711da177e4SLinus Torvalds 			  ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
95721da177e4SLinus Torvalds 	ipr_free_cmd_blks(ioa_cfg);
957305a6538aSwenxiong@linux.vnet.ibm.com 
957405a6538aSwenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++)
9575d73341bfSAnton Blanchard 		dma_free_coherent(&ioa_cfg->pdev->dev,
957605a6538aSwenxiong@linux.vnet.ibm.com 				  sizeof(u32) * ioa_cfg->hrrq[i].size,
957705a6538aSwenxiong@linux.vnet.ibm.com 				  ioa_cfg->hrrq[i].host_rrq,
957805a6538aSwenxiong@linux.vnet.ibm.com 				  ioa_cfg->hrrq[i].host_rrq_dma);
957905a6538aSwenxiong@linux.vnet.ibm.com 
9580d73341bfSAnton Blanchard 	dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size,
9581d73341bfSAnton Blanchard 			  ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
95821da177e4SLinus Torvalds 
9583afc3f83cSBrian King 	for (i = 0; i < IPR_MAX_HCAMS; i++) {
9584d73341bfSAnton Blanchard 		dma_free_coherent(&ioa_cfg->pdev->dev,
95851da177e4SLinus Torvalds 				  sizeof(struct ipr_hostrcb),
95861da177e4SLinus Torvalds 				  ioa_cfg->hostrcb[i],
95871da177e4SLinus Torvalds 				  ioa_cfg->hostrcb_dma[i]);
95881da177e4SLinus Torvalds 	}
95891da177e4SLinus Torvalds 
95901da177e4SLinus Torvalds 	ipr_free_dump(ioa_cfg);
95911da177e4SLinus Torvalds 	kfree(ioa_cfg->trace);
95921da177e4SLinus Torvalds }
95931da177e4SLinus Torvalds 
95941da177e4SLinus Torvalds /**
95952796ca5eSBrian King  * ipr_free_irqs - Free all allocated IRQs for the adapter.
95962796ca5eSBrian King  * @ioa_cfg:	ipr cfg struct
95971da177e4SLinus Torvalds  *
95982796ca5eSBrian King  * This function frees all allocated IRQs for the
95991da177e4SLinus Torvalds  * specified adapter.
96001da177e4SLinus Torvalds  *
96011da177e4SLinus Torvalds  * Return value:
96021da177e4SLinus Torvalds  * 	none
96031da177e4SLinus Torvalds  **/
96042796ca5eSBrian King static void ipr_free_irqs(struct ipr_ioa_cfg *ioa_cfg)
96051da177e4SLinus Torvalds {
96061da177e4SLinus Torvalds 	struct pci_dev *pdev = ioa_cfg->pdev;
960705a6538aSwenxiong@linux.vnet.ibm.com 	int i;
960805a6538aSwenxiong@linux.vnet.ibm.com 
9609a299ee62SChristoph Hellwig 	for (i = 0; i < ioa_cfg->nvectors; i++)
9610a299ee62SChristoph Hellwig 		free_irq(pci_irq_vector(pdev, i), &ioa_cfg->hrrq[i]);
9611a299ee62SChristoph Hellwig 	pci_free_irq_vectors(pdev);
96122796ca5eSBrian King }
961305a6538aSwenxiong@linux.vnet.ibm.com 
96142796ca5eSBrian King /**
96152796ca5eSBrian King  * ipr_free_all_resources - Free all allocated resources for an adapter.
9616a96099e2SLee Jones  * @ioa_cfg:	ioa config struct
96172796ca5eSBrian King  *
96182796ca5eSBrian King  * This function frees all allocated resources for the
96192796ca5eSBrian King  * specified adapter.
96202796ca5eSBrian King  *
96212796ca5eSBrian King  * Return value:
96222796ca5eSBrian King  * 	none
96232796ca5eSBrian King  **/
96242796ca5eSBrian King static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg)
96252796ca5eSBrian King {
96262796ca5eSBrian King 	struct pci_dev *pdev = ioa_cfg->pdev;
96272796ca5eSBrian King 
96282796ca5eSBrian King 	ENTER;
96292796ca5eSBrian King 	ipr_free_irqs(ioa_cfg);
96302796ca5eSBrian King 	if (ioa_cfg->reset_work_q)
96312796ca5eSBrian King 		destroy_workqueue(ioa_cfg->reset_work_q);
96321da177e4SLinus Torvalds 	iounmap(ioa_cfg->hdw_dma_regs);
96331da177e4SLinus Torvalds 	pci_release_regions(pdev);
96341da177e4SLinus Torvalds 	ipr_free_mem(ioa_cfg);
96351da177e4SLinus Torvalds 	scsi_host_put(ioa_cfg->host);
96361da177e4SLinus Torvalds 	pci_disable_device(pdev);
96371da177e4SLinus Torvalds 	LEAVE;
96381da177e4SLinus Torvalds }
96391da177e4SLinus Torvalds 
96401da177e4SLinus Torvalds /**
96411da177e4SLinus Torvalds  * ipr_alloc_cmd_blks - Allocate command blocks for an adapter
96421da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
96431da177e4SLinus Torvalds  *
96441da177e4SLinus Torvalds  * Return value:
96451da177e4SLinus Torvalds  * 	0 on success / -ENOMEM on allocation failure
96461da177e4SLinus Torvalds  **/
96476f039790SGreg Kroah-Hartman static int ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
96481da177e4SLinus Torvalds {
96491da177e4SLinus Torvalds 	struct ipr_cmnd *ipr_cmd;
96501da177e4SLinus Torvalds 	struct ipr_ioarcb *ioarcb;
96511da177e4SLinus Torvalds 	dma_addr_t dma_addr;
965205a6538aSwenxiong@linux.vnet.ibm.com 	int i, entries_each_hrrq, hrrq_id = 0;
96531da177e4SLinus Torvalds 
9654d73341bfSAnton Blanchard 	ioa_cfg->ipr_cmd_pool = dma_pool_create(IPR_NAME, &ioa_cfg->pdev->dev,
96551bfff2f8SBrian King 						sizeof(struct ipr_cmnd), 512, 0);
96561da177e4SLinus Torvalds 
96571da177e4SLinus Torvalds 	if (!ioa_cfg->ipr_cmd_pool)
96581da177e4SLinus Torvalds 		return -ENOMEM;
96591da177e4SLinus Torvalds 
966089aad428SBrian King 	ioa_cfg->ipr_cmnd_list = kcalloc(IPR_NUM_CMD_BLKS, sizeof(struct ipr_cmnd *), GFP_KERNEL);
966189aad428SBrian King 	ioa_cfg->ipr_cmnd_list_dma = kcalloc(IPR_NUM_CMD_BLKS, sizeof(dma_addr_t), GFP_KERNEL);
966289aad428SBrian King 
966389aad428SBrian King 	if (!ioa_cfg->ipr_cmnd_list || !ioa_cfg->ipr_cmnd_list_dma) {
966489aad428SBrian King 		ipr_free_cmd_blks(ioa_cfg);
966589aad428SBrian King 		return -ENOMEM;
966689aad428SBrian King 	}
966789aad428SBrian King 
966805a6538aSwenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
966905a6538aSwenxiong@linux.vnet.ibm.com 		if (ioa_cfg->hrrq_num > 1) {
967005a6538aSwenxiong@linux.vnet.ibm.com 			if (i == 0) {
967105a6538aSwenxiong@linux.vnet.ibm.com 				entries_each_hrrq = IPR_NUM_INTERNAL_CMD_BLKS;
967205a6538aSwenxiong@linux.vnet.ibm.com 				ioa_cfg->hrrq[i].min_cmd_id = 0;
967305a6538aSwenxiong@linux.vnet.ibm.com 				ioa_cfg->hrrq[i].max_cmd_id =
967405a6538aSwenxiong@linux.vnet.ibm.com 					(entries_each_hrrq - 1);
967505a6538aSwenxiong@linux.vnet.ibm.com 			} else {
967605a6538aSwenxiong@linux.vnet.ibm.com 				entries_each_hrrq =
967705a6538aSwenxiong@linux.vnet.ibm.com 					IPR_NUM_BASE_CMD_BLKS/
967805a6538aSwenxiong@linux.vnet.ibm.com 					(ioa_cfg->hrrq_num - 1);
967905a6538aSwenxiong@linux.vnet.ibm.com 				ioa_cfg->hrrq[i].min_cmd_id =
968005a6538aSwenxiong@linux.vnet.ibm.com 					IPR_NUM_INTERNAL_CMD_BLKS +
968105a6538aSwenxiong@linux.vnet.ibm.com 					(i - 1) * entries_each_hrrq;
968205a6538aSwenxiong@linux.vnet.ibm.com 				ioa_cfg->hrrq[i].max_cmd_id =
968305a6538aSwenxiong@linux.vnet.ibm.com 					(IPR_NUM_INTERNAL_CMD_BLKS +
968405a6538aSwenxiong@linux.vnet.ibm.com 					i * entries_each_hrrq - 1);
968505a6538aSwenxiong@linux.vnet.ibm.com 			}
968605a6538aSwenxiong@linux.vnet.ibm.com 		} else {
968705a6538aSwenxiong@linux.vnet.ibm.com 			entries_each_hrrq = IPR_NUM_CMD_BLKS;
968805a6538aSwenxiong@linux.vnet.ibm.com 			ioa_cfg->hrrq[i].min_cmd_id = 0;
968905a6538aSwenxiong@linux.vnet.ibm.com 			ioa_cfg->hrrq[i].max_cmd_id = (entries_each_hrrq - 1);
969005a6538aSwenxiong@linux.vnet.ibm.com 		}
969105a6538aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].size = entries_each_hrrq;
969205a6538aSwenxiong@linux.vnet.ibm.com 	}
969305a6538aSwenxiong@linux.vnet.ibm.com 
969405a6538aSwenxiong@linux.vnet.ibm.com 	BUG_ON(ioa_cfg->hrrq_num == 0);
969505a6538aSwenxiong@linux.vnet.ibm.com 
969605a6538aSwenxiong@linux.vnet.ibm.com 	i = IPR_NUM_CMD_BLKS -
969705a6538aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[ioa_cfg->hrrq_num - 1].max_cmd_id - 1;
969805a6538aSwenxiong@linux.vnet.ibm.com 	if (i > 0) {
969905a6538aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[ioa_cfg->hrrq_num - 1].size += i;
970005a6538aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[ioa_cfg->hrrq_num - 1].max_cmd_id += i;
970105a6538aSwenxiong@linux.vnet.ibm.com 	}
970205a6538aSwenxiong@linux.vnet.ibm.com 
97031da177e4SLinus Torvalds 	for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
97048b1bb6dcSSouptick Joarder 		ipr_cmd = dma_pool_zalloc(ioa_cfg->ipr_cmd_pool,
97058b1bb6dcSSouptick Joarder 				GFP_KERNEL, &dma_addr);
97061da177e4SLinus Torvalds 
97071da177e4SLinus Torvalds 		if (!ipr_cmd) {
97081da177e4SLinus Torvalds 			ipr_free_cmd_blks(ioa_cfg);
97091da177e4SLinus Torvalds 			return -ENOMEM;
97101da177e4SLinus Torvalds 		}
97111da177e4SLinus Torvalds 
97121da177e4SLinus Torvalds 		ioa_cfg->ipr_cmnd_list[i] = ipr_cmd;
97131da177e4SLinus Torvalds 		ioa_cfg->ipr_cmnd_list_dma[i] = dma_addr;
97141da177e4SLinus Torvalds 
97151da177e4SLinus Torvalds 		ioarcb = &ipr_cmd->ioarcb;
9716a32c055fSWayne Boyer 		ipr_cmd->dma_addr = dma_addr;
9717a32c055fSWayne Boyer 		if (ioa_cfg->sis64)
9718a32c055fSWayne Boyer 			ioarcb->a.ioarcb_host_pci_addr64 = cpu_to_be64(dma_addr);
9719a32c055fSWayne Boyer 		else
9720a32c055fSWayne Boyer 			ioarcb->a.ioarcb_host_pci_addr = cpu_to_be32(dma_addr);
9721a32c055fSWayne Boyer 
97221da177e4SLinus Torvalds 		ioarcb->host_response_handle = cpu_to_be32(i << 2);
9723a32c055fSWayne Boyer 		if (ioa_cfg->sis64) {
9724a32c055fSWayne Boyer 			ioarcb->u.sis64_addr_data.data_ioadl_addr =
9725a32c055fSWayne Boyer 				cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ioadl64));
9726a32c055fSWayne Boyer 			ioarcb->u.sis64_addr_data.ioasa_host_pci_addr =
972796d21f00SWayne Boyer 				cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, s.ioasa64));
9728a32c055fSWayne Boyer 		} else {
97291da177e4SLinus Torvalds 			ioarcb->write_ioadl_addr =
9730a32c055fSWayne Boyer 				cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, i.ioadl));
97311da177e4SLinus Torvalds 			ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
97321da177e4SLinus Torvalds 			ioarcb->ioasa_host_pci_addr =
973396d21f00SWayne Boyer 				cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, s.ioasa));
9734a32c055fSWayne Boyer 		}
97351da177e4SLinus Torvalds 		ioarcb->ioasa_len = cpu_to_be16(sizeof(struct ipr_ioasa));
97361da177e4SLinus Torvalds 		ipr_cmd->cmd_index = i;
97371da177e4SLinus Torvalds 		ipr_cmd->ioa_cfg = ioa_cfg;
97381da177e4SLinus Torvalds 		ipr_cmd->sense_buffer_dma = dma_addr +
97391da177e4SLinus Torvalds 			offsetof(struct ipr_cmnd, sense_buffer);
97401da177e4SLinus Torvalds 
974105a6538aSwenxiong@linux.vnet.ibm.com 		ipr_cmd->ioarcb.cmd_pkt.hrrq_id = hrrq_id;
974205a6538aSwenxiong@linux.vnet.ibm.com 		ipr_cmd->hrrq = &ioa_cfg->hrrq[hrrq_id];
974305a6538aSwenxiong@linux.vnet.ibm.com 		list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
974405a6538aSwenxiong@linux.vnet.ibm.com 		if (i >= ioa_cfg->hrrq[hrrq_id].max_cmd_id)
974505a6538aSwenxiong@linux.vnet.ibm.com 			hrrq_id++;
97461da177e4SLinus Torvalds 	}
97471da177e4SLinus Torvalds 
97481da177e4SLinus Torvalds 	return 0;
97491da177e4SLinus Torvalds }
97501da177e4SLinus Torvalds 
97511da177e4SLinus Torvalds /**
97521da177e4SLinus Torvalds  * ipr_alloc_mem - Allocate memory for an adapter
97531da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
97541da177e4SLinus Torvalds  *
97551da177e4SLinus Torvalds  * Return value:
97561da177e4SLinus Torvalds  * 	0 on success / non-zero for error
97571da177e4SLinus Torvalds  **/
97586f039790SGreg Kroah-Hartman static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
97591da177e4SLinus Torvalds {
97601da177e4SLinus Torvalds 	struct pci_dev *pdev = ioa_cfg->pdev;
97611da177e4SLinus Torvalds 	int i, rc = -ENOMEM;
97621da177e4SLinus Torvalds 
97631da177e4SLinus Torvalds 	ENTER;
97646396bb22SKees Cook 	ioa_cfg->res_entries = kcalloc(ioa_cfg->max_devs_supported,
97656396bb22SKees Cook 				       sizeof(struct ipr_resource_entry),
97666396bb22SKees Cook 				       GFP_KERNEL);
97671da177e4SLinus Torvalds 
97681da177e4SLinus Torvalds 	if (!ioa_cfg->res_entries)
97691da177e4SLinus Torvalds 		goto out;
97701da177e4SLinus Torvalds 
97713e7ebdfaSWayne Boyer 	for (i = 0; i < ioa_cfg->max_devs_supported; i++) {
97721da177e4SLinus Torvalds 		list_add_tail(&ioa_cfg->res_entries[i].queue, &ioa_cfg->free_res_q);
97733e7ebdfaSWayne Boyer 		ioa_cfg->res_entries[i].ioa_cfg = ioa_cfg;
97743e7ebdfaSWayne Boyer 	}
97751da177e4SLinus Torvalds 
9776d73341bfSAnton Blanchard 	ioa_cfg->vpd_cbs = dma_alloc_coherent(&pdev->dev,
97771da177e4SLinus Torvalds 					      sizeof(struct ipr_misc_cbs),
9778d73341bfSAnton Blanchard 					      &ioa_cfg->vpd_cbs_dma,
9779d73341bfSAnton Blanchard 					      GFP_KERNEL);
97801da177e4SLinus Torvalds 
97811da177e4SLinus Torvalds 	if (!ioa_cfg->vpd_cbs)
97821da177e4SLinus Torvalds 		goto out_free_res_entries;
97831da177e4SLinus Torvalds 
97841da177e4SLinus Torvalds 	if (ipr_alloc_cmd_blks(ioa_cfg))
97851da177e4SLinus Torvalds 		goto out_free_vpd_cbs;
97861da177e4SLinus Torvalds 
978705a6538aSwenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
9788d73341bfSAnton Blanchard 		ioa_cfg->hrrq[i].host_rrq = dma_alloc_coherent(&pdev->dev,
978905a6538aSwenxiong@linux.vnet.ibm.com 					sizeof(u32) * ioa_cfg->hrrq[i].size,
9790d73341bfSAnton Blanchard 					&ioa_cfg->hrrq[i].host_rrq_dma,
9791d73341bfSAnton Blanchard 					GFP_KERNEL);
97921da177e4SLinus Torvalds 
979305a6538aSwenxiong@linux.vnet.ibm.com 		if (!ioa_cfg->hrrq[i].host_rrq)  {
979405a6538aSwenxiong@linux.vnet.ibm.com 			while (--i > 0)
9795d73341bfSAnton Blanchard 				dma_free_coherent(&pdev->dev,
979605a6538aSwenxiong@linux.vnet.ibm.com 					sizeof(u32) * ioa_cfg->hrrq[i].size,
979705a6538aSwenxiong@linux.vnet.ibm.com 					ioa_cfg->hrrq[i].host_rrq,
979805a6538aSwenxiong@linux.vnet.ibm.com 					ioa_cfg->hrrq[i].host_rrq_dma);
97991da177e4SLinus Torvalds 			goto out_ipr_free_cmd_blocks;
980005a6538aSwenxiong@linux.vnet.ibm.com 		}
980105a6538aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->hrrq[i].ioa_cfg = ioa_cfg;
980205a6538aSwenxiong@linux.vnet.ibm.com 	}
98031da177e4SLinus Torvalds 
9804d73341bfSAnton Blanchard 	ioa_cfg->u.cfg_table = dma_alloc_coherent(&pdev->dev,
98053e7ebdfaSWayne Boyer 						  ioa_cfg->cfg_table_size,
9806d73341bfSAnton Blanchard 						  &ioa_cfg->cfg_table_dma,
9807d73341bfSAnton Blanchard 						  GFP_KERNEL);
98081da177e4SLinus Torvalds 
98093e7ebdfaSWayne Boyer 	if (!ioa_cfg->u.cfg_table)
98101da177e4SLinus Torvalds 		goto out_free_host_rrq;
98111da177e4SLinus Torvalds 
9812afc3f83cSBrian King 	for (i = 0; i < IPR_MAX_HCAMS; i++) {
9813d73341bfSAnton Blanchard 		ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev,
98141da177e4SLinus Torvalds 							 sizeof(struct ipr_hostrcb),
9815d73341bfSAnton Blanchard 							 &ioa_cfg->hostrcb_dma[i],
9816d73341bfSAnton Blanchard 							 GFP_KERNEL);
98171da177e4SLinus Torvalds 
98181da177e4SLinus Torvalds 		if (!ioa_cfg->hostrcb[i])
98191da177e4SLinus Torvalds 			goto out_free_hostrcb_dma;
98201da177e4SLinus Torvalds 
98211da177e4SLinus Torvalds 		ioa_cfg->hostrcb[i]->hostrcb_dma =
98221da177e4SLinus Torvalds 			ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam);
982349dc6a18SBrian King 		ioa_cfg->hostrcb[i]->ioa_cfg = ioa_cfg;
98241da177e4SLinus Torvalds 		list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q);
98251da177e4SLinus Torvalds 	}
98261da177e4SLinus Torvalds 
98276396bb22SKees Cook 	ioa_cfg->trace = kcalloc(IPR_NUM_TRACE_ENTRIES,
98286396bb22SKees Cook 				 sizeof(struct ipr_trace_entry),
98296396bb22SKees Cook 				 GFP_KERNEL);
98301da177e4SLinus Torvalds 
98311da177e4SLinus Torvalds 	if (!ioa_cfg->trace)
98321da177e4SLinus Torvalds 		goto out_free_hostrcb_dma;
98331da177e4SLinus Torvalds 
98341da177e4SLinus Torvalds 	rc = 0;
98351da177e4SLinus Torvalds out:
98361da177e4SLinus Torvalds 	LEAVE;
98371da177e4SLinus Torvalds 	return rc;
98381da177e4SLinus Torvalds 
98391da177e4SLinus Torvalds out_free_hostrcb_dma:
98401da177e4SLinus Torvalds 	while (i-- > 0) {
9841d73341bfSAnton Blanchard 		dma_free_coherent(&pdev->dev, sizeof(struct ipr_hostrcb),
98421da177e4SLinus Torvalds 				  ioa_cfg->hostrcb[i],
98431da177e4SLinus Torvalds 				  ioa_cfg->hostrcb_dma[i]);
98441da177e4SLinus Torvalds 	}
9845d73341bfSAnton Blanchard 	dma_free_coherent(&pdev->dev, ioa_cfg->cfg_table_size,
9846d73341bfSAnton Blanchard 			  ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
98471da177e4SLinus Torvalds out_free_host_rrq:
984805a6538aSwenxiong@linux.vnet.ibm.com 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
9849d73341bfSAnton Blanchard 		dma_free_coherent(&pdev->dev,
985005a6538aSwenxiong@linux.vnet.ibm.com 				  sizeof(u32) * ioa_cfg->hrrq[i].size,
985105a6538aSwenxiong@linux.vnet.ibm.com 				  ioa_cfg->hrrq[i].host_rrq,
985205a6538aSwenxiong@linux.vnet.ibm.com 				  ioa_cfg->hrrq[i].host_rrq_dma);
985305a6538aSwenxiong@linux.vnet.ibm.com 	}
98541da177e4SLinus Torvalds out_ipr_free_cmd_blocks:
98551da177e4SLinus Torvalds 	ipr_free_cmd_blks(ioa_cfg);
98561da177e4SLinus Torvalds out_free_vpd_cbs:
9857d73341bfSAnton Blanchard 	dma_free_coherent(&pdev->dev, sizeof(struct ipr_misc_cbs),
98581da177e4SLinus Torvalds 			  ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
98591da177e4SLinus Torvalds out_free_res_entries:
98601da177e4SLinus Torvalds 	kfree(ioa_cfg->res_entries);
98611da177e4SLinus Torvalds 	goto out;
98621da177e4SLinus Torvalds }
98631da177e4SLinus Torvalds 
98641da177e4SLinus Torvalds /**
98651da177e4SLinus Torvalds  * ipr_initialize_bus_attr - Initialize SCSI bus attributes to default values
98661da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
98671da177e4SLinus Torvalds  *
98681da177e4SLinus Torvalds  * Return value:
98691da177e4SLinus Torvalds  * 	none
98701da177e4SLinus Torvalds  **/
98716f039790SGreg Kroah-Hartman static void ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg)
98721da177e4SLinus Torvalds {
98731da177e4SLinus Torvalds 	int i;
98741da177e4SLinus Torvalds 
98751da177e4SLinus Torvalds 	for (i = 0; i < IPR_MAX_NUM_BUSES; i++) {
98761da177e4SLinus Torvalds 		ioa_cfg->bus_attr[i].bus = i;
98771da177e4SLinus Torvalds 		ioa_cfg->bus_attr[i].qas_enabled = 0;
98781da177e4SLinus Torvalds 		ioa_cfg->bus_attr[i].bus_width = IPR_DEFAULT_BUS_WIDTH;
98791da177e4SLinus Torvalds 		if (ipr_max_speed < ARRAY_SIZE(ipr_max_bus_speeds))
98801da177e4SLinus Torvalds 			ioa_cfg->bus_attr[i].max_xfer_rate = ipr_max_bus_speeds[ipr_max_speed];
98811da177e4SLinus Torvalds 		else
98821da177e4SLinus Torvalds 			ioa_cfg->bus_attr[i].max_xfer_rate = IPR_U160_SCSI_RATE;
98831da177e4SLinus Torvalds 	}
98841da177e4SLinus Torvalds }
98851da177e4SLinus Torvalds 
98861da177e4SLinus Torvalds /**
98876270e593SBrian King  * ipr_init_regs - Initialize IOA registers
98881da177e4SLinus Torvalds  * @ioa_cfg:	ioa config struct
98891da177e4SLinus Torvalds  *
98901da177e4SLinus Torvalds  * Return value:
98911da177e4SLinus Torvalds  *	none
98921da177e4SLinus Torvalds  **/
98936270e593SBrian King static void ipr_init_regs(struct ipr_ioa_cfg *ioa_cfg)
98941da177e4SLinus Torvalds {
98951da177e4SLinus Torvalds 	const struct ipr_interrupt_offsets *p;
98961da177e4SLinus Torvalds 	struct ipr_interrupts *t;
98971da177e4SLinus Torvalds 	void __iomem *base;
98981da177e4SLinus Torvalds 
98991da177e4SLinus Torvalds 	p = &ioa_cfg->chip_cfg->regs;
99001da177e4SLinus Torvalds 	t = &ioa_cfg->regs;
99011da177e4SLinus Torvalds 	base = ioa_cfg->hdw_dma_regs;
99021da177e4SLinus Torvalds 
99031da177e4SLinus Torvalds 	t->set_interrupt_mask_reg = base + p->set_interrupt_mask_reg;
99041da177e4SLinus Torvalds 	t->clr_interrupt_mask_reg = base + p->clr_interrupt_mask_reg;
9905214777baSWayne Boyer 	t->clr_interrupt_mask_reg32 = base + p->clr_interrupt_mask_reg32;
99061da177e4SLinus Torvalds 	t->sense_interrupt_mask_reg = base + p->sense_interrupt_mask_reg;
9907214777baSWayne Boyer 	t->sense_interrupt_mask_reg32 = base + p->sense_interrupt_mask_reg32;
99081da177e4SLinus Torvalds 	t->clr_interrupt_reg = base + p->clr_interrupt_reg;
9909214777baSWayne Boyer 	t->clr_interrupt_reg32 = base + p->clr_interrupt_reg32;
99101da177e4SLinus Torvalds 	t->sense_interrupt_reg = base + p->sense_interrupt_reg;
9911214777baSWayne Boyer 	t->sense_interrupt_reg32 = base + p->sense_interrupt_reg32;
99121da177e4SLinus Torvalds 	t->ioarrin_reg = base + p->ioarrin_reg;
99131da177e4SLinus Torvalds 	t->sense_uproc_interrupt_reg = base + p->sense_uproc_interrupt_reg;
9914214777baSWayne Boyer 	t->sense_uproc_interrupt_reg32 = base + p->sense_uproc_interrupt_reg32;
99151da177e4SLinus Torvalds 	t->set_uproc_interrupt_reg = base + p->set_uproc_interrupt_reg;
9916214777baSWayne Boyer 	t->set_uproc_interrupt_reg32 = base + p->set_uproc_interrupt_reg32;
99171da177e4SLinus Torvalds 	t->clr_uproc_interrupt_reg = base + p->clr_uproc_interrupt_reg;
9918214777baSWayne Boyer 	t->clr_uproc_interrupt_reg32 = base + p->clr_uproc_interrupt_reg32;
9919dcbad00eSWayne Boyer 
9920dcbad00eSWayne Boyer 	if (ioa_cfg->sis64) {
9921214777baSWayne Boyer 		t->init_feedback_reg = base + p->init_feedback_reg;
9922dcbad00eSWayne Boyer 		t->dump_addr_reg = base + p->dump_addr_reg;
9923dcbad00eSWayne Boyer 		t->dump_data_reg = base + p->dump_data_reg;
99248701f185SWayne Boyer 		t->endian_swap_reg = base + p->endian_swap_reg;
9925dcbad00eSWayne Boyer 	}
99261da177e4SLinus Torvalds }
99271da177e4SLinus Torvalds 
99281da177e4SLinus Torvalds /**
99296270e593SBrian King  * ipr_init_ioa_cfg - Initialize IOA config struct
99306270e593SBrian King  * @ioa_cfg:	ioa config struct
99316270e593SBrian King  * @host:		scsi host struct
99326270e593SBrian King  * @pdev:		PCI dev struct
99336270e593SBrian King  *
99346270e593SBrian King  * Return value:
99356270e593SBrian King  * 	none
99366270e593SBrian King  **/
99376270e593SBrian King static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
99386270e593SBrian King 			     struct Scsi_Host *host, struct pci_dev *pdev)
99396270e593SBrian King {
99406270e593SBrian King 	int i;
99416270e593SBrian King 
99426270e593SBrian King 	ioa_cfg->host = host;
99436270e593SBrian King 	ioa_cfg->pdev = pdev;
99446270e593SBrian King 	ioa_cfg->log_level = ipr_log_level;
99456270e593SBrian King 	ioa_cfg->doorbell = IPR_DOORBELL;
99466270e593SBrian King 	sprintf(ioa_cfg->eye_catcher, IPR_EYECATCHER);
99476270e593SBrian King 	sprintf(ioa_cfg->trace_start, IPR_TRACE_START_LABEL);
99486270e593SBrian King 	sprintf(ioa_cfg->cfg_table_start, IPR_CFG_TBL_START);
99496270e593SBrian King 	sprintf(ioa_cfg->resource_table_label, IPR_RES_TABLE_LABEL);
99506270e593SBrian King 	sprintf(ioa_cfg->ipr_hcam_label, IPR_HCAM_LABEL);
99516270e593SBrian King 	sprintf(ioa_cfg->ipr_cmd_label, IPR_CMD_LABEL);
99526270e593SBrian King 
99536270e593SBrian King 	INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q);
99546270e593SBrian King 	INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q);
9955afc3f83cSBrian King 	INIT_LIST_HEAD(&ioa_cfg->hostrcb_report_q);
99566270e593SBrian King 	INIT_LIST_HEAD(&ioa_cfg->free_res_q);
99576270e593SBrian King 	INIT_LIST_HEAD(&ioa_cfg->used_res_q);
99586270e593SBrian King 	INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread);
9959318ddb34SWen Xiong 	INIT_WORK(&ioa_cfg->scsi_add_work_q, ipr_add_remove_thread);
99606270e593SBrian King 	init_waitqueue_head(&ioa_cfg->reset_wait_q);
99616270e593SBrian King 	init_waitqueue_head(&ioa_cfg->msi_wait_q);
99626270e593SBrian King 	init_waitqueue_head(&ioa_cfg->eeh_wait_q);
99636270e593SBrian King 	ioa_cfg->sdt_state = INACTIVE;
99646270e593SBrian King 
99656270e593SBrian King 	ipr_initialize_bus_attr(ioa_cfg);
99666270e593SBrian King 	ioa_cfg->max_devs_supported = ipr_max_devs;
99676270e593SBrian King 
99686270e593SBrian King 	if (ioa_cfg->sis64) {
9969394b6171SWen Xiong 		host->max_channel = IPR_MAX_SIS64_BUSES;
99706270e593SBrian King 		host->max_id = IPR_MAX_SIS64_TARGETS_PER_BUS;
99716270e593SBrian King 		host->max_lun = IPR_MAX_SIS64_LUNS_PER_TARGET;
99726270e593SBrian King 		if (ipr_max_devs > IPR_MAX_SIS64_DEVS)
99736270e593SBrian King 			ioa_cfg->max_devs_supported = IPR_MAX_SIS64_DEVS;
99746270e593SBrian King 		ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr64)
99756270e593SBrian King 					   + ((sizeof(struct ipr_config_table_entry64)
99766270e593SBrian King 					       * ioa_cfg->max_devs_supported)));
99776270e593SBrian King 	} else {
9978394b6171SWen Xiong 		host->max_channel = IPR_VSET_BUS;
99796270e593SBrian King 		host->max_id = IPR_MAX_NUM_TARGETS_PER_BUS;
99806270e593SBrian King 		host->max_lun = IPR_MAX_NUM_LUNS_PER_TARGET;
99816270e593SBrian King 		if (ipr_max_devs > IPR_MAX_PHYSICAL_DEVS)
99826270e593SBrian King 			ioa_cfg->max_devs_supported = IPR_MAX_PHYSICAL_DEVS;
99836270e593SBrian King 		ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr)
99846270e593SBrian King 					   + ((sizeof(struct ipr_config_table_entry)
99856270e593SBrian King 					       * ioa_cfg->max_devs_supported)));
99866270e593SBrian King 	}
99876270e593SBrian King 
99886270e593SBrian King 	host->unique_id = host->host_no;
99896270e593SBrian King 	host->max_cmd_len = IPR_MAX_CDB_LEN;
99906270e593SBrian King 	host->can_queue = ioa_cfg->max_cmds;
99916270e593SBrian King 	pci_set_drvdata(pdev, ioa_cfg);
99926270e593SBrian King 
99936270e593SBrian King 	for (i = 0; i < ARRAY_SIZE(ioa_cfg->hrrq); i++) {
99946270e593SBrian King 		INIT_LIST_HEAD(&ioa_cfg->hrrq[i].hrrq_free_q);
99956270e593SBrian King 		INIT_LIST_HEAD(&ioa_cfg->hrrq[i].hrrq_pending_q);
99966270e593SBrian King 		spin_lock_init(&ioa_cfg->hrrq[i]._lock);
99976270e593SBrian King 		if (i == 0)
99986270e593SBrian King 			ioa_cfg->hrrq[i].lock = ioa_cfg->host->host_lock;
99996270e593SBrian King 		else
100006270e593SBrian King 			ioa_cfg->hrrq[i].lock = &ioa_cfg->hrrq[i]._lock;
100016270e593SBrian King 	}
100026270e593SBrian King }
100036270e593SBrian King 
100046270e593SBrian King /**
100051be7bd82SWayne Boyer  * ipr_get_chip_info - Find adapter chip information
100061da177e4SLinus Torvalds  * @dev_id:		PCI device id struct
100071da177e4SLinus Torvalds  *
100081da177e4SLinus Torvalds  * Return value:
100091be7bd82SWayne Boyer  * 	ptr to chip information on success / NULL on failure
100101da177e4SLinus Torvalds  **/
100116f039790SGreg Kroah-Hartman static const struct ipr_chip_t *
100121be7bd82SWayne Boyer ipr_get_chip_info(const struct pci_device_id *dev_id)
100131da177e4SLinus Torvalds {
100141da177e4SLinus Torvalds 	int i;
100151da177e4SLinus Torvalds 
100161da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(ipr_chip); i++)
100171da177e4SLinus Torvalds 		if (ipr_chip[i].vendor == dev_id->vendor &&
100181da177e4SLinus Torvalds 		    ipr_chip[i].device == dev_id->device)
100191be7bd82SWayne Boyer 			return &ipr_chip[i];
100201da177e4SLinus Torvalds 	return NULL;
100211da177e4SLinus Torvalds }
100221da177e4SLinus Torvalds 
100236270e593SBrian King /**
100246270e593SBrian King  * ipr_wait_for_pci_err_recovery - Wait for any PCI error recovery to complete
100256270e593SBrian King  *						during probe time
100266270e593SBrian King  * @ioa_cfg:	ioa config struct
100276270e593SBrian King  *
100286270e593SBrian King  * Return value:
100296270e593SBrian King  * 	None
100306270e593SBrian King  **/
100316270e593SBrian King static void ipr_wait_for_pci_err_recovery(struct ipr_ioa_cfg *ioa_cfg)
100326270e593SBrian King {
100336270e593SBrian King 	struct pci_dev *pdev = ioa_cfg->pdev;
100346270e593SBrian King 
100356270e593SBrian King 	if (pci_channel_offline(pdev)) {
100366270e593SBrian King 		wait_event_timeout(ioa_cfg->eeh_wait_q,
100376270e593SBrian King 				   !pci_channel_offline(pdev),
100386270e593SBrian King 				   IPR_PCI_ERROR_RECOVERY_TIMEOUT);
100396270e593SBrian King 		pci_restore_state(pdev);
100406270e593SBrian King 	}
100416270e593SBrian King }
100426270e593SBrian King 
1004305a6538aSwenxiong@linux.vnet.ibm.com static void name_msi_vectors(struct ipr_ioa_cfg *ioa_cfg)
1004405a6538aSwenxiong@linux.vnet.ibm.com {
1004505a6538aSwenxiong@linux.vnet.ibm.com 	int vec_idx, n = sizeof(ioa_cfg->vectors_info[0].desc) - 1;
1004605a6538aSwenxiong@linux.vnet.ibm.com 
1004705a6538aSwenxiong@linux.vnet.ibm.com 	for (vec_idx = 0; vec_idx < ioa_cfg->nvectors; vec_idx++) {
1004805a6538aSwenxiong@linux.vnet.ibm.com 		snprintf(ioa_cfg->vectors_info[vec_idx].desc, n,
1004905a6538aSwenxiong@linux.vnet.ibm.com 			 "host%d-%d", ioa_cfg->host->host_no, vec_idx);
1005005a6538aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->vectors_info[vec_idx].
1005105a6538aSwenxiong@linux.vnet.ibm.com 			desc[strlen(ioa_cfg->vectors_info[vec_idx].desc)] = 0;
1005205a6538aSwenxiong@linux.vnet.ibm.com 	}
1005305a6538aSwenxiong@linux.vnet.ibm.com }
1005405a6538aSwenxiong@linux.vnet.ibm.com 
10055a299ee62SChristoph Hellwig static int ipr_request_other_msi_irqs(struct ipr_ioa_cfg *ioa_cfg,
10056a299ee62SChristoph Hellwig 		struct pci_dev *pdev)
1005705a6538aSwenxiong@linux.vnet.ibm.com {
1005805a6538aSwenxiong@linux.vnet.ibm.com 	int i, rc;
1005905a6538aSwenxiong@linux.vnet.ibm.com 
1006005a6538aSwenxiong@linux.vnet.ibm.com 	for (i = 1; i < ioa_cfg->nvectors; i++) {
10061a299ee62SChristoph Hellwig 		rc = request_irq(pci_irq_vector(pdev, i),
1006205a6538aSwenxiong@linux.vnet.ibm.com 			ipr_isr_mhrrq,
1006305a6538aSwenxiong@linux.vnet.ibm.com 			0,
1006405a6538aSwenxiong@linux.vnet.ibm.com 			ioa_cfg->vectors_info[i].desc,
1006505a6538aSwenxiong@linux.vnet.ibm.com 			&ioa_cfg->hrrq[i]);
1006605a6538aSwenxiong@linux.vnet.ibm.com 		if (rc) {
1006705a6538aSwenxiong@linux.vnet.ibm.com 			while (--i >= 0)
10068a299ee62SChristoph Hellwig 				free_irq(pci_irq_vector(pdev, i),
1006905a6538aSwenxiong@linux.vnet.ibm.com 					&ioa_cfg->hrrq[i]);
1007005a6538aSwenxiong@linux.vnet.ibm.com 			return rc;
1007105a6538aSwenxiong@linux.vnet.ibm.com 		}
1007205a6538aSwenxiong@linux.vnet.ibm.com 	}
1007305a6538aSwenxiong@linux.vnet.ibm.com 	return 0;
1007405a6538aSwenxiong@linux.vnet.ibm.com }
1007505a6538aSwenxiong@linux.vnet.ibm.com 
100761da177e4SLinus Torvalds /**
1007795fecd90SWayne Boyer  * ipr_test_intr - Handle the interrupt generated in ipr_test_msi().
10078a96099e2SLee Jones  * @devp:		PCI device struct
10079a96099e2SLee Jones  * @irq:		IRQ number
1008095fecd90SWayne Boyer  *
1008195fecd90SWayne Boyer  * Description: Simply set the msi_received flag to 1 indicating that
1008295fecd90SWayne Boyer  * Message Signaled Interrupts are supported.
1008395fecd90SWayne Boyer  *
1008495fecd90SWayne Boyer  * Return value:
1008595fecd90SWayne Boyer  * 	0 on success / non-zero on failure
1008695fecd90SWayne Boyer  **/
100876f039790SGreg Kroah-Hartman static irqreturn_t ipr_test_intr(int irq, void *devp)
1008895fecd90SWayne Boyer {
1008995fecd90SWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
1009095fecd90SWayne Boyer 	unsigned long lock_flags = 0;
1009195fecd90SWayne Boyer 	irqreturn_t rc = IRQ_HANDLED;
1009295fecd90SWayne Boyer 
1009305a6538aSwenxiong@linux.vnet.ibm.com 	dev_info(&ioa_cfg->pdev->dev, "Received IRQ : %d\n", irq);
1009495fecd90SWayne Boyer 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
1009595fecd90SWayne Boyer 
1009695fecd90SWayne Boyer 	ioa_cfg->msi_received = 1;
1009795fecd90SWayne Boyer 	wake_up(&ioa_cfg->msi_wait_q);
1009895fecd90SWayne Boyer 
1009995fecd90SWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
1010095fecd90SWayne Boyer 	return rc;
1010195fecd90SWayne Boyer }
1010295fecd90SWayne Boyer 
1010395fecd90SWayne Boyer /**
1010495fecd90SWayne Boyer  * ipr_test_msi - Test for Message Signaled Interrupt (MSI) support.
10105a96099e2SLee Jones  * @ioa_cfg:		ioa config struct
1010695fecd90SWayne Boyer  * @pdev:		PCI device struct
1010795fecd90SWayne Boyer  *
10108a299ee62SChristoph Hellwig  * Description: This routine sets up and initiates a test interrupt to determine
1010995fecd90SWayne Boyer  * if the interrupt is received via the ipr_test_intr() service routine.
1011095fecd90SWayne Boyer  * If the tests fails, the driver will fall back to LSI.
1011195fecd90SWayne Boyer  *
1011295fecd90SWayne Boyer  * Return value:
1011395fecd90SWayne Boyer  * 	0 on success / non-zero on failure
1011495fecd90SWayne Boyer  **/
101156f039790SGreg Kroah-Hartman static int ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg, struct pci_dev *pdev)
1011695fecd90SWayne Boyer {
1011795fecd90SWayne Boyer 	int rc;
1011895fecd90SWayne Boyer 	unsigned long lock_flags = 0;
10119a299ee62SChristoph Hellwig 	int irq = pci_irq_vector(pdev, 0);
1012095fecd90SWayne Boyer 
1012195fecd90SWayne Boyer 	ENTER;
1012295fecd90SWayne Boyer 
1012395fecd90SWayne Boyer 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
1012495fecd90SWayne Boyer 	init_waitqueue_head(&ioa_cfg->msi_wait_q);
1012595fecd90SWayne Boyer 	ioa_cfg->msi_received = 0;
1012695fecd90SWayne Boyer 	ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
10127214777baSWayne Boyer 	writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_mask_reg32);
101284dc83399SLee Jones 	readl(ioa_cfg->regs.sense_interrupt_mask_reg);
1012995fecd90SWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
1013095fecd90SWayne Boyer 
10131a299ee62SChristoph Hellwig 	rc = request_irq(irq, ipr_test_intr, 0, IPR_NAME, ioa_cfg);
1013295fecd90SWayne Boyer 	if (rc) {
10133a299ee62SChristoph Hellwig 		dev_err(&pdev->dev, "Can not assign irq %d\n", irq);
1013495fecd90SWayne Boyer 		return rc;
1013595fecd90SWayne Boyer 	} else if (ipr_debug)
10136a299ee62SChristoph Hellwig 		dev_info(&pdev->dev, "IRQ assigned: %d\n", irq);
1013795fecd90SWayne Boyer 
10138214777baSWayne Boyer 	writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.sense_interrupt_reg32);
101394dc83399SLee Jones 	readl(ioa_cfg->regs.sense_interrupt_reg);
1014095fecd90SWayne Boyer 	wait_event_timeout(ioa_cfg->msi_wait_q, ioa_cfg->msi_received, HZ);
1014156d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
1014295fecd90SWayne Boyer 	ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
1014395fecd90SWayne Boyer 
1014495fecd90SWayne Boyer 	if (!ioa_cfg->msi_received) {
1014595fecd90SWayne Boyer 		/* MSI test failed */
1014695fecd90SWayne Boyer 		dev_info(&pdev->dev, "MSI test failed.  Falling back to LSI.\n");
1014795fecd90SWayne Boyer 		rc = -EOPNOTSUPP;
1014895fecd90SWayne Boyer 	} else if (ipr_debug)
1014995fecd90SWayne Boyer 		dev_info(&pdev->dev, "MSI test succeeded.\n");
1015095fecd90SWayne Boyer 
1015195fecd90SWayne Boyer 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
1015295fecd90SWayne Boyer 
10153a299ee62SChristoph Hellwig 	free_irq(irq, ioa_cfg);
1015495fecd90SWayne Boyer 
1015595fecd90SWayne Boyer 	LEAVE;
1015695fecd90SWayne Boyer 
1015795fecd90SWayne Boyer 	return rc;
1015895fecd90SWayne Boyer }
1015995fecd90SWayne Boyer 
1016005a6538aSwenxiong@linux.vnet.ibm.com  /* ipr_probe_ioa - Allocates memory and does first stage of initialization
101611da177e4SLinus Torvalds  * @pdev:		PCI device struct
101621da177e4SLinus Torvalds  * @dev_id:		PCI device id struct
101631da177e4SLinus Torvalds  *
101641da177e4SLinus Torvalds  * Return value:
101651da177e4SLinus Torvalds  * 	0 on success / non-zero on failure
101661da177e4SLinus Torvalds  **/
101676f039790SGreg Kroah-Hartman static int ipr_probe_ioa(struct pci_dev *pdev,
101681da177e4SLinus Torvalds 			 const struct pci_device_id *dev_id)
101691da177e4SLinus Torvalds {
101701da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
101711da177e4SLinus Torvalds 	struct Scsi_Host *host;
101721da177e4SLinus Torvalds 	unsigned long ipr_regs_pci;
101731da177e4SLinus Torvalds 	void __iomem *ipr_regs;
10174a2a65a3eSEric Sesterhenn 	int rc = PCIBIOS_SUCCESSFUL;
10175473b1e8eSBrian King 	volatile u32 mask, uproc, interrupts;
10176feccada9Swenxiong@linux.vnet.ibm.com 	unsigned long lock_flags, driver_lock_flags;
10177a299ee62SChristoph Hellwig 	unsigned int irq_flag;
101781da177e4SLinus Torvalds 
101791da177e4SLinus Torvalds 	ENTER;
101801da177e4SLinus Torvalds 
101811da177e4SLinus Torvalds 	dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq);
101821da177e4SLinus Torvalds 	host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg));
101831da177e4SLinus Torvalds 
101841da177e4SLinus Torvalds 	if (!host) {
101851da177e4SLinus Torvalds 		dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n");
101861da177e4SLinus Torvalds 		rc = -ENOMEM;
101876270e593SBrian King 		goto out;
101881da177e4SLinus Torvalds 	}
101891da177e4SLinus Torvalds 
101901da177e4SLinus Torvalds 	ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata;
101911da177e4SLinus Torvalds 	memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg));
101928d8e7d13SDan Williams 	ata_host_init(&ioa_cfg->ata_host, &pdev->dev, &ipr_sata_ops);
101931da177e4SLinus Torvalds 
101941be7bd82SWayne Boyer 	ioa_cfg->ipr_chip = ipr_get_chip_info(dev_id);
101951da177e4SLinus Torvalds 
101961be7bd82SWayne Boyer 	if (!ioa_cfg->ipr_chip) {
101971da177e4SLinus Torvalds 		dev_err(&pdev->dev, "Unknown adapter chipset 0x%04X 0x%04X\n",
101981da177e4SLinus Torvalds 			dev_id->vendor, dev_id->device);
101991da177e4SLinus Torvalds 		goto out_scsi_host_put;
102001da177e4SLinus Torvalds 	}
102011da177e4SLinus Torvalds 
10202a32c055fSWayne Boyer 	/* set SIS 32 or SIS 64 */
10203a32c055fSWayne Boyer 	ioa_cfg->sis64 = ioa_cfg->ipr_chip->sis_type == IPR_SIS64 ? 1 : 0;
102041be7bd82SWayne Boyer 	ioa_cfg->chip_cfg = ioa_cfg->ipr_chip->cfg;
102057dd21308SBrian King 	ioa_cfg->clear_isr = ioa_cfg->chip_cfg->clear_isr;
1020689aad428SBrian King 	ioa_cfg->max_cmds = ioa_cfg->chip_cfg->max_cmds;
102071be7bd82SWayne Boyer 
102085469cb5bSBrian King 	if (ipr_transop_timeout)
102095469cb5bSBrian King 		ioa_cfg->transop_timeout = ipr_transop_timeout;
102105469cb5bSBrian King 	else if (dev_id->driver_data & IPR_USE_LONG_TRANSOP_TIMEOUT)
102115469cb5bSBrian King 		ioa_cfg->transop_timeout = IPR_LONG_OPERATIONAL_TIMEOUT;
102125469cb5bSBrian King 	else
102135469cb5bSBrian King 		ioa_cfg->transop_timeout = IPR_OPERATIONAL_TIMEOUT;
102145469cb5bSBrian King 
1021544c10138SAuke Kok 	ioa_cfg->revid = pdev->revision;
10216463fc696SBrian King 
102176270e593SBrian King 	ipr_init_ioa_cfg(ioa_cfg, host, pdev);
102186270e593SBrian King 
102191da177e4SLinus Torvalds 	ipr_regs_pci = pci_resource_start(pdev, 0);
102201da177e4SLinus Torvalds 
102211da177e4SLinus Torvalds 	rc = pci_request_regions(pdev, IPR_NAME);
102221da177e4SLinus Torvalds 	if (rc < 0) {
102231da177e4SLinus Torvalds 		dev_err(&pdev->dev,
102241da177e4SLinus Torvalds 			"Couldn't register memory range of registers\n");
102251da177e4SLinus Torvalds 		goto out_scsi_host_put;
102261da177e4SLinus Torvalds 	}
102271da177e4SLinus Torvalds 
102286270e593SBrian King 	rc = pci_enable_device(pdev);
102296270e593SBrian King 
102306270e593SBrian King 	if (rc || pci_channel_offline(pdev)) {
102316270e593SBrian King 		if (pci_channel_offline(pdev)) {
102326270e593SBrian King 			ipr_wait_for_pci_err_recovery(ioa_cfg);
102336270e593SBrian King 			rc = pci_enable_device(pdev);
102346270e593SBrian King 		}
102356270e593SBrian King 
102366270e593SBrian King 		if (rc) {
102376270e593SBrian King 			dev_err(&pdev->dev, "Cannot enable adapter\n");
102386270e593SBrian King 			ipr_wait_for_pci_err_recovery(ioa_cfg);
102396270e593SBrian King 			goto out_release_regions;
102406270e593SBrian King 		}
102416270e593SBrian King 	}
102426270e593SBrian King 
1024325729a7fSArjan van de Ven 	ipr_regs = pci_ioremap_bar(pdev, 0);
102441da177e4SLinus Torvalds 
102451da177e4SLinus Torvalds 	if (!ipr_regs) {
102461da177e4SLinus Torvalds 		dev_err(&pdev->dev,
102471da177e4SLinus Torvalds 			"Couldn't map memory range of registers\n");
102481da177e4SLinus Torvalds 		rc = -ENOMEM;
102496270e593SBrian King 		goto out_disable;
102501da177e4SLinus Torvalds 	}
102511da177e4SLinus Torvalds 
102521da177e4SLinus Torvalds 	ioa_cfg->hdw_dma_regs = ipr_regs;
102531da177e4SLinus Torvalds 	ioa_cfg->hdw_dma_regs_pci = ipr_regs_pci;
102541da177e4SLinus Torvalds 	ioa_cfg->ioa_mailbox = ioa_cfg->chip_cfg->mailbox + ipr_regs;
102551da177e4SLinus Torvalds 
102566270e593SBrian King 	ipr_init_regs(ioa_cfg);
102571da177e4SLinus Torvalds 
10258a32c055fSWayne Boyer 	if (ioa_cfg->sis64) {
10259869404cbSAnton Blanchard 		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
10260a32c055fSWayne Boyer 		if (rc < 0) {
10261869404cbSAnton Blanchard 			dev_dbg(&pdev->dev, "Failed to set 64 bit DMA mask\n");
10262869404cbSAnton Blanchard 			rc = dma_set_mask_and_coherent(&pdev->dev,
10263869404cbSAnton Blanchard 						       DMA_BIT_MASK(32));
10264a32c055fSWayne Boyer 		}
10265a32c055fSWayne Boyer 	} else
10266869404cbSAnton Blanchard 		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
10267a32c055fSWayne Boyer 
102681da177e4SLinus Torvalds 	if (rc < 0) {
10269869404cbSAnton Blanchard 		dev_err(&pdev->dev, "Failed to set DMA mask\n");
102701da177e4SLinus Torvalds 		goto cleanup_nomem;
102711da177e4SLinus Torvalds 	}
102721da177e4SLinus Torvalds 
102731da177e4SLinus Torvalds 	rc = pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
102741da177e4SLinus Torvalds 				   ioa_cfg->chip_cfg->cache_line_size);
102751da177e4SLinus Torvalds 
102761da177e4SLinus Torvalds 	if (rc != PCIBIOS_SUCCESSFUL) {
102771da177e4SLinus Torvalds 		dev_err(&pdev->dev, "Write of cache line size failed\n");
102786270e593SBrian King 		ipr_wait_for_pci_err_recovery(ioa_cfg);
102791da177e4SLinus Torvalds 		rc = -EIO;
102801da177e4SLinus Torvalds 		goto cleanup_nomem;
102811da177e4SLinus Torvalds 	}
102821da177e4SLinus Torvalds 
102836270e593SBrian King 	/* Issue MMIO read to ensure card is not in EEH */
102846270e593SBrian King 	interrupts = readl(ioa_cfg->regs.sense_interrupt_reg);
102856270e593SBrian King 	ipr_wait_for_pci_err_recovery(ioa_cfg);
102866270e593SBrian King 
1028705a6538aSwenxiong@linux.vnet.ibm.com 	if (ipr_number_of_msix > IPR_MAX_MSIX_VECTORS) {
1028805a6538aSwenxiong@linux.vnet.ibm.com 		dev_err(&pdev->dev, "The max number of MSIX is %d\n",
1028905a6538aSwenxiong@linux.vnet.ibm.com 			IPR_MAX_MSIX_VECTORS);
1029005a6538aSwenxiong@linux.vnet.ibm.com 		ipr_number_of_msix = IPR_MAX_MSIX_VECTORS;
1029105a6538aSwenxiong@linux.vnet.ibm.com 	}
1029205a6538aSwenxiong@linux.vnet.ibm.com 
10293a299ee62SChristoph Hellwig 	irq_flag = PCI_IRQ_LEGACY;
10294a299ee62SChristoph Hellwig 	if (ioa_cfg->ipr_chip->has_msi)
10295a299ee62SChristoph Hellwig 		irq_flag |= PCI_IRQ_MSI | PCI_IRQ_MSIX;
10296a299ee62SChristoph Hellwig 	rc = pci_alloc_irq_vectors(pdev, 1, ipr_number_of_msix, irq_flag);
10297a299ee62SChristoph Hellwig 	if (rc < 0) {
10298a299ee62SChristoph Hellwig 		ipr_wait_for_pci_err_recovery(ioa_cfg);
10299a299ee62SChristoph Hellwig 		goto cleanup_nomem;
1030005a6538aSwenxiong@linux.vnet.ibm.com 	}
10301a299ee62SChristoph Hellwig 	ioa_cfg->nvectors = rc;
10302a299ee62SChristoph Hellwig 
10303a299ee62SChristoph Hellwig 	if (!pdev->msi_enabled && !pdev->msix_enabled)
10304a299ee62SChristoph Hellwig 		ioa_cfg->clear_isr = 1;
1030505a6538aSwenxiong@linux.vnet.ibm.com 
103066270e593SBrian King 	pci_set_master(pdev);
103076270e593SBrian King 
103086270e593SBrian King 	if (pci_channel_offline(pdev)) {
103096270e593SBrian King 		ipr_wait_for_pci_err_recovery(ioa_cfg);
103106270e593SBrian King 		pci_set_master(pdev);
103116270e593SBrian King 		if (pci_channel_offline(pdev)) {
103126270e593SBrian King 			rc = -EIO;
103136270e593SBrian King 			goto out_msi_disable;
103146270e593SBrian King 		}
103156270e593SBrian King 	}
103166270e593SBrian King 
10317a299ee62SChristoph Hellwig 	if (pdev->msi_enabled || pdev->msix_enabled) {
1031895fecd90SWayne Boyer 		rc = ipr_test_msi(ioa_cfg, pdev);
10319a299ee62SChristoph Hellwig 		switch (rc) {
10320a299ee62SChristoph Hellwig 		case 0:
10321a299ee62SChristoph Hellwig 			dev_info(&pdev->dev,
10322a299ee62SChristoph Hellwig 				"Request for %d MSI%ss succeeded.", ioa_cfg->nvectors,
10323a299ee62SChristoph Hellwig 				pdev->msix_enabled ? "-X" : "");
10324a299ee62SChristoph Hellwig 			break;
10325a299ee62SChristoph Hellwig 		case -EOPNOTSUPP:
103266270e593SBrian King 			ipr_wait_for_pci_err_recovery(ioa_cfg);
10327a299ee62SChristoph Hellwig 			pci_free_irq_vectors(pdev);
1032805a6538aSwenxiong@linux.vnet.ibm.com 
1032905a6538aSwenxiong@linux.vnet.ibm.com 			ioa_cfg->nvectors = 1;
103309dadfb97SBenjamin Herrenschmidt 			ioa_cfg->clear_isr = 1;
10331a299ee62SChristoph Hellwig 			break;
10332a299ee62SChristoph Hellwig 		default:
1033395fecd90SWayne Boyer 			goto out_msi_disable;
1033405a6538aSwenxiong@linux.vnet.ibm.com 		}
1033505a6538aSwenxiong@linux.vnet.ibm.com 	}
1033605a6538aSwenxiong@linux.vnet.ibm.com 
1033705a6538aSwenxiong@linux.vnet.ibm.com 	ioa_cfg->hrrq_num = min3(ioa_cfg->nvectors,
1033805a6538aSwenxiong@linux.vnet.ibm.com 				(unsigned int)num_online_cpus(),
1033905a6538aSwenxiong@linux.vnet.ibm.com 				(unsigned int)IPR_MAX_HRRQ_NUM);
1034095fecd90SWayne Boyer 
103411da177e4SLinus Torvalds 	if ((rc = ipr_save_pcix_cmd_reg(ioa_cfg)))
10342f170c684SJulia Lawall 		goto out_msi_disable;
103431da177e4SLinus Torvalds 
103441da177e4SLinus Torvalds 	if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg)))
10345f170c684SJulia Lawall 		goto out_msi_disable;
103461da177e4SLinus Torvalds 
103471da177e4SLinus Torvalds 	rc = ipr_alloc_mem(ioa_cfg);
103481da177e4SLinus Torvalds 	if (rc < 0) {
103491da177e4SLinus Torvalds 		dev_err(&pdev->dev,
103501da177e4SLinus Torvalds 			"Couldn't allocate enough memory for device driver!\n");
10351f170c684SJulia Lawall 		goto out_msi_disable;
103521da177e4SLinus Torvalds 	}
103531da177e4SLinus Torvalds 
103546270e593SBrian King 	/* Save away PCI config space for use following IOA reset */
103556270e593SBrian King 	rc = pci_save_state(pdev);
103566270e593SBrian King 
103576270e593SBrian King 	if (rc != PCIBIOS_SUCCESSFUL) {
103586270e593SBrian King 		dev_err(&pdev->dev, "Failed to save PCI config space\n");
103596270e593SBrian King 		rc = -EIO;
103606270e593SBrian King 		goto cleanup_nolog;
103616270e593SBrian King 	}
103626270e593SBrian King 
10363ce155cceSbrking@us.ibm.com 	/*
10364ce155cceSbrking@us.ibm.com 	 * If HRRQ updated interrupt is not masked, or reset alert is set,
10365ce155cceSbrking@us.ibm.com 	 * the card is in an unknown state and needs a hard reset
10366ce155cceSbrking@us.ibm.com 	 */
10367214777baSWayne Boyer 	mask = readl(ioa_cfg->regs.sense_interrupt_mask_reg32);
10368214777baSWayne Boyer 	interrupts = readl(ioa_cfg->regs.sense_interrupt_reg32);
10369214777baSWayne Boyer 	uproc = readl(ioa_cfg->regs.sense_uproc_interrupt_reg32);
10370ce155cceSbrking@us.ibm.com 	if ((mask & IPR_PCII_HRRQ_UPDATED) == 0 || (uproc & IPR_UPROCI_RESET_ALERT))
10371ce155cceSbrking@us.ibm.com 		ioa_cfg->needs_hard_reset = 1;
103725d7c20b7SAnton Blanchard 	if ((interrupts & IPR_PCII_ERROR_INTERRUPTS) || reset_devices)
10373473b1e8eSBrian King 		ioa_cfg->needs_hard_reset = 1;
10374473b1e8eSBrian King 	if (interrupts & IPR_PCII_IOA_UNIT_CHECKED)
10375473b1e8eSBrian King 		ioa_cfg->ioa_unit_checked = 1;
10376ce155cceSbrking@us.ibm.com 
1037756d6aa33Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
103781da177e4SLinus Torvalds 	ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
1037956d6aa33Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
103801da177e4SLinus Torvalds 
10381a299ee62SChristoph Hellwig 	if (pdev->msi_enabled || pdev->msix_enabled) {
1038205a6538aSwenxiong@linux.vnet.ibm.com 		name_msi_vectors(ioa_cfg);
10383a299ee62SChristoph Hellwig 		rc = request_irq(pci_irq_vector(pdev, 0), ipr_isr, 0,
1038405a6538aSwenxiong@linux.vnet.ibm.com 			ioa_cfg->vectors_info[0].desc,
1038505a6538aSwenxiong@linux.vnet.ibm.com 			&ioa_cfg->hrrq[0]);
1038605a6538aSwenxiong@linux.vnet.ibm.com 		if (!rc)
10387a299ee62SChristoph Hellwig 			rc = ipr_request_other_msi_irqs(ioa_cfg, pdev);
1038805a6538aSwenxiong@linux.vnet.ibm.com 	} else {
1038905a6538aSwenxiong@linux.vnet.ibm.com 		rc = request_irq(pdev->irq, ipr_isr,
1039005a6538aSwenxiong@linux.vnet.ibm.com 			 IRQF_SHARED,
1039105a6538aSwenxiong@linux.vnet.ibm.com 			 IPR_NAME, &ioa_cfg->hrrq[0]);
1039205a6538aSwenxiong@linux.vnet.ibm.com 	}
103931da177e4SLinus Torvalds 	if (rc) {
103941da177e4SLinus Torvalds 		dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n",
103951da177e4SLinus Torvalds 			pdev->irq, rc);
103961da177e4SLinus Torvalds 		goto cleanup_nolog;
103971da177e4SLinus Torvalds 	}
103981da177e4SLinus Torvalds 
10399463fc696SBrian King 	if ((dev_id->driver_data & IPR_USE_PCI_WARM_RESET) ||
10400463fc696SBrian King 	    (dev_id->device == PCI_DEVICE_ID_IBM_OBSIDIAN_E && !ioa_cfg->revid)) {
10401463fc696SBrian King 		ioa_cfg->needs_warm_reset = 1;
10402463fc696SBrian King 		ioa_cfg->reset = ipr_reset_slot_reset;
104032796ca5eSBrian King 
104042796ca5eSBrian King 		ioa_cfg->reset_work_q = alloc_ordered_workqueue("ipr_reset_%d",
104052796ca5eSBrian King 								WQ_MEM_RECLAIM, host->host_no);
104062796ca5eSBrian King 
104072796ca5eSBrian King 		if (!ioa_cfg->reset_work_q) {
104082796ca5eSBrian King 			dev_err(&pdev->dev, "Couldn't register reset workqueue\n");
10409c8e18accSWei Yongjun 			rc = -ENOMEM;
104102796ca5eSBrian King 			goto out_free_irq;
104112796ca5eSBrian King 		}
10412463fc696SBrian King 	} else
10413463fc696SBrian King 		ioa_cfg->reset = ipr_reset_start_bist;
10414463fc696SBrian King 
10415feccada9Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
104161da177e4SLinus Torvalds 	list_add_tail(&ioa_cfg->queue, &ipr_ioa_head);
10417feccada9Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
104181da177e4SLinus Torvalds 
104191da177e4SLinus Torvalds 	LEAVE;
104201da177e4SLinus Torvalds out:
104211da177e4SLinus Torvalds 	return rc;
104221da177e4SLinus Torvalds 
104232796ca5eSBrian King out_free_irq:
104242796ca5eSBrian King 	ipr_free_irqs(ioa_cfg);
104251da177e4SLinus Torvalds cleanup_nolog:
104261da177e4SLinus Torvalds 	ipr_free_mem(ioa_cfg);
1042795fecd90SWayne Boyer out_msi_disable:
104286270e593SBrian King 	ipr_wait_for_pci_err_recovery(ioa_cfg);
10429a299ee62SChristoph Hellwig 	pci_free_irq_vectors(pdev);
10430f170c684SJulia Lawall cleanup_nomem:
10431f170c684SJulia Lawall 	iounmap(ipr_regs);
104326270e593SBrian King out_disable:
104336270e593SBrian King 	pci_disable_device(pdev);
104341da177e4SLinus Torvalds out_release_regions:
104351da177e4SLinus Torvalds 	pci_release_regions(pdev);
104361da177e4SLinus Torvalds out_scsi_host_put:
104371da177e4SLinus Torvalds 	scsi_host_put(host);
104381da177e4SLinus Torvalds 	goto out;
104391da177e4SLinus Torvalds }
104401da177e4SLinus Torvalds 
104411da177e4SLinus Torvalds /**
104421da177e4SLinus Torvalds  * ipr_initiate_ioa_bringdown - Bring down an adapter
104431da177e4SLinus Torvalds  * @ioa_cfg:		ioa config struct
104441da177e4SLinus Torvalds  * @shutdown_type:	shutdown type
104451da177e4SLinus Torvalds  *
104461da177e4SLinus Torvalds  * Description: This function will initiate bringing down the adapter.
104471da177e4SLinus Torvalds  * This consists of issuing an IOA shutdown to the adapter
104481da177e4SLinus Torvalds  * to flush the cache, and running BIST.
104491da177e4SLinus Torvalds  * If the caller needs to wait on the completion of the reset,
104501da177e4SLinus Torvalds  * the caller must sleep on the reset_wait_q.
104511da177e4SLinus Torvalds  *
104521da177e4SLinus Torvalds  * Return value:
104531da177e4SLinus Torvalds  * 	none
104541da177e4SLinus Torvalds  **/
104551da177e4SLinus Torvalds static void ipr_initiate_ioa_bringdown(struct ipr_ioa_cfg *ioa_cfg,
104561da177e4SLinus Torvalds 				       enum ipr_shutdown_type shutdown_type)
104571da177e4SLinus Torvalds {
104581da177e4SLinus Torvalds 	ENTER;
104591da177e4SLinus Torvalds 	if (ioa_cfg->sdt_state == WAIT_FOR_DUMP)
104601da177e4SLinus Torvalds 		ioa_cfg->sdt_state = ABORT_DUMP;
104611da177e4SLinus Torvalds 	ioa_cfg->reset_retries = 0;
104621da177e4SLinus Torvalds 	ioa_cfg->in_ioa_bringdown = 1;
104631da177e4SLinus Torvalds 	ipr_initiate_ioa_reset(ioa_cfg, shutdown_type);
104641da177e4SLinus Torvalds 	LEAVE;
104651da177e4SLinus Torvalds }
104661da177e4SLinus Torvalds 
104671da177e4SLinus Torvalds /**
104681da177e4SLinus Torvalds  * __ipr_remove - Remove a single adapter
104691da177e4SLinus Torvalds  * @pdev:	pci device struct
104701da177e4SLinus Torvalds  *
104711da177e4SLinus Torvalds  * Adapter hot plug remove entry point.
104721da177e4SLinus Torvalds  *
104731da177e4SLinus Torvalds  * Return value:
104741da177e4SLinus Torvalds  * 	none
104751da177e4SLinus Torvalds  **/
104761da177e4SLinus Torvalds static void __ipr_remove(struct pci_dev *pdev)
104771da177e4SLinus Torvalds {
104781da177e4SLinus Torvalds 	unsigned long host_lock_flags = 0;
104791da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
10480bfae7820SBrian King 	int i;
10481feccada9Swenxiong@linux.vnet.ibm.com 	unsigned long driver_lock_flags;
104821da177e4SLinus Torvalds 	ENTER;
104831da177e4SLinus Torvalds 
104841da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
10485970ea294SBrian King 	while (ioa_cfg->in_reset_reload) {
10486970ea294SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
10487970ea294SBrian King 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
10488970ea294SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
10489970ea294SBrian King 	}
10490970ea294SBrian King 
10491bfae7820SBrian King 	for (i = 0; i < ioa_cfg->hrrq_num; i++) {
10492bfae7820SBrian King 		spin_lock(&ioa_cfg->hrrq[i]._lock);
10493bfae7820SBrian King 		ioa_cfg->hrrq[i].removing_ioa = 1;
10494bfae7820SBrian King 		spin_unlock(&ioa_cfg->hrrq[i]._lock);
10495bfae7820SBrian King 	}
10496bfae7820SBrian King 	wmb();
104971da177e4SLinus Torvalds 	ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL);
104981da177e4SLinus Torvalds 
104991da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
105001da177e4SLinus Torvalds 	wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
1050143829731STejun Heo 	flush_work(&ioa_cfg->work_q);
105022796ca5eSBrian King 	if (ioa_cfg->reset_work_q)
105032796ca5eSBrian King 		flush_workqueue(ioa_cfg->reset_work_q);
105049077a944Swenxiong@linux.vnet.ibm.com 	INIT_LIST_HEAD(&ioa_cfg->used_res_q);
105051da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
105061da177e4SLinus Torvalds 
10507feccada9Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
105081da177e4SLinus Torvalds 	list_del(&ioa_cfg->queue);
10509feccada9Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
105101da177e4SLinus Torvalds 
105111da177e4SLinus Torvalds 	if (ioa_cfg->sdt_state == ABORT_DUMP)
105121da177e4SLinus Torvalds 		ioa_cfg->sdt_state = WAIT_FOR_DUMP;
105131da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
105141da177e4SLinus Torvalds 
105151da177e4SLinus Torvalds 	ipr_free_all_resources(ioa_cfg);
105161da177e4SLinus Torvalds 
105171da177e4SLinus Torvalds 	LEAVE;
105181da177e4SLinus Torvalds }
105191da177e4SLinus Torvalds 
105201da177e4SLinus Torvalds /**
105211da177e4SLinus Torvalds  * ipr_remove - IOA hot plug remove entry point
105221da177e4SLinus Torvalds  * @pdev:	pci device struct
105231da177e4SLinus Torvalds  *
105241da177e4SLinus Torvalds  * Adapter hot plug remove entry point.
105251da177e4SLinus Torvalds  *
105261da177e4SLinus Torvalds  * Return value:
105271da177e4SLinus Torvalds  * 	none
105281da177e4SLinus Torvalds  **/
105296f039790SGreg Kroah-Hartman static void ipr_remove(struct pci_dev *pdev)
105301da177e4SLinus Torvalds {
105311da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
105321da177e4SLinus Torvalds 
105331da177e4SLinus Torvalds 	ENTER;
105341da177e4SLinus Torvalds 
10535ee959b00STony Jones 	ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
105361da177e4SLinus Torvalds 			      &ipr_trace_attr);
10537ee959b00STony Jones 	ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj,
105381da177e4SLinus Torvalds 			     &ipr_dump_attr);
10539afc3f83cSBrian King 	sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj,
10540afc3f83cSBrian King 			&ipr_ioa_async_err_log);
105411da177e4SLinus Torvalds 	scsi_remove_host(ioa_cfg->host);
105421da177e4SLinus Torvalds 
105431da177e4SLinus Torvalds 	__ipr_remove(pdev);
105441da177e4SLinus Torvalds 
105451da177e4SLinus Torvalds 	LEAVE;
105461da177e4SLinus Torvalds }
105471da177e4SLinus Torvalds 
105481da177e4SLinus Torvalds /**
105491da177e4SLinus Torvalds  * ipr_probe - Adapter hot plug add entry point
10550a96099e2SLee Jones  * @pdev:	pci device struct
10551a96099e2SLee Jones  * @dev_id:	pci device ID
105521da177e4SLinus Torvalds  *
105531da177e4SLinus Torvalds  * Return value:
105541da177e4SLinus Torvalds  * 	0 on success / non-zero on failure
105551da177e4SLinus Torvalds  **/
105566f039790SGreg Kroah-Hartman static int ipr_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
105571da177e4SLinus Torvalds {
105581da177e4SLinus Torvalds 	struct ipr_ioa_cfg *ioa_cfg;
10559b195d5e2SBrian King 	unsigned long flags;
10560b53d124aSwenxiong@linux.vnet.ibm.com 	int rc, i;
105611da177e4SLinus Torvalds 
105621da177e4SLinus Torvalds 	rc = ipr_probe_ioa(pdev, dev_id);
105631da177e4SLinus Torvalds 
105641da177e4SLinus Torvalds 	if (rc)
105651da177e4SLinus Torvalds 		return rc;
105661da177e4SLinus Torvalds 
105671da177e4SLinus Torvalds 	ioa_cfg = pci_get_drvdata(pdev);
105681da177e4SLinus Torvalds 	rc = ipr_probe_ioa_part2(ioa_cfg);
105691da177e4SLinus Torvalds 
105701da177e4SLinus Torvalds 	if (rc) {
105711da177e4SLinus Torvalds 		__ipr_remove(pdev);
105721da177e4SLinus Torvalds 		return rc;
105731da177e4SLinus Torvalds 	}
105741da177e4SLinus Torvalds 
105751da177e4SLinus Torvalds 	rc = scsi_add_host(ioa_cfg->host, &pdev->dev);
105761da177e4SLinus Torvalds 
105771da177e4SLinus Torvalds 	if (rc) {
105781da177e4SLinus Torvalds 		__ipr_remove(pdev);
105791da177e4SLinus Torvalds 		return rc;
105801da177e4SLinus Torvalds 	}
105811da177e4SLinus Torvalds 
10582ee959b00STony Jones 	rc = ipr_create_trace_file(&ioa_cfg->host->shost_dev.kobj,
105831da177e4SLinus Torvalds 				   &ipr_trace_attr);
105841da177e4SLinus Torvalds 
105851da177e4SLinus Torvalds 	if (rc) {
105861da177e4SLinus Torvalds 		scsi_remove_host(ioa_cfg->host);
105871da177e4SLinus Torvalds 		__ipr_remove(pdev);
105881da177e4SLinus Torvalds 		return rc;
105891da177e4SLinus Torvalds 	}
105901da177e4SLinus Torvalds 
10591afc3f83cSBrian King 	rc = sysfs_create_bin_file(&ioa_cfg->host->shost_dev.kobj,
10592afc3f83cSBrian King 			&ipr_ioa_async_err_log);
10593afc3f83cSBrian King 
10594afc3f83cSBrian King 	if (rc) {
10595afc3f83cSBrian King 		ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj,
10596afc3f83cSBrian King 				&ipr_dump_attr);
10597afc3f83cSBrian King 		ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
10598afc3f83cSBrian King 				&ipr_trace_attr);
10599afc3f83cSBrian King 		scsi_remove_host(ioa_cfg->host);
10600afc3f83cSBrian King 		__ipr_remove(pdev);
10601afc3f83cSBrian King 		return rc;
10602afc3f83cSBrian King 	}
10603afc3f83cSBrian King 
10604ee959b00STony Jones 	rc = ipr_create_dump_file(&ioa_cfg->host->shost_dev.kobj,
106051da177e4SLinus Torvalds 				   &ipr_dump_attr);
106061da177e4SLinus Torvalds 
106071da177e4SLinus Torvalds 	if (rc) {
10608afc3f83cSBrian King 		sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj,
10609afc3f83cSBrian King 				      &ipr_ioa_async_err_log);
10610ee959b00STony Jones 		ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
106111da177e4SLinus Torvalds 				      &ipr_trace_attr);
106121da177e4SLinus Torvalds 		scsi_remove_host(ioa_cfg->host);
106131da177e4SLinus Torvalds 		__ipr_remove(pdev);
106141da177e4SLinus Torvalds 		return rc;
106151da177e4SLinus Torvalds 	}
10616a3d1ddd9SBrian King 	spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
10617a3d1ddd9SBrian King 	ioa_cfg->scan_enabled = 1;
10618a3d1ddd9SBrian King 	schedule_work(&ioa_cfg->work_q);
10619a3d1ddd9SBrian King 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
106201da177e4SLinus Torvalds 
10621b53d124aSwenxiong@linux.vnet.ibm.com 	ioa_cfg->iopoll_weight = ioa_cfg->chip_cfg->iopoll_weight;
10622b53d124aSwenxiong@linux.vnet.ibm.com 
1062389f8b33cSJens Axboe 	if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
10624b53d124aSwenxiong@linux.vnet.ibm.com 		for (i = 1; i < ioa_cfg->hrrq_num; i++) {
10625511cbce2SChristoph Hellwig 			irq_poll_init(&ioa_cfg->hrrq[i].iopoll,
10626b53d124aSwenxiong@linux.vnet.ibm.com 					ioa_cfg->iopoll_weight, ipr_iopoll);
10627b53d124aSwenxiong@linux.vnet.ibm.com 		}
10628b53d124aSwenxiong@linux.vnet.ibm.com 	}
10629b53d124aSwenxiong@linux.vnet.ibm.com 
10630a3d1ddd9SBrian King 	scsi_scan_host(ioa_cfg->host);
10631a3d1ddd9SBrian King 
106321da177e4SLinus Torvalds 	return 0;
106331da177e4SLinus Torvalds }
106341da177e4SLinus Torvalds 
106351da177e4SLinus Torvalds /**
106361da177e4SLinus Torvalds  * ipr_shutdown - Shutdown handler.
10637d18c3db5SGreg Kroah-Hartman  * @pdev:	pci device struct
106381da177e4SLinus Torvalds  *
106391da177e4SLinus Torvalds  * This function is invoked upon system shutdown/reboot. It will issue
106401da177e4SLinus Torvalds  * an adapter shutdown to the adapter to flush the write cache.
106411da177e4SLinus Torvalds  *
106421da177e4SLinus Torvalds  * Return value:
106431da177e4SLinus Torvalds  * 	none
106441da177e4SLinus Torvalds  **/
10645d18c3db5SGreg Kroah-Hartman static void ipr_shutdown(struct pci_dev *pdev)
106461da177e4SLinus Torvalds {
10647d18c3db5SGreg Kroah-Hartman 	struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
106481da177e4SLinus Torvalds 	unsigned long lock_flags = 0;
106494fdd7c7aSBrian King 	enum ipr_shutdown_type shutdown_type = IPR_SHUTDOWN_NORMAL;
10650b53d124aSwenxiong@linux.vnet.ibm.com 	int i;
106511da177e4SLinus Torvalds 
106521da177e4SLinus Torvalds 	spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
1065389f8b33cSJens Axboe 	if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
10654b53d124aSwenxiong@linux.vnet.ibm.com 		ioa_cfg->iopoll_weight = 0;
10655b53d124aSwenxiong@linux.vnet.ibm.com 		for (i = 1; i < ioa_cfg->hrrq_num; i++)
10656511cbce2SChristoph Hellwig 			irq_poll_disable(&ioa_cfg->hrrq[i].iopoll);
10657b53d124aSwenxiong@linux.vnet.ibm.com 	}
10658b53d124aSwenxiong@linux.vnet.ibm.com 
10659970ea294SBrian King 	while (ioa_cfg->in_reset_reload) {
10660970ea294SBrian King 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
10661970ea294SBrian King 		wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
10662970ea294SBrian King 		spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
10663970ea294SBrian King 	}
10664970ea294SBrian King 
106654fdd7c7aSBrian King 	if (ipr_fast_reboot && system_state == SYSTEM_RESTART && ioa_cfg->sis64)
106664fdd7c7aSBrian King 		shutdown_type = IPR_SHUTDOWN_QUIESCE;
106674fdd7c7aSBrian King 
106684fdd7c7aSBrian King 	ipr_initiate_ioa_bringdown(ioa_cfg, shutdown_type);
106691da177e4SLinus Torvalds 	spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
106701da177e4SLinus Torvalds 	wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
106714fdd7c7aSBrian King 	if (ipr_fast_reboot && system_state == SYSTEM_RESTART && ioa_cfg->sis64) {
106722796ca5eSBrian King 		ipr_free_irqs(ioa_cfg);
106734fdd7c7aSBrian King 		pci_disable_device(ioa_cfg->pdev);
106744fdd7c7aSBrian King 	}
106751da177e4SLinus Torvalds }
106761da177e4SLinus Torvalds 
106776f039790SGreg Kroah-Hartman static struct pci_device_id ipr_pci_table[] = {
106781da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
106796d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5702, 0, 0, 0 },
106801da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
106816d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5703, 0, 0, 0 },
106821da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
106836d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573D, 0, 0, 0 },
106841da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
106856d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573E, 0, 0, 0 },
106861da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
106876d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571B, 0, 0, 0 },
106881da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
106896d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572E, 0, 0, 0 },
106901da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
106916d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571A, 0, 0, 0 },
1069286f51436Sbrking@us.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
106935469cb5bSBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575B, 0, 0,
106945469cb5bSBrian King 		IPR_USE_LONG_TRANSOP_TIMEOUT },
1069586f51436Sbrking@us.ibm.com 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
106966d84c944SBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572A, 0, 0, 0 },
1069786f51436Sbrking@us.ibm.com 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
1069822d2e402SBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B, 0, 0,
1069922d2e402SBrian King 	      IPR_USE_LONG_TRANSOP_TIMEOUT },
1070060e7486bSBrian King 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
107015469cb5bSBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C, 0, 0,
107025469cb5bSBrian King 	      IPR_USE_LONG_TRANSOP_TIMEOUT },
1070386f51436Sbrking@us.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
107046d84c944SBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572A, 0, 0, 0 },
1070586f51436Sbrking@us.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
1070622d2e402SBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B, 0, 0,
1070722d2e402SBrian King 	      IPR_USE_LONG_TRANSOP_TIMEOUT},
1070860e7486bSBrian King 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
107095469cb5bSBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C, 0, 0,
107105469cb5bSBrian King 	      IPR_USE_LONG_TRANSOP_TIMEOUT },
1071160e7486bSBrian King 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
1071222d2e402SBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_574E, 0, 0,
1071322d2e402SBrian King 	      IPR_USE_LONG_TRANSOP_TIMEOUT },
10714185eb31cSBrian King 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
10715185eb31cSBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B3, 0, 0, 0 },
10716185eb31cSBrian King 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
10717b0f56d3dSWayne Boyer 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57CC, 0, 0, 0 },
10718b0f56d3dSWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
107195469cb5bSBrian King 	      PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B7, 0, 0,
10720463fc696SBrian King 	      IPR_USE_LONG_TRANSOP_TIMEOUT | IPR_USE_PCI_WARM_RESET },
107211da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE,
107226d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780, 0, 0, 0 },
107231da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
107246d84c944SBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571E, 0, 0, 0 },
1072586f51436Sbrking@us.ibm.com 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
107265469cb5bSBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571F, 0, 0,
107275469cb5bSBrian King 		IPR_USE_LONG_TRANSOP_TIMEOUT },
1072860e7486bSBrian King 	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
107295469cb5bSBrian King 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572F, 0, 0,
107305469cb5bSBrian King 		IPR_USE_LONG_TRANSOP_TIMEOUT },
10731d7b4627fSWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
10732d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B5, 0, 0, 0 },
10733d7b4627fSWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
10734d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_574D, 0, 0, 0 },
10735d7b4627fSWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
10736d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B2, 0, 0, 0 },
1073732622bdeSWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
10738b8d5d568Swenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C0, 0, 0, 0 },
10739b8d5d568Swenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
107405a918353SWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C3, 0, 0, 0 },
107415a918353SWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
1074232622bdeSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C4, 0, 0, 0 },
10743cd9b3d04SWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10744d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B4, 0, 0, 0 },
10745cd9b3d04SWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10746d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B1, 0, 0, 0 },
10747cd9b3d04SWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10748d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C6, 0, 0, 0 },
10749cd9b3d04SWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10750cd9b3d04SWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C8, 0, 0, 0 },
10751cd9b3d04SWayne Boyer 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10752d7b4627fSWayne Boyer 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57CE, 0, 0, 0 },
10753b8d5d568Swenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10754b8d5d568Swenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D5, 0, 0, 0 },
10755b8d5d568Swenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10756b8d5d568Swenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D6, 0, 0, 0 },
10757b8d5d568Swenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10758b8d5d568Swenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D7, 0, 0, 0 },
10759b8d5d568Swenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10760b8d5d568Swenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D8, 0, 0, 0 },
1076143c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1076243c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D9, 0, 0, 0 },
1076343c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
10764f94d9964SWendy Xiong 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57DA, 0, 0, 0 },
10765f94d9964SWendy Xiong 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1076643c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EB, 0, 0, 0 },
1076743c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1076843c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EC, 0, 0, 0 },
1076943c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1077043c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57ED, 0, 0, 0 },
1077143c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1077243c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EE, 0, 0, 0 },
1077343c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1077443c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EF, 0, 0, 0 },
1077543c5fdafSwenxiong@linux.vnet.ibm.com 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
1077643c5fdafSwenxiong@linux.vnet.ibm.com 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57F0, 0, 0, 0 },
107775eeac3e9SWendy Xiong 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
107785eeac3e9SWendy Xiong 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2CCA, 0, 0, 0 },
107795eeac3e9SWendy Xiong 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
107805eeac3e9SWendy Xiong 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2CD2, 0, 0, 0 },
107815eeac3e9SWendy Xiong 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
107825eeac3e9SWendy Xiong 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2CCD, 0, 0, 0 },
1078300da9ffaSWen Xiong 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_RATTLESNAKE,
1078400da9ffaSWen Xiong 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_580A, 0, 0, 0 },
1078500da9ffaSWen Xiong 	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_RATTLESNAKE,
1078600da9ffaSWen Xiong 		PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_580B, 0, 0, 0 },
107871da177e4SLinus Torvalds 	{ }
107881da177e4SLinus Torvalds };
107891da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, ipr_pci_table);
107901da177e4SLinus Torvalds 
10791a55b2d21SStephen Hemminger static const struct pci_error_handlers ipr_err_handler = {
10792f8a88b19SLinas Vepstas 	.error_detected = ipr_pci_error_detected,
107936270e593SBrian King 	.mmio_enabled = ipr_pci_mmio_enabled,
10794f8a88b19SLinas Vepstas 	.slot_reset = ipr_pci_slot_reset,
10795f8a88b19SLinas Vepstas };
10796f8a88b19SLinas Vepstas 
107971da177e4SLinus Torvalds static struct pci_driver ipr_driver = {
107981da177e4SLinus Torvalds 	.name = IPR_NAME,
107991da177e4SLinus Torvalds 	.id_table = ipr_pci_table,
108001da177e4SLinus Torvalds 	.probe = ipr_probe,
108016f039790SGreg Kroah-Hartman 	.remove = ipr_remove,
108021da177e4SLinus Torvalds 	.shutdown = ipr_shutdown,
10803f8a88b19SLinas Vepstas 	.err_handler = &ipr_err_handler,
108041da177e4SLinus Torvalds };
108051da177e4SLinus Torvalds 
108061da177e4SLinus Torvalds /**
10807f72919ecSWayne Boyer  * ipr_halt_done - Shutdown prepare completion
10808a96099e2SLee Jones  * @ipr_cmd:   ipr command struct
10809f72919ecSWayne Boyer  *
10810f72919ecSWayne Boyer  * Return value:
10811f72919ecSWayne Boyer  * 	none
10812f72919ecSWayne Boyer  **/
10813f72919ecSWayne Boyer static void ipr_halt_done(struct ipr_cmnd *ipr_cmd)
10814f72919ecSWayne Boyer {
1081505a6538aSwenxiong@linux.vnet.ibm.com 	list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
10816f72919ecSWayne Boyer }
10817f72919ecSWayne Boyer 
10818f72919ecSWayne Boyer /**
10819f72919ecSWayne Boyer  * ipr_halt - Issue shutdown prepare to all adapters
10820a96099e2SLee Jones  * @nb: Notifier block
10821a96099e2SLee Jones  * @event: Notifier event
10822a96099e2SLee Jones  * @buf: Notifier data (unused)
10823f72919ecSWayne Boyer  *
10824f72919ecSWayne Boyer  * Return value:
10825f72919ecSWayne Boyer  * 	NOTIFY_OK on success / NOTIFY_DONE on failure
10826f72919ecSWayne Boyer  **/
10827f72919ecSWayne Boyer static int ipr_halt(struct notifier_block *nb, ulong event, void *buf)
10828f72919ecSWayne Boyer {
10829f72919ecSWayne Boyer 	struct ipr_cmnd *ipr_cmd;
10830f72919ecSWayne Boyer 	struct ipr_ioa_cfg *ioa_cfg;
10831feccada9Swenxiong@linux.vnet.ibm.com 	unsigned long flags = 0, driver_lock_flags;
10832f72919ecSWayne Boyer 
10833f72919ecSWayne Boyer 	if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
10834f72919ecSWayne Boyer 		return NOTIFY_DONE;
10835f72919ecSWayne Boyer 
10836feccada9Swenxiong@linux.vnet.ibm.com 	spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
10837f72919ecSWayne Boyer 
10838f72919ecSWayne Boyer 	list_for_each_entry(ioa_cfg, &ipr_ioa_head, queue) {
10839f72919ecSWayne Boyer 		spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
108404fdd7c7aSBrian King 		if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds ||
108414fdd7c7aSBrian King 		    (ipr_fast_reboot && event == SYS_RESTART && ioa_cfg->sis64)) {
10842f72919ecSWayne Boyer 			spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
10843f72919ecSWayne Boyer 			continue;
10844f72919ecSWayne Boyer 		}
10845f72919ecSWayne Boyer 
10846f72919ecSWayne Boyer 		ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
10847f72919ecSWayne Boyer 		ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
10848f72919ecSWayne Boyer 		ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
10849f72919ecSWayne Boyer 		ipr_cmd->ioarcb.cmd_pkt.cdb[0] = IPR_IOA_SHUTDOWN;
10850f72919ecSWayne Boyer 		ipr_cmd->ioarcb.cmd_pkt.cdb[1] = IPR_SHUTDOWN_PREPARE_FOR_NORMAL;
10851f72919ecSWayne Boyer 
10852f72919ecSWayne Boyer 		ipr_do_req(ipr_cmd, ipr_halt_done, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
10853f72919ecSWayne Boyer 		spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
10854f72919ecSWayne Boyer 	}
10855feccada9Swenxiong@linux.vnet.ibm.com 	spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
10856f72919ecSWayne Boyer 
10857f72919ecSWayne Boyer 	return NOTIFY_OK;
10858f72919ecSWayne Boyer }
10859f72919ecSWayne Boyer 
10860f72919ecSWayne Boyer static struct notifier_block ipr_notifier = {
10861f72919ecSWayne Boyer 	ipr_halt, NULL, 0
10862f72919ecSWayne Boyer };
10863f72919ecSWayne Boyer 
10864f72919ecSWayne Boyer /**
108651da177e4SLinus Torvalds  * ipr_init - Module entry point
108661da177e4SLinus Torvalds  *
108671da177e4SLinus Torvalds  * Return value:
108681da177e4SLinus Torvalds  * 	0 on success / negative value on failure
108691da177e4SLinus Torvalds  **/
108701da177e4SLinus Torvalds static int __init ipr_init(void)
108711da177e4SLinus Torvalds {
108721da177e4SLinus Torvalds 	ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n",
108731da177e4SLinus Torvalds 		 IPR_DRIVER_VERSION, IPR_DRIVER_DATE);
108741da177e4SLinus Torvalds 
10875f72919ecSWayne Boyer 	register_reboot_notifier(&ipr_notifier);
10876dcbccbdeSHenrik Kretzschmar 	return pci_register_driver(&ipr_driver);
108771da177e4SLinus Torvalds }
108781da177e4SLinus Torvalds 
108791da177e4SLinus Torvalds /**
108801da177e4SLinus Torvalds  * ipr_exit - Module unload
108811da177e4SLinus Torvalds  *
108821da177e4SLinus Torvalds  * Module unload entry point.
108831da177e4SLinus Torvalds  *
108841da177e4SLinus Torvalds  * Return value:
108851da177e4SLinus Torvalds  * 	none
108861da177e4SLinus Torvalds  **/
108871da177e4SLinus Torvalds static void __exit ipr_exit(void)
108881da177e4SLinus Torvalds {
10889f72919ecSWayne Boyer 	unregister_reboot_notifier(&ipr_notifier);
108901da177e4SLinus Torvalds 	pci_unregister_driver(&ipr_driver);
108911da177e4SLinus Torvalds }
108921da177e4SLinus Torvalds 
108931da177e4SLinus Torvalds module_init(ipr_init);
108941da177e4SLinus Torvalds module_exit(ipr_exit);
10895