libata: tell scsi layer device supports runtime power off
authorAaron Lu <aaron.lu@amd.com>
Mon, 25 Jun 2012 08:13:09 +0000 (16:13 +0800)
committerJeff Garzik <jgarzik@redhat.com>
Fri, 29 Jun 2012 15:38:45 +0000 (11:38 -0400)
If ATA device supports "Device Attention", then tell scsi layer that
the device supports runtime power off.

Signed-off-by: Aaron Lu <aaron.lu@amd.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/ata/libata-acpi.c
include/scsi/scsi_device.h

index f36284e3290d5def51bbcbf0390556ae61067248..f1d6901de37d4f8d6360e37edf737eb63cc4479a 100644 (file)
@@ -1000,7 +1000,10 @@ static void ata_acpi_add_pm_notifier(struct ata_device *dev)
                return;
 
        status = acpi_bus_get_device(handle, &acpi_dev);
-       if (ACPI_SUCCESS(status)) {
+       if (ACPI_FAILURE(status))
+               return;
+
+       if (dev->sdev->can_power_off) {
                acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
                        ata_acpi_wake_dev, dev);
                device_set_run_wake(&dev->sdev->sdev_gendev, true);
@@ -1018,7 +1021,10 @@ static void ata_acpi_remove_pm_notifier(struct ata_device *dev)
                return;
 
        status = acpi_bus_get_device(handle, &acpi_dev);
-       if (ACPI_SUCCESS(status)) {
+       if (ACPI_FAILURE(status))
+               return;
+
+       if (dev->sdev->can_power_off) {
                device_set_run_wake(&dev->sdev->sdev_gendev, false);
                acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
                        ata_acpi_wake_dev);
@@ -1102,6 +1108,9 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev,
                                acpi_handle *handle)
 {
        struct ata_device *ata_dev;
+       acpi_status status;
+       struct acpi_device *acpi_dev;
+       struct acpi_device_power_state *states;
 
        if (ap->flags & ATA_FLAG_ACPI_SATA)
                ata_dev = &ap->link.device[sdev->channel];
@@ -1113,6 +1122,21 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev,
        if (!*handle)
                return -ENODEV;
 
+       status = acpi_bus_get_device(*handle, &acpi_dev);
+       if (ACPI_FAILURE(status))
+               return 0;
+
+       /*
+        * If firmware has _PS3 or _PR3 for this device,
+        * and this ata ODD device support device attention,
+        * it means this device can be powered off
+        */
+       states = acpi_dev->power.states;
+       if ((states[ACPI_STATE_D3_HOT].flags.valid ||
+                       states[ACPI_STATE_D3_COLD].flags.explicit_set) &&
+                       ata_dev->flags & ATA_DFLAG_DA)
+               sdev->can_power_off = 1;
+
        return 0;
 }
 
index ba969885232101e9eeed555e11c2385887b9cb85..aff7525de194611392e1ee61f536a25aa0a312bf 100644 (file)
@@ -153,6 +153,7 @@ struct scsi_device {
        unsigned no_read_capacity_16:1; /* Avoid READ_CAPACITY_16 cmds */
        unsigned try_rc_10_first:1;     /* Try READ_CAPACACITY_10 first */
        unsigned is_visible:1;  /* is the device visible in sysfs */
+       unsigned can_power_off:1; /* Device supports runtime power off */
 
        DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
        struct list_head event_list;    /* asserted events */