diff options
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_scb.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_scb.c | 83 |
1 files changed, 66 insertions, 17 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index 7ee49b51b724..1911c5d17875 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c | |||
@@ -25,6 +25,7 @@ | |||
25 | */ | 25 | */ |
26 | 26 | ||
27 | #include <linux/pci.h> | 27 | #include <linux/pci.h> |
28 | #include <scsi/scsi_host.h> | ||
28 | 29 | ||
29 | #include "aic94xx.h" | 30 | #include "aic94xx.h" |
30 | #include "aic94xx_reg.h" | 31 | #include "aic94xx_reg.h" |
@@ -342,6 +343,18 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) | |||
342 | } | 343 | } |
343 | } | 344 | } |
344 | 345 | ||
346 | /* start up the ABORT TASK tmf... */ | ||
347 | static void task_kill_later(struct asd_ascb *ascb) | ||
348 | { | ||
349 | struct asd_ha_struct *asd_ha = ascb->ha; | ||
350 | struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; | ||
351 | struct Scsi_Host *shost = sas_ha->core.shost; | ||
352 | struct sas_task *task = ascb->uldd_task; | ||
353 | |||
354 | INIT_WORK(&task->abort_work, (void (*)(void *))sas_task_abort, task); | ||
355 | queue_work(shost->work_q, &task->abort_work); | ||
356 | } | ||
357 | |||
345 | static void escb_tasklet_complete(struct asd_ascb *ascb, | 358 | static void escb_tasklet_complete(struct asd_ascb *ascb, |
346 | struct done_list_struct *dl) | 359 | struct done_list_struct *dl) |
347 | { | 360 | { |
@@ -368,6 +381,58 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
368 | ascb->scb->header.opcode); | 381 | ascb->scb->header.opcode); |
369 | } | 382 | } |
370 | 383 | ||
384 | /* Catch these before we mask off the sb_opcode bits */ | ||
385 | switch (sb_opcode) { | ||
386 | case REQ_TASK_ABORT: { | ||
387 | struct asd_ascb *a, *b; | ||
388 | u16 tc_abort; | ||
389 | |||
390 | tc_abort = *((u16*)(&dl->status_block[1])); | ||
391 | tc_abort = le16_to_cpu(tc_abort); | ||
392 | |||
393 | ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n", | ||
394 | __FUNCTION__, dl->status_block[3]); | ||
395 | |||
396 | /* Find the pending task and abort it. */ | ||
397 | list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) | ||
398 | if (a->tc_index == tc_abort) { | ||
399 | task_kill_later(a); | ||
400 | break; | ||
401 | } | ||
402 | goto out; | ||
403 | } | ||
404 | case REQ_DEVICE_RESET: { | ||
405 | struct asd_ascb *a, *b; | ||
406 | u16 conn_handle; | ||
407 | |||
408 | conn_handle = *((u16*)(&dl->status_block[1])); | ||
409 | conn_handle = le16_to_cpu(conn_handle); | ||
410 | |||
411 | ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__, | ||
412 | dl->status_block[3]); | ||
413 | |||
414 | /* Kill all pending tasks and reset the device */ | ||
415 | list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { | ||
416 | struct sas_task *task = a->uldd_task; | ||
417 | struct domain_device *dev = task->dev; | ||
418 | u16 x; | ||
419 | |||
420 | x = *((u16*)(&dev->lldd_dev)); | ||
421 | if (x == conn_handle) | ||
422 | task_kill_later(a); | ||
423 | } | ||
424 | |||
425 | /* FIXME: Reset device port (huh?) */ | ||
426 | goto out; | ||
427 | } | ||
428 | case SIGNAL_NCQ_ERROR: | ||
429 | ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__); | ||
430 | goto out; | ||
431 | case CLEAR_NCQ_ERROR: | ||
432 | ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__); | ||
433 | goto out; | ||
434 | } | ||
435 | |||
371 | sb_opcode &= ~DL_PHY_MASK; | 436 | sb_opcode &= ~DL_PHY_MASK; |
372 | 437 | ||
373 | switch (sb_opcode) { | 438 | switch (sb_opcode) { |
@@ -397,22 +462,6 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
397 | sas_phy_disconnected(sas_phy); | 462 | sas_phy_disconnected(sas_phy); |
398 | sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); | 463 | sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); |
399 | break; | 464 | break; |
400 | case REQ_TASK_ABORT: | ||
401 | ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__, | ||
402 | phy_id); | ||
403 | break; | ||
404 | case REQ_DEVICE_RESET: | ||
405 | ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__, | ||
406 | phy_id); | ||
407 | break; | ||
408 | case SIGNAL_NCQ_ERROR: | ||
409 | ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__, | ||
410 | phy_id); | ||
411 | break; | ||
412 | case CLEAR_NCQ_ERROR: | ||
413 | ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__, | ||
414 | phy_id); | ||
415 | break; | ||
416 | default: | 465 | default: |
417 | ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, | 466 | ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, |
418 | phy_id, sb_opcode); | 467 | phy_id, sb_opcode); |
@@ -432,7 +481,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
432 | 481 | ||
433 | break; | 482 | break; |
434 | } | 483 | } |
435 | 484 | out: | |
436 | asd_invalidate_edb(ascb, edb); | 485 | asd_invalidate_edb(ascb, edb); |
437 | } | 486 | } |
438 | 487 | ||