ahci: port_no should be used when clearing IRQ in ahci_thaw()
[powerpc.git] / drivers / ata / ahci.c
index cef2e70..48616c6 100644 (file)
@@ -53,6 +53,7 @@
 
 enum {
        AHCI_PCI_BAR            = 5,
+       AHCI_MAX_PORTS          = 32,
        AHCI_MAX_SG             = 168, /* hardware max is 64K */
        AHCI_DMA_BOUNDARY       = 0xffffffff,
        AHCI_USE_CLUSTERING     = 0,
@@ -74,10 +75,13 @@ enum {
        AHCI_CMD_CLR_BUSY       = (1 << 10),
 
        RX_FIS_D2H_REG          = 0x40, /* offset of D2H Register FIS data */
+       RX_FIS_SDB              = 0x58, /* offset of SDB FIS data */
        RX_FIS_UNK              = 0x60, /* offset of Unknown FIS data */
 
        board_ahci              = 0,
-       board_ahci_vt8251       = 1,
+       board_ahci_pi           = 1,
+       board_ahci_vt8251       = 2,
+       board_ahci_ign_iferr    = 3,
 
        /* global controller registers */
        HOST_CAP                = 0x00, /* host capabilities */
@@ -166,8 +170,9 @@ enum {
        AHCI_FLAG_MSI           = (1 << 0),
 
        /* ap->flags bits */
-       AHCI_FLAG_RESET_NEEDS_CLO       = (1 << 24),
-       AHCI_FLAG_NO_NCQ                = (1 << 25),
+       AHCI_FLAG_NO_NCQ                = (1 << 24),
+       AHCI_FLAG_IGN_IRQ_IF_ERR        = (1 << 25), /* ignore IRQ_IF_ERR */
+       AHCI_FLAG_HONOR_PI              = (1 << 26), /* honor PORTS_IMPL */
 };
 
 struct ahci_cmd_hdr {
@@ -198,6 +203,10 @@ struct ahci_port_priv {
        dma_addr_t              cmd_tbl_dma;
        void                    *rx_fis;
        dma_addr_t              rx_fis_dma;
+       /* for NCQ spurious interrupt analysis */
+       int                     ncq_saw_spurious_sdb_cnt;
+       unsigned int            ncq_saw_d2h:1;
+       unsigned int            ncq_saw_dmas:1;
 };
 
 static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
@@ -214,6 +223,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);
@@ -273,6 +283,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 */
        {
@@ -284,13 +325,34 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
                .port_ops       = &ahci_ops,
        },
+       /* board_ahci_pi */
+       {
+               .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_HONOR_PI,
+               .pio_mask       = 0x1f, /* pio0-4 */
+               .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
+               .port_ops       = &ahci_ops,
+       },
        /* board_ahci_vt8251 */
        {
                .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_RESET_NEEDS_CLO | AHCI_FLAG_NO_NCQ,
+                                 ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ,
+               .pio_mask       = 0x1f, /* pio0-4 */
+               .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
+               .port_ops       = &ahci_vt8251_ops,
+       },
+       /* board_ahci_ign_iferr */
+       {
+               .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_IGN_IRQ_IF_ERR,
                .pio_mask       = 0x1f, /* pio0-4 */
                .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
                .port_ops       = &ahci_ops,
@@ -304,23 +366,34 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */
        { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */
        { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */
-       { PCI_VDEVICE(AL, 0x5288), board_ahci }, /* ULi M5288 */
+       { PCI_VDEVICE(AL, 0x5288), board_ahci_ign_iferr }, /* ULi M5288 */
        { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */
        { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
        { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
        { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
-       { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
-       { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */
-       { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
-       { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
-       { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
+       { PCI_VDEVICE(INTEL, 0x2821), board_ahci_pi }, /* ICH8 */
+       { PCI_VDEVICE(INTEL, 0x2822), board_ahci_pi }, /* ICH8 */
+       { PCI_VDEVICE(INTEL, 0x2824), board_ahci_pi }, /* ICH8 */
+       { PCI_VDEVICE(INTEL, 0x2829), board_ahci_pi }, /* ICH8M */
+       { PCI_VDEVICE(INTEL, 0x282a), board_ahci_pi }, /* ICH8M */
+       { PCI_VDEVICE(INTEL, 0x2922), board_ahci_pi }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2923), board_ahci_pi }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2924), board_ahci_pi }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2925), board_ahci_pi }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2927), board_ahci_pi }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x2929), board_ahci_pi }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292a), board_ahci_pi }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292b), board_ahci_pi }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x292f), board_ahci_pi }, /* ICH9M */
+       { PCI_VDEVICE(INTEL, 0x294d), board_ahci_pi }, /* ICH9 */
+       { PCI_VDEVICE(INTEL, 0x294e), board_ahci_pi }, /* ICH9M */
 
        /* JMicron */
