diff options
| -rw-r--r-- | drivers/ata/ahci.c | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 08186ecbaf8d..6b91c26a4635 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
| @@ -220,6 +220,7 @@ enum { | |||
| 220 | AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ | 220 | AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ |
| 221 | AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ | 221 | AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ |
| 222 | AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */ | 222 | AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */ |
| 223 | AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */ | ||
| 223 | 224 | ||
| 224 | /* ap->flags bits */ | 225 | /* ap->flags bits */ |
| 225 | 226 | ||
| @@ -2316,9 +2317,17 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) | |||
| 2316 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) | 2317 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) |
| 2317 | { | 2318 | { |
| 2318 | struct ata_host *host = dev_get_drvdata(&pdev->dev); | 2319 | struct ata_host *host = dev_get_drvdata(&pdev->dev); |
| 2320 | struct ahci_host_priv *hpriv = host->private_data; | ||
| 2319 | void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; | 2321 | void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; |
| 2320 | u32 ctl; | 2322 | u32 ctl; |
| 2321 | 2323 | ||
| 2324 | if (mesg.event & PM_EVENT_SUSPEND && | ||
| 2325 | hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { | ||
| 2326 | dev_printk(KERN_ERR, &pdev->dev, | ||
| 2327 | "BIOS update required for suspend/resume\n"); | ||
| 2328 | return -EIO; | ||
| 2329 | } | ||
| 2330 | |||
| 2322 | if (mesg.event & PM_EVENT_SLEEP) { | 2331 | if (mesg.event & PM_EVENT_SLEEP) { |
| 2323 | /* AHCI spec rev1.1 section 8.3.3: | 2332 | /* AHCI spec rev1.1 section 8.3.3: |
| 2324 | * Software must disable interrupts prior to requesting a | 2333 | * Software must disable interrupts prior to requesting a |
| @@ -2610,6 +2619,63 @@ static bool ahci_broken_system_poweroff(struct pci_dev *pdev) | |||
| 2610 | return false; | 2619 | return false; |
| 2611 | } | 2620 | } |
| 2612 | 2621 | ||
| 2622 | static bool ahci_broken_suspend(struct pci_dev *pdev) | ||
| 2623 | { | ||
| 2624 | static const struct dmi_system_id sysids[] = { | ||
| 2625 | /* | ||
| 2626 | * On HP dv[4-6] and HDX18 with earlier BIOSen, link | ||
| 2627 | * to the harddisk doesn't become online after | ||
| 2628 | * resuming from STR. Warn and fail suspend. | ||
| 2629 | */ | ||
| 2630 | { | ||
| 2631 | .ident = "dv4", | ||
| 2632 | .matches = { | ||
| 2633 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
| 2634 | DMI_MATCH(DMI_PRODUCT_NAME, | ||
| 2635 | "HP Pavilion dv4 Notebook PC"), | ||
| 2636 | }, | ||
| 2637 | .driver_data = "F.30", /* cutoff BIOS version */ | ||
| 2638 | }, | ||
| 2639 | { | ||
| 2640 | .ident = "dv5", | ||
| 2641 | .matches = { | ||
| 2642 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
| 2643 | DMI_MATCH(DMI_PRODUCT_NAME, | ||
| 2644 | "HP Pavilion dv5 Notebook PC"), | ||
| 2645 | }, | ||
| 2646 | .driver_data = "F.16", /* cutoff BIOS version */ | ||
| 2647 | }, | ||
| 2648 | { | ||
| 2649 | .ident = "dv6", | ||
| 2650 | .matches = { | ||
| 2651 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
| 2652 | DMI_MATCH(DMI_PRODUCT_NAME, | ||
| 2653 | "HP Pavilion dv6 Notebook PC"), | ||
| 2654 | }, | ||
| 2655 | .driver_data = "F.21", /* cutoff BIOS version */ | ||
| 2656 | }, | ||
| 2657 | { | ||
| 2658 | .ident = "HDX18", | ||
| 2659 | .matches = { | ||
| 2660 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
| 2661 | DMI_MATCH(DMI_PRODUCT_NAME, | ||
| 2662 | "HP HDX18 Notebook PC"), | ||
| 2663 | }, | ||
| 2664 | .driver_data = "F.23", /* cutoff BIOS version */ | ||
| 2665 | }, | ||
| 2666 | { } /* terminate list */ | ||
| 2667 | }; | ||
| 2668 | const struct dmi_system_id *dmi = dmi_first_match(sysids); | ||
| 2669 | const char *ver; | ||
| 2670 | |||
| 2671 | if (!dmi || pdev->bus->number || pdev->devfn != PCI_DEVFN(0x1f, 2)) | ||
| 2672 | return false; | ||
| 2673 | |||
| 2674 | ver = dmi_get_system_info(DMI_BIOS_VERSION); | ||
| 2675 | |||
| 2676 | return !ver || strcmp(ver, dmi->driver_data) < 0; | ||
| 2677 | } | ||
| 2678 | |||
| 2613 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | 2679 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) |
| 2614 | { | 2680 | { |
| 2615 | static int printed_version; | 2681 | static int printed_version; |
| @@ -2715,6 +2781,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 2715 | "quirky BIOS, skipping spindown on poweroff\n"); | 2781 | "quirky BIOS, skipping spindown on poweroff\n"); |
| 2716 | } | 2782 | } |
| 2717 | 2783 | ||
| 2784 | if (ahci_broken_suspend(pdev)) { | ||
| 2785 | hpriv->flags |= AHCI_HFLAG_NO_SUSPEND; | ||
| 2786 | dev_printk(KERN_WARNING, &pdev->dev, | ||
| 2787 | "BIOS update required for suspend/resume\n"); | ||
| 2788 | } | ||
| 2789 | |||
| 2718 | /* CAP.NP sometimes indicate the index of the last enabled | 2790 | /* CAP.NP sometimes indicate the index of the last enabled |
| 2719 | * port, at other times, that of the last possible port, so | 2791 | * port, at other times, that of the last possible port, so |
| 2720 | * determining the maximum port number requires looking at | 2792 | * determining the maximum port number requires looking at |
