diff options
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_scb.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_scb.c | 121 |
1 files changed, 104 insertions, 17 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index b15caf1c8fa2..75ed6b0569d1 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" |
@@ -412,6 +413,40 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) | |||
412 | } | 413 | } |
413 | } | 414 | } |
414 | 415 | ||
416 | /* hard reset a phy later */ | ||
417 | static void do_phy_reset_later(struct work_struct *work) | ||
418 | { | ||
419 | struct sas_phy *sas_phy = | ||
420 | container_of(work, struct sas_phy, reset_work); | ||
421 | int error; | ||
422 | |||
423 | ASD_DPRINTK("%s: About to hard reset phy %d\n", __FUNCTION__, | ||
424 | sas_phy->identify.phy_identifier); | ||
425 | /* Reset device port */ | ||
426 | error = sas_phy_reset(sas_phy, 1); | ||
427 | if (error) | ||
428 | ASD_DPRINTK("%s: Hard reset of phy %d failed (%d).\n", | ||
429 | __FUNCTION__, sas_phy->identify.phy_identifier, error); | ||
430 | } | ||
431 | |||
432 | static void phy_reset_later(struct sas_phy *sas_phy, struct Scsi_Host *shost) | ||
433 | { | ||
434 | INIT_WORK(&sas_phy->reset_work, do_phy_reset_later); | ||
435 | queue_work(shost->work_q, &sas_phy->reset_work); | ||
436 | } | ||
437 | |||
438 | /* start up the ABORT TASK tmf... */ | ||
439 | static void task_kill_later(struct asd_ascb *ascb) | ||
440 | { | ||
441 | struct asd_ha_struct *asd_ha = ascb->ha; | ||
442 | struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; | ||
443 | struct Scsi_Host *shost = sas_ha->core.shost; | ||
444 | struct sas_task *task = ascb->uldd_task; | ||
445 | |||
446 | INIT_WORK(&task->abort_work, sas_task_abort); | ||
447 | queue_work(shost->work_q, &task->abort_work); | ||
448 | } | ||
449 | |||
415 | static void escb_tasklet_complete(struct asd_ascb *ascb, | 450 | static void escb_tasklet_complete(struct asd_ascb *ascb, |
416 | struct done_list_struct *dl) | 451 | struct done_list_struct *dl) |
417 | { | 452 | { |
@@ -439,6 +474,74 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
439 | ascb->scb->header.opcode); | 474 | ascb->scb->header.opcode); |
440 | } | 475 | } |
441 | 476 | ||
477 | /* Catch these before we mask off the sb_opcode bits */ | ||
478 | switch (sb_opcode) { | ||
479 | case REQ_TASK_ABORT: { | ||
480 | struct asd_ascb *a, *b; | ||
481 | u16 tc_abort; | ||
482 | |||
483 | tc_abort = *((u16*)(&dl->status_block[1])); | ||
484 | tc_abort = le16_to_cpu(tc_abort); | ||
485 | |||
486 | ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n", | ||
487 | __FUNCTION__, dl->status_block[3]); | ||
488 | |||
489 | /* Find the pending task and abort it. */ | ||
490 | list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) | ||
491 | if (a->tc_index == tc_abort) { | ||
492 | task_kill_later(a); | ||
493 | break; | ||
494 | } | ||
495 | goto out; | ||
496 | } | ||
497 | case REQ_DEVICE_RESET: { | ||
498 | struct Scsi_Host *shost = sas_ha->core.shost; | ||
499 | struct sas_phy *dev_phy; | ||
500 | struct asd_ascb *a; | ||
501 | u16 conn_handle; | ||
502 | |||
503 | conn_handle = *((u16*)(&dl->status_block[1])); | ||
504 | conn_handle = le16_to_cpu(conn_handle); | ||
505 | |||
506 | ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__, | ||
507 | dl->status_block[3]); | ||
508 | |||
509 | /* Kill all pending tasks and reset the device */ | ||
510 | dev_phy = NULL; | ||
511 | list_for_each_entry(a, &asd_ha->seq.pend_q, list) { | ||
512 | struct sas_task *task; | ||
513 | struct domain_device *dev; | ||
514 | u16 x; | ||
515 | |||
516 | task = a->uldd_task; | ||
517 | if (!task) | ||
518 | continue; | ||
519 | dev = task->dev; | ||
520 | |||
521 | x = (unsigned long)dev->lldd_dev; | ||
522 | if (x == conn_handle) { | ||
523 | dev_phy = dev->port->phy; | ||
524 | task_kill_later(a); | ||
525 | } | ||
526 | } | ||
527 | |||
528 | /* Reset device port */ | ||
529 | if (!dev_phy) { | ||
530 | ASD_DPRINTK("%s: No pending commands; can't reset.\n", | ||
531 | __FUNCTION__); | ||
532 | goto out; | ||
533 | } | ||
534 | phy_reset_later(dev_phy, shost); | ||
535 | goto out; | ||
536 | } | ||
537 | case SIGNAL_NCQ_ERROR: | ||
538 | ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__); | ||
539 | goto out; | ||
540 | case CLEAR_NCQ_ERROR: | ||
541 | ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__); | ||
542 | goto out; | ||
543 | } | ||
544 | |||
442 | sb_opcode &= ~DL_PHY_MASK; | 545 | sb_opcode &= ~DL_PHY_MASK; |
443 | 546 | ||
444 | switch (sb_opcode) { | 547 | switch (sb_opcode) { |
@@ -469,22 +572,6 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
469 | asd_deform_port(asd_ha, phy); | 572 | asd_deform_port(asd_ha, phy); |
470 | sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); | 573 | sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); |
471 | break; | 574 | break; |
472 | case REQ_TASK_ABORT: | ||
473 | ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__, | ||
474 | phy_id); | ||
475 | break; | ||
476 | case REQ_DEVICE_RESET: | ||
477 | ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__, | ||
478 | phy_id); | ||
479 | break; | ||
480 | case SIGNAL_NCQ_ERROR: | ||
481 | ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__, | ||
482 | phy_id); | ||
483 | break; | ||
484 | case CLEAR_NCQ_ERROR: | ||
485 | ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__, | ||
486 | phy_id); | ||
487 | break; | ||
488 | default: | 575 | default: |
489 | ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, | 576 | ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, |
490 | phy_id, sb_opcode); | 577 | phy_id, sb_opcode); |
@@ -504,7 +591,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
504 | 591 | ||
505 | break; | 592 | break; |
506 | } | 593 | } |
507 | 594 | out: | |
508 | asd_invalidate_edb(ascb, edb); | 595 | asd_invalidate_edb(ascb, edb); |
509 | } | 596 | } |
510 | 597 | ||