-       { PCI_VDEVICE(JMICRON, 0x2360), board_ahci }, /* JMicron JMB360 */
-       { PCI_VDEVICE(JMICRON, 0x2361), board_ahci }, /* JMicron JMB361 */
-       { PCI_VDEVICE(JMICRON, 0x2363), board_ahci }, /* JMicron JMB363 */
-       { PCI_VDEVICE(JMICRON, 0x2365), board_ahci }, /* JMicron JMB365 */
-       { PCI_VDEVICE(JMICRON, 0x2366), board_ahci }, /* JMicron JMB366 */
+       { PCI_VDEVICE(JMICRON, 0x2360), board_ahci_ign_iferr }, /* JMB360 */
+       { PCI_VDEVICE(JMICRON, 0x2361), board_ahci_ign_iferr }, /* JMB361 */
+       { PCI_VDEVICE(JMICRON, 0x2363), board_ahci_ign_iferr }, /* JMB363 */
+       { PCI_VDEVICE(JMICRON, 0x2365), board_ahci_ign_iferr }, /* JMB365 */
+       { PCI_VDEVICE(JMICRON, 0x2366), board_ahci_ign_iferr }, /* JMB366 */
 
        /* ATI */
        { PCI_VDEVICE(ATI, 0x4380), board_ahci }, /* ATI SB600 non-raid */
@@ -334,12 +407,32 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci },            /* MCP65 */
        { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci },            /* MCP65 */
        { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci },            /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci },            /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci },            /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci },            /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci },            /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci },            /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci },            /* MCP67 */
 
        /* SiS */
        { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */
        { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */
        { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */
 
+       /* Generic, PCI class code for AHCI */
+       { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+         0x010601, 0xffffff, board_ahci },
+
        { }     /* terminate list */
 };
 
@@ -354,6 +447,11 @@ static struct pci_driver ahci_pci_driver = {
 };
 
 
+static inline int ahci_nr_ports(u32 cap)
+{
+       return (cap & 0x1f) + 1;
+}
+
 static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port)
 {
        return base + 0x100 + (port * 0x80);
@@ -493,43 +591,23 @@ static void ahci_power_down(void __iomem *port_mmio, u32 cap)
 {
        u32 cmd, scontrol;
 
-       cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
-
-       if (cap & HOST_CAP_SSC) {
-               /* enable transitions to slumber mode */
-               scontrol = readl(port_mmio + PORT_SCR_CTL);
-               if ((scontrol & 0x0f00) > 0x100) {
-                       scontrol &= ~0xf00;
-                       writel(scontrol, port_mmio + PORT_SCR_CTL);
-               }
-
-               /* put device into slumber mode */
-               writel(cmd | PORT_CMD_ICC_SLUMBER, port_mmio + PORT_CMD);
-
-               /* wait for the transition to complete */
-               ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_ICC_SLUMBER,
-                                 PORT_CMD_ICC_SLUMBER, 1, 50);
-       }
+       if (!(cap & HOST_CAP_SSS))
+               return;
 
-       /* put device into listen mode */
-       if (cap & HOST_CAP_SSS) {
-               /* first set PxSCTL.DET to 0 */
-               scontrol = readl(port_mmio + PORT_SCR_CTL);
-               scontrol &= ~0xf;
-               writel(scontrol, port_mmio + PORT_SCR_CTL);
+       /* put device into listen mode, first set PxSCTL.DET to 0 */
+       scontrol = readl(port_mmio + PORT_SCR_CTL);
+       scontrol &= ~0xf;
+       writel(scontrol, port_mmio + PORT_SCR_CTL);
 
-               /* then set PxCMD.SUD to 0 */
-               cmd &= ~PORT_CMD_SPIN_UP;
-               writel(cmd, port_mmio + PORT_CMD);
-       }
+       /* then set PxCMD.SUD to 0 */
+       cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
+       cmd &= ~PORT_CMD_SPIN_UP;
+       writel(cmd, port_mmio + PORT_CMD);
 }
 
 static void ahci_init_port(void __iomem *port_mmio, u32 cap,
                           dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
 {
-       /* power up */
-       ahci_power_up(port_mmio, cap);
-
        /* enable FIS reception */
        ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma);
 
@@ -555,19 +633,15 @@ static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg)
                return rc;
        }
 
