Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 356948
b: refs/heads/master
c: 3dc6744
h: refs/heads/master
v: v3
  • Loading branch information
Aaron Lu authored and Jeff Garzik committed Jan 21, 2013
1 parent 13d94ec commit 9e85b2a
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f064a20dded807448669426c9bfb7d03aba5659c
refs/heads/master: 3dc67440d99b2c718ef5f1eb1424a9066ffa3fb9
14 changes: 12 additions & 2 deletions trunk/drivers/ata/libata-eh.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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] =
Expand Down Expand Up @@ -4022,6 +4022,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);
Expand All @@ -4034,6 +4035,15 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)

WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED);

/*
* If we have a ZPODD attached, check its zero
* power ready status before the port is frozen.
*/
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)
Expand Down
75 changes: 75 additions & 0 deletions trunk/drivers/ata/libata-zpodd.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#include <linux/libata.h>
#include <linux/cdrom.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
#include <scsi/scsi_device.h>

#include "libata.h"

static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */
module_param(zpodd_poweroff_delay, int, 0644);
MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds");

enum odd_mech_type {
ODD_MECH_TYPE_SLOT,
ODD_MECH_TYPE_DRAWER,
Expand All @@ -18,6 +23,9 @@ struct zpodd {
/* The following fields are synchronized by PM core. */
bool from_notify; /* resumed as a result of
* acpi wake notification */
bool zp_ready; /* ZP ready state */
unsigned long last_ready; /* last ZP ready timestamp */
bool zp_sampled; /* ZP ready state sampled */
};

/* Per the spec, only slot type and drawer type ODD can be supported */
Expand Down Expand Up @@ -74,6 +82,73 @@ static bool odd_can_poweroff(struct ata_device *ata_dev)
return acpi_device_can_poweroff(acpi_dev);
}

/* Test if ODD is zero power ready by sense code */
static bool zpready(struct ata_device *dev)
{
u8 sense_key, *sense_buf;
unsigned int ret, asc, ascq, add_len;
struct zpodd *zpodd = dev->zpodd;

ret = atapi_eh_tur(dev, &sense_key);

if (!ret || sense_key != NOT_READY)
return false;

sense_buf = dev->link->ap->sector_buf;
ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
if (ret)
return false;

/* sense valid */
if ((sense_buf[0] & 0x7f) != 0x70)
return false;

add_len = sense_buf[7];
/* has asc and ascq */
if (add_len < 6)
return false;

asc = sense_buf[12];
ascq = sense_buf[13];

if (zpodd->mech_type == ODD_MECH_TYPE_SLOT)
/* no media inside */
return asc == 0x3a;
else
/* no media inside and door closed */
return asc == 0x3a && ascq == 0x01;
}

/*
* Update the zpodd->zp_ready field. This field will only be set
* if the ODD has stayed in ZP ready state for zpodd_poweroff_delay
* time, and will be used to decide if power off is allowed. If it
* is set, it will be cleared during resume from powered off state.
*/
void zpodd_on_suspend(struct ata_device *dev)
{
struct zpodd *zpodd = dev->zpodd;
unsigned long expires;

if (!zpready(dev)) {
zpodd->zp_sampled = false;
return;
}

if (!zpodd->zp_sampled) {
zpodd->zp_sampled = true;
zpodd->last_ready = jiffies;
return;
}

expires = zpodd->last_ready +
msecs_to_jiffies(zpodd_poweroff_delay * 1000);
if (time_before(jiffies, expires))
return;

zpodd->zp_ready = true;
}

static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
{
struct ata_device *ata_dev = context;
Expand Down
5 changes: 5 additions & 0 deletions trunk/drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -238,10 +241,12 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev)
{
return dev->zpodd != NULL;
}
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__ */

0 comments on commit 9e85b2a

Please sign in to comment.