Commit cc8e7a35 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Len Brown

PNP / ACPI: Use DEVICE_ACPI_HANDLE() for device ACPI handle access

The PNP ACPI driver squirrels the ACPI handles of PNP devices' ACPI
companions, but this isn't correct, because those handles should be
accessed using the DEVICE_ACPI_HANDLE() macro operating on struct
device objects.

Using DEVICE_ACPI_HANDLE() in the PNP ACPI driver instead of the
driver's own copies of the ACPI handles allows us to avoid a problem
with docking stations where a machine docked before suspend to RAM
and undocked while suspended crashes during the subsequent resume (in
that case the ACPI companion of the PNP device in question doesn't
exist any more while the device is being resumed).  It also allows us
to avoid the problem where suspend to RAM fails when the machine was
undocked while suspended before (again, the ACPI companion of the PNP
device is not present any more while it is being suspended).

This change doesn't fix all of the the PNP ACPI driver's problems
with PNP devices in docking stations (generally speaking, the driver
has no idea that devices can come and go and doesn't even attempt to
handle such events), but at least it makes suspend work for the
users of docking stations who don't use the PNP devices located in
there.

References: https://bugzilla.kernel.org/show_bug.cgi?id=15100Reported-and-tested-by: default avatarToralf Förster <toralf.foerster@gmx.de>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Acked-by: default avatarBjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 3c0eee3f
...@@ -189,8 +189,11 @@ static int pnp_bus_resume(struct device *dev) ...@@ -189,8 +189,11 @@ static int pnp_bus_resume(struct device *dev)
if (!pnp_drv) if (!pnp_drv)
return 0; return 0;
if (pnp_dev->protocol->resume) if (pnp_dev->protocol->resume) {
pnp_dev->protocol->resume(pnp_dev); error = pnp_dev->protocol->resume(pnp_dev);
if (error)
return error;
}
if (pnp_can_write(pnp_dev)) { if (pnp_can_write(pnp_dev)) {
error = pnp_start_dev(pnp_dev); error = pnp_start_dev(pnp_dev);
......
...@@ -81,12 +81,19 @@ static int pnpacpi_get_resources(struct pnp_dev *dev) ...@@ -81,12 +81,19 @@ static int pnpacpi_get_resources(struct pnp_dev *dev)
static int pnpacpi_set_resources(struct pnp_dev *dev) static int pnpacpi_set_resources(struct pnp_dev *dev)
{ {
struct acpi_device *acpi_dev = dev->data; struct acpi_device *acpi_dev;
acpi_handle handle = acpi_dev->handle; acpi_handle handle;
struct acpi_buffer buffer; struct acpi_buffer buffer;
int ret; int ret;
pnp_dbg(&dev->dev, "set resources\n"); pnp_dbg(&dev->dev, "set resources\n");
handle = DEVICE_ACPI_HANDLE(&dev->dev);
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
return -ENODEV;
}
ret = pnpacpi_build_resource_template(dev, &buffer); ret = pnpacpi_build_resource_template(dev, &buffer);
if (ret) if (ret)
return ret; return ret;
...@@ -105,12 +112,18 @@ static int pnpacpi_set_resources(struct pnp_dev *dev) ...@@ -105,12 +112,18 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
static int pnpacpi_disable_resources(struct pnp_dev *dev) static int pnpacpi_disable_resources(struct pnp_dev *dev)
{ {
struct acpi_device *acpi_dev = dev->data; struct acpi_device *acpi_dev;
acpi_handle handle = acpi_dev->handle; acpi_handle handle;
int ret; int ret;
dev_dbg(&dev->dev, "disable resources\n"); dev_dbg(&dev->dev, "disable resources\n");
handle = DEVICE_ACPI_HANDLE(&dev->dev);
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
return 0;
}
/* acpi_unregister_gsi(pnp_irq(dev, 0)); */ /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
ret = 0; ret = 0;
if (acpi_bus_power_manageable(handle)) if (acpi_bus_power_manageable(handle))
...@@ -124,46 +137,74 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev) ...@@ -124,46 +137,74 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
#ifdef CONFIG_ACPI_SLEEP #ifdef CONFIG_ACPI_SLEEP
static bool pnpacpi_can_wakeup(struct pnp_dev *dev) static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
{ {
struct acpi_device *acpi_dev = dev->data; struct acpi_device *acpi_dev;
acpi_handle handle = acpi_dev->handle; acpi_handle handle;
handle = DEVICE_ACPI_HANDLE(&dev->dev);
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
return false;
}
return acpi_bus_can_wakeup(handle); return acpi_bus_can_wakeup(handle);
} }
static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
{ {
struct acpi_device *acpi_dev = dev->data; struct acpi_device *acpi_dev;
acpi_handle handle = acpi_dev->handle; acpi_handle handle;
int power_state; int error = 0;
handle = DEVICE_ACPI_HANDLE(&dev->dev);
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
return 0;
}
if (device_can_wakeup(&dev->dev)) { if (device_can_wakeup(&dev->dev)) {
int rc = acpi_pm_device_sleep_wake(&dev->dev, error = acpi_pm_device_sleep_wake(&dev->dev,
device_may_wakeup(&dev->dev)); device_may_wakeup(&dev->dev));
if (error)
return error;
}
if (acpi_bus_power_manageable(handle)) {
int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
if (power_state < 0)
power_state = (state.event == PM_EVENT_ON) ?
ACPI_STATE_D0 : ACPI_STATE_D3;
if (rc) /*
return rc; * acpi_bus_set_power() often fails (keyboard port can't be
* powered-down?), and in any case, our return value is ignored
* by pnp_bus_suspend(). Hence we don't revert the wakeup
* setting if the set_power fails.
*/
error = acpi_bus_set_power(handle, power_state);
} }
power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
if (power_state < 0) return error;
power_state = (state.event == PM_EVENT_ON) ?
ACPI_STATE_D0 : ACPI_STATE_D3;
/* acpi_bus_set_power() often fails (keyboard port can't be
* powered-down?), and in any case, our return value is ignored
* by pnp_bus_suspend(). Hence we don't revert the wakeup
* setting if the set_power fails.
*/
return acpi_bus_set_power(handle, power_state);
} }
static int pnpacpi_resume(struct pnp_dev *dev) static int pnpacpi_resume(struct pnp_dev *dev)
{ {
struct acpi_device *acpi_dev = dev->data; struct acpi_device *acpi_dev;
acpi_handle handle = acpi_dev->handle; acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
int error = 0;
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
return -ENODEV;
}
if (device_may_wakeup(&dev->dev)) if (device_may_wakeup(&dev->dev))
acpi_pm_device_sleep_wake(&dev->dev, false); acpi_pm_device_sleep_wake(&dev->dev, false);
return acpi_bus_set_power(handle, ACPI_STATE_D0);
if (acpi_bus_power_manageable(handle))
error = acpi_bus_set_power(handle, ACPI_STATE_D0);
return error;
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment