]> err.no Git - linux-2.6/blobdiff - drivers/ata/ahci.c
ata_piix: SATA 2port controller port map fix
[linux-2.6] / drivers / ata / ahci.c
index 35ede5c31b9fc8c83c323776c4ad220774b335ca..10bc3f64c453d50896be9178d20879aef65abb1a 100644 (file)
@@ -176,6 +176,7 @@ enum {
        AHCI_HFLAG_32BIT_ONLY           = (1 << 3), /* force 32bit */
        AHCI_HFLAG_MV_PATA              = (1 << 4), /* PATA port */
        AHCI_HFLAG_NO_MSI               = (1 << 5), /* no PCI MSI */
+       AHCI_HFLAG_NO_PMP               = (1 << 6), /* no PMP */
 
        /* ap->flags bits */
        AHCI_FLAG_NO_HOTPLUG            = (1 << 24), /* ignore PxSERR.DIAG.N */
@@ -238,8 +239,6 @@ static void ahci_freeze(struct ata_port *ap);
 static void ahci_thaw(struct ata_port *ap);
 static void ahci_pmp_attach(struct ata_port *ap);
 static void ahci_pmp_detach(struct ata_port *ap);
-static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val);
-static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val);
 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);
@@ -296,8 +295,6 @@ static const struct ata_port_operations ahci_ops = {
 
        .pmp_attach             = ahci_pmp_attach,
        .pmp_detach             = ahci_pmp_detach,
-       .pmp_read               = ahci_pmp_read,
-       .pmp_write              = ahci_pmp_write,
 
 #ifdef CONFIG_PM
        .port_suspend           = ahci_port_suspend,
@@ -332,8 +329,6 @@ static const struct ata_port_operations ahci_vt8251_ops = {
 
        .pmp_attach             = ahci_pmp_attach,
        .pmp_detach             = ahci_pmp_detach,
-       .pmp_read               = ahci_pmp_read,
-       .pmp_write              = ahci_pmp_write,
 
 #ifdef CONFIG_PM
        .port_suspend           = ahci_port_suspend,
@@ -357,7 +352,7 @@ static const struct ata_port_info ahci_port_info[] = {
        },
        /* board_ahci_vt8251 */
        {
-               AHCI_HFLAGS     (AHCI_HFLAG_NO_NCQ),
+               AHCI_HFLAGS     (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),
                .flags          = AHCI_FLAG_COMMON,
                .link_flags     = AHCI_LFLAG_COMMON | ATA_LFLAG_HRST_TO_RESUME,
                .pio_mask       = 0x1f, /* pio0-4 */
@@ -376,7 +371,7 @@ static const struct ata_port_info ahci_port_info[] = {
        /* board_ahci_sb600 */
        {
                AHCI_HFLAGS     (AHCI_HFLAG_IGN_SERR_INTERNAL |
-                                AHCI_HFLAG_32BIT_ONLY),
+                                AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_PMP),
                .flags          = AHCI_FLAG_COMMON,
                .link_flags     = AHCI_LFLAG_COMMON,
                .pio_mask       = 0x1f, /* pio0-4 */
@@ -490,6 +485,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci },            /* MCP77 */
        { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci },            /* MCP77 */
        { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci },            /* MCP77 */
+       { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci },            /* MCP79 */
+       { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci },            /* MCP79 */
 
        /* SiS */
        { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */
@@ -578,6 +581,12 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
                cap &= ~HOST_CAP_NCQ;
        }
 
+       if ((cap && HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
+               dev_printk(KERN_INFO, &pdev->dev,
+                          "controller can't do PMP, turning off CAP_PMP\n");
+               cap &= ~HOST_CAP_PMP;
+       }
+
        /*
         * Temporary Marvell 6145 hack: PATA port presence
         * is asserted through the standard AHCI port
@@ -1405,29 +1414,46 @@ static void ahci_port_intr(struct ata_port *ap)
        void __iomem *port_mmio = ap->ioaddr.cmd_addr;
        struct ata_eh_info *ehi = &ap->link.eh_info;
        struct ahci_port_priv *pp = ap->private_data;
+       struct ahci_host_priv *hpriv = ap->host->private_data;
+       int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
        u32 status, qc_active;
        int rc, known_irq = 0;
 
        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);
 
+       /* ignore BAD_PMP while resetting */
+       if (unlikely(resetting))
+               status &= ~PORT_IRQ_BAD_PMP;
+
        if (unlikely(status & PORT_IRQ_ERROR)) {
                ahci_error_intr(ap, status);
                return;
        }
 
        if (status & PORT_IRQ_SDB_FIS) {
-               /* If the 'N' bit in word 0 of the FIS is set, we just
-                * received asynchronous notification.  Tell libata
-                * about it.  Note that as the SDB FIS itself is
-                * accessible, SNotification can be emulated by the
-                * driver but don't bother for the time being.
+               /* If SNotification is available, leave notification
+                * handling to sata_async_notification().  If not,
+                * emulate it by snooping SDB FIS RX area.
+                *
+                * Snooping FIS RX area is probably cheaper than
+                * poking SNotification but some constrollers which
+                * implement SNotification, ICH9 for example, don't
+                * store AN SDB FIS into receive area.
                 */
-               const __le32 *f = pp->rx_fis + RX_FIS_SDB;
-               u32 f0 = le32_to_cpu(f[0]);
-
-               if (f0 & (1 << 15))
+               if (hpriv->cap & HOST_CAP_SNTF)
                        sata_async_notification(ap);
+               else {
+                       /* If the 'N' bit in word 0 of the FIS is set,
+                        * we just received asynchronous notification.
+                        * Tell libata about it.
+                        */
+                       const __le32 *f = pp->rx_fis + RX_FIS_SDB;
+                       u32 f0 = le32_to_cpu(f[0]);
+
+                       if (f0 & (1 << 15))
+                               sata_async_notification(ap);
+               }
        }
 
        /* pp->active_link is valid iff any command is in flight */
@@ -1437,6 +1463,13 @@ static void ahci_port_intr(struct ata_port *ap)
                qc_active = readl(port_mmio + PORT_CMD_ISSUE);
 
        rc = ata_qc_complete_multiple(ap, qc_active, NULL);
+
+       /* If resetting, spurious or invalid completions are expected,
+        * return unconditionally.
+        */
+       if (resetting)
+               return;
+
        if (rc > 0)
                return;
        if (rc < 0) {
@@ -1605,11 +1638,8 @@ static void ahci_thaw(struct ata_port *ap)
        writel(tmp, port_mmio + PORT_IRQ_STAT);
        writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);
 
-       /* turn IRQ back on, ignore BAD_PMP if PMP isn't attached */
-       tmp = pp->intr_mask;
-       if (!ap->nr_pmp_links)
-               tmp &= ~PORT_IRQ_BAD_PMP;
-       writel(tmp, port_mmio + PORT_IRQ_MASK);
+       /* turn IRQ back on */
+       writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
 }
 
 static void ahci_error_handler(struct ata_port *ap)
@@ -1652,59 +1682,29 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
 static void ahci_pmp_attach(struct ata_port *ap)
 {
        void __iomem *port_mmio = ahci_port_base(ap);
+       struct ahci_port_priv *pp = ap->private_data;
        u32 cmd;
 
        cmd = readl(port_mmio + PORT_CMD);
        cmd |= PORT_CMD_PMP;
        writel(cmd, port_mmio + PORT_CMD);
+
+       pp->intr_mask |= PORT_IRQ_BAD_PMP;
+       writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
 }
 
 static void ahci_pmp_detach(struct ata_port *ap)
 {
        void __iomem *port_mmio = ahci_port_base(ap);
-       struct ahci_host_priv *hpriv = ap->host->private_data;
-       unsigned long flags;
+       struct ahci_port_priv *pp = ap->private_data;
        u32 cmd;
 
        cmd = readl(port_mmio + PORT_CMD);
        cmd &= ~PORT_CMD_PMP;
        writel(cmd, port_mmio + PORT_CMD);
 
-       if (hpriv->cap & HOST_CAP_NCQ) {
-               spin_lock_irqsave(ap->lock, flags);
-               ap->flags |= ATA_FLAG_NCQ;
-               spin_unlock_irqrestore(ap->lock, flags);
-       }
-}
-
-static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val)
-{
-       struct ata_port *ap = dev->link->ap;
-       struct ata_taskfile tf;
-       int rc;
-
-       ahci_kick_engine(ap, 0);
-
-       sata_pmp_read_init_tf(&tf, dev, pmp, reg);
-       rc = ahci_exec_polled_cmd(ap, SATA_PMP_CTRL_PORT, &tf, 1, 0,
-                                 SATA_PMP_SCR_TIMEOUT);
-       if (rc == 0) {
-               ahci_tf_read(ap, &tf);
-               *r_val = sata_pmp_read_val(&tf);
-       }
-       return rc;
-}
-
-static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val)
-{
-       struct ata_port *ap = dev->link->ap;
-       struct ata_taskfile tf;
-
-       ahci_kick_engine(ap, 0);
-
-       sata_pmp_write_init_tf(&tf, dev, pmp, reg, val);
-       return ahci_exec_polled_cmd(ap, SATA_PMP_CTRL_PORT, &tf, 1, 0,
-                                   SATA_PMP_SCR_TIMEOUT);
+       pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
+       writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
 }
 
 static int ahci_port_resume(struct ata_port *ap)