aboutsummaryrefslogtreecommitdiff
path: root/drivers/ata/libata-zpodd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-zpodd.c')
-rw-r--r--drivers/ata/libata-zpodd.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 27eed2f09a8..9a0d90d09d8 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -1,5 +1,7 @@
#include <linux/libata.h>
#include <linux/cdrom.h>
+#include <linux/pm_runtime.h>
+#include <scsi/scsi_device.h>
#include "libata.h"
@@ -12,6 +14,10 @@ enum odd_mech_type {
struct zpodd {
enum odd_mech_type mech_type; /* init during probe, RO afterwards */
struct ata_device *dev;
+
+ /* The following fields are synchronized by PM core. */
+ bool from_notify; /* resumed as a result of
+ * acpi wake notification */
};
/* Per the spec, only slot type and drawer type ODD can be supported */
@@ -68,6 +74,32 @@ static bool odd_can_poweroff(struct ata_device *ata_dev)
return acpi_device_can_poweroff(acpi_dev);
}
+static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
+{
+ struct ata_device *ata_dev = context;
+ struct zpodd *zpodd = ata_dev->zpodd;
+ struct device *dev = &ata_dev->sdev->sdev_gendev;
+
+ if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev &&
+ pm_runtime_suspended(dev)) {
+ zpodd->from_notify = true;
+ pm_runtime_resume(dev);
+ }
+}
+
+static void ata_acpi_add_pm_notifier(struct ata_device *dev)
+{
+ acpi_handle handle = ata_dev_acpi_handle(dev);
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ zpodd_wake_dev, dev);
+}
+
+static void ata_acpi_remove_pm_notifier(struct ata_device *dev)
+{
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->sdev->sdev_gendev);
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev);
+}
+
void zpodd_init(struct ata_device *dev)
{
enum odd_mech_type mech_type;
@@ -89,12 +121,14 @@ void zpodd_init(struct ata_device *dev)
zpodd->mech_type = mech_type;
+ ata_acpi_add_pm_notifier(dev);
zpodd->dev = dev;
dev->zpodd = zpodd;
}
void zpodd_exit(struct ata_device *dev)
{
+ ata_acpi_remove_pm_notifier(dev);
kfree(dev->zpodd);
dev->zpodd = NULL;
}