ipr.c 220 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
 * ipr.c -- driver for IBM Power Linux RAID adapters
 *
 * Written By: Brian King <brking@us.ibm.com>, IBM Corporation
 *
 * Copyright (C) 2003, 2004 IBM Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/*
 * Notes:
 *
 * This driver is used to control the following SCSI adapters:
 *
 * IBM iSeries: 5702, 5703, 2780, 5709, 570A, 570B
 *
 * IBM pSeries: PCI-X Dual Channel Ultra 320 SCSI RAID Adapter
 *              PCI-X Dual Channel Ultra 320 SCSI Adapter
 *              PCI-X Dual Channel Ultra 320 SCSI RAID Enablement Card
 *              Embedded SCSI adapter on p615 and p655 systems
 *
 * Supported Hardware Features:
 *	- Ultra 320 SCSI controller
 *	- PCI-X host interface
 *	- Embedded PowerPC RISC Processor and Hardware XOR DMA Engine
 *	- Non-Volatile Write Cache
 *	- Supports attachment of non-RAID disks, tape, and optical devices
 *	- RAID Levels 0, 5, 10
 *	- Hot spare
 *	- Background Parity Checking
 *	- Background Data Scrubbing
 *	- Ability to increase the capacity of an existing RAID 5 disk array
 *		by adding disks
 *
 * Driver Features:
 *	- Tagged command queuing
 *	- Adapter microcode download
 *	- PCI hot plug
 *	- SCSI device hot plug
 *
 */

#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
73
#include <linux/libata.h>
74
#include <linux/hdreg.h>
Linus Torvalds's avatar
Linus Torvalds committed
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/processor.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_cmnd.h>
#include "ipr.h"

/*
 *   Global Data
 */
88
static LIST_HEAD(ipr_ioa_head);
Linus Torvalds's avatar
Linus Torvalds committed
89
90
91
92
static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL;
static unsigned int ipr_max_speed = 1;
static int ipr_testmode = 0;
static unsigned int ipr_fastfail = 0;
93
static unsigned int ipr_transop_timeout = 0;
94
static unsigned int ipr_enable_cache = 1;
95
static unsigned int ipr_debug = 0;
96
static unsigned int ipr_dual_ioa_raid = 1;
Linus Torvalds's avatar
Linus Torvalds committed
97
98
99
100
static DEFINE_SPINLOCK(ipr_driver_lock);

/* This table describes the differences between DMA controller chips */
static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
101
	{ /* Gemstone, Citrine, Obsidian, and Obsidian-E */
Linus Torvalds's avatar
Linus Torvalds committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
		.mailbox = 0x0042C,
		.cache_line_size = 0x20,
		{
			.set_interrupt_mask_reg = 0x0022C,
			.clr_interrupt_mask_reg = 0x00230,
			.sense_interrupt_mask_reg = 0x0022C,
			.clr_interrupt_reg = 0x00228,
			.sense_interrupt_reg = 0x00224,
			.ioarrin_reg = 0x00404,
			.sense_uproc_interrupt_reg = 0x00214,
			.set_uproc_interrupt_reg = 0x00214,
			.clr_uproc_interrupt_reg = 0x00218
		}
	},
	{ /* Snipe and Scamp */
		.mailbox = 0x0052C,
		.cache_line_size = 0x20,
		{
			.set_interrupt_mask_reg = 0x00288,
			.clr_interrupt_mask_reg = 0x0028C,
			.sense_interrupt_mask_reg = 0x00288,
			.clr_interrupt_reg = 0x00284,
			.sense_interrupt_reg = 0x00280,
			.ioarrin_reg = 0x00504,
			.sense_uproc_interrupt_reg = 0x00290,
			.set_uproc_interrupt_reg = 0x00290,
			.clr_uproc_interrupt_reg = 0x00294
		}
	},
};

static const struct ipr_chip_t ipr_chip[] = {
134
135
136
137
138
139
140
	{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, IPR_USE_LSI, &ipr_chip_cfg[0] },
	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, IPR_USE_LSI, &ipr_chip_cfg[0] },
	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, IPR_USE_LSI, &ipr_chip_cfg[0] },
	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, IPR_USE_LSI, &ipr_chip_cfg[0] },
	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, IPR_USE_MSI, &ipr_chip_cfg[0] },
	{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, IPR_USE_LSI, &ipr_chip_cfg[1] },
	{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, IPR_USE_LSI, &ipr_chip_cfg[1] }
Linus Torvalds's avatar
Linus Torvalds committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
};

static int ipr_max_bus_speeds [] = {
	IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
};

MODULE_AUTHOR("Brian King <brking@us.ibm.com>");
MODULE_DESCRIPTION("IBM Power RAID SCSI Adapter Driver");
module_param_named(max_speed, ipr_max_speed, uint, 0);
MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320");
module_param_named(log_level, ipr_log_level, uint, 0);
MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver");
module_param_named(testmode, ipr_testmode, int, 0);
MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations");
155
module_param_named(fastfail, ipr_fastfail, int, S_IRUGO | S_IWUSR);
Linus Torvalds's avatar
Linus Torvalds committed
156
157
158
MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries");
module_param_named(transop_timeout, ipr_transop_timeout, int, 0);
MODULE_PARM_DESC(transop_timeout, "Time in seconds to wait for adapter to come operational (default: 300)");
159
160
module_param_named(enable_cache, ipr_enable_cache, int, 0);
MODULE_PARM_DESC(enable_cache, "Enable adapter's non-volatile write cache (default: 1)");
161
module_param_named(debug, ipr_debug, int, S_IRUGO | S_IWUSR);
162
MODULE_PARM_DESC(debug, "Enable device driver debugging logging. Set to 1 to enable. (default: 0)");
163
164
module_param_named(dual_ioa_raid, ipr_dual_ioa_raid, int, 0);
MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to enable. (default: 1)");
Linus Torvalds's avatar
Linus Torvalds committed
165
166
167
168
169
170
MODULE_LICENSE("GPL");
MODULE_VERSION(IPR_DRIVER_VERSION);

