From: Tejun Heo <tj@kernel.org>
To: Pedro Ribeiro <pedrib@gmail.com>
Cc: linux-ide@vger.kernel.org
Subject: Re: Long delays while hibernating
Date: Fri, 25 Dec 2009 23:56:44 +0900 [thread overview]
Message-ID: <4B34D2AC.8040908@kernel.org> (raw)
In-Reply-To: <74fd948d0912240759n72e8c141y6b75f1ee7caadf60@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 303 bytes --]
On 12/25/2009 12:59 AM, Pedro Ribeiro wrote:
> I just noticed that this patch is for ata_piix. I don't have this
> module loaded, as I have an ICH9 chip. The module used is ahci. Here
> is the output of lsmod:
> http://pastebin.com/m5468985b
Oh... Can you please try this one then?
Thanks.
--
tejun
[-- Attachment #2: insomnia.patch --]
[-- Type: text/x-patch, Size: 8910 bytes --]
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index b8bea10..dadf8b2 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -2980,6 +2980,35 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
{}
#endif
+#ifdef CONFIG_PM
+static void ahci_insomnia_workaround(struct ata_host *host)
+{
+ static const struct dmi_system_id sysids[] = {
+ {
+ .ident = "ThinkPad T400",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "7417PLU"),
+ },
+ },
+
+ { } /* terminate list */
+ };
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+
+ if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x1f, 2) ||
+ !dmi_check_system(sysids))
+ return;
+
+ dev_printk(KERN_INFO, &pdev->dev, "BIOS may access controller "
+ "after suspend, setting INSOMNIA\n");
+ host->flags |= ATA_HOST_INSOMNIA;
+}
+#else
+static inline void ahci_insomnia_workaround(struct ata_host *host)
+{}
+#endif
+
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
@@ -3156,6 +3185,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* apply gtf filter quirk */
ahci_gtf_filter_workaround(host);
+ /* need to set insomnia? */
+ ahci_insomnia_workaround(host);
+
/* initialize adapter */
rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64);
if (rc)
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 19136a7..5ef948d 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -125,9 +125,6 @@ enum {
RV = -3, /* reserved */
PIIX_AHCI_DEVICE = 6,
-
- /* host->flags bits */
- PIIX_HOST_BROKEN_SUSPEND = (1 << 24),
};
enum piix_controller_ids {
@@ -173,10 +170,6 @@ static int piix_sidpr_scr_read(struct ata_link *link,
unsigned int reg, u32 *val);
static int piix_sidpr_scr_write(struct ata_link *link,
unsigned int reg, u32 val);
-#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;
@@ -300,8 +293,8 @@ static struct pci_driver piix_pci_driver = {
.probe = piix_init_one,
.remove = piix_remove_one,
#ifdef CONFIG_PM
- .suspend = piix_pci_device_suspend,
- .resume = piix_pci_device_resume,
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
#endif
};
@@ -963,7 +956,7 @@ static int piix_sidpr_scr_write(struct ata_link *link,
}
#ifdef CONFIG_PM
-static int piix_broken_suspend(void)
+static void piix_insomnia_workaround(struct ata_host *host)
{
static const struct dmi_system_id sysids[] = {
{
@@ -1091,14 +1084,15 @@ static int piix_broken_suspend(void)
static const char *oemstrs[] = {
"Tecra M3,",
};
+ struct pci_dev *pdev = to_pci_dev(host->dev);
int i;
if (dmi_check_system(sysids))
- return 1;
+ goto apply;
for (i = 0; i < ARRAY_SIZE(oemstrs); i++)
if (dmi_find_device(DMI_DEV_TYPE_OEM_STRING, oemstrs[i], NULL))
- return 1;
+ goto apply;
/* TECRA M4 sometimes forgets its identify and reports bogus
* DMI information. As the bogus information is a bit
@@ -1113,76 +1107,18 @@ static int piix_broken_suspend(void)
dmi_match(DMI_BOARD_VENDOR, "TOSHIBA") &&
dmi_match(DMI_BOARD_NAME, "Portable PC") &&
dmi_match(DMI_BOARD_VERSION, "Version A0"))
- return 1;
-
- return 0;
-}
-
-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 (piix_broken_suspend() && (mesg.event & PM_EVENT_SLEEP)) {
- 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);
+ goto apply;
- /* 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);
+ return;
- if (rc == 0)
- ata_host_resume(host);
-
- return rc;
+apply:
+ dev_printk(KERN_INFO, &pdev->dev, "BIOS may access controller "
+ "after suspend, setting INSOMNIA\n");
+ host->flags |= ATA_HOST_INSOMNIA;
}
+#else
+static inline void piix_insomnia_workaround(struct ata_host *host)
+{ }
#endif
static u8 piix_vmw_bmdma_status(struct ata_port *ap)
@@ -1604,6 +1540,9 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
host->ports[1]->mwdma_mask = 0;
host->ports[1]->udma_mask = 0;
}
+
+ piix_insomnia_workaround(host);
+
host->flags |= ATA_HOST_PARALLEL_SCAN;
pci_set_master(pdev);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 22ff51b..3c7a1f3 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6381,21 +6381,62 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
#ifdef CONFIG_PM
void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ unsigned long flags;
+
pci_save_state(pdev);
- pci_disable_device(pdev);
- if (mesg.event & PM_EVENT_SLEEP)
- pci_set_power_state(pdev, PCI_D3hot);
+ /*
+ * 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 ((host->flags & ATA_HOST_INSOMNIA) &&
+ (mesg.event & PM_EVENT_SLEEP)) {
+ /*
+ * 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 insomnia */
+ spin_lock_irqsave(&host->lock, flags);
+ host->flags |= ATA_HOST_IN_INSOMNIA;
+ spin_unlock_irqrestore(&host->lock, flags);
+ } else {
+ pci_disable_device(pdev);
+
+ if (mesg.event & PM_EVENT_SLEEP)
+ pci_set_power_state(pdev, PCI_D3hot);
+ }
}
int ata_pci_device_do_resume(struct pci_dev *pdev)
{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ unsigned long flags;
int rc;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- rc = pcim_enable_device(pdev);
+ if (host->flags & ATA_HOST_IN_INSOMNIA) {
+ spin_lock_irqsave(&host->lock, flags);
+ host->flags &= ~ATA_HOST_IN_INSOMNIA;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /*
+ * PCI device wasn't disabled during suspend. Use
+ * pci_reenable_device() to avoid affecting the enable
+ * count.
+ */
+ rc = pci_reenable_device(pdev);
+ } else
+ rc = pcim_enable_device(pdev);
+
if (rc) {
dev_printk(KERN_ERR, &pdev->dev,
"failed to enable device after resume (%d)\n", rc);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 6a9c4dd..8b60fed 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -243,9 +243,11 @@ enum {
ATA_QCFLAG_EH_SCHEDULED = (1 << 18), /* EH scheduled (obsolete) */
/* host set flags */
- ATA_HOST_SIMPLEX = (1 << 0), /* Host is simplex, one DMA channel per host only */
- ATA_HOST_STARTED = (1 << 1), /* Host started */
- ATA_HOST_PARALLEL_SCAN = (1 << 2), /* Ports on this host can be scanned in parallel */
+ ATA_HOST_SIMPLEX = (1 << 0), /* Host is simplex, one DMA channel per host only */
+ ATA_HOST_STARTED = (1 << 1), /* Host started */
+ ATA_HOST_PARALLEL_SCAN = (1 << 2), /* Ports on this host can be scanned in parallel */
+ ATA_HOST_INSOMNIA = (1 << 3), /* Don't power down on suspend */
+ ATA_HOST_IN_INSOMNIA = (1 << 4), /* Insomnia in progress */
/* bits 24:31 of host->flags are reserved for LLD specific flags */
next prev parent reply other threads:[~2009-12-25 14:54 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-12-23 3:50 Long delays while hibernating Pedro Ribeiro
2009-12-23 7:47 ` Tejun Heo
2009-12-23 13:50 ` Pedro Ribeiro
2009-12-24 7:45 ` Tejun Heo
2009-12-24 14:34 ` Pedro Ribeiro
2009-12-24 15:59 ` Pedro Ribeiro
2009-12-25 14:56 ` Tejun Heo [this message]
2009-12-25 18:10 ` Pedro Ribeiro
2009-12-26 1:26 ` Tejun Heo
2009-12-26 16:32 ` Pedro Ribeiro
2009-12-26 21:21 ` Rafael J. Wysocki
2009-12-26 22:35 ` Pedro Ribeiro
2009-12-27 14:09 ` Rafael J. Wysocki
2009-12-27 17:59 ` Pedro Ribeiro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4B34D2AC.8040908@kernel.org \
--to=tj@kernel.org \
--cc=linux-ide@vger.kernel.org \
--cc=pedrib@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.