Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 356946
b: refs/heads/master
c: afe7595
h: refs/heads/master
v: v3
  • Loading branch information
Aaron Lu authored and Jeff Garzik committed Jan 21, 2013
1 parent a3f629f commit f94f0d6
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 2 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: 1757d902b029a29dfcef63609964385cf8865b5a
refs/heads/master: afe759511808cd5bb508b598007cf0c7b0ca8e08
13 changes: 13 additions & 0 deletions trunk/drivers/ata/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/ata/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 3 additions & 1 deletion trunk/drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2401,8 +2401,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)
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
mutex_lock(&ap->scsi_host->scan_mutex);
spin_lock_irqsave(ap->lock, flags);

if (zpodd_dev_enabled(dev))
zpodd_exit(dev);
ata_acpi_unbind(dev);

/* clearing dev->sdev is protected by host lock */
Expand Down
100 changes: 100 additions & 0 deletions trunk/drivers/ata/libata-zpodd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <linux/libata.h>
#include <linux/cdrom.h>

#include "libata.h"

enum odd_mech_type {
ODD_MECH_TYPE_SLOT,
ODD_MECH_TYPE_DRAWER,
ODD_MECH_TYPE_UNSUPPORTED,
};

struct zpodd {
enum odd_mech_type mech_type; /* init during probe, RO afterwards */
struct ata_device *dev;
};

/* Per the spec, only slot type and drawer type ODD can be supported */
static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
{
char buf[16];
unsigned int ret;
struct rm_feature_desc *desc = (void *)(buf + 8);
struct ata_taskfile tf = {};

char cdb[] = { GPCMD_GET_CONFIGURATION,
2, /* only 1 feature descriptor requested */
0, 3, /* 3, removable medium feature */
0, 0, 0,/* reserved */
0, sizeof(buf),
0, 0, 0,
};

tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.command = ATA_CMD_PACKET;
tf.protocol = ATAPI_PROT_PIO;
tf.lbam = sizeof(buf);

ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
buf, sizeof(buf), 0);
if (ret)
return ODD_MECH_TYPE_UNSUPPORTED;

if (be16_to_cpu(desc->feature_code) != 3)
return ODD_MECH_TYPE_UNSUPPORTED;

if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
return ODD_MECH_TYPE_SLOT;
else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
return ODD_MECH_TYPE_DRAWER;
else
return ODD_MECH_TYPE_UNSUPPORTED;
}

static bool odd_can_poweroff(struct ata_device *ata_dev)
{
acpi_handle handle;
acpi_status status;
struct acpi_device *acpi_dev;

handle = ata_dev_acpi_handle(ata_dev);
if (!handle)
return false;

status = acpi_bus_get_device(handle, &acpi_dev);
if (ACPI_FAILURE(status))
return false;

return acpi_device_can_poweroff(acpi_dev);
}

void zpodd_init(struct ata_device *dev)
{
enum odd_mech_type mech_type;
struct zpodd *zpodd;

if (dev->zpodd)
return;

if (!odd_can_poweroff(dev))
return;

mech_type = zpodd_get_mech_type(dev);
if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
return;

zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
if (!zpodd)
return;

zpodd->mech_type = mech_type;

zpodd->dev = dev;
dev->zpodd = zpodd;
}

void zpodd_exit(struct ata_device *dev)
{
kfree(dev->zpodd);
dev->zpodd = NULL;
}
14 changes: 14 additions & 0 deletions trunk/drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 != NULL;
}
#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__ */
3 changes: 3 additions & 0 deletions trunk/include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@ struct ata_device {
#ifdef CONFIG_ATA_ACPI
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 */
Expand Down
34 changes: 34 additions & 0 deletions trunk/include/uapi/linux/cdrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

0 comments on commit f94f0d6

Please sign in to comment.