/*  A constant array of IOASCs/URCs/Error Messages */
static const
struct ipr_error_table_t ipr_error_table[] = {
171
	{0x00000000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
172
173
174
175
176
177
178
	"8155: An unknown error was received"},
	{0x00330000, 0, 0,
	"Soft underlength error"},
	{0x005A0000, 0, 0,
	"Command to be cancelled not found"},
	{0x00808000, 0, 0,
	"Qualified success"},
179
	{0x01080000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
180
	"FFFE: Soft device bus error recovered by the IOA"},
181
	{0x01088100, 0, IPR_DEFAULT_LOG_LEVEL,
182
	"4101: Soft device bus fabric error"},
183
	{0x01170600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
184
	"FFF9: Device sector reassign successful"},
185
	{0x01170900, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
186
	"FFF7: Media error recovered by device rewrite procedures"},
187
	{0x01180200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
188
	"7001: IOA sector reassignment successful"},
189
	{0x01180500, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
190
	"FFF9: Soft media error. Sector reassignment recommended"},
191
	{0x01180600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
192
	"FFF7: Media error recovered by IOA rewrite procedures"},
193
	{0x01418000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
194
	"FF3D: Soft PCI bus error recovered by the IOA"},
195
	{0x01440000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
196
	"FFF6: Device hardware error recovered by the IOA"},
197
	{0x01448100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
198
	"FFF6: Device hardware error recovered by the device"},
199
	{0x01448200, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
200
	"FF3D: Soft IOA error recovered by the IOA"},
201
	{0x01448300, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
202
	"FFFA: Undefined device response recovered by the IOA"},
203
	{0x014A0000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
204
	"FFF6: Device bus error, message or command phase"},
205
	{0x014A8000, 0, IPR_DEFAULT_LOG_LEVEL,
206
	"FFFE: Task Management Function failed"},
207
	{0x015D0000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
208
	"FFF6: Failure prediction threshold exceeded"},
209
	{0x015D9200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
210
211
212
	"8009: Impending cache battery pack failure"},
	{0x02040400, 0, 0,
	"34FF: Disk device format in progress"},
213
214
	{0x02048000, 0, IPR_DEFAULT_LOG_LEVEL,
	"9070: IOA requested reset"},
Linus Torvalds's avatar
Linus Torvalds committed
215
216
217
218
219
220
	{0x023F0000, 0, 0,
	"Synchronization required"},
	{0x024E0000, 0, 0,
	"No ready, IOA shutdown"},
	{0x025A0000, 0, 0,
	"Not ready, IOA has been shutdown"},
221
	{0x02670100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225
226
	"3020: Storage subsystem configuration error"},
	{0x03110B00, 0, 0,
	"FFF5: Medium error, data unreadable, recommend reassign"},
	{0x03110C00, 0, 0,
	"7000: Medium error, data unreadable, do not reassign"},
227
	{0x03310000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
228
	"FFF3: Disk media format bad"},
229
	{0x04050000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
230
	"3002: Addressed device failed to respond to selection"},
231
	{0x04080000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
232
	"3100: Device bus error"},
233
	{0x04080100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
234
235
236
	"3109: IOA timed out a device command"},
	{0x04088000, 0, 0,
	"3120: SCSI bus is not operational"},
237
	{0x04088100, 0, IPR_DEFAULT_LOG_LEVEL,
238
	"4100: Hard device bus fabric error"},
239
	{0x04118000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
240
	"9000: IOA reserved area data check"},
241
	{0x04118100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
242
	"9001: IOA reserved area invalid data pattern"},
243
	{0x04118200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
244
	"9002: IOA reserved area LRC error"},
245
	{0x04320000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
246
	"102E: Out of alternate sectors for disk storage"},
247
	{0x04330000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
248
	"FFF4: Data transfer underlength error"},
249
	{0x04338000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
250
	"FFF4: Data transfer overlength error"},
251
	{0x043E0100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
252
	"3400: Logical unit failure"},
253
	{0x04408500, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
254
	"FFF4: Device microcode is corrupt"},
255
	{0x04418000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
256
257
258
	"8150: PCI bus error"},
	{0x04430000, 1, 0,
	"Unsupported device bus message received"},
259
	{0x04440000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
260
	"FFF4: Disk device problem"},
261
	{0x04448200, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
262
	"8150: Permanent IOA failure"},
263
	{0x04448300, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
264
	"3010: Disk device returned wrong response to IOA"},
265
	{0x04448400, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
266
267
268
	"8151: IOA microcode error"},
	{0x04448500, 0, 0,
	"Device bus status error"},
269
	{0x04448600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
270
	"8157: IOA error requiring IOA reset to recover"},
271
272
	{0x04448700, 0, 0,
	"ATA device status error"},
Linus Torvalds's avatar
Linus Torvalds committed
273
274
	{0x04490000, 0, 0,
	"Message reject received from the device"},
275
	{0x04449200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
276
	"8008: A permanent cache battery pack failure occurred"},
277
	{0x0444A000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
278
	"9090: Disk unit has been modified after the last known status"},
279
	{0x0444A200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
280
	"9081: IOA detected device error"},
281
	{0x0444A300, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
282
	"9082: IOA detected device error"},
283
	{0x044A0000, 1, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
284
	"3110: Device bus error, message or command phase"},
285
	{0x044A8000, 1, IPR_DEFAULT_LOG_LEVEL,
286
	"3110: SAS Command / Task Management Function failed"},
287
	{0x04670400, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
288
	"9091: Incorrect hardware configuration change has been detected"},
289
	{0x04678000, 0, IPR_DEFAULT_LOG_LEVEL,
290
	"9073: Invalid multi-adapter configuration"},
291
	{0x04678100, 0, IPR_DEFAULT_LOG_LEVEL,
292
	"4010: Incorrect connection between cascaded expanders"},
293
	{0x04678200, 0, IPR_DEFAULT_LOG_LEVEL,
294
	"4020: Connections exceed IOA design limits"},
295
	{0x04678300, 0, IPR_DEFAULT_LOG_LEVEL,
296
	"4030: Incorrect multipath connection"},
297
	{0x04679000, 0, IPR_DEFAULT_LOG_LEVEL,
298
	"4110: Unsupported enclosure function"},
299
	{0x046E0000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
300
301
302
303
304
	"FFF4: Command to logical unit failed"},
	{0x05240000, 1, 0,
	"Illegal request, invalid request type or request packet"},
	{0x05250000, 0, 0,
	"Illegal request, invalid resource handle"},
305
306
307
308
	{0x05258000, 0, 0,
	"Illegal request, commands not allowed to this device"},
	{0x05258100, 0, 0,
	"Illegal request, command not allowed to a secondary adapter"},
Linus Torvalds's avatar
Linus Torvalds committed
309
310
311
312
313
314
315
316
	{0x05260000, 0, 0,
	"Illegal request, invalid field in parameter list"},
	{0x05260100, 0, 0,
	"Illegal request, parameter not supported"},
	{0x05260200, 0, 0,
	"Illegal request, parameter value invalid"},
	{0x052C0000, 0, 0,
	"Illegal request, command sequence error"},
317
318
	{0x052C8000, 1, 0,
	"Illegal request, dual adapter support not enabled"},
319
	{0x06040500, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
320
	"9031: Array protection temporarily suspended, protection resuming"},
321
	{0x06040600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
322
	"9040: Array protection temporarily suspended, protection resuming"},
323
	{0x06288000, 0, IPR_DEFAULT_LOG_LEVEL,
324
	"3140: Device bus not ready to ready transition"},
325
	{0x06290000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
326
327
328
329
330
	"FFFB: SCSI bus was reset"},
	{0x06290500, 0, 0,
	"FFFE: SCSI bus transition to single ended"},
	{0x06290600, 0, 0,
	"FFFE: SCSI bus transition to LVD"},
331
	{0x06298000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
332
	"FFFB: SCSI bus was reset by another initiator"},
333
	{0x063F0300, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
334
	"3029: A device replacement has occurred"},
335
	{0x064C8000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
336
	"9051: IOA cache data exists for a missing or failed device"},
337
	{0x064C8100, 0, IPR_DEFAULT_LOG_LEVEL,
338
	"9055: Auxiliary cache IOA contains cache data needed by the primary IOA"},
339
	{0x06670100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
340
	"9025: Disk unit is not supported at its physical location"},
341
	{0x06670600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
342
	"3020: IOA detected a SCSI bus configuration error"},
343
	{0x06678000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
344
	"3150: SCSI bus configuration error"},
345
	{0x06678100, 0, IPR_DEFAULT_LOG_LEVEL,
346
	"9074: Asymmetric advanced function disk configuration"},
347
	{0x06678300, 0, IPR_DEFAULT_LOG_LEVEL,
348
	"4040: Incomplete multipath connection between IOA and enclosure"},
349
	{0x06678400, 0, IPR_DEFAULT_LOG_LEVEL,
350
	"4041: Incomplete multipath connection between enclosure and device"},
351
	{0x06678500, 0, IPR_DEFAULT_LOG_LEVEL,
352
	"9075: Incomplete multipath connection between IOA and remote IOA"},
353
	{0x06678600, 0, IPR_DEFAULT_LOG_LEVEL,
354
	"9076: Configuration error, missing remote IOA"},
355
	{0x06679100, 0, IPR_DEFAULT_LOG_LEVEL,
356
	"4050: Enclosure does not support a required multipath function"},
357
358
	{0x06690000, 0, IPR_DEFAULT_LOG_LEVEL,
	"4070: Logically bad block written on device"},
359
	{0x06690200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
360
	"9041: Array protection temporarily suspended"},
361
	{0x06698200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
362
	"9042: Corrupt array parity detected on specified device"},
363
	{0x066B0200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
364
	"9030: Array no longer protected due to missing or failed disk unit"},
365
	{0x066B8000, 0, IPR_DEFAULT_LOG_LEVEL,
366
	"9071: Link operational transition"},
367
	{0x066B8100, 0, IPR_DEFAULT_LOG_LEVEL,
368
	"9072: Link not operational transition"},
369
	{0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
370
	"9032: Array exposed but still protected"},
Brian King's avatar
Brian King committed
371
372
	{0x066B8300, 0, IPR_DEFAULT_LOG_LEVEL + 1,
	"70DD: Device forced failed by disrupt device command"},
373
	{0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL,
374
	"4061: Multipath redundancy level got better"},
375
	{0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL,
376
	"4060: Multipath redundancy level got worse"},
Linus Torvalds's avatar
Linus Torvalds committed
377
378
	{0x07270000, 0, 0,
	"Failure due to other device"},
379
	{0x07278000, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
380
	"9008: IOA does not support functions expected by devices"},
381
	{0x07278100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
382
	"9010: Cache data associated with attached devices cannot be found"},
383
	{0x07278200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
384
	"9011: Cache data belongs to devices other than those attached"},
385
	{0x07278400, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
386
	"9020: Array missing 2 or more devices with only 1 device present"},
387
	{0x07278500, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
388
	"9021: Array missing 2 or more devices with 2 or more devices present"},
389
	{0x07278600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
390
	"9022: Exposed array is missing a required device"},
391
	{0x07278700, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
392
	"9023: Array member(s) not at required physical locations"},
393
	{0x07278800, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
394
	"9024: Array not functional due to present hardware configuration"},
395
	{0x07278900, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
396
	"9026: Array not functional due to present hardware configuration"},
397
	{0x07278A00, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
398
	"9027: Array is missing a device and parity is out of sync"},
399
	{0x07278B00, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
400
	"9028: Maximum number of arrays already exist"},
401
	{0x07278C00, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
402
	"9050: Required cache data cannot be located for a disk unit"},
403
	{0x07278D00, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
404
	"9052: Cache data exists for a device that has been modified"},
405
	{0x07278F00, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
406
	"9054: IOA resources not available due to previous problems"},
407
	{0x07279100, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
408
	"9092: Disk unit requires initialization before use"},
409
	{0x07279200, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
410
	"9029: Incorrect hardware configuration change has been detected"},
411
	{0x07279600, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
412
	"9060: One or more disk pairs are missing from an array"},
413
	{0x07279700, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
414
	"9061: One or more disks are missing from an array"},
415
	{0x07279800, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
416
	"9062: One or more disks are missing from an array"},
417
	{0x07279900, 0, IPR_DEFAULT_LOG_LEVEL,
Linus Torvalds's avatar
Linus Torvalds committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
	"9063: Maximum number of functional arrays has been exceeded"},
	{0x0B260000, 0, 0,
	"Aborted command, invalid descriptor"},
	{0x0B5A0000, 0, 0,
	"Command terminated by host"}
};

