aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/ata/libata-acpi.c35
-rw-r--r--drivers/ata/libata-eh.c2
-rw-r--r--drivers/ata/libata-zpodd.c83
-rw-r--r--drivers/ata/libata.h8
4 files changed, 118 insertions, 10 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 446b4e761af0..6f72c648ea14 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -835,6 +835,22 @@ void ata_acpi_on_resume(struct ata_port *ap)
835 } 835 }
836} 836}
837 837
838static int ata_acpi_choose_suspend_state(struct ata_device *dev)
839{
840 int d_max_in = ACPI_STATE_D3_COLD;
841
842 /*
843 * For ATAPI, runtime D3 cold is only allowed
844 * for ZPODD in zero power ready state
845 */
846 if (dev->class == ATA_DEV_ATAPI &&
847 !(zpodd_dev_enabled(dev) && zpodd_zpready(dev)))
848 d_max_in = ACPI_STATE_D3_HOT;
849
850 return acpi_pm_device_sleep_state(&dev->sdev->sdev_gendev,
851 NULL, d_max_in);
852}
853
838/** 854/**
839 * ata_acpi_set_state - set the port power state 855 * ata_acpi_set_state - set the port power state
840 * @ap: target ATA port 856 * @ap: target ATA port
@@ -861,17 +877,16 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state)
861 continue; 877 continue;
862 878
863 if (state.event != PM_EVENT_ON) { 879 if (state.event != PM_EVENT_ON) {
864 acpi_state = acpi_pm_device_sleep_state( 880 acpi_state = ata_acpi_choose_suspend_state(dev);
865 &dev->sdev->sdev_gendev, NULL, ACPI_STATE_D3); 881 if (acpi_state == ACPI_STATE_D0)
866 if (acpi_state > 0) 882 continue;
867 acpi_bus_set_power(handle, acpi_state); 883 if (zpodd_dev_enabled(dev) &&
868 /* TBD: need to check if it's runtime pm request */ 884 acpi_state == ACPI_STATE_D3_COLD)
869 acpi_pm_device_run_wake( 885 zpodd_enable_run_wake(dev);
870 &dev->sdev->sdev_gendev, true); 886 acpi_bus_set_power(handle, acpi_state);
871 } else { 887 } else {
872 /* Ditto */ 888 if (zpodd_dev_enabled(dev))
873 acpi_pm_device_run_wake( 889 zpodd_disable_run_wake(dev);
874 &dev->sdev->sdev_gendev, false);
875 acpi_bus_set_power(handle, ACPI_STATE_D0); 890 acpi_bus_set_power(handle, ACPI_STATE_D0);
876 } 891 }
877 } 892 }
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index a0dddc3b4924..50f3ef04809d 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -3857,6 +3857,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
3857 rc = atapi_eh_clear_ua(dev); 3857 rc = atapi_eh_clear_ua(dev);
3858 if (rc) 3858 if (rc)
3859 goto rest_fail; 3859 goto rest_fail;
3860 if (zpodd_dev_enabled(dev))
3861 zpodd_post_poweron(dev);
3860 } 3862 }
3861 } 3863 }
3862 3864
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 71dd48c94f2c..1f5d52ae3974 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -26,8 +26,26 @@ struct zpodd {
26 bool zp_ready; /* ZP ready state */ 26 bool zp_ready; /* ZP ready state */
27 unsigned long last_ready; /* last ZP ready timestamp */ 27 unsigned long last_ready; /* last ZP ready timestamp */
28 bool zp_sampled; /* ZP ready state sampled */ 28 bool zp_sampled; /* ZP ready state sampled */
29 bool powered_off; /* ODD is powered off
30 * during suspend */
29}; 31};
30 32
33static int eject_tray(struct ata_device *dev)
34{
35 struct ata_taskfile tf = {};
36 const char cdb[] = { GPCMD_START_STOP_UNIT,
37 0, 0, 0,
38 0x02, /* LoEj */
39 0, 0, 0, 0, 0, 0, 0,
40 };
41
42 tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
43 tf.command = ATA_CMD_PACKET;
44 tf.protocol = ATAPI_PROT_NODATA;
45
46 return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
47}
48
31/* Per the spec, only slot type and drawer type ODD can be supported */ 49/* Per the spec, only slot type and drawer type ODD can be supported */
32static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) 50static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
33{ 51{
@@ -149,6 +167,71 @@ void zpodd_on_suspend(struct ata_device *dev)
149 zpodd->zp_ready = true; 167 zpodd->zp_ready = true;
150} 168}
151 169
170bool zpodd_zpready(struct ata_device *dev)
171{
172 struct zpodd *zpodd = dev->zpodd;
173 return zpodd->zp_ready;
174}
175
176/*
177 * Enable runtime wake capability through ACPI and set the powered_off flag,
178 * this flag will be used during resume to decide what operations are needed
179 * to take.
180 */
181void zpodd_enable_run_wake(struct ata_device *dev)
182{
183 struct zpodd *zpodd = dev->zpodd;
184
185 zpodd->powered_off = true;
186 device_set_run_wake(&dev->sdev->sdev_gendev, true);
187 acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, true);
188}
189
190/* Disable runtime wake capability if it is enabled */
191void zpodd_disable_run_wake(struct ata_device *dev)
192{
193 struct zpodd *zpodd = dev->zpodd;
194
195 if (zpodd->powered_off) {
196 acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, false);
197 device_set_run_wake(&dev->sdev->sdev_gendev, false);
198 }
199}
200
201/*
202 * Post power on processing after the ODD has been recovered. If the
203 * ODD wasn't powered off during suspend, it doesn't do anything.
204 *
205 * For drawer type ODD, if it is powered on due to user pressed the
206 * eject button, the tray needs to be ejected. This can only be done
207 * after the ODD has been recovered, i.e. link is initialized and
208 * device is able to process NON_DATA PIO command, as eject needs to
209 * send command for the ODD to process.
210 *
211 * The from_notify flag set in wake notification handler function
212 * zpodd_wake_dev represents if power on is due to user's action.
213 *
214 * For both types of ODD, several fields need to be reset.
215 */
216void zpodd_post_poweron(struct ata_device *dev)
217{
218 struct zpodd *zpodd = dev->zpodd;
219
220 if (!zpodd->powered_off)
221 return;
222
223 zpodd->powered_off = false;
224
225 if (zpodd->from_notify) {
226 zpodd->from_notify = false;
227 if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
228 eject_tray(dev);
229 }
230
231 zpodd->zp_sampled = false;
232 zpodd->zp_ready = false;
233}
234
152static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) 235static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
153{ 236{
154 struct ata_device *ata_dev = context; 237 struct ata_device *ata_dev = context;
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index b9b2bb1d5dc6..c949dd311b2e 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -242,11 +242,19 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev)
242 return dev->zpodd != NULL; 242 return dev->zpodd != NULL;
243} 243}
244void zpodd_on_suspend(struct ata_device *dev); 244void zpodd_on_suspend(struct ata_device *dev);
245bool zpodd_zpready(struct ata_device *dev);
246void zpodd_enable_run_wake(struct ata_device *dev);
247void zpodd_disable_run_wake(struct ata_device *dev);
248void zpodd_post_poweron(struct ata_device *dev);
245#else /* CONFIG_SATA_ZPODD */ 249#else /* CONFIG_SATA_ZPODD */
246static inline void zpodd_init(struct ata_device *dev) {} 250static inline void zpodd_init(struct ata_device *dev) {}
247static inline void zpodd_exit(struct ata_device *dev) {} 251static inline void zpodd_exit(struct ata_device *dev) {}
248static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } 252static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; }
249static inline void zpodd_on_suspend(struct ata_device *dev) {} 253static inline void zpodd_on_suspend(struct ata_device *dev) {}
254static inline bool zpodd_zpready(struct ata_device *dev) { return false; }
255static inline void zpodd_enable_run_wake(struct ata_device *dev) {}
256static inline void zpodd_disable_run_wake(struct ata_device *dev) {}
257static inline void zpodd_post_poweron(struct ata_device *dev) {}
250#endif /* CONFIG_SATA_ZPODD */ 258#endif /* CONFIG_SATA_ZPODD */
251 259
252#endif /* __LIBATA_H__ */ 260#endif /* __LIBATA_H__ */