From b8b275efc28e34f9b1d1e382d0b02dfa381b2a79 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 10 Jul 2007 15:55:43 +0900 Subject: ata_piix: fix suspend/resume for some TOSHIBA laptops ACPI implementations in several TOSHIBA laptops are weird and burn cpu cycles for tens of seconds while trying to suspend if the PCI device for the ATA controller is disabled when the ACPI suspend is called. This patch uses DMI to match those machines and bypass device disable on those machines during suspend. As the device needs to be put into enabled state on resume without affecting PCI enable count, matching resume callback uses __pci_reenable_device(). This bug is reported in bugzilla bug 7780. http://bugzilla.kernel.org/show_bug.cgi?id=7780 Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ata_piix.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) (limited to 'drivers/ata') diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index d9fa329fd157..ad070861bb53 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -91,6 +91,7 @@ #include #include #include +#include #define DRV_NAME "ata_piix" #define DRV_VERSION "2.11" @@ -140,6 +141,9 @@ enum { RV = -3, /* reserved */ PIIX_AHCI_DEVICE = 6, + + /* host->flags bits */ + PIIX_HOST_BROKEN_SUSPEND = (1 << 24), }; struct piix_map_db { @@ -159,6 +163,10 @@ static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev); static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev); static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev); static int ich_pata_cable_detect(struct ata_port *ap); +#ifdef CONFIG_PM +static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); +static int piix_pci_device_resume(struct pci_dev *pdev); +#endif static unsigned int in_module_init = 1; @@ -255,8 +263,8 @@ static struct pci_driver piix_pci_driver = { .probe = piix_init_one, .remove = ata_pci_remove_one, #ifdef CONFIG_PM - .suspend = ata_pci_device_suspend, - .resume = ata_pci_device_resume, + .suspend = piix_pci_device_suspend, + .resume = piix_pci_device_resume, #endif }; @@ -881,6 +889,107 @@ static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev) do_pata_set_dmamode(ap, adev, 1); } +#ifdef CONFIG_PM +static struct dmi_system_id piix_broken_suspend_dmi_table[] = { + { + .ident = "TECRA M5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M5"), + }, + }, + { + .ident = "Satellite U200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U200"), + }, + }, + { + .ident = "Satellite U205", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U205"), + }, + }, + { + .ident = "Portege M500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M500"), + }, + }, + { } +}; + +static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + unsigned long flags; + int rc = 0; + + rc = ata_host_suspend(host, mesg); + if (rc) + return rc; + + /* Some braindamaged ACPI suspend implementations expect the + * controller to be awake on entry; otherwise, it burns cpu + * cycles and power trying to do something to the sleeping + * beauty. + */ + if (dmi_check_system(piix_broken_suspend_dmi_table) && + mesg.event == PM_EVENT_SUSPEND) { + pci_save_state(pdev); + + /* mark its power state as "unknown", since we don't + * know if e.g. the BIOS will change its device state + * when we suspend. + */ + if (pdev->current_state == PCI_D0) + pdev->current_state = PCI_UNKNOWN; + + /* tell resume that it's waking up from broken suspend */ + spin_lock_irqsave(&host->lock, flags); + host->flags |= PIIX_HOST_BROKEN_SUSPEND; + spin_unlock_irqrestore(&host->lock, flags); + } else + ata_pci_device_do_suspend(pdev, mesg); + + return 0; +} + +static int piix_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + unsigned long flags; + int rc; + + if (host->flags & PIIX_HOST_BROKEN_SUSPEND) { + spin_lock_irqsave(&host->lock, flags); + host->flags &= ~PIIX_HOST_BROKEN_SUSPEND; + spin_unlock_irqrestore(&host->lock, flags); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + /* PCI device wasn't disabled during suspend. Use + * __pci_reenable_device() to avoid affecting the + * enable count. + */ + rc = __pci_reenable_device(pdev); + if (rc) + dev_printk(KERN_ERR, &pdev->dev, "failed to enable " + "device after resume (%d)\n", rc); + } else + rc = ata_pci_device_do_resume(pdev); + + if (rc == 0) + ata_host_resume(host); + + return rc; +} +#endif + #define AHCI_PCI_BAR 5 #define AHCI_GLOBAL_CTL 0x04 #define AHCI_ENABLE (1 << 31) -- cgit v1.2.3 From 5c8d52015c09b42632a73896f8a80cff64c5c1d1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 20 Jul 2007 15:34:49 +0100 Subject: pata_ali: Correct HP detect Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/pata_ali.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/ata') diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index 010436795d20..e8a28e94fe47 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -45,7 +45,7 @@ static struct dmi_system_id cable_dmi_table[] = { .ident = "HP Pavilion N5430", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_BOARD_NAME, "OmniBook N32N-736"), + DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"), }, }, { } -- cgit v1.2.3 From 64a8170975947978ea4094dd6c4b1f41119d6ae6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 24 Jul 2007 15:17:48 +0100 Subject: pata_hpt37x: Fix 2.6.22 clock PLL regression Just one version of Linux ago The PLL code broke - oh no! But set the right mode And fix up the code Makes the PLL timing sync go [whatever happened to the sailor from Nantucket, hero of many limericks? -jg] Closes-bug: #8791 Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/ata/pata_hpt37x.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/ata') diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index b0af65aadde3..84d9c5568567 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -26,7 +26,7 @@ #include #define DRV_NAME "pata_hpt37x" -#define DRV_VERSION "0.6.6" +#define DRV_VERSION "0.6.7" struct hpt_clock { u8 xfer_speed; @@ -1103,17 +1103,17 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) /* Select the DPLL clock. */ pci_write_config_byte(dev, 0x5b, 0x21); - pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low); + pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low | 0x100); for(adjust = 0; adjust < 8; adjust++) { if (hpt37x_calibrate_dpll(dev)) break; /* See if it'll settle at a fractionally different clock */ - if ((adjust & 3) == 3) { - f_low --; - f_high ++; - } - pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low); + if (adjust & 1) + f_low -= adjust >> 1; + else + f_high += adjust >> 1; + pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low | 0x100); } if (adjust == 8) { printk(KERN_WARNING "hpt37x: DPLL did not stabilize.\n"); -- cgit v1.2.3