aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic94xx/aic94xx_scb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_scb.c')
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c121
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 */
417static 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
432static 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... */
439static 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
415static void escb_tasklet_complete(struct asd_ascb *ascb, 450static 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 594out:
508 asd_invalidate_edb(ascb, edb); 595 asd_invalidate_edb(ascb, edb);
509} 596}
510 597