diff options
Diffstat (limited to 'drivers/ata/libata-zpodd.c')
-rw-r--r-- | drivers/ata/libata-zpodd.c | 75 |
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 | ||
9 | static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */ | ||
10 | module_param(zpodd_poweroff_delay, int, 0644); | ||
11 | MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds"); | ||
12 | |||
8 | enum odd_mech_type { | 13 | enum 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 */ | ||
86 | static 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 | */ | ||
128 | void 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 | |||
77 | static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) | 152 | static 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; |