-       /* put device into slumber mode */
-       ahci_power_down(port_mmio, cap);
-
        return 0;
 }
 
 static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
 {
-       u32 cap_save, tmp;
+       u32 cap_save, impl_save, tmp;
 
        cap_save = readl(mmio + HOST_CAP);
-       cap_save &= ( (1<<28) | (1<<17) );
-       cap_save |= (1 << 27);
+       impl_save = readl(mmio + HOST_PORTS_IMPL);
 
        /* global controller reset */
        tmp = readl(mmio + HOST_CTL);
@@ -588,10 +662,21 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
                return -EIO;
        }
 
+       /* turn on AHCI mode */
        writel(HOST_AHCI_EN, mmio + HOST_CTL);
        (void) readl(mmio + HOST_CTL);  /* flush */
+
+       /* These write-once registers are normally cleared on reset.
+        * Restore BIOS values... which we HOPE were present before
+        * reset.
+        */
+       if (!impl_save) {
+               impl_save = (1 << ahci_nr_ports(cap_save)) - 1;
+               dev_printk(KERN_WARNING, &pdev->dev,
+                          "PORTS_IMPL is zero, forcing 0x%x\n", impl_save);
+       }
        writel(cap_save, mmio + HOST_CAP);
-       writel(0xf, mmio + HOST_PORTS_IMPL);
+       writel(impl_save, mmio + HOST_PORTS_IMPL);
        (void) readl(mmio + HOST_PORTS_IMPL);   /* flush */
 
        if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
@@ -607,7 +692,8 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
 }
 
 static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev,
-                                int n_ports, u32 cap)
+                                int n_ports, unsigned int port_flags,
+                                struct ahci_host_priv *hpriv)
 {
        int i, rc;
        u32 tmp;
@@ -616,13 +702,12 @@ static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev,
                void __iomem *port_mmio = ahci_port_base(mmio, i);
                const char *emsg = NULL;
 
-#if 0 /* BIOSen initialize this incorrectly */
-               if (!(hpriv->port_map & (1 << i)))
+               if ((port_flags & AHCI_FLAG_HONOR_PI) &&
+                   !(hpriv->port_map & (1 << i)))
                        continue;
-#endif
 
                /* make sure port is not active */
-               rc = ahci_deinit_port(port_mmio, cap, &emsg);
+               rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
                if (rc)
                        dev_printk(KERN_WARNING, &pdev->dev,
                                   "%s (%d)\n", emsg, rc);
@@ -697,17 +782,6 @@ static int ahci_clo(struct ata_port *ap)
        return 0;
 }
 
-static int ahci_prereset(struct ata_port *ap)
-{
-       if ((ap->flags & AHCI_FLAG_RESET_NEEDS_CLO) &&
-           (ata_busy_wait(ap, ATA_BUSY, 1000) & ATA_BUSY)) {
-               /* ATA_BUSY hasn't cleared, so send a CLO */
-               ahci_clo(ap);
-       }
-
-       return ata_std_prereset(ap);
-}
-
 static int ahci_softreset(struct ata_port *ap, unsigned int *class)
 {
        struct ahci_port_priv *pp = ap->private_data;
@@ -736,8 +810,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
        }
 
        /* check BUSY/DRQ, perform Command List Override if necessary */