static const struct ipr_ses_table_entry ipr_ses_table[] = {
	{ "2104-DL1        ", "XXXXXXXXXXXXXXXX", 80 },
	{ "2104-TL1        ", "XXXXXXXXXXXXXXXX", 80 },
	{ "HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 7 slot */
	{ "HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 5 slot */
	{ "HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Bowtie */
	{ "HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* MartinFenning */
	{ "2104-DU3        ", "XXXXXXXXXXXXXXXX", 160 },
	{ "2104-TU3        ", "XXXXXXXXXXXXXXXX", 160 },
	{ "HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 },
	{ "HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 },
	{ "St  V1S2        ", "XXXXXXXXXXXXXXXX", 160 },
	{ "HSBPD4M  PU3SCSI", "XXXXXXX*XXXXXXXX", 160 },
	{ "VSBPD1H   U3SCSI", "XXXXXXX*XXXXXXXX", 160 }
};

/*
 *  Function Prototypes
 */
static int ipr_reset_alert(struct ipr_cmnd *);
static void ipr_process_ccn(struct ipr_cmnd *);
static void ipr_process_error(struct ipr_cmnd *);
static void ipr_reset_ioa_job(struct ipr_cmnd *);
static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *,
				   enum ipr_shutdown_type);

#ifdef CONFIG_SCSI_IPR_TRACE
/**
 * ipr_trc_hook - Add a trace entry to the driver trace
 * @ipr_cmd:	ipr command struct
 * @type:		trace type
 * @add_data:	additional data
 *
 * Return value:
 * 	none
 **/
static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
			 u8 type, u32 add_data)
{
	struct ipr_trace_entry *trace_entry;
	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;

	trace_entry = &ioa_cfg->trace[ioa_cfg->trace_index++];
	trace_entry->time = jiffies;
	trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0];
	trace_entry->type = type;
471
472
	trace_entry->ata_op_code = ipr_cmd->ioarcb.add_data.u.regs.command;
	trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff;
Linus Torvalds's avatar
Linus Torvalds committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
	trace_entry->res_handle = ipr_cmd->ioarcb.res_handle;
	trace_entry->u.add_data = add_data;
}
#else
#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0)
#endif

/**
 * ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse
 * @ipr_cmd:	ipr command struct
 *
 * Return value:
 * 	none
 **/
static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
{
	struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
	struct ipr_ioasa *ioasa = &ipr_cmd->ioasa;
Brian King's avatar
Brian King committed
491
	dma_addr_t dma_addr = be32_to_cpu(ioarcb->ioarcb_host_pci_addr);
Linus Torvalds's avatar
Linus Torvalds committed
492
493
494
495
496
497

	memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt));
	ioarcb->write_data_transfer_length = 0;
	ioarcb->read_data_transfer_length = 0;
	ioarcb->write_ioadl_len = 0;
	ioarcb->read_ioadl_len = 0;
Brian King's avatar
Brian King committed
498
499
500
	ioarcb->write_ioadl_addr =
		cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, ioadl));
	ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
