]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/ahci.c
ahci: jmb361 has only one port
[linux-2.6-omap-h63xx.git] / drivers / ata / ahci.c
index 97f83fb2ee2eef25af748297cb474c859ba6545e..29f34d03bd75bd207c169d58e6d846954f3b7947 100644 (file)
@@ -89,6 +89,7 @@ enum {
        board_ahci_sb600        = 3,
        board_ahci_mv           = 4,
        board_ahci_sb700        = 5,
+       board_ahci_mcp65        = 6,
 
        /* global controller registers */
        HOST_CAP                = 0x00, /* host capabilities */
@@ -190,6 +191,7 @@ enum {
        AHCI_HFLAG_NO_PMP               = (1 << 6), /* no PMP */
        AHCI_HFLAG_NO_HOTPLUG           = (1 << 7), /* ignore PxSERR.DIAG.N */
        AHCI_HFLAG_SECT255              = (1 << 8), /* max 255 sectors */
+       AHCI_HFLAG_YES_NCQ              = (1 << 9), /* force NCQ cap on */
 
        /* ap->flags bits */
 
@@ -253,6 +255,8 @@ static void ahci_pmp_attach(struct ata_port *ap);
 static void ahci_pmp_detach(struct ata_port *ap);
 static int ahci_softreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline);
+static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
+                         unsigned long deadline);
 static int ahci_hardreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline);
 static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
@@ -329,6 +333,12 @@ static struct ata_port_operations ahci_p5wdh_ops = {
        .hardreset              = ahci_p5wdh_hardreset,
 };
 
+static struct ata_port_operations ahci_sb600_ops = {
+       .inherits               = &ahci_ops,
+       .softreset              = ahci_sb600_softreset,
+       .pmp_softreset          = ahci_sb600_softreset,
+};
+
 #define AHCI_HFLAGS(flags)     .private_data   = (void *)(flags)
 
 static const struct ata_port_info ahci_port_info[] = {
@@ -359,11 +369,11 @@ static const struct ata_port_info ahci_port_info[] = {
        {
                AHCI_HFLAGS     (AHCI_HFLAG_IGN_SERR_INTERNAL |
                                 AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |
-                                AHCI_HFLAG_SECT255 | AHCI_HFLAG_NO_PMP),
+                                AHCI_HFLAG_SECT255),
                .flags          = AHCI_FLAG_COMMON,
                .pio_mask       = 0x1f, /* pio0-4 */
                .udma_mask      = ATA_UDMA6,
-               .port_ops       = &ahci_ops,
+               .port_ops       = &ahci_sb600_ops,
        },
        /* board_ahci_mv */
        {
@@ -377,8 +387,15 @@ static const struct ata_port_info ahci_port_info[] = {
        },
        /* board_ahci_sb700 */
        {
-               AHCI_HFLAGS     (AHCI_HFLAG_IGN_SERR_INTERNAL |
-                                AHCI_HFLAG_NO_PMP),
+               AHCI_HFLAGS     (AHCI_HFLAG_IGN_SERR_INTERNAL),
+               .flags          = AHCI_FLAG_COMMON,
+               .pio_mask       = 0x1f, /* pio0-4 */
+               .udma_mask      = ATA_UDMA6,
+               .port_ops       = &ahci_sb600_ops,
+       },
+       /* board_ahci_mcp65 */
+       {
+               AHCI_HFLAGS     (AHCI_HFLAG_YES_NCQ),
                .flags          = AHCI_FLAG_COMMON,
                .pio_mask       = 0x1f, /* pio0-4 */
                .udma_mask      = ATA_UDMA6,
@@ -438,14 +455,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
 
        /* NVIDIA */
-       { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci },            /* MCP65 */
-       { 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, 0x044c), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 },      /* MCP65 */
+       { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 },      /* MCP65 */
        { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci },            /* MCP67 */
        { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci },            /* MCP67 */
        { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci },            /* MCP67 */
@@ -502,10 +519,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(NVIDIA, 0x0bcd), board_ahci },            /* MCP7B */
        { PCI_VDEVICE(NVIDIA, 0x0bce), board_ahci },            /* MCP7B */
        { PCI_VDEVICE(NVIDIA, 0x0bcf), board_ahci },            /* MCP7B */
-       { PCI_VDEVICE(NVIDIA, 0x0bd0), board_ahci },            /* MCP7B */
-       { PCI_VDEVICE(NVIDIA, 0x0bd1), board_ahci },            /* MCP7B */
-       { PCI_VDEVICE(NVIDIA, 0x0bd2), board_ahci },            /* MCP7B */
-       { PCI_VDEVICE(NVIDIA, 0x0bd3), board_ahci },            /* MCP7B */
+       { PCI_VDEVICE(NVIDIA, 0x0bc4), board_ahci },            /* MCP7B */
+       { PCI_VDEVICE(NVIDIA, 0x0bc5), board_ahci },            /* MCP7B */
+       { PCI_VDEVICE(NVIDIA, 0x0bc6), board_ahci },            /* MCP7B */
+       { PCI_VDEVICE(NVIDIA, 0x0bc7), board_ahci },            /* MCP7B */
 
        /* SiS */
        { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */
@@ -624,12 +641,26 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
                cap &= ~HOST_CAP_NCQ;
        }
 
+       if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
+               dev_printk(KERN_INFO, &pdev->dev,
+                          "controller can do NCQ, turning on CAP_NCQ\n");
+               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;
        }
 
