libata: handle power transition of ODD
[cascardo/linux.git] / drivers / ata / libata-zpodd.c
index 71dd48c..1f5d52a 100644 (file)
@@ -26,8 +26,26 @@ struct zpodd {
        bool                    zp_ready; /* ZP ready state */
        unsigned long           last_ready; /* last ZP ready timestamp */
        bool                    zp_sampled; /* ZP ready state sampled */
+       bool                    powered_off; /* ODD is powered off
+                                             * during suspend */
 };
 
+static int eject_tray(struct ata_device *dev)
+{
+       struct ata_taskfile tf = {};
+       const char cdb[] = {  GPCMD_START_STOP_UNIT,
+               0, 0, 0,
+               0x02,     /* LoEj */
+               0, 0, 0, 0, 0, 0, 0,
+       };
+
+       tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+       tf.command = ATA_CMD_PACKET;
+       tf.protocol = ATAPI_PROT_NODATA;
+
+       return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
+}
+
 /* Per the spec, only slot type and drawer type ODD can be supported */
 static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
 {
@@ -149,6 +167,71 @@ void zpodd_on_suspend(struct ata_device *dev)
        zpodd->zp_ready = true;
 }
 
+bool zpodd_zpready(struct ata_device *dev)
+{
+       struct zpodd *zpodd = dev->zpodd;
+       return zpodd->zp_ready;
+}
+
+/*
+ * Enable runtime wake capability through ACPI and set the powered_off flag,
+ * this flag will be used during resume to decide what operations are needed
+ * to take.
+ */
+void zpodd_enable_run_wake(struct ata_device *dev)
+{
+       struct zpodd *zpodd = dev->zpodd;
+
+       zpodd->powered_off = true;
+       device_set_run_wake(&dev->sdev->sdev_gendev, true);
+       acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, true);
+}
+
+/* Disable runtime wake capability if it is enabled */
+void zpodd_disable_run_wake(struct ata_device *dev)
+{
+       struct zpodd *zpodd = dev->zpodd;
+
+       if (zpodd->powered_off) {
+               acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, false);
+               device_set_run_wake(&dev->sdev->sdev_gendev, false);
+       }
+}
+
+/*
+ * Post power on processing after the ODD has been recovered. If the
+ * ODD wasn't powered off during suspend, it doesn't do anything.
+ *
+ * For drawer type ODD, if it is powered on due to user pressed the
+ * eject button, the tray needs to be ejected. This can only be done
+ * after the ODD has been recovered, i.e. link is initialized and
+ * device is able to process NON_DATA PIO command, as eject needs to
+ * send command for the ODD to process.
+ *
+ * The from_notify flag set in wake notification handler function
+ * zpodd_wake_dev represents if power on is due to user's action.
+ *
+ * For both types of ODD, several fields need to be reset.
+ */
+void zpodd_post_poweron(struct ata_device *dev)
+{
+       struct zpodd *zpodd = dev->zpodd;
+
+       if (!zpodd->powered_off)
+               return;
+
+       zpodd->powered_off = false;
+
+       if (zpodd->from_notify) {
+               zpodd->from_notify = false;
+               if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
+                       eject_tray(dev);
+       }
+
+       zpodd->zp_sampled = false;
+       zpodd->zp_ready = false;
+}
+
 static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
 {
        struct ata_device *ata_dev = context;