Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / ata / ahci.c
index 966ab40..5e6468a 100644 (file)
@@ -90,6 +90,7 @@ enum {
        board_ahci_mv           = 4,
        board_ahci_sb700        = 5,
        board_ahci_mcp65        = 6,
+       board_ahci_nopmp        = 7,
 
        /* global controller registers */
        HOST_CAP                = 0x00, /* host capabilities */
@@ -401,6 +402,14 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
+       /* board_ahci_nopmp */
+       {
+               AHCI_HFLAGS     (AHCI_HFLAG_NO_PMP),
+               .flags          = AHCI_FLAG_COMMON,
+               .pio_mask       = 0x1f, /* pio0-4 */
+               .udma_mask      = ATA_UDMA6,
+               .port_ops       = &ahci_ops,
+       },
 };
 
 static const struct pci_device_id ahci_pci_tbl[] = {
@@ -525,9 +534,9 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(NVIDIA, 0x0bc7), board_ahci },            /* MCP7B */
 
        /* 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 */
+       { PCI_VDEVICE(SI, 0x1184), board_ahci_nopmp },          /* SiS 966 */
+       { PCI_VDEVICE(SI, 0x1185), board_ahci_nopmp },          /* SiS 968 */
+       { PCI_VDEVICE(SI, 0x0186), board_ahci_nopmp },          /* SiS 968 */
 
        /* Marvell */
        { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv },        /* 6145 */
@@ -653,6 +662,14 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
                cap &= ~HOST_CAP_PMP;
        }
 
+       if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361 &&
+           port_map != 1) {
+               dev_printk(KERN_INFO, &pdev->dev,
+                          "JMB361 has only one port, port_map 0x%x -> 0x%x\n",
+                          port_map, 1);
+               port_map = 1;
+       }
+
        /*
         * Temporary Marvell 6145 hack: PATA port presence
         * is asserted through the standard AHCI port
@@ -1760,7 +1777,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
        struct ahci_host_priv *hpriv;
        unsigned int i, handled = 0;
        void __iomem *mmio;
-       u32 irq_stat, irq_ack = 0;
+       u32 irq_stat, irq_masked;
 
        VPRINTK("ENTER\n");
 
@@ -1769,16 +1786,17 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
 
        /* sigh.  0xffffffff is a valid return from h/w */
        irq_stat = readl(mmio + HOST_IRQ_STAT);
-       irq_stat &= hpriv->port_map;
        if (!irq_stat)
                return IRQ_NONE;
 
+       irq_masked = irq_stat & hpriv->port_map;
+
        spin_lock(&host->lock);
 
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;
 
-               if (!(irq_stat & (1 << i)))
+               if (!(irq_masked & (1 << i)))
                        continue;
 
                ap = host->ports[i];
@@ -1792,14 +1810,20 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
                                        "interrupt on disabled port %u\n", i);
                }
 
-               irq_ack |= (1 << i);
-       }
-
-       if (irq_ack) {
-               writel(irq_ack, mmio + HOST_IRQ_STAT);
                handled = 1;
        }
 
+       /* HOST_IRQ_STAT behaves as level triggered latch meaning that
+        * it should be cleared after all the port events are cleared;
+        * otherwise, it will raise a spurious interrupt after each
+        * valid one.  Please read section 10.6.2 of ahci 1.1 for more
+        * information.
+        *
+        * Also, use the unmasked value to clear interrupt as spurious
+        * pending event on a dummy port might cause screaming IRQ.
+        */
+       writel(irq_stat, mmio + HOST_IRQ_STAT);
+
        spin_unlock(&host->lock);
 
        VPRINTK("EXIT\n");