Linus Torvalds's avatar
Linus Torvalds committed
501
502
	ioasa->ioasc = 0;
	ioasa->residual_data_len = 0;
503
	ioasa->u.gata.status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
504
505

	ipr_cmd->scsi_cmd = NULL;
506
	ipr_cmd->qc = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
	ipr_cmd->sense_buffer[0] = 0;
	ipr_cmd->dma_use_sg = 0;
}

/**
 * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block
 * @ipr_cmd:	ipr command struct
 *
 * Return value:
 * 	none
 **/
static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
{
	ipr_reinit_ipr_cmnd(ipr_cmd);
	ipr_cmd->u.scratch = 0;
	ipr_cmd->sibling = NULL;
	init_timer(&ipr_cmd->timer);
}

/**
 * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block
 * @ioa_cfg:	ioa config struct
 *
 * Return value:
 * 	pointer to ipr command struct
 **/
static
struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg)
{
	struct ipr_cmnd *ipr_cmd;

	ipr_cmd = list_entry(ioa_cfg->free_q.next, struct ipr_cmnd, queue);
	list_del(&ipr_cmd->queue);
	ipr_init_ipr_cmnd(ipr_cmd);

	return ipr_cmd;
}

/**
 * ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts
 * @ioa_cfg:	ioa config struct
 * @clr_ints:     interrupts to clear
 *
 * This function masks all interrupts on the adapter, then clears the
 * interrupts specified in the mask
 *
 * Return value:
 * 	none
 **/
static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
					  u32 clr_ints)
{
	volatile u32 int_reg;

	/* Stop new interrupts */
	ioa_cfg->allow_interrupts = 0;

	/* Set interrupt mask to stop all new interrupts */
	writel(~0, ioa_cfg->regs.set_interrupt_mask_reg);

	/* Clear any pending interrupts */
	writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg);
	int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
}

/**
 * ipr_save_pcix_cmd_reg - Save PCI-X command register
 * @ioa_cfg:	ioa config struct
 *
 * Return value:
 * 	0 on success / -EIO on failure
 **/
static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
{
	int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);

583
584
	if (pcix_cmd_reg == 0)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617

	if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
				 &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
		dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n");
		return -EIO;
	}

	ioa_cfg->saved_pcix_cmd_reg |= PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO;
	return 0;
}

/**
 * ipr_set_pcix_cmd_reg - Setup PCI-X command register
 * @ioa_cfg:	ioa config struct
 *
 * Return value:
 * 	0 on success / -EIO on failure
 **/
static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
{
	int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);

	if (pcix_cmd_reg) {
		if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
					  ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
			dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n");
			return -EIO;
		}
	}

	return 0;
}

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
/**
 * ipr_sata_eh_done - done function for aborted SATA commands
 * @ipr_cmd:	ipr command struct
 *
 * This function is invoked for ops generated to SATA
 * devices which are being aborted.
 *
 * Return value:
 * 	none
 **/
static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
{
	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
	struct ata_queued_cmd *qc = ipr_cmd->qc;
	struct ipr_sata_port *sata_port = qc->ap->private_data;

	qc->err_mask |= AC_ERR_OTHER;
	sata_port->ioasa.status |= ATA_BUSY;
	list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
	ata_qc_complete(qc);
}

Linus Torvalds's avatar
Linus Torvalds committed
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
/**
 * ipr_scsi_eh_done - mid-layer done function for aborted ops
 * @ipr_cmd:	ipr command struct
 *
 * This function is invoked by the interrupt handler for
 * ops generated by the SCSI mid-layer which are being aborted.
 *
 * Return value:
 * 	none
 **/
static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
{
	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
	struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;

	scsi_cmd->result |= (DID_ERROR << 16);

657
	scsi_dma_unmap(ipr_cmd->scsi_cmd);
Linus Torvalds's avatar
Linus Torvalds committed
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
	scsi_cmd->scsi_done(scsi_cmd);
	list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
}

/**
 * ipr_fail_all_ops - Fails all outstanding ops.
 * @ioa_cfg:	ioa config struct
 *
 * This function fails all outstanding ops.
 *
 * Return value:
 * 	none
 **/
static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg)
{
	struct ipr_cmnd *ipr_cmd, *temp;

	ENTER;
	list_for_each_entry_safe(ipr_cmd, temp, &ioa_cfg->pending_q, queue) {
		list_del(&ipr_cmd->queue);

		ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_IOA_WAS_RESET);
		ipr_cmd->ioasa.ilid = cpu_to_be32(IPR_DRIVER_ILID);

		if (ipr_cmd->scsi_cmd)
			ipr_cmd->done = ipr_scsi_eh_done;
684
685
		else if (ipr_cmd->qc)
			ipr_cmd->done = ipr_sata_eh_done;
Linus Torvalds's avatar
Linus Torvalds committed
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835

		ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET);
		del_timer(&ipr_cmd->timer);
		ipr_cmd->done(ipr_cmd);
	}

	LEAVE;
}

/**
 * ipr_do_req -  Send driver initiated requests.
 * @ipr_cmd:		ipr command struct
 * @done:			done function
 * @timeout_func:	timeout function
 * @timeout:		timeout value
 *
 * This function sends the specified command to the adapter with the
 * timeout given. The done function is invoked on command completion.
 *
 * Return value:
 * 	none
 **/
static void ipr_do_req(struct ipr_cmnd *ipr_cmd,
		       void (*done) (struct ipr_cmnd *),
		       void (*timeout_func) (struct ipr_cmnd *), u32 timeout)
{
	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;

	list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);

	ipr_cmd->done = done;

	ipr_cmd->timer.data = (unsigned long) ipr_cmd;
	ipr_cmd->timer.expires = jiffies + timeout;
	ipr_cmd->timer.function = (void (*)(unsigned long))timeout_func;

	add_timer(&ipr_cmd->timer);

	ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0);

	mb();
	writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr),
	       ioa_cfg->regs.ioarrin_reg);
}

