aboutsummaryrefslogtreecommitdiffstats
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.c75
1 files changed, 75 insertions, 0 deletions
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 9a0d90d09d81..71dd48c94f2c 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -1,10 +1,15 @@
1#include <linux/libata.h> 1#include <linux/libata.h>
2#include <linux/cdrom.h> 2#include <linux/cdrom.h>
3#include <linux/pm_runtime.h> 3#include <linux/pm_runtime.h>
4#include <linux/module.h>
4#include <scsi/scsi_device.h> 5#include <scsi/scsi_device.h>
5 6
6#include "libata.h" 7#include "libata.h"
7 8
9static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */
10module_param(zpodd_poweroff_delay, int, 0644);
11MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds");
12
8enum odd_mech_type { 13enum odd_mech_type {
9 ODD_MECH_TYPE_SLOT, 14 ODD_MECH_TYPE_SLOT,
10 ODD_MECH_TYPE_DRAWER, 15 ODD_MECH_TYPE_DRAWER,
@@ -18,6 +23,9 @@ struct zpodd {
18 /* The following fields are synchronized by PM core. */ 23 /* The following fields are synchronized by PM core. */
19 bool from_notify; /* resumed as a result of 24 bool from_notify; /* resumed as a result of
20 * acpi wake notification */ 25 * acpi wake notification */
26 bool zp_ready; /* ZP ready state */
27 unsigned long last_ready; /* last ZP ready timestamp */
28 bool zp_sampled; /* ZP ready state sampled */
21}; 29};
22 30
23/* Per the spec, only slot type and drawer type ODD can be supported */ 31/* Per the spec, only slot type and drawer type ODD can be supported */
@@ -74,6 +82,73 @@ static bool odd_can_poweroff(struct ata_device *ata_dev)
74 return acpi_device_can_poweroff(acpi_dev); 82 return acpi_device_can_poweroff(acpi_dev);
75} 83}
76 84
85/* Test if ODD is zero power ready by sense code */
86static bool zpready(struct ata_device *dev)
87{
88 u8 sense_key, *sense_buf;
89 unsigned int ret, asc, ascq, add_len;
90 struct zpodd *zpodd = dev->zpodd;
91
92 ret = atapi_eh_tur(dev, &sense_key);
93
94 if (!ret || sense_key != NOT_READY)
95 return false;
96
97 sense_buf = dev->link->ap->sector_buf;
98 ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
99 if (ret)
100 return false;
101
102 /* sense valid */
103 if ((sense_buf[0] & 0x7f) != 0x70)
104 return false;
105
106 add_len = sense_buf[7];
107 /* has asc and ascq */
108 if (add_len < 6)
109 return false;
110
111 asc = sense_buf[12];
112 ascq = sense_buf[13];
113
114 if (zpodd->mech_type == ODD_MECH_TYPE_SLOT)
115 /* no media inside */
116 return asc == 0x3a;
117 else
118 /* no media inside and door closed */
119 return asc == 0x3a && ascq == 0x01;
120}
121
122/*
123 * Update the zpodd->zp_ready field. This field will only be set
124 * if the ODD has stayed in ZP ready state for zpodd_poweroff_delay
125 * time, and will be used to decide if power off is allowed. If it
126 * is set, it will be cleared during resume from powered off state.
127 */
128void zpodd_on_suspend(struct ata_device *dev)
129{
130 struct zpodd *zpodd = dev->zpodd;
131 unsigned long expires;
132
133 if (!zpready(dev)) {
134 zpodd->zp_sampled = false;
135 return;
136 }
137
138 if (!zpodd->zp_sampled) {
139 zpodd->zp_sampled = true;
140 zpodd->last_ready = jiffies;
141 return;
142 }
143
144 expires = zpodd->last_ready +
145 msecs_to_jiffies(zpodd_poweroff_delay * 1000);
146 if (time_before(jiffies, expires))
147 return;
148
149 zpodd->zp_ready = true;
150}
151
77static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) 152static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
78{ 153{
79 struct ata_device *ata_dev = context; 154 struct ata_device *ata_dev = context;