-       ahci_tf_read(ap, &tf);
-       if (tf.command & (ATA_BUSY | ATA_DRQ)) {
+       if (ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ)) {
                rc = ahci_clo(ap);
 
                if (rc == -EOPNOTSUPP) {
@@ -830,7 +903,7 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class)
 
        /* clear D2H reception area to properly wait for D2H FIS */
        ata_tf_init(ap->device, &tf);
-       tf.command = 0xff;
+       tf.command = 0x80;
        ata_tf_to_fis(&tf, d2h_fis, 0);
 
        rc = sata_std_hardreset(ap, class);
@@ -846,6 +919,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;
@@ -962,6 +1060,10 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
        /* analyze @irq_stat */
        ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
 
+       /* some controllers set IRQ_IF_ERR on device errors, ignore it */
+       if (ap->flags & AHCI_FLAG_IGN_IRQ_IF_ERR)
+               irq_stat &= ~PORT_IRQ_IF_ERR;
+
        if (irq_stat & PORT_IRQ_TF_ERR)
                err_mask |= AC_ERR_DEV;
 
@@ -1012,8 +1114,9 @@ static void ahci_host_intr(struct ata_port *ap)
        void __iomem *mmio = ap->host->mmio_base;
        void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
        struct ata_eh_info *ehi = &ap->eh_info;
+       struct ahci_port_priv *pp = ap->private_data;
        u32 status, qc_active;
-       int rc;
+       int rc, known_irq = 0;
 
        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);
@@ -1040,17 +1143,53 @@ static void ahci_host_intr(struct ata_port *ap)
 
        /* hmmm... a spurious interupt */
 
-       /* some devices send D2H reg with I bit set during NCQ command phase */
-       if (ap->sactive && (status & PORT_IRQ_D2H_REG_FIS))
+       /* if !NCQ, ignore.  No modern ATA device has broken HSM
+        * implementation for non-NCQ commands.
+        */
+       if (!ap->sactive)
                return;
 
-       /* ignore interim PIO setup fis interrupts */
-       if (ata_tag_valid(ap->active_tag) && (status & PORT_IRQ_PIOS_FIS))
-               return;
+       if (status & PORT_IRQ_D2H_REG_FIS) {
+               if (!pp->ncq_saw_d2h)
+                       ata_port_printk(ap, KERN_INFO,
+                               "D2H reg with I during NCQ, "
+                               "this message won't be printed again\n");
+               pp->ncq_saw_d2h = 1;
+               known_irq = 1;
+       }
 
-       if (ata_ratelimit())
+       if (status & PORT_IRQ_DMAS_FIS) {
+               if (!pp->ncq_saw_dmas)
+                       ata_port_printk(ap, KERN_INFO,
+                               "DMAS FIS during NCQ, "
+                               "this message won't be printed again\n");
+               pp->ncq_saw_dmas = 1;
+               known_irq = 1;
+       }
+
+       if (status & PORT_IRQ_SDB_FIS &&
+                  pp->ncq_saw_spurious_sdb_cnt < 10) {
+               /* SDB FIS containing spurious completions might be
+                * dangerous, we need to know more about them.  Print
+                * more of it.
+                */
+               const u32 *f = pp->rx_fis + RX_FIS_SDB;
+
+               ata_port_printk(ap, KERN_INFO, "Spurious SDB FIS during NCQ "
+                               "issue=0x%x SAct=0x%x FIS=%08x:%08x%s\n",
+                               readl(port_mmio + PORT_CMD_ISSUE),
+                               readl(port_mmio + PORT_SCR_ACT),
+                               le32_to_cpu(f[0]), le32_to_cpu(f[1]),
+                               pp->ncq_saw_spurious_sdb_cnt < 10 ?
+                               "" : ", shutting up");
+
+               pp->ncq_saw_spurious_sdb_cnt++;
+               known_irq = 1;
+       }
+
+       if (!known_irq)
                ata_port_printk(ap, KERN_INFO, "spurious interrupt "
-                               "(irq_stat 0x%x active_tag %d sactive 0x%x)\n",
+                               "(irq_stat 0x%x active_tag 0x%x sactive 0x%x)\n",
                                status, ap->active_tag, ap->sactive);
 }
 