/**
 * ipr_internal_cmd_done - Op done function for an internally generated op.
 * @ipr_cmd:	ipr command struct
 *
 * This function is the op done function for an internally generated,
 * blocking op. It simply wakes the sleeping thread.
 *
 * Return value:
 * 	none
 **/
static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd)
{
	if (ipr_cmd->sibling)
		ipr_cmd->sibling = NULL;
	else
		complete(&ipr_cmd->completion);
}

/**
 * ipr_send_blocking_cmd - Send command and sleep on its completion.
 * @ipr_cmd:	ipr command struct
 * @timeout_func:	function to invoke if command times out
 * @timeout:	timeout
 *
 * Return value:
 * 	none
 **/
static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
				  void (*timeout_func) (struct ipr_cmnd *ipr_cmd),
				  u32 timeout)
{
	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;

	init_completion(&ipr_cmd->completion);
	ipr_do_req(ipr_cmd, ipr_internal_cmd_done, timeout_func, timeout);

	spin_unlock_irq(ioa_cfg->host->host_lock);
	wait_for_completion(&ipr_cmd->completion);
	spin_lock_irq(ioa_cfg->host->host_lock);
}

/**
 * ipr_send_hcam - Send an HCAM to the adapter.
 * @ioa_cfg:	ioa config struct
 * @type:		HCAM type
 * @hostrcb:	hostrcb struct
 *
 * This function will send a Host Controlled Async command to the adapter.
 * If HCAMs are currently not allowed to be issued to the adapter, it will
 * place the hostrcb on the free queue.
 *
 * Return value:
 * 	none
 **/
static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
			  struct ipr_hostrcb *hostrcb)
{
	struct ipr_cmnd *ipr_cmd;
	struct ipr_ioarcb *ioarcb;

	if (ioa_cfg->allow_cmds) {
		ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
		list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
		list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q);

		ipr_cmd->u.hostrcb = hostrcb;
		ioarcb = &ipr_cmd->ioarcb;

		ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
		ioarcb->cmd_pkt.request_type = IPR_RQTYPE_HCAM;
		ioarcb->cmd_pkt.cdb[0] = IPR_HOST_CONTROLLED_ASYNC;
		ioarcb->cmd_pkt.cdb[1] = type;
		ioarcb->cmd_pkt.cdb[7] = (sizeof(hostrcb->hcam) >> 8) & 0xff;
		ioarcb->cmd_pkt.cdb[8] = sizeof(hostrcb->hcam) & 0xff;

		ioarcb->read_data_transfer_length = cpu_to_be32(sizeof(hostrcb->hcam));
		ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
		ipr_cmd->ioadl[0].flags_and_data_len =
			cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | sizeof(hostrcb->hcam));
		ipr_cmd->ioadl[0].address = cpu_to_be32(hostrcb->hostrcb_dma);

		if (type == IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE)
			ipr_cmd->done = ipr_process_ccn;
		else
			ipr_cmd->done = ipr_process_error;

		ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR);

		mb();
		writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr),
		       ioa_cfg->regs.ioarrin_reg);
	} else {
		list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
	}
}

/**
 * ipr_init_res_entry - Initialize a resource entry struct.
 * @res:	resource entry struct
 *
 * Return value:
 * 	none
 **/
static void ipr_init_res_entry(struct ipr_resource_entry *res)
{
836
	res->needs_sync_complete = 0;
Linus Torvalds's avatar
Linus Torvalds committed
837
838
839
840
841
	res->in_erp = 0;
	res->add_to_ml = 0;
	res->del_from_ml = 0;
	res->resetting_device = 0;
	res->sdev = NULL;
842
	res->sata_port = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
}

/**
 * ipr_handle_config_change - Handle a config change from the adapter
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb
 *
 * Return value:
 * 	none
 **/
static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg,
			      struct ipr_hostrcb *hostrcb)
{
	struct ipr_resource_entry *res = NULL;
	struct ipr_config_table_entry *cfgte;
	u32 is_ndn = 1;

	cfgte = &hostrcb->hcam.u.ccn.cfgte;

	list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
		if (!memcmp(&res->cfgte.res_addr, &cfgte->res_addr,
			    sizeof(cfgte->res_addr))) {
			is_ndn = 0;
			break;
		}
	}

	if (is_ndn) {
		if (list_empty(&ioa_cfg->free_res_q)) {
			ipr_send_hcam(ioa_cfg,
				      IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE,
				      hostrcb);
			return;
		}

		res = list_entry(ioa_cfg->free_res_q.next,
				 struct ipr_resource_entry, queue);

		list_del(&res->queue);
		ipr_init_res_entry(res);
		list_add_tail(&res->queue, &ioa_cfg->used_res_q);
	}

	memcpy(&res->cfgte, cfgte, sizeof(struct ipr_config_table_entry));

	if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) {
		if (res->sdev) {
			res->del_from_ml = 1;
891
			res->cfgte.res_handle = IPR_INVALID_RES_HANDLE;
Linus Torvalds's avatar
Linus Torvalds committed
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
			if (ioa_cfg->allow_ml_add_del)
				schedule_work(&ioa_cfg->work_q);
		} else
			list_move_tail(&res->queue, &ioa_cfg->free_res_q);
	} else if (!res->sdev) {
		res->add_to_ml = 1;
		if (ioa_cfg->allow_ml_add_del)
			schedule_work(&ioa_cfg->work_q);
	}

	ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
}

/**
 * ipr_process_ccn - Op done function for a CCN.
 * @ipr_cmd:	ipr command struct
 *
 * This function is the op done function for a configuration
 * change notification host controlled async from the adapter.
 *
 * Return value:
 * 	none
 **/
static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd)
{
	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
	struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
	u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);

	list_del(&hostrcb->queue);
	list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);

	if (ioasc) {
		if (ioasc != IPR_IOASC_IOA_WAS_RESET)
			dev_err(&ioa_cfg->pdev->dev,
				"Host RCB failed with IOASC: 0x%08X\n", ioasc);

		ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
	} else {
		ipr_handle_config_change(ioa_cfg, hostrcb);
	}
}

935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
/**
 * strip_and_pad_whitespace - Strip and pad trailing whitespace.
 * @i:		index into buffer
 * @buf:		string to modify
 *
 * This function will strip all trailing whitespace, pad the end
 * of the string with a single space, and NULL terminate the string.
 *
 * Return value:
 * 	new length of string
 **/
static int strip_and_pad_whitespace(int i, char *buf)
{
	while (i && buf[i] == ' ')
		i--;
	buf[i+1] = ' ';
	buf[i+2] = '\0';
	return i + 2;
}

