diff options
author | David Somayajulu <david.somayajulu@qlogic.com> | 2006-09-19 13:28:00 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-10-04 14:34:04 -0400 |
commit | afaf5a2d341d33b66b47c2716a263ce593460a08 (patch) | |
tree | 70aeac2f11cc644114eead55fa003be8288666f0 /drivers/scsi/qla4xxx/ql4_isr.c | |
parent | ed542bed126caeefc6546b276e4af852d4d34f33 (diff) |
[SCSI] Initial Commit of qla4xxx
open-iSCSI driver for Qlogic Corporation's iSCSI HBAs
Signed-off-by: Ravi Anand <ravi.anand@qlogic.com>
Signed-off-by: David Somayajulu <david.somayajulu@qlogic.com>
Signed-off-by: Doug Maxey <dwm@bubba.enoyolf.org>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_isr.c')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_isr.c | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c new file mode 100644 index 000000000000..b584317608d1 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_isr.c | |||
@@ -0,0 +1,797 @@ | |||
1 | /* | ||
2 | * QLogic iSCSI HBA Driver | ||
3 | * Copyright (c) 2003-2006 QLogic Corporation | ||
4 | * | ||
5 | * See LICENSE.qla4xxx for copyright and licensing details. | ||
6 | */ | ||
7 | |||
8 | #include "ql4_def.h" | ||
9 | |||
10 | /** | ||
11 | * qla2x00_process_completed_request() - Process a Fast Post response. | ||
12 | * @ha: SCSI driver HA context | ||
13 | * @index: SRB index | ||
14 | **/ | ||
15 | static void qla4xxx_process_completed_request(struct scsi_qla_host *ha, | ||
16 | uint32_t index) | ||
17 | { | ||
18 | struct srb *srb; | ||
19 | |||
20 | srb = qla4xxx_del_from_active_array(ha, index); | ||
21 | if (srb) { | ||
22 | /* Save ISP completion status */ | ||
23 | srb->cmd->result = DID_OK << 16; | ||
24 | qla4xxx_srb_compl(ha, srb); | ||
25 | } else { | ||
26 | DEBUG2(printk("scsi%ld: Invalid ISP SCSI completion handle = " | ||
27 | "%d\n", ha->host_no, index)); | ||
28 | set_bit(DPC_RESET_HA, &ha->dpc_flags); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * qla4xxx_status_entry - processes status IOCBs | ||
34 | * @ha: Pointer to host adapter structure. | ||
35 | * @sts_entry: Pointer to status entry structure. | ||
36 | **/ | ||
37 | static void qla4xxx_status_entry(struct scsi_qla_host *ha, | ||
38 | struct status_entry *sts_entry) | ||
39 | { | ||
40 | uint8_t scsi_status; | ||
41 | struct scsi_cmnd *cmd; | ||
42 | struct srb *srb; | ||
43 | struct ddb_entry *ddb_entry; | ||
44 | uint32_t residual; | ||
45 | uint16_t sensebytecnt; | ||
46 | |||
47 | if (sts_entry->completionStatus == SCS_COMPLETE && | ||
48 | sts_entry->scsiStatus == 0) { | ||
49 | qla4xxx_process_completed_request(ha, | ||
50 | le32_to_cpu(sts_entry-> | ||
51 | handle)); | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle)); | ||
56 | if (!srb) { | ||
57 | /* FIXMEdg: Don't we need to reset ISP in this case??? */ | ||
58 | DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Status Entry invalid " | ||
59 | "handle 0x%x, sp=%p. This cmd may have already " | ||
60 | "been completed.\n", ha->host_no, __func__, | ||
61 | le32_to_cpu(sts_entry->handle), srb)); | ||
62 | return; | ||
63 | } | ||
64 | |||
65 | cmd = srb->cmd; | ||
66 | if (cmd == NULL) { | ||
67 | DEBUG2(printk("scsi%ld: %s: Command already returned back to " | ||
68 | "OS pkt->handle=%d srb=%p srb->state:%d\n", | ||
69 | ha->host_no, __func__, sts_entry->handle, | ||
70 | srb, srb->state)); | ||
71 | dev_warn(&ha->pdev->dev, "Command is NULL:" | ||
72 | " already returned to OS (srb=%p)\n", srb); | ||
73 | return; | ||
74 | } | ||
75 | |||
76 | ddb_entry = srb->ddb; | ||
77 | if (ddb_entry == NULL) { | ||
78 | cmd->result = DID_NO_CONNECT << 16; | ||
79 | goto status_entry_exit; | ||
80 | } | ||
81 | |||
82 | residual = le32_to_cpu(sts_entry->residualByteCnt); | ||
83 | |||
84 | /* Translate ISP error to a Linux SCSI error. */ | ||
85 | scsi_status = sts_entry->scsiStatus; | ||
86 | switch (sts_entry->completionStatus) { | ||
87 | case SCS_COMPLETE: | ||
88 | if (scsi_status == 0) { | ||
89 | cmd->result = DID_OK << 16; | ||
90 | break; | ||
91 | } | ||
92 | |||
93 | if (sts_entry->iscsiFlags & | ||
94 | (ISCSI_FLAG_RESIDUAL_OVER|ISCSI_FLAG_RESIDUAL_UNDER)) | ||
95 | cmd->resid = residual; | ||
96 | |||
97 | cmd->result = DID_OK << 16 | scsi_status; | ||
98 | |||
99 | if (scsi_status != SCSI_CHECK_CONDITION) | ||
100 | break; | ||
101 | |||
102 | /* Copy Sense Data into sense buffer. */ | ||
103 | memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); | ||
104 | |||
105 | sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt); | ||
106 | if (sensebytecnt == 0) | ||
107 | break; | ||
108 | |||
109 | memcpy(cmd->sense_buffer, sts_entry->senseData, | ||
110 | min(sensebytecnt, | ||
111 | (uint16_t) sizeof(cmd->sense_buffer))); | ||
112 | |||
113 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, " | ||
114 | "ASC/ASCQ = %02x/%02x\n", ha->host_no, | ||
115 | cmd->device->channel, cmd->device->id, | ||
116 | cmd->device->lun, __func__, | ||
117 | sts_entry->senseData[2] & 0x0f, | ||
118 | sts_entry->senseData[12], | ||
119 | sts_entry->senseData[13])); | ||
120 | |||
121 | srb->flags |= SRB_GOT_SENSE; | ||
122 | break; | ||
123 | |||
124 | case SCS_INCOMPLETE: | ||
125 | /* Always set the status to DID_ERROR, since | ||
126 | * all conditions result in that status anyway */ | ||
127 | cmd->result = DID_ERROR << 16; | ||
128 | break; | ||
129 | |||
130 | case SCS_RESET_OCCURRED: | ||
131 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Device RESET occurred\n", | ||
132 | ha->host_no, cmd->device->channel, | ||
133 | cmd->device->id, cmd->device->lun, __func__)); | ||
134 | |||
135 | cmd->result = DID_RESET << 16; | ||
136 | break; | ||
137 | |||
138 | case SCS_ABORTED: | ||
139 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Abort occurred\n", | ||
140 | ha->host_no, cmd->device->channel, | ||
141 | cmd->device->id, cmd->device->lun, __func__)); | ||
142 | |||
143 | cmd->result = DID_RESET << 16; | ||
144 | break; | ||
145 | |||
146 | case SCS_TIMEOUT: | ||
147 | DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: Timeout\n", | ||
148 | ha->host_no, cmd->device->channel, | ||
149 | cmd->device->id, cmd->device->lun)); | ||
150 | |||
151 | cmd->result = DID_BUS_BUSY << 16; | ||
152 | |||
153 | /* | ||
154 | * Mark device missing so that we won't continue to send | ||
155 | * I/O to this device. We should get a ddb state change | ||
156 | * AEN soon. | ||
157 | */ | ||
158 | if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) | ||
159 | qla4xxx_mark_device_missing(ha, ddb_entry); | ||
160 | break; | ||
161 | |||
162 | case SCS_DATA_UNDERRUN: | ||
163 | case SCS_DATA_OVERRUN: | ||
164 | if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) { | ||
165 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, " | ||
166 | "residual = 0x%x\n", ha->host_no, | ||
167 | cmd->device->channel, cmd->device->id, | ||
168 | cmd->device->lun, __func__, residual)); | ||
169 | |||
170 | cmd->result = DID_ERROR << 16; | ||
171 | break; | ||
172 | } | ||
173 | |||
174 | if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) == 0) { | ||
175 | /* | ||
176 | * Firmware detected a SCSI transport underrun | ||
177 | * condition | ||
178 | */ | ||
179 | cmd->resid = residual; | ||
180 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: UNDERRUN status " | ||
181 | "detected, xferlen = 0x%x, residual = " | ||
182 | "0x%x\n", | ||
183 | ha->host_no, cmd->device->channel, | ||
184 | cmd->device->id, | ||
185 | cmd->device->lun, __func__, | ||
186 | cmd->request_bufflen, | ||
187 | residual)); | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * If there is scsi_status, it takes precedense over | ||
192 | * underflow condition. | ||
193 | */ | ||
194 | if (scsi_status != 0) { | ||
195 | cmd->result = DID_OK << 16 | scsi_status; | ||
196 | |||
197 | if (scsi_status != SCSI_CHECK_CONDITION) | ||
198 | break; | ||
199 | |||
200 | /* Copy Sense Data into sense buffer. */ | ||
201 | memset(cmd->sense_buffer, 0, | ||
202 | sizeof(cmd->sense_buffer)); | ||
203 | |||
204 | sensebytecnt = | ||
205 | le16_to_cpu(sts_entry->senseDataByteCnt); | ||
206 | if (sensebytecnt == 0) | ||
207 | break; | ||
208 | |||
209 | memcpy(cmd->sense_buffer, sts_entry->senseData, | ||
210 | min(sensebytecnt, | ||
211 | (uint16_t) sizeof(cmd->sense_buffer))); | ||
212 | |||
213 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, " | ||
214 | "ASC/ASCQ = %02x/%02x\n", ha->host_no, | ||
215 | cmd->device->channel, cmd->device->id, | ||
216 | cmd->device->lun, __func__, | ||
217 | sts_entry->senseData[2] & 0x0f, | ||
218 | sts_entry->senseData[12], | ||
219 | sts_entry->senseData[13])); | ||
220 | } else { | ||
221 | /* | ||
222 | * If RISC reports underrun and target does not | ||
223 | * report it then we must have a lost frame, so | ||
224 | * tell upper layer to retry it by reporting a | ||
225 | * bus busy. | ||
226 | */ | ||
227 | if ((sts_entry->iscsiFlags & | ||
228 | ISCSI_FLAG_RESIDUAL_UNDER) == 0) { | ||
229 | cmd->result = DID_BUS_BUSY << 16; | ||
230 | } else if ((cmd->request_bufflen - residual) < | ||
231 | cmd->underflow) { | ||
232 | /* | ||
233 | * Handle mid-layer underflow??? | ||
234 | * | ||
235 | * For kernels less than 2.4, the driver must | ||
236 | * return an error if an underflow is detected. | ||
237 | * For kernels equal-to and above 2.4, the | ||
238 | * mid-layer will appearantly handle the | ||
239 | * underflow by detecting the residual count -- | ||
240 | * unfortunately, we do not see where this is | ||
241 | * actually being done. In the interim, we | ||
242 | * will return DID_ERROR. | ||
243 | */ | ||
244 | DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " | ||
245 | "Mid-layer Data underrun, " | ||
246 | "xferlen = 0x%x, " | ||
247 | "residual = 0x%x\n", ha->host_no, | ||
248 | cmd->device->channel, | ||
249 | cmd->device->id, | ||
250 | cmd->device->lun, __func__, | ||
251 | cmd->request_bufflen, residual)); | ||
252 | |||
253 | cmd->result = DID_ERROR << 16; | ||
254 | } else { | ||
255 | cmd->result = DID_OK << 16; | ||
256 | } | ||
257 | } | ||
258 | break; | ||
259 | |||
260 | case SCS_DEVICE_LOGGED_OUT: | ||
261 | case SCS_DEVICE_UNAVAILABLE: | ||
262 | /* | ||
263 | * Mark device missing so that we won't continue to | ||
264 | * send I/O to this device. We should get a ddb | ||
265 | * state change AEN soon. | ||
266 | */ | ||
267 | if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) | ||
268 | qla4xxx_mark_device_missing(ha, ddb_entry); | ||
269 | |||
270 | cmd->result = DID_BUS_BUSY << 16; | ||
271 | break; | ||
272 | |||
273 | case SCS_QUEUE_FULL: | ||
274 | /* | ||
275 | * SCSI Mid-Layer handles device queue full | ||
276 | */ | ||
277 | cmd->result = DID_OK << 16 | sts_entry->scsiStatus; | ||
278 | DEBUG2(printk("scsi%ld:%d:%d: %s: QUEUE FULL detected " | ||
279 | "compl=%02x, scsi=%02x, state=%02x, iFlags=%02x," | ||
280 | " iResp=%02x\n", ha->host_no, cmd->device->id, | ||
281 | cmd->device->lun, __func__, | ||
282 | sts_entry->completionStatus, | ||
283 | sts_entry->scsiStatus, sts_entry->state_flags, | ||
284 | sts_entry->iscsiFlags, | ||
285 | sts_entry->iscsiResponse)); | ||
286 | break; | ||
287 | |||
288 | default: | ||
289 | cmd->result = DID_ERROR << 16; | ||
290 | break; | ||
291 | } | ||
292 | |||
293 | status_entry_exit: | ||
294 | |||
295 | /* complete the request */ | ||
296 | srb->cc_stat = sts_entry->completionStatus; | ||
297 | qla4xxx_srb_compl(ha, srb); | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * qla4xxx_process_response_queue - process response queue completions | ||
302 | * @ha: Pointer to host adapter structure. | ||
303 | * | ||
304 | * This routine process response queue completions in interrupt context. | ||
305 | * Hardware_lock locked upon entry | ||
306 | **/ | ||
307 | static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) | ||
308 | { | ||
309 | uint32_t count = 0; | ||
310 | struct srb *srb = NULL; | ||
311 | struct status_entry *sts_entry; | ||
312 | |||
313 | /* Process all responses from response queue */ | ||
314 | while ((ha->response_in = | ||
315 | (uint16_t)le32_to_cpu(ha->shadow_regs->rsp_q_in)) != | ||
316 | ha->response_out) { | ||
317 | sts_entry = (struct status_entry *) ha->response_ptr; | ||
318 | count++; | ||
319 | |||
320 | /* Advance pointers for next entry */ | ||
321 | if (ha->response_out == (RESPONSE_QUEUE_DEPTH - 1)) { | ||
322 | ha->response_out = 0; | ||
323 | ha->response_ptr = ha->response_ring; | ||
324 | } else { | ||
325 | ha->response_out++; | ||
326 | ha->response_ptr++; | ||
327 | } | ||
328 | |||
329 | /* process entry */ | ||
330 | switch (sts_entry->hdr.entryType) { | ||
331 | case ET_STATUS: | ||
332 | /* | ||
333 | * Common status - Single completion posted in single | ||
334 | * IOSB. | ||
335 | */ | ||
336 | qla4xxx_status_entry(ha, sts_entry); | ||
337 | break; | ||
338 | |||
339 | case ET_PASSTHRU_STATUS: | ||
340 | break; | ||
341 | |||
342 | case ET_STATUS_CONTINUATION: | ||
343 | /* Just throw away the status continuation entries */ | ||
344 | DEBUG2(printk("scsi%ld: %s: Status Continuation entry " | ||
345 | "- ignoring\n", ha->host_no, __func__)); | ||
346 | break; | ||
347 | |||
348 | case ET_COMMAND: | ||
349 | /* ISP device queue is full. Command not | ||
350 | * accepted by ISP. Queue command for | ||
351 | * later */ | ||
352 | |||
353 | srb = qla4xxx_del_from_active_array(ha, | ||
354 | le32_to_cpu(sts_entry-> | ||
355 | handle)); | ||
356 | if (srb == NULL) | ||
357 | goto exit_prq_invalid_handle; | ||
358 | |||
359 | DEBUG2(printk("scsi%ld: %s: FW device queue full, " | ||
360 | "srb %p\n", ha->host_no, __func__, srb)); | ||
361 | |||
362 | /* ETRY normally by sending it back with | ||
363 | * DID_BUS_BUSY */ | ||
364 | srb->cmd->result = DID_BUS_BUSY << 16; | ||
365 | qla4xxx_srb_compl(ha, srb); | ||
366 | break; | ||
367 | |||
368 | case ET_CONTINUE: | ||
369 | /* Just throw away the continuation entries */ | ||
370 | DEBUG2(printk("scsi%ld: %s: Continuation entry - " | ||
371 | "ignoring\n", ha->host_no, __func__)); | ||
372 | break; | ||
373 | |||
374 | default: | ||
375 | /* | ||
376 | * Invalid entry in response queue, reset RISC | ||
377 | * firmware. | ||
378 | */ | ||
379 | DEBUG2(printk("scsi%ld: %s: Invalid entry %x in " | ||
380 | "response queue \n", ha->host_no, | ||
381 | __func__, | ||
382 | sts_entry->hdr.entryType)); | ||
383 | goto exit_prq_error; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Done with responses, update the ISP For QLA4010, this also clears | ||
389 | * the interrupt. | ||
390 | */ | ||
391 | writel(ha->response_out, &ha->reg->rsp_q_out); | ||
392 | readl(&ha->reg->rsp_q_out); | ||
393 | |||
394 | return; | ||
395 | |||
396 | exit_prq_invalid_handle: | ||
397 | DEBUG2(printk("scsi%ld: %s: Invalid handle(srb)=%p type=%x IOCS=%x\n", | ||
398 | ha->host_no, __func__, srb, sts_entry->hdr.entryType, | ||
399 | sts_entry->completionStatus)); | ||
400 | |||
401 | exit_prq_error: | ||
402 | writel(ha->response_out, &ha->reg->rsp_q_out); | ||
403 | readl(&ha->reg->rsp_q_out); | ||
404 | |||
405 | set_bit(DPC_RESET_HA, &ha->dpc_flags); | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * qla4xxx_isr_decode_mailbox - decodes mailbox status | ||
410 | * @ha: Pointer to host adapter structure. | ||
411 | * @mailbox_status: Mailbox status. | ||
412 | * | ||
413 | * This routine decodes the mailbox status during the ISR. | ||
414 | * Hardware_lock locked upon entry. runs in interrupt context. | ||
415 | **/ | ||
416 | static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, | ||
417 | uint32_t mbox_status) | ||
418 | { | ||
419 | int i; | ||
420 | |||
421 | if ((mbox_status == MBOX_STS_BUSY) || | ||
422 | (mbox_status == MBOX_STS_INTERMEDIATE_COMPLETION) || | ||
423 | (mbox_status >> 12 == MBOX_COMPLETION_STATUS)) { | ||
424 | ha->mbox_status[0] = mbox_status; | ||
425 | |||
426 | if (test_bit(AF_MBOX_COMMAND, &ha->flags)) { | ||
427 | /* | ||
428 | * Copy all mailbox registers to a temporary | ||
429 | * location and set mailbox command done flag | ||
430 | */ | ||
431 | for (i = 1; i < ha->mbox_status_count; i++) | ||
432 | ha->mbox_status[i] = | ||
433 | readl(&ha->reg->mailbox[i]); | ||
434 | |||
435 | set_bit(AF_MBOX_COMMAND_DONE, &ha->flags); | ||
436 | wake_up(&ha->mailbox_wait_queue); | ||
437 | } | ||
438 | } else if (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) { | ||
439 | /* Immediately process the AENs that don't require much work. | ||
440 | * Only queue the database_changed AENs */ | ||
441 | switch (mbox_status) { | ||
442 | case MBOX_ASTS_SYSTEM_ERROR: | ||
443 | /* Log Mailbox registers */ | ||
444 | if (ql4xdontresethba) { | ||
445 | DEBUG2(printk("%s:Dont Reset HBA\n", | ||
446 | __func__)); | ||
447 | } else { | ||
448 | set_bit(AF_GET_CRASH_RECORD, &ha->flags); | ||
449 | set_bit(DPC_RESET_HA, &ha->dpc_flags); | ||
450 | } | ||
451 | break; | ||
452 | |||
453 | case MBOX_ASTS_REQUEST_TRANSFER_ERROR: | ||
454 | case MBOX_ASTS_RESPONSE_TRANSFER_ERROR: | ||
455 | case MBOX_ASTS_NVRAM_INVALID: | ||
456 | case MBOX_ASTS_IP_ADDRESS_CHANGED: | ||
457 | case MBOX_ASTS_DHCP_LEASE_EXPIRED: | ||
458 | DEBUG2(printk("scsi%ld: AEN %04x, ERROR Status, " | ||
459 | "Reset HA\n", ha->host_no, mbox_status)); | ||
460 | set_bit(DPC_RESET_HA, &ha->dpc_flags); | ||
461 | break; | ||
462 | |||
463 | case MBOX_ASTS_LINK_UP: | ||
464 | DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", | ||
465 | ha->host_no, mbox_status)); | ||
466 | set_bit(AF_LINK_UP, &ha->flags); | ||
467 | break; | ||
468 | |||
469 | case MBOX_ASTS_LINK_DOWN: | ||
470 | DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", | ||
471 | ha->host_no, mbox_status)); | ||
472 | clear_bit(AF_LINK_UP, &ha->flags); | ||
473 | break; | ||
474 | |||
475 | case MBOX_ASTS_HEARTBEAT: | ||
476 | ha->seconds_since_last_heartbeat = 0; | ||
477 | break; | ||
478 | |||
479 | case MBOX_ASTS_DHCP_LEASE_ACQUIRED: | ||
480 | DEBUG2(printk("scsi%ld: AEN %04x DHCP LEASE " | ||
481 | "ACQUIRED\n", ha->host_no, mbox_status)); | ||
482 | set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags); | ||
483 | break; | ||
484 | |||
485 | case MBOX_ASTS_PROTOCOL_STATISTIC_ALARM: | ||
486 | case MBOX_ASTS_SCSI_COMMAND_PDU_REJECTED: /* Target | ||
487 | * mode | ||
488 | * only */ | ||
489 | case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED: /* Connection mode */ | ||
490 | case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR: | ||
491 | case MBOX_ASTS_SUBNET_STATE_CHANGE: | ||
492 | /* No action */ | ||
493 | DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no, | ||
494 | mbox_status)); | ||
495 | break; | ||
496 | |||
497 | case MBOX_ASTS_MAC_ADDRESS_CHANGED: | ||
498 | case MBOX_ASTS_DNS: | ||
499 | /* No action */ | ||
500 | DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x, " | ||
501 | "mbox_sts[1]=%04x, mbox_sts[2]=%04x\n", | ||
502 | ha->host_no, mbox_status, | ||
503 | readl(&ha->reg->mailbox[1]), | ||
504 | readl(&ha->reg->mailbox[2]))); | ||
505 | break; | ||
506 | |||
507 | case MBOX_ASTS_SELF_TEST_FAILED: | ||
508 | case MBOX_ASTS_LOGIN_FAILED: | ||
509 | /* No action */ | ||
510 | DEBUG2(printk("scsi%ld: AEN %04x, mbox_sts[1]=%04x, " | ||
511 | "mbox_sts[2]=%04x, mbox_sts[3]=%04x\n", | ||
512 | ha->host_no, mbox_status, | ||
513 | readl(&ha->reg->mailbox[1]), | ||
514 | readl(&ha->reg->mailbox[2]), | ||
515 | readl(&ha->reg->mailbox[3]))); | ||
516 | break; | ||
517 | |||
518 | case MBOX_ASTS_DATABASE_CHANGED: | ||
519 | /* Queue AEN information and process it in the DPC | ||
520 | * routine */ | ||
521 | if (ha->aen_q_count > 0) { | ||
522 | /* advance pointer */ | ||
523 | if (ha->aen_in == (MAX_AEN_ENTRIES - 1)) | ||
524 | ha->aen_in = 0; | ||
525 | else | ||
526 | ha->aen_in++; | ||
527 | |||
528 | /* decrement available counter */ | ||
529 | ha->aen_q_count--; | ||
530 | |||
531 | for (i = 1; i < MBOX_AEN_REG_COUNT; i++) | ||
532 | ha->aen_q[ha->aen_in].mbox_sts[i] = | ||
533 | readl(&ha->reg->mailbox[i]); | ||
534 | |||
535 | ha->aen_q[ha->aen_in].mbox_sts[0] = mbox_status; | ||
536 | |||
537 | /* print debug message */ | ||
538 | DEBUG2(printk("scsi%ld: AEN[%d] %04x queued" | ||
539 | " mb1:0x%x mb2:0x%x mb3:0x%x mb4:0x%x\n", | ||
540 | ha->host_no, ha->aen_in, | ||
541 | mbox_status, | ||
542 | ha->aen_q[ha->aen_in].mbox_sts[1], | ||
543 | ha->aen_q[ha->aen_in].mbox_sts[2], | ||
544 | ha->aen_q[ha->aen_in].mbox_sts[3], | ||
545 | ha->aen_q[ha->aen_in]. mbox_sts[4])); | ||
546 | |||
547 | /* The DPC routine will process the aen */ | ||
548 | set_bit(DPC_AEN, &ha->dpc_flags); | ||
549 | } else { | ||
550 | DEBUG2(printk("scsi%ld: %s: aen %04x, queue " | ||
551 | "overflowed! AEN LOST!!\n", | ||
552 | ha->host_no, __func__, | ||
553 | mbox_status)); | ||
554 | |||
555 | DEBUG2(printk("scsi%ld: DUMP AEN QUEUE\n", | ||
556 | ha->host_no)); | ||
557 | |||
558 | for (i = 0; i < MAX_AEN_ENTRIES; i++) { | ||
559 | DEBUG2(printk("AEN[%d] %04x %04x %04x " | ||
560 | "%04x\n", i, | ||
561 | ha->aen_q[i].mbox_sts[0], | ||
562 | ha->aen_q[i].mbox_sts[1], | ||
563 | ha->aen_q[i].mbox_sts[2], | ||
564 | ha->aen_q[i].mbox_sts[3])); | ||
565 | } | ||
566 | } | ||
567 | break; | ||
568 | |||
569 | default: | ||
570 | DEBUG2(printk(KERN_WARNING | ||
571 | "scsi%ld: AEN %04x UNKNOWN\n", | ||
572 | ha->host_no, mbox_status)); | ||
573 | break; | ||
574 | } | ||
575 | } else { | ||
576 | DEBUG2(printk("scsi%ld: Unknown mailbox status %08X\n", | ||
577 | ha->host_no, mbox_status)); | ||
578 | |||
579 | ha->mbox_status[0] = mbox_status; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | /** | ||
584 | * qla4xxx_interrupt_service_routine - isr | ||
585 | * @ha: pointer to host adapter structure. | ||
586 | * | ||
587 | * This is the main interrupt service routine. | ||
588 | * hardware_lock locked upon entry. runs in interrupt context. | ||
589 | **/ | ||
590 | void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, | ||
591 | uint32_t intr_status) | ||
592 | { | ||
593 | /* Process response queue interrupt. */ | ||
594 | if (intr_status & CSR_SCSI_COMPLETION_INTR) | ||
595 | qla4xxx_process_response_queue(ha); | ||
596 | |||
597 | /* Process mailbox/asynch event interrupt.*/ | ||
598 | if (intr_status & CSR_SCSI_PROCESSOR_INTR) { | ||
599 | qla4xxx_isr_decode_mailbox(ha, | ||
600 | readl(&ha->reg->mailbox[0])); | ||
601 | |||
602 | /* Clear Mailbox Interrupt */ | ||
603 | writel(set_rmask(CSR_SCSI_PROCESSOR_INTR), | ||
604 | &ha->reg->ctrl_status); | ||
605 | readl(&ha->reg->ctrl_status); | ||
606 | } | ||
607 | } | ||
608 | |||
609 | /** | ||
610 | * qla4xxx_intr_handler - hardware interrupt handler. | ||
611 | * @irq: Unused | ||
612 | * @dev_id: Pointer to host adapter structure | ||
613 | * @regs: Unused | ||
614 | **/ | ||
615 | irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
616 | { | ||
617 | struct scsi_qla_host *ha; | ||
618 | uint32_t intr_status; | ||
619 | unsigned long flags = 0; | ||
620 | uint8_t reqs_count = 0; | ||
621 | |||
622 | ha = (struct scsi_qla_host *) dev_id; | ||
623 | if (!ha) { | ||
624 | DEBUG2(printk(KERN_INFO | ||
625 | "qla4xxx: Interrupt with NULL host ptr\n")); | ||
626 | return IRQ_NONE; | ||
627 | } | ||
628 | |||
629 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
630 | |||
631 | /* | ||
632 | * Repeatedly service interrupts up to a maximum of | ||
633 | * MAX_REQS_SERVICED_PER_INTR | ||
634 | */ | ||
635 | while (1) { | ||
636 | /* | ||
637 | * Read interrupt status | ||
638 | */ | ||
639 | if (le32_to_cpu(ha->shadow_regs->rsp_q_in) != | ||
640 | ha->response_out) | ||
641 | intr_status = CSR_SCSI_COMPLETION_INTR; | ||
642 | else | ||
643 | intr_status = readl(&ha->reg->ctrl_status); | ||
644 | |||
645 | if ((intr_status & | ||
646 | (CSR_SCSI_RESET_INTR|CSR_FATAL_ERROR|INTR_PENDING)) == | ||
647 | 0) { | ||
648 | if (reqs_count == 0) | ||
649 | ha->spurious_int_count++; | ||
650 | break; | ||
651 | } | ||
652 | |||
653 | if (intr_status & CSR_FATAL_ERROR) { | ||
654 | DEBUG2(printk(KERN_INFO "scsi%ld: Fatal Error, " | ||
655 | "Status 0x%04x\n", ha->host_no, | ||
656 | readl(isp_port_error_status (ha)))); | ||
657 | |||
658 | /* Issue Soft Reset to clear this error condition. | ||
659 | * This will prevent the RISC from repeatedly | ||
660 | * interrupting the driver; thus, allowing the DPC to | ||
661 | * get scheduled to continue error recovery. | ||
662 | * NOTE: Disabling RISC interrupts does not work in | ||
663 | * this case, as CSR_FATAL_ERROR overrides | ||
664 | * CSR_SCSI_INTR_ENABLE */ | ||
665 | if ((readl(&ha->reg->ctrl_status) & | ||
666 | CSR_SCSI_RESET_INTR) == 0) { | ||
667 | writel(set_rmask(CSR_SOFT_RESET), | ||
668 | &ha->reg->ctrl_status); | ||
669 | readl(&ha->reg->ctrl_status); | ||
670 | } | ||
671 | |||
672 | writel(set_rmask(CSR_FATAL_ERROR), | ||
673 | &ha->reg->ctrl_status); | ||
674 | readl(&ha->reg->ctrl_status); | ||
675 | |||
676 | __qla4xxx_disable_intrs(ha); | ||
677 | |||
678 | set_bit(DPC_RESET_HA, &ha->dpc_flags); | ||
679 | |||
680 | break; | ||
681 | } else if (intr_status & CSR_SCSI_RESET_INTR) { | ||
682 | clear_bit(AF_ONLINE, &ha->flags); | ||
683 | __qla4xxx_disable_intrs(ha); | ||
684 | |||
685 | writel(set_rmask(CSR_SCSI_RESET_INTR), | ||
686 | &ha->reg->ctrl_status); | ||
687 | readl(&ha->reg->ctrl_status); | ||
688 | |||
689 | set_bit(DPC_RESET_HA_INTR, &ha->dpc_flags); | ||
690 | |||
691 | break; | ||
692 | } else if (intr_status & INTR_PENDING) { | ||
693 | qla4xxx_interrupt_service_routine(ha, intr_status); | ||
694 | ha->total_io_count++; | ||
695 | if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) | ||
696 | break; | ||
697 | |||
698 | intr_status = 0; | ||
699 | } | ||
700 | } | ||
701 | |||
702 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
703 | |||
704 | return IRQ_HANDLED; | ||
705 | } | ||
706 | |||
707 | /** | ||
708 | * qla4xxx_process_aen - processes AENs generated by firmware | ||
709 | * @ha: pointer to host adapter structure. | ||
710 | * @process_aen: type of AENs to process | ||
711 | * | ||
712 | * Processes specific types of Asynchronous Events generated by firmware. | ||
713 | * The type of AENs to process is specified by process_aen and can be | ||
714 | * PROCESS_ALL_AENS 0 | ||
715 | * FLUSH_DDB_CHANGED_AENS 1 | ||
716 | * RELOGIN_DDB_CHANGED_AENS 2 | ||
717 | **/ | ||
718 | void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen) | ||
719 | { | ||
720 | uint32_t mbox_sts[MBOX_AEN_REG_COUNT]; | ||
721 | struct aen *aen; | ||
722 | int i; | ||
723 | unsigned long flags; | ||
724 | |||
725 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
726 | while (ha->aen_out != ha->aen_in) { | ||
727 | /* Advance pointers for next entry */ | ||
728 | if (ha->aen_out == (MAX_AEN_ENTRIES - 1)) | ||
729 | ha->aen_out = 0; | ||
730 | else | ||
731 | ha->aen_out++; | ||
732 | |||
733 | ha->aen_q_count++; | ||
734 | aen = &ha->aen_q[ha->aen_out]; | ||
735 | |||
736 | /* copy aen information to local structure */ | ||
737 | for (i = 0; i < MBOX_AEN_REG_COUNT; i++) | ||
738 | mbox_sts[i] = aen->mbox_sts[i]; | ||
739 | |||
740 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
741 | |||
742 | DEBUG(printk("scsi%ld: AEN[%d] %04x, index [%d] state=%04x " | ||
743 | "mod=%x conerr=%08x \n", ha->host_no, ha->aen_out, | ||
744 | mbox_sts[0], mbox_sts[2], mbox_sts[3], | ||
745 | mbox_sts[1], mbox_sts[4])); | ||
746 | |||
747 | switch (mbox_sts[0]) { | ||
748 | case MBOX_ASTS_DATABASE_CHANGED: | ||
749 | if (process_aen == FLUSH_DDB_CHANGED_AENS) { | ||
750 | DEBUG2(printk("scsi%ld: AEN[%d] %04x, index " | ||
751 | "[%d] state=%04x FLUSHED!\n", | ||
752 | ha->host_no, ha->aen_out, | ||
753 | mbox_sts[0], mbox_sts[2], | ||
754 | mbox_sts[3])); | ||
755 | break; | ||
756 | } else if (process_aen == RELOGIN_DDB_CHANGED_AENS) { | ||
757 | /* for use during init time, we only want to | ||
758 | * relogin non-active ddbs */ | ||
759 | struct ddb_entry *ddb_entry; | ||
760 | |||
761 | ddb_entry = | ||
762 | /* FIXME: name length? */ | ||
763 | qla4xxx_lookup_ddb_by_fw_index(ha, | ||
764 | mbox_sts[2]); | ||
765 | if (!ddb_entry) | ||
766 | break; | ||
767 | |||
768 | ddb_entry->dev_scan_wait_to_complete_relogin = | ||
769 | 0; | ||
770 | ddb_entry->dev_scan_wait_to_start_relogin = | ||
771 | jiffies + | ||
772 | ((ddb_entry->default_time2wait + | ||
773 | 4) * HZ); | ||
774 | |||
775 | DEBUG2(printk("scsi%ld: ddb index [%d] initate" | ||
776 | " RELOGIN after %d seconds\n", | ||
777 | ha->host_no, | ||
778 | ddb_entry->fw_ddb_index, | ||
779 | ddb_entry->default_time2wait + | ||
780 | 4)); | ||
781 | break; | ||
782 | } | ||
783 | |||
784 | if (mbox_sts[1] == 0) { /* Global DB change. */ | ||
785 | qla4xxx_reinitialize_ddb_list(ha); | ||
786 | } else if (mbox_sts[1] == 1) { /* Specific device. */ | ||
787 | qla4xxx_process_ddb_changed(ha, mbox_sts[2], | ||
788 | mbox_sts[3]); | ||
789 | } | ||
790 | break; | ||
791 | } | ||
792 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
793 | } | ||
794 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
795 | |||
796 | } | ||
797 | |||