@@ -1143,7 +1282,7 @@ static void ahci_thaw(struct ata_port *ap)
        /* clear IRQ */
        tmp = readl(port_mmio + PORT_IRQ_STAT);
        writel(tmp, port_mmio + PORT_IRQ_STAT);
-       writel(1 << ap->id, mmio + HOST_IRQ_STAT);
+       writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);
 
        /* turn IRQ back on */
        writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
@@ -1161,7 +1300,23 @@ static void ahci_error_handler(struct ata_port *ap)
        }
 
        /* perform recovery */
-       ata_do_eh(ap, ahci_prereset, ahci_softreset, ahci_hardreset,
+       ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset,
+                 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);
 }
 
@@ -1191,7 +1346,9 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
        int rc;
 
        rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
-       if (rc) {
+       if (rc == 0)
+               ahci_power_down(port_mmio, hpriv->cap);
+       else {
                ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
                ahci_init_port(port_mmio, hpriv->cap,
                               pp->cmd_slot_dma, pp->rx_fis_dma);
@@ -1207,6 +1364,7 @@ static int ahci_port_resume(struct ata_port *ap)
        void __iomem *mmio = ap->host->mmio_base;
        void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 
+       ahci_power_up(port_mmio, hpriv->cap);
        ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
 
        return 0;
@@ -1246,7 +1404,8 @@ static int ahci_pci_device_resume(struct pci_dev *pdev)
                if (rc)
                        return rc;
 
-               ahci_init_controller(mmio, pdev, host->n_ports, hpriv->cap);
+               ahci_init_controller(mmio, pdev, host->n_ports,
+                                    host->ports[0]->flags, hpriv);
        }
 
        ata_host_resume(host);
@@ -1312,6 +1471,9 @@ static int ahci_port_start(struct ata_port *ap)
 
        ap->private_data = pp;
 
+       /* power up port */
+       ahci_power_up(port_mmio, hpriv->cap);
+
        /* initialize port */
        ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
 
@@ -1358,7 +1520,7 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
        struct ahci_host_priv *hpriv = probe_ent->private_data;
        struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
        void __iomem *mmio = probe_ent->mmio_base;
-       unsigned int i, using_dac;
+       unsigned int i, cap_n_ports, using_dac;
        int rc;
 
        rc = ahci_reset_controller(mmio, pdev);
@@ -1367,10 +1529,34 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
 
        hpriv->cap = readl(mmio + HOST_CAP);
        hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
-       probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
+       cap_n_ports = ahci_nr_ports(hpriv->cap);
 
        VPRINTK("cap 0x%x  port_map 0x%x  n_ports %d\n",
-               hpriv->cap, hpriv->port_map, probe_ent->n_ports);
+               hpriv->cap, hpriv->port_map, cap_n_ports);
+
+       if (probe_ent->port_flags & AHCI_FLAG_HONOR_PI) {
+               unsigned int n_ports = cap_n_ports;
+               u32 port_map = hpriv->port_map;
+               int max_port = 0;
+
+               for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) {
+                       if (port_map & (1 << i)) {
+                               n_ports--;
+                               port_map &= ~(1 << i);
+                               max_port = i;
+                       } else
+                               probe_ent->dummy_port_mask |= 1 << i;
+               }
+
+               if (n_ports || port_map)
+                       dev_printk(KERN_WARNING, &pdev->dev,
+                                  "nr_ports (%u) and implemented port map "
+                                  "(0x%x) don't match\n",
+                                  cap_n_ports, hpriv->port_map);
+
+               probe_ent->n_ports = max_port + 1;
+       } else
+               probe_ent->n_ports = cap_n_ports;
 
        using_dac = hpriv->cap & HOST_CAP_64;
        if (using_dac &&
@@ -1402,7 +1588,8 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
        for (i = 0; i < probe_ent->n_ports; i++)
                ahci_setup_port(&probe_ent->port[i], (unsigned long) mmio, i);
 
-       ahci_init_controller(mmio, pdev, probe_ent->n_ports, hpriv->cap);
+       ahci_init_controller(mmio, pdev, probe_ent->n_ports,
+                            probe_ent->port_flags, hpriv);
 
        pci_set_master(pdev);