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