/**
 * ipr_log_vpd_compact - Log the passed extended VPD compactly.
 * @prefix:		string to print at start of printk
 * @hostrcb:	hostrcb pointer
 * @vpd:		vendor/product id/sn struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb,
				struct ipr_vpd *vpd)
{
	char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN + 3];
	int i = 0;

	memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN);
	i = strip_and_pad_whitespace(IPR_VENDOR_ID_LEN - 1, buffer);

	memcpy(&buffer[i], vpd->vpids.product_id, IPR_PROD_ID_LEN);
	i = strip_and_pad_whitespace(i + IPR_PROD_ID_LEN - 1, buffer);

	memcpy(&buffer[i], vpd->sn, IPR_SERIAL_NUM_LEN);
	buffer[IPR_SERIAL_NUM_LEN + i] = '\0';

	ipr_hcam_err(hostrcb, "%s VPID/SN: %s\n", prefix, buffer);
}

Linus Torvalds's avatar
Linus Torvalds committed
982
983
/**
 * ipr_log_vpd - Log the passed VPD to the error log.
984
 * @vpd:		vendor/product id/sn struct
Linus Torvalds's avatar
Linus Torvalds committed
985
986
987
988
 *
 * Return value:
 * 	none
 **/
989
static void ipr_log_vpd(struct ipr_vpd *vpd)
Linus Torvalds's avatar
Linus Torvalds committed
990
991
992
993
{
	char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN
		    + IPR_SERIAL_NUM_LEN];

994
995
	memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN);
	memcpy(buffer + IPR_VENDOR_ID_LEN, vpd->vpids.product_id,
Linus Torvalds's avatar
Linus Torvalds committed
996
997
998
999
	       IPR_PROD_ID_LEN);
	buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0';
	ipr_err("Vendor/Product ID: %s\n", buffer);

1000
	memcpy(buffer, vpd->sn, IPR_SERIAL_NUM_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
1001
1002
1003
1004
	buffer[IPR_SERIAL_NUM_LEN] = '\0';
	ipr_err("    Serial Number: %s\n", buffer);
}

1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
/**
 * ipr_log_ext_vpd_compact - Log the passed extended VPD compactly.
 * @prefix:		string to print at start of printk
 * @hostrcb:	hostrcb pointer
 * @vpd:		vendor/product id/sn/wwn struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_ext_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb,
				    struct ipr_ext_vpd *vpd)
{
	ipr_log_vpd_compact(prefix, hostrcb, &vpd->vpd);
	ipr_hcam_err(hostrcb, "%s WWN: %08X%08X\n", prefix,
		     be32_to_cpu(vpd->wwid[0]), be32_to_cpu(vpd->wwid[1]));
}

1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
/**
 * ipr_log_ext_vpd - Log the passed extended VPD to the error log.
 * @vpd:		vendor/product id/sn/wwn struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_ext_vpd(struct ipr_ext_vpd *vpd)
{
	ipr_log_vpd(&vpd->vpd);
	ipr_err("    WWN: %08X%08X\n", be32_to_cpu(vpd->wwid[0]),
		be32_to_cpu(vpd->wwid[1]));
}

/**
 * ipr_log_enhanced_cache_error - Log a cache error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_enhanced_cache_error(struct ipr_ioa_cfg *ioa_cfg,
					 struct ipr_hostrcb *hostrcb)
{
	struct ipr_hostrcb_type_12_error *error =
		&hostrcb->hcam.u.error.u.type_12_error;

	ipr_err("-----Current Configuration-----\n");
	ipr_err("Cache Directory Card Information:\n");
	ipr_log_ext_vpd(&error->ioa_vpd);
	ipr_err("Adapter Card Information:\n");
	ipr_log_ext_vpd(&error->cfc_vpd);

	ipr_err("-----Expected Configuration-----\n");
	ipr_err("Cache Directory Card Information:\n");
	ipr_log_ext_vpd(&error->ioa_last_attached_to_cfc_vpd);
	ipr_err("Adapter Card Information:\n");
	ipr_log_ext_vpd(&error->cfc_last_attached_to_ioa_vpd);

	ipr_err("Additional IOA Data: %08X %08X %08X\n",
		     be32_to_cpu(error->ioa_data[0]),
		     be32_to_cpu(error->ioa_data[1]),
		     be32_to_cpu(error->ioa_data[2]));
}

Linus Torvalds's avatar
Linus Torvalds committed
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
/**
 * ipr_log_cache_error - Log a cache error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_cache_error(struct ipr_ioa_cfg *ioa_cfg,
				struct ipr_hostrcb *hostrcb)
{
	struct ipr_hostrcb_type_02_error *error =
		&hostrcb->hcam.u.error.u.type_02_error;

	ipr_err("-----Current Configuration-----\n");
	ipr_err("Cache Directory Card Information:\n");
1084
	ipr_log_vpd(&error->ioa_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1085
	ipr_err("Adapter Card Information:\n");
1086
	ipr_log_vpd(&error->cfc_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1087
1088
1089

	ipr_err("-----Expected Configuration-----\n");
	ipr_err("Cache Directory Card Information:\n");
1090
	ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1091
	ipr_err("Adapter Card Information:\n");
1092
	ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1093
1094
1095
1096
1097
1098
1099

	ipr_err("Additional IOA Data: %08X %08X %08X\n",
		     be32_to_cpu(error->ioa_data[0]),
		     be32_to_cpu(error->ioa_data[1]),
		     be32_to_cpu(error->ioa_data[2]));
}

1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
/**
 * ipr_log_enhanced_config_error - Log a configuration error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_enhanced_config_error(struct ipr_ioa_cfg *ioa_cfg,
					  struct ipr_hostrcb *hostrcb)
{
	int errors_logged, i;
	struct ipr_hostrcb_device_data_entry_enhanced *dev_entry;
	struct ipr_hostrcb_type_13_error *error;

	error = &hostrcb->hcam.u.error.u.type_13_error;
	errors_logged = be32_to_cpu(error->errors_logged);

	ipr_err("Device Errors Detected/Logged: %d/%d\n",
		be32_to_cpu(error->errors_detected), errors_logged);

	dev_entry = error->dev;

	for (i = 0; i < errors_logged; i++, dev_entry++) {
		ipr_err_separator;

		ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1);
		ipr_log_ext_vpd(&dev_entry->vpd);

		ipr_err("-----New Device Information-----\n");
		ipr_log_ext_vpd(&dev_entry->new_vpd);

		ipr_err("Cache Directory Card Information:\n");
		ipr_log_ext_vpd(&dev_entry->ioa_last_with_dev_vpd);

		ipr_err("Adapter Card Information:\n");
		ipr_log_ext_vpd(&dev_entry->cfc_last_with_dev_vpd);
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
/**
 * ipr_log_config_error - Log a configuration error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_config_error(struct ipr_ioa_cfg *ioa_cfg,
				 struct ipr_hostrcb *hostrcb)
{
	int errors_logged, i;
	struct ipr_hostrcb_device_data_entry *dev_entry;
	struct ipr_hostrcb_type_03_error *error;

	error = &hostrcb->hcam.u.error.u.type_03_error;
	errors_logged = be32_to_cpu(error->errors_logged);

	ipr_err("Device Errors Detected/Logged: %d/%d\n",
		be32_to_cpu(error->errors_detected), errors_logged);

1161
	dev_entry = error->dev;
Linus Torvalds's avatar
Linus Torvalds committed
1162
1163
1164
1165

	for (i = 0; i < errors_logged; i++, dev_entry++) {
		ipr_err_separator;

1166
		ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1);
1167
		ipr_log_vpd(&dev_entry->vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1168
1169

		ipr_err("-----New Device Information-----\n");
1170
		ipr_log_vpd(&dev_entry->new_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1171
1172

		ipr_err("Cache Directory Card Information:\n");
1173
		ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1174
1175

		ipr_err("Adapter Card Information:\n");
1176
		ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186

		ipr_err("Additional IOA Data: %08X %08X %08X %08X %08X\n",
			be32_to_cpu(dev_entry->ioa_data[0]),
			be32_to_cpu(dev_entry->ioa_data[1]),
			be32_to_cpu(dev_entry->ioa_data[2]),
			be32_to_cpu(dev_entry->ioa_data[3]),
			be32_to_cpu(dev_entry->ioa_data[4]));
	}
}

1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
/**
 * ipr_log_enhanced_array_error - Log an array configuration error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_enhanced_array_error(struct ipr_ioa_cfg *ioa_cfg,
					 struct ipr_hostrcb *hostrcb)
{
	int i, num_entries;
	struct ipr_hostrcb_type_14_error *error;
	struct ipr_hostrcb_array_data_entry_enhanced *array_entry;
	const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' };

	error = &hostrcb->hcam.u.error.u.type_14_error;

	ipr_err_separator;

	ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n",
		error->protection_level,
		ioa_cfg->host->host_no,
		error->last_func_vset_res_addr.bus,
		error->last_func_vset_res_addr.target,
		error->last_func_vset_res_addr.lun);

	ipr_err_separator;

	array_entry = error->array_member;
	num_entries = min_t(u32, be32_to_cpu(error->num_entries),
			    sizeof(error->array_member));

	for (i = 0; i < num_entries; i++, array_entry++) {
		if (!memcmp(array_entry->vpd.vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN))
			continue;

		if (be32_to_cpu(error->exposed_mode_adn) == i)
			ipr_err("Exposed Array Member %d:\n", i);
		else
			ipr_err("Array Member %d:\n", i);

		ipr_log_ext_vpd(&array_entry->vpd);
		ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location");
		ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr,
				 "Expected Location");

		ipr_err_separator;
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
/**
 * ipr_log_array_error - Log an array configuration error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg,
				struct ipr_hostrcb *hostrcb)
{
	int i;
	struct ipr_hostrcb_type_04_error *error;
	struct ipr_hostrcb_array_data_entry *array_entry;
	const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' };

	error = &hostrcb->hcam.u.error.u.type_04_error;

	ipr_err_separator;

	ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n",
		error->protection_level,
		ioa_cfg->host->host_no,
		error->last_func_vset_res_addr.bus,
		error->last_func_vset_res_addr.target,
		error->last_func_vset_res_addr.lun);

	ipr_err_separator;

	array_entry = error->array_member;

	for (i = 0; i < 18; i++) {
1270
		if (!memcmp(array_entry->vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN))
Linus Torvalds's avatar
Linus Torvalds committed
1271
1272
			continue;

1273
		if (be32_to_cpu(error->exposed_mode_adn) == i)
Linus Torvalds's avatar
Linus Torvalds committed
1274
			ipr_err("Exposed Array Member %d:\n", i);
1275
		else
Linus Torvalds's avatar
Linus Torvalds committed
1276
1277
			ipr_err("Array Member %d:\n", i);

1278
		ipr_log_vpd(&array_entry->vpd);
Linus Torvalds's avatar
Linus Torvalds committed
1279

1280
1281
1282
		ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location");
		ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr,
				 "Expected Location");
Linus Torvalds's avatar
Linus Torvalds committed
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293

		ipr_err_separator;

		if (i == 9)
			array_entry = error->array_member2;
		else
			array_entry++;
	}
}

/**
1294
 * ipr_log_hex_data - Log additional hex IOA error data.
1295
 * @ioa_cfg:	ioa config struct
1296
1297
 * @data:		IOA error data
 * @len:		data length
Linus Torvalds's avatar
Linus Torvalds committed
1298
1299
1300
1301
 *
 * Return value:
 * 	none
 **/
