[PATCH] ahci: update ahci-vt8251 reset sequence
Tejun Heo [Wed, 1 Nov 2006 09:00:24 +0000 (18:00 +0900)]
ahci-vt8251

* requires hardreset after PHY status change

* doesn't clear BSY on signature FIS after hardreset

* needs SError cleared for the port to operate after hardreset

This patch implements ahci_vt8251_hardreset() and sets
ATA_FLAG_HRST_TO_RESUME to handle the above behaviors.  This fixes EH
including hotplug on vt8251.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>

drivers/ata/ahci.c

index 413270b..8a1f4f0 100644 (file)
@@ -215,6 +215,7 @@ static u8 ahci_check_status(struct ata_port *ap);
 static void ahci_freeze(struct ata_port *ap);
 static void ahci_thaw(struct ata_port *ap);
 static void ahci_error_handler(struct ata_port *ap);
+static void ahci_vt8251_error_handler(struct ata_port *ap);
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
 static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
 static int ahci_port_resume(struct ata_port *ap);
@@ -274,6 +275,37 @@ static const struct ata_port_operations ahci_ops = {
        .port_stop              = ahci_port_stop,
 };
 
+static const struct ata_port_operations ahci_vt8251_ops = {
+       .port_disable           = ata_port_disable,
+
+       .check_status           = ahci_check_status,
+       .check_altstatus        = ahci_check_status,
+       .dev_select             = ata_noop_dev_select,
+
+       .tf_read                = ahci_tf_read,
+
+       .qc_prep                = ahci_qc_prep,
+       .qc_issue               = ahci_qc_issue,
+
+       .irq_handler            = ahci_interrupt,
+       .irq_clear              = ahci_irq_clear,
+
+       .scr_read               = ahci_scr_read,
+       .scr_write              = ahci_scr_write,
+
+       .freeze                 = ahci_freeze,
+       .thaw                   = ahci_thaw,
+
+       .error_handler          = ahci_vt8251_error_handler,
+       .post_internal_cmd      = ahci_post_internal_cmd,
+
+       .port_suspend           = ahci_port_suspend,
+       .port_resume            = ahci_port_resume,
+
+       .port_start             = ahci_port_start,
+       .port_stop              = ahci_port_stop,
+};
+
 static const struct ata_port_info ahci_port_info[] = {
        /* board_ahci */
        {
@@ -290,10 +322,11 @@ static const struct ata_port_info ahci_port_info[] = {
                .sht            = &ahci_sht,
                .flags          = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-                                 ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_NO_NCQ,
+                                 ATA_FLAG_SKIP_D2H_BSY |
+                                 ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ,
                .pio_mask       = 0x1f, /* pio0-4 */
                .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
-               .port_ops       = &ahci_ops,
+               .port_ops       = &ahci_vt8251_ops,
        },
        /* board_ahci_ign_iferr */
        {
@@ -864,6 +897,31 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class)
        return rc;
 }
 
+static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class)
+{
+       void __iomem *mmio = ap->host->mmio_base;
+       void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+       int rc;
+
+       DPRINTK("ENTER\n");
+
+       ahci_stop_engine(port_mmio);
+
+       rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context));
+
+       /* vt8251 needs SError cleared for the port to operate */
+       ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR));
+
+       ahci_start_engine(port_mmio);
+
+       DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
+
+       /* vt8251 doesn't clear BSY on signature FIS reception,
+        * request follow-up softreset.
+        */
+       return rc ?: -EAGAIN;
+}
+
 static void ahci_postreset(struct ata_port *ap, unsigned int *class)
 {
        void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -1187,6 +1245,22 @@ static void ahci_error_handler(struct ata_port *ap)
                  ahci_postreset);
 }
 
+static void ahci_vt8251_error_handler(struct ata_port *ap)
+{
+       void __iomem *mmio = ap->host->mmio_base;
+       void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+
+       if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
+               /* restart engine */
+               ahci_stop_engine(port_mmio);
+               ahci_start_engine(port_mmio);
+       }
+
+       /* perform recovery */
+       ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_vt8251_hardreset,
+                 ahci_postreset);
+}
+
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;