PCI: disable pci_find_device warnings when deprecated pci functions are enabled
[safe/jmp/linux-2.6] / drivers / ata / ahci.c
index 6ca4bc0..fe3eba5 100644 (file)
@@ -77,8 +77,6 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
                              size_t size);
 static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
                                        ssize_t size);
-#define MAX_SLOTS 8
-#define MAX_RETRY 15
 
 enum {
        AHCI_PCI_BAR            = 5,
@@ -114,6 +112,7 @@ enum {
        board_ahci_sb700        = 5, /* for SB700 and SB800 */
        board_ahci_mcp65        = 6,
        board_ahci_nopmp        = 7,
+       board_ahci_yesncq       = 8,
 
        /* global controller registers */
        HOST_CAP                = 0x00, /* host capabilities */
@@ -219,6 +218,9 @@ enum {
        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 */
+       AHCI_HFLAG_NO_SUSPEND           = (1 << 10), /* don't suspend */
+       AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as
+                                                       link offline */
 
        /* ap->flags bits */
 
@@ -229,6 +231,10 @@ enum {
 
        ICH_MAP                         = 0x90, /* ICH MAP register */
 
+       /* em constants */
+       EM_MAX_SLOTS                    = 8,
+       EM_MAX_RETRY                    = 5,
+
        /* em_ctl bits */
        EM_CTL_RST                      = (1 << 9), /* Reset */
        EM_CTL_TM                       = (1 << 8), /* Transmit Message */
@@ -280,8 +286,8 @@ struct ahci_port_priv {
        unsigned int            ncq_saw_dmas:1;
        unsigned int            ncq_saw_sdb:1;
        u32                     intr_mask;      /* interrupts to enable */
-       struct ahci_em_priv     em_priv[MAX_SLOTS];/* enclosure management info
-                                                * per PM slot */
+       /* enclosure management info per PM slot */
+       struct ahci_em_priv     em_priv[EM_MAX_SLOTS];
 };
 
 static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
@@ -311,7 +317,6 @@ static void ahci_error_handler(struct ata_port *ap);
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
 static int ahci_port_resume(struct ata_port *ap);
 static void ahci_dev_config(struct ata_device *dev);
-static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl);
 static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
                               u32 opts);
 #ifdef CONFIG_PM
@@ -402,14 +407,14 @@ static struct ata_port_operations ahci_sb600_ops = {
 #define AHCI_HFLAGS(flags)     .private_data   = (void *)(flags)
 
 static const struct ata_port_info ahci_port_info[] = {
-       /* board_ahci */
+       [board_ahci] =
        {
                .flags          = AHCI_FLAG_COMMON,
                .pio_mask       = ATA_PIO4,
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
-       /* board_ahci_vt8251 */
+       [board_ahci_vt8251] =
        {
                AHCI_HFLAGS     (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),
                .flags          = AHCI_FLAG_COMMON,
@@ -417,7 +422,7 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_vt8251_ops,
        },
-       /* board_ahci_ign_iferr */
+       [board_ahci_ign_iferr] =
        {
                AHCI_HFLAGS     (AHCI_HFLAG_IGN_IRQ_IF_ERR),
                .flags          = AHCI_FLAG_COMMON,
@@ -425,17 +430,16 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
-       /* board_ahci_sb600 */
+       [board_ahci_sb600] =
        {
                AHCI_HFLAGS     (AHCI_HFLAG_IGN_SERR_INTERNAL |
-                                AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |
-                                AHCI_HFLAG_SECT255),
+                                AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255),
                .flags          = AHCI_FLAG_COMMON,
                .pio_mask       = ATA_PIO4,
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_sb600_ops,
        },
-       /* board_ahci_mv */
+       [board_ahci_mv] =
        {
                AHCI_HFLAGS     (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI |
                                 AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP),
@@ -445,7 +449,7 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
-       /* board_ahci_sb700, for SB700 and SB800 */
+       [board_ahci_sb700] =    /* for SB700 and SB800 */
        {
                AHCI_HFLAGS     (AHCI_HFLAG_IGN_SERR_INTERNAL),
                .flags          = AHCI_FLAG_COMMON,
@@ -453,7 +457,7 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_sb600_ops,
        },
-       /* board_ahci_mcp65 */
+       [board_ahci_mcp65] =
        {
                AHCI_HFLAGS     (AHCI_HFLAG_YES_NCQ),
                .flags          = AHCI_FLAG_COMMON,
@@ -461,7 +465,7 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
-       /* board_ahci_nopmp */
+       [board_ahci_nopmp] =
        {
                AHCI_HFLAGS     (AHCI_HFLAG_NO_PMP),
                .flags          = AHCI_FLAG_COMMON,
@@ -469,6 +473,14 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
+       /* board_ahci_yesncq */
+       {
+               AHCI_HFLAGS     (AHCI_HFLAG_YES_NCQ),
+               .flags          = AHCI_FLAG_COMMON,
+               .pio_mask       = ATA_PIO4,
+               .udma_mask      = ATA_UDMA6,
+               .port_ops       = &ahci_ops,
+       },
 };
 
 static const struct pci_device_id ahci_pci_tbl[] = {
@@ -503,11 +515,16 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
        { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
        { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
+       { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
        { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
+       { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
+       { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
        { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
        { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
+       { PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */
        { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
        { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
+       { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
 
        /* JMicron 360/1/3/5/6, match class to avoid IDE function */
        { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -535,30 +552,30 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { 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 */
-       { 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 */
-       { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci },            /* MCP73 */
-       { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci },            /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq },     /* MCP67 */
+       { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_yesncq },     /* MCP73 */
+       { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_yesncq },     /* MCP73 */
        { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci },            /* MCP77 */
        { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci },            /* MCP77 */
        { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci },            /* MCP77 */
@@ -1131,12 +1148,12 @@ static void ahci_start_port(struct ata_port *ap)
                        emp = &pp->em_priv[link->pmp];
 
                        /* EM Transmit bit maybe busy during init */
-                       for (i = 0; i < MAX_RETRY; i++) {
+                       for (i = 0; i < EM_MAX_RETRY; i++) {
                                rc = ahci_transmit_led_message(ap,
                                                               emp->led_state,
                                                               4);
                                if (rc == -EBUSY)
-                                       udelay(100);
+                                       msleep(1);
                                else
                                        break;
                        }
@@ -1330,7 +1347,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
 
        /* get the slot number from the message */
        pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
-       if (pmp < MAX_SLOTS)
+       if (pmp < EM_MAX_SLOTS)
                emp = &pp->em_priv[pmp];
        else
                return -EINVAL;
@@ -1398,7 +1415,7 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
 
        /* get the slot number from the message */
        pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
-       if (pmp < MAX_SLOTS)
+       if (pmp < EM_MAX_SLOTS)
                emp = &pp->em_priv[pmp];
        else
                return -EINVAL;
@@ -1648,6 +1665,7 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
                             int (*check_ready)(struct ata_link *link))
 {
        struct ata_port *ap = link->ap;
+       struct ahci_host_priv *hpriv = ap->host->private_data;
        const char *reason = NULL;
        unsigned long now, msecs;
        struct ata_taskfile tf;
@@ -1686,12 +1704,21 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
 
        /* wait for link to become ready */
        rc = ata_wait_after_reset(link, deadline, check_ready);
-       /* link occupied, -ENODEV too is an error */
-       if (rc) {
+       if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) {
+               /*
+                * Workaround for cases where link online status can't
+                * be trusted.  Treat device readiness timeout as link
+                * offline.
+                */
+               ata_link_printk(link, KERN_INFO,
+                               "device not ready, treating as offline\n");
+               *class = ATA_DEV_NONE;
+       } else if (rc) {
+               /* link occupied, -ENODEV too is an error */
                reason = "device not ready";
                goto fail;
-       }
-       *class = ahci_dev_classify(ap);
+       } else
+               *class = ahci_dev_classify(ap);
 
        DPRINTK("EXIT, class=%u\n", *class);
        return 0;
@@ -1758,7 +1785,8 @@ static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
                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");
+                                       "applying SB600 PMP SRST workaround "
+                                       "and retrying\n");
                        rc = ahci_do_softreset(link, class, 0, deadline,
                                               ahci_check_ready);
                }
@@ -2307,9 +2335,17 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
 static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
 {
        struct ata_host *host = dev_get_drvdata(&pdev->dev);
+       struct ahci_host_priv *hpriv = host->private_data;
        void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
        u32 ctl;
 
+       if (mesg.event & PM_EVENT_SUSPEND &&
+           hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "BIOS update required for suspend/resume\n");
+               return -EIO;
+       }
+
        if (mesg.event & PM_EVENT_SLEEP) {
                /* AHCI spec rev1.1 section 8.3.3:
                 * Software must disable interrupts prior to requesting a
@@ -2418,10 +2454,10 @@ static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
        int rc;
 
        if (using_dac &&
-           !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
-               rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+           !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
                if (rc) {
-                       rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+                       rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
                        if (rc) {
                                dev_printk(KERN_ERR, &pdev->dev,
                                           "64-bit DMA enable failed\n");
@@ -2429,13 +2465,13 @@ static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
                        }
                }
        } else {
-               rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+               rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (rc) {
                        dev_printk(KERN_ERR, &pdev->dev,
                                   "32-bit DMA enable failed\n");
                        return rc;
                }
-               rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
                if (rc) {
                        dev_printk(KERN_ERR, &pdev->dev,
                                   "32-bit consistent DMA enable failed\n");
@@ -2566,6 +2602,51 @@ static void ahci_p5wdh_workaround(struct ata_host *host)
        }
 }
 
+/*
+ * SB600 ahci controller on ASUS M2A-VM can't do 64bit DMA with older
+ * BIOS.  The oldest version known to be broken is 0901 and working is
+ * 1501 which was released on 2007-10-26.  Force 32bit DMA on anything
+ * older than 1501.  Please read bko#9412 for more info.
+ */
+static bool ahci_asus_m2a_vm_32bit_only(struct pci_dev *pdev)
+{
+       static const struct dmi_system_id sysids[] = {
+               {
+                       .ident = "ASUS M2A-VM",
+                       .matches = {
+                               DMI_MATCH(DMI_BOARD_VENDOR,
+                                         "ASUSTeK Computer INC."),
+                               DMI_MATCH(DMI_BOARD_NAME, "M2A-VM"),
+                       },
+               },
+               { }
+       };
+       const char *cutoff_mmdd = "10/26";
+       const char *date;
+       int year;
+
+       if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x12, 0) ||
+           !dmi_check_system(sysids))
+               return false;
+
+       /*
+        * Argh.... both version and date are free form strings.
+        * Let's hope they're using the same date format across
+        * different versions.
+        */
+       date = dmi_get_system_info(DMI_BIOS_DATE);
+       year = dmi_get_year(DMI_BIOS_DATE);
+       if (date && strlen(date) >= 10 && date[2] == '/' && date[5] == '/' &&
+           (year > 2007 ||
+            (year == 2007 && strncmp(date, cutoff_mmdd, 5) >= 0)))
+               return false;
+
+       dev_printk(KERN_WARNING, &pdev->dev, "ASUS M2A-VM: BIOS too old, "
+                  "forcing 32bit DMA, update BIOS\n");
+
+       return true;
+}
+
 static bool ahci_broken_system_poweroff(struct pci_dev *pdev)
 {
        static const struct dmi_system_id broken_systems[] = {
@@ -2601,6 +2682,113 @@ static bool ahci_broken_system_poweroff(struct pci_dev *pdev)
        return false;
 }
 
+static bool ahci_broken_suspend(struct pci_dev *pdev)
+{
+       static const struct dmi_system_id sysids[] = {
+               /*
+                * On HP dv[4-6] and HDX18 with earlier BIOSen, link
+                * to the harddisk doesn't become online after
+                * resuming from STR.  Warn and fail suspend.
+                */
+               {
+                       .ident = "dv4",
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                               DMI_MATCH(DMI_PRODUCT_NAME,
+                                         "HP Pavilion dv4 Notebook PC"),
+                       },
+                       .driver_data = "F.30", /* cutoff BIOS version */
+               },
+               {
+                       .ident = "dv5",
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                               DMI_MATCH(DMI_PRODUCT_NAME,
+                                         "HP Pavilion dv5 Notebook PC"),
+                       },
+                       .driver_data = "F.16", /* cutoff BIOS version */
+               },
+               {
+                       .ident = "dv6",
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                               DMI_MATCH(DMI_PRODUCT_NAME,
+                                         "HP Pavilion dv6 Notebook PC"),
+                       },
+                       .driver_data = "F.21",  /* cutoff BIOS version */
+               },
+               {
+                       .ident = "HDX18",
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                               DMI_MATCH(DMI_PRODUCT_NAME,
+                                         "HP HDX18 Notebook PC"),
+                       },
+                       .driver_data = "F.23",  /* cutoff BIOS version */
+               },
+               { }     /* terminate list */
+       };
+       const struct dmi_system_id *dmi = dmi_first_match(sysids);
+       const char *ver;
+
+       if (!dmi || pdev->bus->number || pdev->devfn != PCI_DEVFN(0x1f, 2))
+               return false;
+
+       ver = dmi_get_system_info(DMI_BIOS_VERSION);
+
+       return !ver || strcmp(ver, dmi->driver_data) < 0;
+}
+
+static bool ahci_broken_online(struct pci_dev *pdev)
+{
+#define ENCODE_BUSDEVFN(bus, slot, func)                       \
+       (void *)(unsigned long)(((bus) << 8) | PCI_DEVFN((slot), (func)))
+       static const struct dmi_system_id sysids[] = {
+               /*
+                * There are several gigabyte boards which use
+                * SIMG5723s configured as hardware RAID.  Certain
+                * 5723 firmware revisions shipped there keep the link
+                * online but fail to answer properly to SRST or
+                * IDENTIFY when no device is attached downstream
+                * causing libata to retry quite a few times leading
+                * to excessive detection delay.
+                *
+                * As these firmwares respond to the second reset try
+                * with invalid device signature, considering unknown
+                * sig as offline works around the problem acceptably.
+                */
+               {
+                       .ident = "EP45-DQ6",
+                       .matches = {
+                               DMI_MATCH(DMI_BOARD_VENDOR,
+                                         "Gigabyte Technology Co., Ltd."),
+                               DMI_MATCH(DMI_BOARD_NAME, "EP45-DQ6"),
+                       },
+                       .driver_data = ENCODE_BUSDEVFN(0x0a, 0x00, 0),
+               },
+               {
+                       .ident = "EP45-DS5",
+                       .matches = {
+                               DMI_MATCH(DMI_BOARD_VENDOR,
+                                         "Gigabyte Technology Co., Ltd."),
+                               DMI_MATCH(DMI_BOARD_NAME, "EP45-DS5"),
+                       },
+                       .driver_data = ENCODE_BUSDEVFN(0x03, 0x00, 0),
+               },
+               { }     /* terminate list */
+       };
+#undef ENCODE_BUSDEVFN
+       const struct dmi_system_id *dmi = dmi_first_match(sysids);
+       unsigned int val;
+
+       if (!dmi)
+               return false;
+
+       val = (unsigned long)dmi->driver_data;
+
+       return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);
+}
+
 static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version;
@@ -2669,6 +2857,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (board_id == board_ahci_sb700 && pdev->revision >= 0x40)
                hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL;
 
+       /* apply ASUS M2A_VM quirk */
+       if (ahci_asus_m2a_vm_32bit_only(pdev))
+               hpriv->flags |= AHCI_HFLAG_32BIT_ONLY;
+
        if (!(hpriv->flags & AHCI_HFLAG_NO_MSI))
                pci_enable_msi(pdev);
 
@@ -2706,6 +2898,18 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                        "quirky BIOS, skipping spindown on poweroff\n");
        }
 
+       if (ahci_broken_suspend(pdev)) {
+               hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
+               dev_printk(KERN_WARNING, &pdev->dev,
+                          "BIOS update required for suspend/resume\n");
+       }
+
+       if (ahci_broken_online(pdev)) {
+               hpriv->flags |= AHCI_HFLAG_SRST_TOUT_IS_OFFLINE;
+               dev_info(&pdev->dev,
+                        "online status unreliable, applying workaround\n");
+       }
+
        /* CAP.NP sometimes indicate the index of the last enabled
         * port, at other times, that of the last possible port, so
         * determining the maximum port number requires looking at