1302
static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len)
Linus Torvalds's avatar
Linus Torvalds committed
1303
1304
1305
{
	int i;

1306
	if (len == 0)
Linus Torvalds's avatar
Linus Torvalds committed
1307
1308
		return;

1309
1310
1311
	if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL)
		len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP);

1312
	for (i = 0; i < len / 4; i += 4) {
Linus Torvalds's avatar
Linus Torvalds committed
1313
		ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
1314
1315
1316
1317
			be32_to_cpu(data[i]),
			be32_to_cpu(data[i+1]),
			be32_to_cpu(data[i+2]),
			be32_to_cpu(data[i+3]));
Linus Torvalds's avatar
Linus Torvalds committed
1318
1319
1320
	}
}

1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
/**
 * ipr_log_enhanced_dual_ioa_error - Log an enhanced dual adapter error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
					    struct ipr_hostrcb *hostrcb)
{
	struct ipr_hostrcb_type_17_error *error;

	error = &hostrcb->hcam.u.error.u.type_17_error;
	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
1336
	strstrip(error->failure_reason);
1337

1338
1339
1340
	ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
		     be32_to_cpu(hostrcb->hcam.u.error.prc));
	ipr_log_ext_vpd_compact("Remote IOA", hostrcb, &error->vpd);
1341
	ipr_log_hex_data(ioa_cfg, error->data,
1342
1343
1344
1345
1346
			 be32_to_cpu(hostrcb->hcam.length) -
			 (offsetof(struct ipr_hostrcb_error, u) +
			  offsetof(struct ipr_hostrcb_type_17_error, data)));
}

1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
/**
 * ipr_log_dual_ioa_error - Log a dual adapter error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
				   struct ipr_hostrcb *hostrcb)
{
	struct ipr_hostrcb_type_07_error *error;

	error = &hostrcb->hcam.u.error.u.type_07_error;
	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
1362
	strstrip(error->failure_reason);
1363

1364
1365
1366
	ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
		     be32_to_cpu(hostrcb->hcam.u.error.prc));
	ipr_log_vpd_compact("Remote IOA", hostrcb, &error->vpd);
1367
	ipr_log_hex_data(ioa_cfg, error->data,
1368
1369
1370
1371
1372
			 be32_to_cpu(hostrcb->hcam.length) -
			 (offsetof(struct ipr_hostrcb_error, u) +
			  offsetof(struct ipr_hostrcb_type_07_error, data)));
}

1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
static const struct {
	u8 active;
	char *desc;
} path_active_desc[] = {
	{ IPR_PATH_NO_INFO, "Path" },
	{ IPR_PATH_ACTIVE, "Active path" },
	{ IPR_PATH_NOT_ACTIVE, "Inactive path" }
};

static const struct {
	u8 state;
	char *desc;
} path_state_desc[] = {
	{ IPR_PATH_STATE_NO_INFO, "has no path state information available" },
	{ IPR_PATH_HEALTHY, "is healthy" },
	{ IPR_PATH_DEGRADED, "is degraded" },
	{ IPR_PATH_FAILED, "is failed" }
};

/**
 * ipr_log_fabric_path - Log a fabric path error
 * @hostrcb:	hostrcb struct
 * @fabric:		fabric descriptor
 *
 * Return value:
 * 	none
 **/
