diff options
author | Tejun Heo <htejun@gmail.com> | 2006-03-22 07:07:03 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-03-22 19:56:53 -0500 |
commit | 4658f79bec0b51222e769e328c2923f39f3bda77 (patch) | |
tree | 3b09b794cde1164adc606861ddd3df6ddeb94ce2 /drivers/scsi | |
parent | f0c8bbfa154f4481623a4478b0ae94a6ceeaa026 (diff) |
[PATCH] ahci: add softreset
Now that libata is smart enought to handle both soft and hard resets,
add softreset method.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/ahci.c | 135 |
1 files changed, 134 insertions, 1 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index a1ddbba2cbdf..ffba65656a83 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c | |||
@@ -513,6 +513,138 @@ static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, u32 opts) | |||
513 | pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); | 513 | pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); |
514 | } | 514 | } |
515 | 515 | ||
516 | static int ahci_poll_register(void __iomem *reg, u32 mask, u32 val, | ||
517 | unsigned long interval_msec, | ||
518 | unsigned long timeout_msec) | ||
519 | { | ||
520 | unsigned long timeout; | ||
521 | u32 tmp; | ||
522 | |||
523 | timeout = jiffies + (timeout_msec * HZ) / 1000; | ||
524 | do { | ||
525 | tmp = readl(reg); | ||
526 | if ((tmp & mask) == val) | ||
527 | return 0; | ||
528 | msleep(interval_msec); | ||
529 | } while (time_before(jiffies, timeout)); | ||
530 | |||
531 | return -1; | ||
532 | } | ||
533 | |||
534 | static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) | ||
535 | { | ||
536 | struct ahci_host_priv *hpriv = ap->host_set->private_data; | ||
537 | struct ahci_port_priv *pp = ap->private_data; | ||
538 | void __iomem *mmio = ap->host_set->mmio_base; | ||
539 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); | ||
540 | const u32 cmd_fis_len = 5; /* five dwords */ | ||
541 | const char *reason = NULL; | ||
542 | struct ata_taskfile tf; | ||
543 | u8 *fis; | ||
544 | int rc; | ||
545 | |||
546 | DPRINTK("ENTER\n"); | ||
547 | |||
548 | /* prepare for SRST (AHCI-1.1 10.4.1) */ | ||
549 | rc = ahci_stop_engine(ap); | ||
550 | if (rc) { | ||
551 | reason = "failed to stop engine"; | ||
552 | goto fail_restart; | ||
553 | } | ||
554 | |||
555 | /* check BUSY/DRQ, perform Command List Override if necessary */ | ||
556 | ahci_tf_read(ap, &tf); | ||
557 | if (tf.command & (ATA_BUSY | ATA_DRQ)) { | ||
558 | u32 tmp; | ||
559 | |||
560 | if (!(hpriv->cap & HOST_CAP_CLO)) { | ||
561 | rc = -EIO; | ||
562 | reason = "port busy but no CLO"; | ||
563 | goto fail_restart; | ||
564 | } | ||
565 | |||
566 | tmp = readl(port_mmio + PORT_CMD); | ||
567 | tmp |= PORT_CMD_CLO; | ||
568 | writel(tmp, port_mmio + PORT_CMD); | ||
569 | readl(port_mmio + PORT_CMD); /* flush */ | ||
570 | |||
571 | if (ahci_poll_register(port_mmio + PORT_CMD, PORT_CMD_CLO, 0x0, | ||
572 | 1, 500)) { | ||
573 | rc = -EIO; | ||
574 | reason = "CLO failed"; | ||
575 | goto fail_restart; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | /* restart engine */ | ||
580 | ahci_start_engine(ap); | ||
581 | |||
582 | ata_tf_init(ap, &tf, 0); | ||
583 | fis = pp->cmd_tbl; | ||
584 | |||
585 | /* issue the first D2H Register FIS */ | ||
586 | ahci_fill_cmd_slot(pp, cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY); | ||
587 | |||
588 | tf.ctl |= ATA_SRST; | ||
589 | ata_tf_to_fis(&tf, fis, 0); | ||
590 | fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ | ||
591 | |||
592 | writel(1, port_mmio + PORT_CMD_ISSUE); | ||
593 | readl(port_mmio + PORT_CMD_ISSUE); /* flush */ | ||
594 | |||
595 | if (ahci_poll_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x0, 1, 500)) { | ||
596 | rc = -EIO; | ||
597 | reason = "1st FIS failed"; | ||
598 | goto fail; | ||
599 | } | ||
600 | |||
601 | /* spec says at least 5us, but be generous and sleep for 1ms */ | ||
602 | msleep(1); | ||
603 | |||
604 | /* issue the second D2H Register FIS */ | ||
605 | ahci_fill_cmd_slot(pp, cmd_fis_len); | ||
606 | |||
607 | tf.ctl &= ~ATA_SRST; | ||
608 | ata_tf_to_fis(&tf, fis, 0); | ||
609 | fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ | ||
610 | |||
611 | writel(1, port_mmio + PORT_CMD_ISSUE); | ||
612 | readl(port_mmio + PORT_CMD_ISSUE); /* flush */ | ||
613 | |||
614 | /* spec mandates ">= 2ms" before checking status. | ||
615 | * We wait 150ms, because that was the magic delay used for | ||
616 | * ATAPI devices in Hale Landis's ATADRVR, for the period of time | ||
617 | * between when the ATA command register is written, and then | ||
618 | * status is checked. Because waiting for "a while" before | ||
619 | * checking status is fine, post SRST, we perform this magic | ||
620 | * delay here as well. | ||
621 | */ | ||
622 | msleep(150); | ||
623 | |||
624 | *class = ATA_DEV_NONE; | ||
625 | if (sata_dev_present(ap)) { | ||
626 | if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { | ||
627 | rc = -EIO; | ||
628 | reason = "device not ready"; | ||
629 | goto fail; | ||
630 | } | ||
631 | *class = ahci_dev_classify(ap); | ||
632 | } | ||
633 | |||
634 | DPRINTK("EXIT, class=%u\n", *class); | ||
635 | return 0; | ||
636 | |||
637 | fail_restart: | ||
638 | ahci_start_engine(ap); | ||
639 | fail: | ||
640 | if (verbose) | ||
641 | printk(KERN_ERR "ata%u: softreset failed (%s)\n", | ||
642 | ap->id, reason); | ||
643 | else | ||
644 | DPRINTK("EXIT, rc=%d reason=\"%s\"\n", rc, reason); | ||
645 | return rc; | ||
646 | } | ||
647 | |||
516 | static int ahci_hardreset(struct ata_port *ap, int verbose, unsigned int *class) | 648 | static int ahci_hardreset(struct ata_port *ap, int verbose, unsigned int *class) |
517 | { | 649 | { |
518 | int rc; | 650 | int rc; |
@@ -553,7 +685,8 @@ static void ahci_postreset(struct ata_port *ap, unsigned int *class) | |||
553 | 685 | ||
554 | static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes) | 686 | static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes) |
555 | { | 687 | { |
556 | return ata_drive_probe_reset(ap, NULL, NULL, ahci_hardreset, | 688 | return ata_drive_probe_reset(ap, ata_std_probeinit, |
689 | ahci_softreset, ahci_hardreset, | ||
557 | ahci_postreset, classes); | 690 | ahci_postreset, classes); |
558 | } | 691 | } |
559 | 692 | ||