ZPODD Patches
Subsystems (6)
v10 cover ZPODD Patches
v10:
Introduce PM_QOS_NO_POLL flag to skip calling disk's events_check callback;
Do not use zero power ready hint information from event poll; Check attached device in port's runtime idle callback to decide if suspend is desired;
Address various comments from Tejun Heo.
v9:
Build ZPODD as part of libata instead of another standalone module as it is tightly related to other libata files.
Identify and init ZPODD during probe time instead of after SCSI device is created as suggested by Tejun Heo.
Make use of pm qos flag to give ACPI hint when choosing ACPI state. Expose qos flag to give user control of whether power off is allowed.
This patchset used Rafael's pm-qos work:git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git pm-qos
v8:
This version is a redesign, it doesn't have much to do with previous versions. The ZPODD implementation is done almost entirely in ATA layer now, except 2 helper functions from SCSI sr driver to block disk events.
The basic idea is that, when ata port is runtime suspended, it will check if the ODD is ready to be powered off. And if yes, events is blocked and power omitted; if not, ODD's power supply remains unchanged by keeping ACPI state at D0.
Some background knowledge about ZPODD is added below v1 history log.
v7:
Re work of runtime pm of sr driver, based on ideas of Alan Stern and Oliver Neukum.
Jeff, due to the ready_to_power_off flag added, there is a small change in [PATCH v7 6/6] libata: acpi: respect may_power_off flag, please check if I can still get your ack, thanks.
v6:
When user changes may_power_off flag through sysfs entry and if device is already runtime suspended, resume resume it so that it can respect this flag next time it is runtime suspended as suggested by Alan Stern. Call scsi_autopm_get/put_device once in sr_check_events as suggested by Alan Stern.
v5:
Add may_power_off flag to scsi device.
Alan Stern suggested that I should not mess runtime suspend with runtime power off, but the current zpodd implementation made it not easy to seperate. So I re-wrote the zpodd implementation, the end result is, normal ODD can also enter runtime suspended state, but their power won't be removed.
v4:
Rebase on top of Linus' tree, due to this, the problem of a missing flag in v3 is gone;
Add a new function scsi_autopm_put_device_autosuspend to first mark last busy for the device and then put autosuspend it as suggested by Oliver Neukum.
Typo fix as pointed by Sergei Shtylyov.
Check can_power_off flag before any runtime pm operations in sr.
v3:
Rebase on top of scsi-misc tree;
Add the sr related patches previously in Jeff's libata tree; Re-organize the sr patches.
A problem for now: for patch
scsi: sr: support zero power ODD(ZPODD)
I can't set a flag in libata-acpi.c since a related function is missing in scsi-misc tree. Will fix this when 3.6-rc1 released.
v2:
Bug fix for v1;
Use scsi_autopm_* in sr driver instead of pm_runtime_*;
v1:
Here are some patches to make ZPODD easier to use for end users and a fix for using ZPODD with system suspend.
Some background knowledge about ZPODD:
ODD means Optical Disc Drive.
ZPODD means Zero Power ODD, it is a mechanism to place the ODD into zero power state when the system is running at S0 system state without user's awareness.
It achieved this by ACPI and SATA device attention pin. For power off, normal ACPI control method is used to place the device into D3 cold ACPI device state, aka. device power supply omitted. For power on, when user press the eject button of a drawer type ODD or when user inserts an ODD into a slot type ODD, the device attention pin will trigger. In the current x86 implementation, this pin will connect to a GPE, and the GPE will trigger an ACPI interrupt. With our pre-registered ACPI notification code, the device can be runtime resumed, and we place the device back to full power state by setting its ACPI state to D0. The whole process is transparent to the end user.
Aaron Lu (10):
scsi: sr: support runtime pm
libata: Add CONFIG_SATA_ZPODD
libata: identify and init ZPODD devices
libata: move acpi notification code to zpodd
libata: check zero power ready status for ZPODD
libata: handle power transition of ODD
libata: expose pm qos flags to user space for ata device PM / BLOCK: add PM_QOS_FLAG_NO_POLL to skip events checking
libata: use PM_QOS_FLAG_NO_POLL to avoid events checking for ZPODD
libata: do not suspend port if normal ODD is attached
block/genhd.c | 8 +- drivers/ata/Kconfig | 13 +++ drivers/ata/Makefile | 1 + drivers/ata/libata-acpi.c | 116 ++++++------------- drivers/ata/libata-core.c | 23 +++- drivers/ata/libata-eh.c | 12 +- drivers/ata/libata-zpodd.c | 282 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 27 +++++ drivers/scsi/sr.c | 30 ++++- include/linux/libata.h | 5 + include/linux/pm_qos.h | 1 + include/uapi/linux/cdrom.h | 34 ++++++ 12 files changed, 463 insertions(+), 89 deletions(-) create mode 100644 drivers/ata/libata-zpodd.c
Signature · 1 line
1.7.12.4
PATCH v10 1/10 scsi: sr: support runtime pm
This patch adds runtime pm support for sr.
It did this by increasing the runtime usage_count of the device when:
- its block device is opened;
- the events checking is to run.
And decreasing the runtime usage_count of the device when:
- its block device is closed;
- After the events checking is done.
The idea is discussed in this mail thread:
http://thread.gmane.org/gmane.linux.acpi.devel/55243/focus=52703
--- drivers/scsi/sr.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-)
drivers/scsi/sr.c +25 -5
@@ -45,6 +45,7 @@#include <linux/blkdev.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <asm/uaccess.h> #include <scsi/scsi.h>
@@ -146,7 +147,8 @@ static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk)kref_get (&cd->kref);if (scsi_device_get (cd->device)) goto out_put; - goto out; +if (!scsi_autopm_get_device (cd->device)) + goto out; out_put:kref_put (&cd->kref, sr_kref_release);
@@ -162,6 +164,7 @@ static void scsi_cd_put(struct scsi_cd *cd)mutex_lock (&sr_ref_mutex);kref_put (&cd->kref, sr_kref_release); +scsi_autopm_put_device (sdev);scsi_device_put (sdev);mutex_unlock (&sr_ref_mutex); }
@@ -211,7 +214,7 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, unsigned int clearing, int slot) {struct scsi_cd *cd = cdi->handle; -bool last_present; +bool last_present = cd->media_present;struct scsi_sense_hdr sshdr;unsigned int events;int ret;
@@ -220,6 +223,8 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi,if (CDSL_CURRENT != slot)return 0 ; +scsi_autopm_get_device (cd->device); + events =sr_get_events (cd->device); cd->get_event_changed |= events &DISK_EVENT_MEDIA_CHANGE ;
@@ -246,10 +251,9 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, }if (!(clearing &DISK_EVENT_MEDIA_CHANGE )) -return events; + goto out; do_tur:/* let's see whether the media is there with TUR */ - last_present = cd->media_present; ret =scsi_test_unit_ready (cd->device,SR_TIMEOUT ,MAX_RETRIES , &sshdr);/*
@@ -270,7 +274,7 @@ do_tur: }if (cd->ignore_get_event) -return events; + goto out;/* check whether GET_EVENT is reporting spurious MEDIA_CHANGE */ if (!cd->tur_changed) {
@@ -287,6 +291,18 @@ do_tur: cd->tur_changed = false; cd->get_event_changed = false; +out: +/* +* If there is no medium detected or the medium has been there +* since last poll, try to suspend the device. Otherwise, keep +* it active for one more poll interval so that if user space +* application opens the block device, we can avoid a runtime +* status change. +*/ +pm_runtime_put_noidle (&cd->device->sdev_gendev); +if (!cd->media_present || last_present) +pm_runtime_suspend (&cd->device->sdev_gendev); +return events; }
@@ -718,6 +734,8 @@ static int sr_probe(struct device *dev)sdev_printk (KERN_DEBUG , sdev,"Attached scsi CD-ROM %s\n" , cd->cdi.name); +scsi_autopm_put_device (cd->device); +return 0 ; fail_put:
@@ -965,6 +983,8 @@ static int sr_remove(struct device *dev) {struct scsi_cd *cd =dev_get_drvdata (dev); +scsi_autopm_get_device (cd->device); +blk_queue_prep_rq (cd->device->request_queue, scsi_prep_fn);del_gendisk (cd->disk);
Signature · 1 line
1.7.12.4
PATCH v10 2/10 libata: Add CONFIG_SATA_ZPODD
In reply to Aaron LuAdded a new config CONFIG_SATA_ZPODD, which is used to support SATA based zero power ODD, and depends on ATA_ACPI.
--- drivers/ata/Kconfig | 13 +++++++++++++ drivers/ata/Makefile | 1 + 2 files changed, 14 insertions(+)
drivers/ata/Kconfig +13 -0
@@ -58,6 +58,19 @@ config ATA_ACPI You can disable this at kernel boot time by using the option libata.noacpi=1 +config SATA_ZPODD + bool "SATA Zero Power ODD Support" + depends on ATA_ACPI + default n + help + This option adds support for SATA ZPODD. It requires both + ODD and the platform support, and if enabled, will automatically + power on/off the ODD when certain condition is satisfied. This + does not impact user's experience of the ODD, only power is saved + when ODD is not in use(i.e. no disc inside). + + If unsure, say N. + config SATA_PMP bool "SATA Port Multiplier support" default y
drivers/ata/Makefile +1 -0
@@ -107,3 +107,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o libata-$(CONFIG_ATA_SFF) += libata-sff.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o +libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o
Signature · 1 line
1.7.12.4
PATCH v10 3/10 libata: identify and init ZPODD devices
In reply to Aaron LuThe ODD can be enabled for ZPODD if the following three conditions are satisfied:
1 The ODD supports device attention;
2 The platform can runtime power off the ODD through ACPI;
3 The ODD is either slot type or drawer type.
For such ODDs, zpodd_init is called and a new structure is allocated for it to store ZPODD related stuffs.
And the zpodd_dev_enabled function is used to test if ZPODD is currently enabled for this ODD.
--- drivers/ata/libata-acpi.c | 2 + drivers/ata/libata-core.c | 4 +- drivers/ata/libata-zpodd.c | 124 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 14 +++++ include/linux/libata.h | 3 ++ include/uapi/linux/cdrom.h | 34 +++++++++++++ 6 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 drivers/ata/libata-zpodd.c
drivers/ata/libata-acpi.c +2 -0
@@ -1059,6 +1059,8 @@ void ata_acpi_bind(struct ata_device *dev)void ata_acpi_unbind (struct ata_device *dev) { +if (zpodd_dev_enabled (dev)) +zpodd_exit (dev);ata_acpi_remove_pm_notifier (dev);ata_acpi_unregister_power_resource (dev); }
drivers/ata/libata-core.c +3 -1
@@ -2395,8 +2395,10 @@ int ata_dev_configure(struct ata_device *dev) dma_dir_string =", DMADIR" ; } -if (ata_id_has_da (dev->id)) +if (ata_id_has_da (dev->id)) { dev->flags |=ATA_DFLAG_DA ; +zpodd_init (dev); + }/* print device info to dmesg */ if (ata_msg_drv (ap) && print_info)
drivers/ata/libata-zpodd.c new file +124 -0
124 lines not on this page · raw message
drivers/ata/libata.h +14 -0
@@ -230,4 +230,18 @@ static inline void ata_sff_exit(void) { }#endif /* CONFIG_ATA_SFF */ +/* libata-zpodd.c */ +#ifdef CONFIG_SATA_ZPODD +void zpodd_init (struct ata_device *dev); +void zpodd_exit (struct ata_device *dev); +static inline bool zpodd_dev_enabled (struct ata_device *dev) +{ +return dev->zpodd ? true : false; +} +#else /* CONFIG_SATA_ZPODD */ +static inline void zpodd_init (struct ata_device *dev) {} +static inline void zpodd_exit (struct ata_device *dev) {} +static inline bool zpodd_dev_enabled (struct ata_device *dev) {return false; } +#endif /* CONFIG_SATA_ZPODD */ +#endif /* __LIBATA_H__ */
include/linux/libata.h +3 -0
@@ -619,6 +619,9 @@ struct ata_device {union acpi_object *gtf_cache;unsigned int gtf_filter;#endif +#ifdef CONFIG_SATA_ZPODD +void *zpodd; +#endif struct device tdev;/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ u64 n_sectors;/* size of device, if ATA */
include/uapi/linux/cdrom.h +34 -0
@@ -908,5 +908,39 @@ struct mode_page_header {__be16 desc_length; }; +/* removable medium feature descriptor */ +struct rm_feature_desc { +__be16 feature_code; +#if defined(__BIG_ENDIAN_BITFIELD) +__u8 reserved1:2 ; +__u8 feature_version:4 ; +__u8 persistent:1 ; +__u8 curr:1 ; +#elif defined(__LITTLE_ENDIAN_BITFIELD) +__u8 curr:1 ; +__u8 persistent:1 ; +__u8 feature_version:4 ; +__u8 reserved1:2 ; +#endif +__u8 add_len; +#if defined(__BIG_ENDIAN_BITFIELD) +__u8 mech_type:3 ; +__u8 load:1 ; +__u8 eject:1 ; +__u8 pvnt_jmpr:1 ; +__u8 dbml:1 ; +__u8 lock:1 ; +#elif defined(__LITTLE_ENDIAN_BITFIELD) +__u8 lock:1 ; +__u8 dbml:1 ; +__u8 pvnt_jmpr:1 ; +__u8 eject:1 ; +__u8 load:1 ; +__u8 mech_type:3 ; +#endif +__u8 reserved2; +__u8 reserved3; +__u8 reserved4; +};#endif /* _UAPI_LINUX_CDROM_H */
Signature · 1 line
1.7.12.4
PATCH v10 4/10 libata: move acpi notification code to zpodd
In reply to Aaron LuSince the ata acpi notification code introduced in commit 3bd46600a7a7e938c54df8cdbac9910668c7dfb0 is solely for ZPODD, and we now have a dedicated place for it, move these code there.
And the acpi_add_pm_notifier code is changed a little bit in that it is now invoked when scsi device is not bind with ACPI yet, so the way to get the acpi handle is different with the previous version. And the acpi_add/remove_pm_notifier is also simplified a little bit in that it doesn't check if the acpi_device for the handle exists or not as the device_can_poweroff function already checked that.
--- drivers/ata/libata-acpi.c | 71 ---------------------------------------------- drivers/ata/libata-zpodd.c | 33 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 71 deletions(-)
drivers/ata/libata-acpi.c +0 -71
@@ -970,57 +970,6 @@ void ata_acpi_on_disable(struct ata_device *dev)ata_acpi_clear_gtf (dev); } -static void ata_acpi_wake_dev (acpi_handle handle,u32 event,void *context) -{ -struct ata_device *ata_dev = context; - -if (event ==ACPI_NOTIFY_DEVICE_WAKE && ata_dev && -pm_runtime_suspended (&ata_dev->sdev->sdev_gendev)) -scsi_autopm_get_device (ata_dev->sdev); -} - -static void ata_acpi_add_pm_notifier (struct ata_device *dev) -{ -struct acpi_device *acpi_dev; -acpi_handle handle; -acpi_status status; - - handle =ata_dev_acpi_handle (dev); -if (!handle) -return ; - - status =acpi_bus_get_device (handle, &acpi_dev); -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); - } -} - -static void ata_acpi_remove_pm_notifier (struct ata_device *dev) -{ -struct acpi_device *acpi_dev; -acpi_handle handle; -acpi_status status; - - handle =ata_dev_acpi_handle (dev); -if (!handle) -return ; - - status =acpi_bus_get_device (handle, &acpi_dev); -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); - } -} -static void ata_acpi_register_power_resource (struct ata_device *dev) {struct scsi_device *sdev = dev->sdev;
@@ -1053,7 +1002,6 @@ static void ata_acpi_unregister_power_resource(struct ata_device *dev)void ata_acpi_bind (struct ata_device *dev) { -ata_acpi_add_pm_notifier (dev);ata_acpi_register_power_resource (dev); }
@@ -1061,7 +1009,6 @@ void ata_acpi_unbind(struct ata_device *dev) {if (zpodd_dev_enabled (dev))zpodd_exit (dev); -ata_acpi_remove_pm_notifier (dev);ata_acpi_unregister_power_resource (dev); }
@@ -1103,9 +1050,6 @@ 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 ) {if (!sata_pmp_attached (ap))
@@ -1122,21 +1066,6 @@ 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 ; }
drivers/ata/libata-zpodd.c +33 -0
@@ -1,5 +1,7 @@#include <linux/libata.h> #include <linux/cdrom.h> +#include <linux/pm_runtime.h> +#include <scsi/scsi_device.h> #include "libata.h"
@@ -7,6 +9,9 @@ struct zpodd { bool slot:1; bool drawer:1; +/* The following bits are synchronized by PM core */ + bool from_notify:1; /* resumed as a result of acpi notification */ +struct ata_device *dev; };
@@ -90,6 +95,32 @@ static bool device_can_poweroff(struct ata_device *ata_dev)return false; } +static void zpodd_wake_dev (acpi_handle handle,u32 event,void *context) +{ +struct ata_device *ata_dev = context; +struct zpodd *zpodd = ata_dev->zpodd; +struct device *dev = &ata_dev->sdev->sdev_gendev; + +if (event ==ACPI_NOTIFY_DEVICE_WAKE && ata_dev && +pm_runtime_suspended (dev)) { + zpodd->from_notify = true; +pm_runtime_resume (dev); + } +} + +static void acpi_add_pm_notifier (struct ata_device *dev) +{ +acpi_handle handle =ata_dev_acpi_handle (dev); +acpi_install_notify_handler (handle,ACPI_SYSTEM_NOTIFY , + zpodd_wake_dev, dev); +} + +static void acpi_remove_pm_notifier (struct ata_device *dev) +{ +acpi_handle handle =DEVICE_ACPI_HANDLE (&dev->sdev->sdev_gendev); +acpi_remove_notify_handler (handle,ACPI_SYSTEM_NOTIFY , zpodd_wake_dev); +} +void zpodd_init (struct ata_device *dev) {int ret;
@@ -113,12 +144,14 @@ void zpodd_init(struct ata_device *dev)else zpodd->slot = true; +acpi_add_pm_notifier (dev); zpodd->dev = dev; dev->zpodd = zpodd; }void zpodd_exit (struct ata_device *dev) { +acpi_remove_pm_notifier (dev);kfree (dev->zpodd); dev->zpodd =NULL ; }
Signature · 1 line
1.7.12.4
PATCH v10 5/10 libata: check zero power ready status for ZPODD
In reply to Aaron LuPer the Mount Fuji spec, the ODD is considered zero power ready when:
- For slot type ODD, no media inside;
- For tray type ODD, no media inside and tray closed.
The information can be retrieved by either the returned information of command GET_EVENT_STATUS_NOTIFICATION(the command is used to poll for media event) or sense code.
The information provided by the media status byte is not accurate, it is possible that after a new disc is just inserted, the status byte still returns media not present. So this information can not be used as the deciding factor, we use sense code to decide if zpready status is true.
When we first sensed the ODD in the zero power ready state, the timestamp will be recoreded. And after ODD stayed in this state for some pre-defined period, the ODD is considered as power off ready and the zp_ready flag will be set. The zp_ready flag serves as the deciding factor other code will use to see if power off is OK for the ODD.
The Mount Fuji spec suggests a delay should be used here, to avoid the case user ejects the ODD and then instantly inserts a new one again, so that we can avoid a power transition. And some ODDs may be slow to place its head to the home position after disc is ejected, so a delay here is generally a good idea.
The zero power ready status check is performed in the ata port's runtime suspend code path, when port is not frozen yet, as we need to issue some IOs to the ODD.
--- drivers/ata/libata-eh.c | 10 +++++-- drivers/ata/libata-zpodd.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 5 ++++ 3 files changed, 79 insertions(+), 2 deletions(-)
drivers/ata/libata-eh.c +8 -2
@@ -1591,7 +1591,7 @@ static int ata_eh_read_log_10h(struct ata_device *dev,* RETURNS: * 0 on success, AC_ERR_* mask on failure. */ -static unsigned int atapi_eh_tur (struct ata_device *dev,u8 *r_sense_key) +unsigned int atapi_eh_tur (struct ata_device *dev,u8 *r_sense_key) {u8 cdb[ATAPI_CDB_LEN ] = {TEST_UNIT_READY ,0 ,0 ,0 ,0 ,0 };struct ata_taskfile tf;
@@ -1624,7 +1624,7 @@ static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)* RETURNS: * 0 on success, AC_ERR_* mask on failure */ -static unsigned int atapi_eh_request_sense (struct ata_device *dev, +unsigned int atapi_eh_request_sense (struct ata_device *dev,u8 *sense_buf,u8 dfl_sense_key) { u8 cdb[ATAPI_CDB_LEN] =
@@ -4021,6 +4021,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) {unsigned long flags;int rc =0 ; +struct ata_device *dev;/* are we suspending? */ spin_lock_irqsave (ap->lock, flags);
@@ -4033,6 +4034,11 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)WARN_ON (ap->pflags &ATA_PFLAG_SUSPENDED ); +ata_for_each_dev (dev, &ap->link,ENABLED ) { +if (zpodd_dev_enabled (dev)) +zpodd_on_suspend (dev); + } +/* tell ACPI we're suspending */ rc =ata_acpi_on_suspend (ap);if (rc)
drivers/ata/libata-zpodd.c +66 -0
84 lines not on this page · raw message
drivers/ata/libata.h +5 -0
@@ -182,6 +182,9 @@ extern void ata_eh_finish(struct ata_port *ap);extern int ata_ering_map (struct ata_ering *ering,int (*map_fn)(struct ata_ering_entry *,void *),void *arg); +extern unsigned int atapi_eh_tur (struct ata_device *dev,u8 *r_sense_key); +extern unsigned int atapi_eh_request_sense (struct ata_device *dev, +u8 *sense_buf,u8 dfl_sense_key);/* libata-pmp.c */ #ifdef CONFIG_SATA_PMP
@@ -238,10 +241,12 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev) {return dev->zpodd ? true : false; } +void zpodd_on_suspend (struct ata_device *dev);#else /* CONFIG_SATA_ZPODD */ static inline void zpodd_init (struct ata_device *dev) {}static inline void zpodd_exit (struct ata_device *dev) {}static inline bool zpodd_dev_enabled (struct ata_device *dev) {return false; } +static inline void zpodd_on_suspend (struct ata_device *dev) {}#endif /* CONFIG_SATA_ZPODD */ #endif /* __LIBATA_H__ */
Signature · 1 line
1.7.12.4
PATCH v10 6/10 libata: handle power transition of ODD
In reply to Aaron LuWhen ata port is runtime suspended, it will check if the ODD attched to it is in zero power ready state by checking the zp_ready field. And if this field indicates it is not ready to be powered off, NO_POWEROFF qos flag will be set to avoid choosing ACPI_STATE_D3_COLD for it.
And on resume, it will re-gain power and go through the recovery process. When reset for the ata port is done, the ODD is considered functional, and post processing like eject tray if the ODD is drawer type is done there.
For normal ODDs that do not support zero power state, NO_POWEROFF qos flag will always be set to avoid putting it into powered off state.
--- drivers/ata/libata-acpi.c | 40 +++++++++++++++++++++++++-------- drivers/ata/libata-eh.c | 2 ++ drivers/ata/libata-zpodd.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 8 +++++++ include/linux/libata.h | 2 ++ 5 files changed, 99 insertions(+), 9 deletions(-)
drivers/ata/libata-acpi.c +31 -9
@@ -832,6 +832,24 @@ void ata_acpi_on_resume(struct ata_port *ap) } } +static int ata_acpi_choose_state (struct ata_device *dev) +{ +/* Always choose D3 for PATA devices */ +if (!(dev->link->ap->flags &ATA_FLAG_ACPI_SATA )) +return ACPI_STATE_D3 ; + +if (zpodd_dev_enabled (dev)) { +if (zpodd_poweroff_ready (dev)) +dev_pm_qos_update_request (&dev->pm_qos_req,0 ); +else +dev_pm_qos_update_request (&dev->pm_qos_req, +PM_QOS_FLAG_NO_POWER_OFF ); + } + +return acpi_pm_device_sleep_state (&dev->sdev->sdev_gendev, +NULL ,ACPI_STATE_D3_COLD ); +} +/** * ata_acpi_set_state - set the port power state * @ap: target ATA port
@@ -858,17 +876,16 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state)continue ;if (state.event !=PM_EVENT_ON ) { - acpi_state =acpi_pm_device_sleep_state ( - &dev->sdev->sdev_gendev,NULL ,ACPI_STATE_D3 ); -if (acpi_state >0 ) + acpi_state =ata_acpi_choose_state (dev); +if (acpi_state >0 ) {acpi_bus_set_power (handle, acpi_state); -/* TBD: need to check if it's runtime pm request */ -acpi_pm_device_run_wake ( - &dev->sdev->sdev_gendev, true); +if (zpodd_dev_enabled (dev) && + acpi_state ==ACPI_STATE_D3_COLD ) +zpodd_post_poweroff (dev); + } }else { -/* Ditto */ -acpi_pm_device_run_wake ( - &dev->sdev->sdev_gendev, false); +if (zpodd_dev_enabled (dev)) +zpodd_pre_poweron (dev);acpi_bus_set_power (handle,ACPI_STATE_D0 ); } }
@@ -1002,7 +1019,12 @@ static void ata_acpi_unregister_power_resource(struct ata_device *dev)void ata_acpi_bind (struct ata_device *dev) { +/* ODD can't be put to D3 cold state, unless it is zero power capable */ +s32 value = dev->class ==ATA_DEV_ATAPI ?PM_QOS_FLAG_NO_POWER_OFF :0 ; +ata_acpi_register_power_resource (dev); +dev_pm_qos_add_request (&dev->sdev->sdev_gendev, &dev->pm_qos_req, +DEV_PM_QOS_FLAGS , value); }void ata_acpi_unbind (struct ata_device *dev)
drivers/ata/libata-eh.c +2 -0
@@ -3856,6 +3856,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, rc =atapi_eh_clear_ua (dev);if (rc) goto rest_fail; +if (zpodd_dev_enabled (dev)) +zpodd_post_resume (dev); } }
drivers/ata/libata-zpodd.c +56 -0
74 lines not on this page · raw message
drivers/ata/libata.h +8 -0
@@ -242,11 +242,19 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev)return dev->zpodd ? true : false; }void zpodd_on_suspend (struct ata_device *dev); +bool zpodd_poweroff_ready (struct ata_device *dev); +void zpodd_post_poweroff (struct ata_device *dev); +void zpodd_pre_poweron (struct ata_device *dev); +void zpodd_post_resume (struct ata_device *dev);#else /* CONFIG_SATA_ZPODD */ static inline void zpodd_init (struct ata_device *dev) {}static inline void zpodd_exit (struct ata_device *dev) {}static inline bool zpodd_dev_enabled (struct ata_device *dev) {return false; }static inline void zpodd_on_suspend (struct ata_device *dev) {} +static inline bool zpodd_poweroff_ready (struct ata_device *dev) {return false; } +static inline void zpodd_post_poweroff (struct ata_device *dev) {} +static inline void zpodd_pre_poweron (struct ata_device *dev) {} +static inline void zpodd_post_resume (struct ata_device *dev) {}#endif /* CONFIG_SATA_ZPODD */ #endif /* __LIBATA_H__ */
include/linux/libata.h +2 -0
@@ -38,6 +38,7 @@#include <linux/acpi.h> #include <linux/cdrom.h> #include <linux/sched.h> +#include <linux/pm_qos.h> /* * Define if arch has non-standard setup. This is a _PCI_ standard
@@ -618,6 +619,7 @@ struct ata_device {#ifdef CONFIG_ATA_ACPI union acpi_object *gtf_cache;unsigned int gtf_filter; +struct dev_pm_qos_request pm_qos_req;#endif #ifdef CONFIG_SATA_ZPODD void *zpodd;
Signature · 1 line
1.7.12.4
PATCH v10 7/10 libata: expose pm qos flags to user space for ata device
In reply to Aaron LuExpose pm qos flags to user space so that user has a chance to disable pm features like power off, if he/she has a broken platform or devices or simply does not like this pm feature.
This flag is exposed to user space only for ata device or atapi device that is zero power capable. For normal atapi device, it will never be powered off.
--- drivers/ata/libata-acpi.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/ata/libata-acpi.c +2 -0
@@ -1025,6 +1025,8 @@ void ata_acpi_bind(struct ata_device *dev)ata_acpi_register_power_resource (dev);dev_pm_qos_add_request (&dev->sdev->sdev_gendev, &dev->pm_qos_req,DEV_PM_QOS_FLAGS , value); +if (dev->class ==ATA_DEV_ATA ||zpodd_dev_enabled (dev)) +dev_pm_qos_expose_flags (&dev->sdev->sdev_gendev,0 ); }void ata_acpi_unbind (struct ata_device *dev)
Signature · 1 line
1.7.12.4
PATCH v10 8/10 PM / BLOCK: add PM_QOS_FLAG_NO_POLL to skip events checking
In reply to Aaron LuSome device may enter into a power state when events checking is no longer necessary, this patch introduces the PM_QOS_FLAG_NO_POLL flag and once set, events checking work function in block layer will skip invoke the disk's check_events callback.
This will be used by ZPODD, when ODD is powered off, it doesn't make sense to poll for media change events anymore.
--- block/genhd.c | 8 +++++++- include/linux/pm_qos.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-)
block/genhd.c +7 -1
@@ -18,6 +18,7 @@#include <linux/mutex.h> #include <linux/idr.h> #include <linux/log2.h> +#include <linux/pm_qos.h> #include "blk.h"
@@ -1595,9 +1596,14 @@ static void disk_events_workfn(struct work_struct *work)unsigned int events;unsigned long intv;int nr_events =0 , i; +enum pm_qos_flags_status stat;/* check events */ - events = disk->fops->check_events (disk, clearing); + stat =dev_pm_qos_flags (disk->driverfs_dev,PM_QOS_FLAG_NO_POLL ); +if (stat ==PM_QOS_FLAGS_ALL ) + events =0 ; +else + events = disk->fops->check_events (disk, clearing);/* accumulate pending events and schedule next poll if necessary */ spin_lock_irq (&ev->lock);
include/linux/pm_qos.h +1 -0
@@ -36,6 +36,7 @@ enum pm_qos_flags_status {#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1) +#define PM_QOS_FLAG_NO_POLL (1 << 3)struct pm_qos_request {struct plist_node node;
Signature · 1 line
1.7.12.4
PATCH v10 9/10 libata: use PM_QOS_FLAG_NO_POLL to avoid events checking for ZPODD
In reply to Aaron LuWhen ODD is powered off, there is no need to do events checking, so set the PM_QOS_FLAG_NO_POLL flag. And the flag is cleared on power on.
--- drivers/ata/libata-acpi.c | 3 ++- drivers/ata/libata-zpodd.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-)
drivers/ata/libata-acpi.c +2 -1
@@ -840,7 +840,8 @@ static int ata_acpi_choose_state(struct ata_device *dev)if (zpodd_dev_enabled (dev)) {if (zpodd_poweroff_ready (dev)) -dev_pm_qos_update_request (&dev->pm_qos_req,0 ); +dev_pm_qos_update_request (&dev->pm_qos_req, +PM_QOS_FLAG_NO_POLL );else dev_pm_qos_update_request (&dev->pm_qos_req,PM_QOS_FLAG_NO_POWER_OFF );
drivers/ata/libata-zpodd.c +3 -0
@@ -2,6 +2,7 @@#include <linux/cdrom.h> #include <linux/pm_runtime.h> #include <scsi/scsi_device.h> +#include <linux/pm_qos.h> #include "libata.h"
@@ -215,6 +216,8 @@ void zpodd_post_resume(struct ata_device *dev) zpodd->last_ready =0 ; zpodd->zp_ready = false; + +dev_pm_qos_update_request (&dev->pm_qos_req,0 ); }static void zpodd_wake_dev (acpi_handle handle,u32 event,void *context)
Signature · 1 line
1.7.12.4
PATCH v10 10/10 libata: do not suspend port if normal ODD is attached
In reply to Aaron LuFor ODDs, the upper layer will poll for media change every a few seconds, which will make it enter and leave suspend state very oftern. And as each suspend will also cause a hard/soft reset, the gain of runtime suspend is very little while the ODD may mis-function after constantly being reset. So the idle callback here will not proceed to suspend if a non-ZPODD capable ODD is attached to the port.
--- drivers/ata/libata-core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
drivers/ata/libata-core.c +19 -0
@@ -5406,8 +5406,27 @@ static int ata_port_resume(struct device *dev)return rc; } +/* +* For ODDs, the upper layer will poll for media change every a few seconds, +* which will make it enter and leave suspend state every a few seconds. And +* as each suspend will cause a hard/soft reset, the gain of runtime suspend +* is very little and the ODD may mis-function after constantly being reset. +* So the idle callback here will not proceed to suspend if a non-ZPODD capable +* ODD is attached to the port. +*/ static int ata_port_runtime_idle (struct device *dev) { +struct ata_port *ap =to_ata_port (dev); +struct ata_link *link; +struct ata_device *adev; + +ata_for_each_link (link, ap,HOST_FIRST ) { +ata_for_each_dev (adev, link,ENABLED ) +if (adev->class ==ATA_DEV_ATAPI && + !zpodd_dev_enabled (adev)) +return -EBUSY ; + } +return pm_runtime_suspend (dev); }
Signature · 1 line
1.7.12.4