static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb,
				struct ipr_hostrcb_fabric_desc *fabric)
{
	int i, j;
	u8 path_state = fabric->path_state;
	u8 active = path_state & IPR_PATH_ACTIVE_MASK;
	u8 state = path_state & IPR_PATH_STATE_MASK;

	for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) {
		if (path_active_desc[i].active != active)
			continue;

		for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) {
			if (path_state_desc[j].state != state)
				continue;

			if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) {
				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n",
					     path_active_desc[i].desc, path_state_desc[j].desc,
					     fabric->ioa_port);
			} else if (fabric->cascaded_expander == 0xff) {
				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n",
					     path_active_desc[i].desc, path_state_desc[j].desc,
					     fabric->ioa_port, fabric->phy);
			} else if (fabric->phy == 0xff) {
				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n",
					     path_active_desc[i].desc, path_state_desc[j].desc,
					     fabric->ioa_port, fabric->cascaded_expander);
			} else {
				ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n",
					     path_active_desc[i].desc, path_state_desc[j].desc,
					     fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
			}
			return;
		}
	}

	ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state,
		fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
}

static const struct {
	u8 type;
	char *desc;
} path_type_desc[] = {
	{ IPR_PATH_CFG_IOA_PORT, "IOA port" },
	{ IPR_PATH_CFG_EXP_PORT, "Expander port" },
	{ IPR_PATH_CFG_DEVICE_PORT, "Device port" },
	{ IPR_PATH_CFG_DEVICE_LUN, "Device LUN" }
};

static const struct {
	u8 status;
	char *desc;
} path_status_desc[] = {
	{ IPR_PATH_CFG_NO_PROB, "Functional" },
	{ IPR_PATH_CFG_DEGRADED, "Degraded" },
	{ IPR_PATH_CFG_FAILED, "Failed" },
	{ IPR_PATH_CFG_SUSPECT, "Suspect" },
	{ IPR_PATH_NOT_DETECTED, "Missing" },
	{ IPR_PATH_INCORRECT_CONN, "Incorrectly connected" }
};

static const char *link_rate[] = {
	"unknown",
	"disabled",
	"phy reset problem",
	"spinup hold",
	"port selector",
	"unknown",
	"unknown",
	"unknown",
	"1.5Gbps",
	"3.0Gbps",
	"unknown",
	"unknown",
	"unknown",
	"unknown",
	"unknown",
	"unknown"
};

/**
 * ipr_log_path_elem - Log a fabric path element.
 * @hostrcb:	hostrcb struct
 * @cfg:		fabric path element struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb,
			      struct ipr_hostrcb_config_element *cfg)
{
	int i, j;
	u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK;
	u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK;

	if (type == IPR_PATH_CFG_NOT_EXIST)
		return;

	for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) {
		if (path_type_desc[i].type != type)
			continue;

		for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) {
			if (path_status_desc[j].status != status)
				continue;

			if (type == IPR_PATH_CFG_IOA_PORT) {
				ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n",
					     path_status_desc[j].desc, path_type_desc[i].desc,
					     cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
					     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
			} else {
				if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) {
					ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n",
						     path_status_desc[j].desc, path_type_desc[i].desc,
						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
				} else if (cfg->cascaded_expander == 0xff) {
					ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, "
						     "WWN=%08X%08X\n", path_status_desc[j].desc,
						     path_type_desc[i].desc, cfg->phy,
						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
				} else if (cfg->phy == 0xff) {
					ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, "
						     "WWN=%08X%08X\n", path_status_desc[j].desc,
						     path_type_desc[i].desc, cfg->cascaded_expander,
						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
				} else {
					ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s "
						     "WWN=%08X%08X\n", path_status_desc[j].desc,
						     path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy,
						     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
						     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
				}
			}
			return;
		}
	}

	ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s "
		     "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy,
		     link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
		     be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
}

/**
 * ipr_log_fabric_error - Log a fabric error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
				 struct ipr_hostrcb *hostrcb)
{
	struct ipr_hostrcb_type_20_error *error;
	struct ipr_hostrcb_fabric_desc *fabric;
	struct ipr_hostrcb_config_element *cfg;
	int i, add_len;

	error = &hostrcb->hcam.u.error.u.type_20_error;
	error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
	ipr_hcam_err(hostrcb, "%s\n", error->failure_reason);

	add_len = be32_to_cpu(hostrcb->hcam.length) -
		(offsetof(struct ipr_hostrcb_error, u) +
		 offsetof(struct ipr_hostrcb_type_20_error, desc));

	for (i = 0, fabric = error->desc; i < error->num_entries; i++) {
		ipr_log_fabric_path(hostrcb, fabric);
		for_each_fabric_cfg(fabric, cfg)
			ipr_log_path_elem(hostrcb, cfg);

		add_len -= be16_to_cpu(fabric->length);
		fabric = (struct ipr_hostrcb_fabric_desc *)
			((unsigned long)fabric + be16_to_cpu(fabric->length));
	}

1583
	ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len);
1584
1585
}

1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
/**
 * ipr_log_generic_error - Log an adapter error.
 * @ioa_cfg:	ioa config struct
 * @hostrcb:	hostrcb struct
 *
 * Return value:
 * 	none
 **/
static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg,
				  struct ipr_hostrcb *hostrcb)
{
1597
	ipr_log_hex_data(ioa_cfg, hostrcb->hcam.u.raw.data,
1598
1599
1600
			 be32_to_cpu(hostrcb->hcam.length));
}

Linus Torvalds's avatar
Linus Torvalds committed
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
/**
 * ipr_get_error - Find the specfied IOASC in the ipr_error_table.
 * @ioasc:	IOASC
 *
 * This function will return the index of into the ipr_error_table
 * for the specified IOASC. If the IOASC is not in the table,
 * 0 will be returned, which points to the entry used for unknown errors.
 *
 * Return value:
 * 	index into the ipr_error_table
 **/
static u32 ipr_get_error(u32 ioasc)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++)
1617
		if (ipr_error_table[i].ioasc == (ioasc & IPR_IOASC_IOASC_MASK))
Linus Torvalds's avatar
Linus Torvalds committed
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651