+       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
@@ -1262,19 +1293,11 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
        return 0;
 }
 
-static int ahci_check_ready(struct ata_link *link)
-{
-       void __iomem *port_mmio = ahci_port_base(link->ap);
-       u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
-
-       return ata_check_ready(status);
-}
-
-static int ahci_softreset(struct ata_link *link, unsigned int *class,
-                         unsigned long deadline)
+static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
+                            int pmp, unsigned long deadline,
+                            int (*check_ready)(struct ata_link *link))
 {
        struct ata_port *ap = link->ap;
-       int pmp = sata_srst_pmp(link);
        const char *reason = NULL;
        unsigned long now, msecs;
        struct ata_taskfile tf;
@@ -1312,7 +1335,7 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
        ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);
 
        /* wait for link to become ready */
-       rc = ata_wait_after_reset(link, deadline, ahci_check_ready);
+       rc = ata_wait_after_reset(link, deadline, check_ready);
        /* link occupied, -ENODEV too is an error */
        if (rc) {
                reason = "device not ready";
@@ -1328,6 +1351,72 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
        return rc;
 }
 
+static int ahci_check_ready(struct ata_link *link)
+{
+       void __iomem *port_mmio = ahci_port_base(link->ap);
+       u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
+
+       return ata_check_ready(status);
+}
+
+static int ahci_softreset(struct ata_link *link, unsigned int *class,
+                         unsigned long deadline)
+{
+       int pmp = sata_srst_pmp(link);
+
+       DPRINTK("ENTER\n");
+
+       return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
+}
+
+static int ahci_sb600_check_ready(struct ata_link *link)
+{
+       void __iomem *port_mmio = ahci_port_base(link->ap);
+       u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
+       u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
+
+       /*
+        * There is no need to check TFDATA if BAD PMP is found due to HW bug,
+        * which can save timeout delay.
+        */
+       if (irq_status & PORT_IRQ_BAD_PMP)
+               return -EIO;
+
+       return ata_check_ready(status);
+}
+
+static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
+                               unsigned long deadline)
+{
+       struct ata_port *ap = link->ap;
+       void __iomem *port_mmio = ahci_port_base(ap);
+       int pmp = sata_srst_pmp(link);
+       int rc;
+       u32 irq_sts;
+
+       DPRINTK("ENTER\n");
+
+       rc = ahci_do_softreset(link, class, pmp, deadline,
+                              ahci_sb600_check_ready);
+
+       /*
+        * Soft reset fails on some ATI chips with IPMS set when PMP
+        * is enabled but SATA HDD/ODD is connected to SATA port,
+        * do soft reset again to port 0.
+        */
+       if (rc == -EIO) {
+               irq_sts = readl(port_mmio + PORT_IRQ_STAT);
+               if (irq_sts & PORT_IRQ_BAD_PMP) {
+                       ata_link_printk(link, KERN_WARNING,
+                                       "failed due to HW bug, retry pmp=0\n");
+                       rc = ahci_do_softreset(link, class, 0, deadline,
+                                              ahci_check_ready);
+               }
+       }
+
+       return rc;
+}
+
 static int ahci_hardreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline)
 {
@@ -2118,7 +2207,8 @@ static void ahci_p5wdh_workaround(struct ata_host *host)
 static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version;
-       struct ata_port_info pi = ahci_port_info[ent->driver_data];
+       unsigned int board_id = ent->driver_data;
+       struct ata_port_info pi = ahci_port_info[board_id];
        const struct ata_port_info *ppi[] = { &pi, NULL };
        struct device *dev = &pdev->dev;
        struct ahci_host_priv *hpriv;
@@ -2167,6 +2257,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                return -ENOMEM;
        hpriv->flags |= (unsigned long)pi.private_data;
 
+       /* MCP65 revision A1 and A2 can't do MSI */
+       if (board_id == board_ahci_mcp65 &&
+           (pdev->revision == 0xa1 || pdev->revision == 0xa2))
+               hpriv->flags |= AHCI_HFLAG_NO_MSI;
+
        if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
                pci_intx(pdev, 1);