Skip to content

Commit afe7595

Browse files
Aaron LuJeff Garzik
authored andcommitted
libata: identify and init ZPODD devices
The 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. A new config CONFIG_SATA_ZPODD is added to selectively build ZPODD code. Signed-off-by: Aaron Lu <[email protected]> Acked-by: Tejun Heo <[email protected]> Signed-off-by: Jeff Garzik <[email protected]>
1 parent 1757d90 commit afe7595

File tree

8 files changed

+170
-1
lines changed

8 files changed

+170
-1
lines changed

drivers/ata/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ config ATA_ACPI
5858
You can disable this at kernel boot time by using the
5959
option libata.noacpi=1
6060

61+
config SATA_ZPODD
62+
bool "SATA Zero Power ODD Support"
63+
depends on ATA_ACPI
64+
default n
65+
help
66+
This option adds support for SATA ZPODD. It requires both
67+
ODD and the platform support, and if enabled, will automatically
68+
power on/off the ODD when certain condition is satisfied. This
69+
does not impact user's experience of the ODD, only power is saved
70+
when ODD is not in use(i.e. no disc inside).
71+
72+
If unsure, say N.
73+
6174
config SATA_PMP
6275
bool "SATA Port Multiplier support"
6376
default y

drivers/ata/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o
107107
libata-$(CONFIG_ATA_SFF) += libata-sff.o
108108
libata-$(CONFIG_SATA_PMP) += libata-pmp.o
109109
libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
110+
libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o

drivers/ata/libata-core.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2401,8 +2401,10 @@ int ata_dev_configure(struct ata_device *dev)
24012401
dma_dir_string = ", DMADIR";
24022402
}
24032403

2404-
if (ata_id_has_da(dev->id))
2404+
if (ata_id_has_da(dev->id)) {
24052405
dev->flags |= ATA_DFLAG_DA;
2406+
zpodd_init(dev);
2407+
}
24062408

24072409
/* print device info to dmesg */
24082410
if (ata_msg_drv(ap) && print_info)

drivers/ata/libata-scsi.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
37553755
mutex_lock(&ap->scsi_host->scan_mutex);
37563756
spin_lock_irqsave(ap->lock, flags);
37573757

3758+
if (zpodd_dev_enabled(dev))
3759+
zpodd_exit(dev);
37583760
ata_acpi_unbind(dev);
37593761

37603762
/* clearing dev->sdev is protected by host lock */

drivers/ata/libata-zpodd.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <linux/libata.h>
2+
#include <linux/cdrom.h>
3+
4+
#include "libata.h"
5+
6+
enum odd_mech_type {
7+
ODD_MECH_TYPE_SLOT,
8+
ODD_MECH_TYPE_DRAWER,
9+
ODD_MECH_TYPE_UNSUPPORTED,
10+
};
11+
12+
struct zpodd {
13+
enum odd_mech_type mech_type; /* init during probe, RO afterwards */
14+
struct ata_device *dev;
15+
};
16+
17+
/* Per the spec, only slot type and drawer type ODD can be supported */
18+
static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
19+
{
20+
char buf[16];
21+
unsigned int ret;
22+
struct rm_feature_desc *desc = (void *)(buf + 8);
23+
struct ata_taskfile tf = {};
24+
25+
char cdb[] = { GPCMD_GET_CONFIGURATION,
26+
2, /* only 1 feature descriptor requested */
27+
0, 3, /* 3, removable medium feature */
28+
0, 0, 0,/* reserved */
29+
0, sizeof(buf),
30+
0, 0, 0,
31+
};
32+
33+
tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
34+
tf.command = ATA_CMD_PACKET;
35+
tf.protocol = ATAPI_PROT_PIO;
36+
tf.lbam = sizeof(buf);
37+
38+
ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
39+
buf, sizeof(buf), 0);
40+
if (ret)
41+
return ODD_MECH_TYPE_UNSUPPORTED;
42+
43+
if (be16_to_cpu(desc->feature_code) != 3)
44+
return ODD_MECH_TYPE_UNSUPPORTED;
45+
46+
if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
47+
return ODD_MECH_TYPE_SLOT;
48+
else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
49+
return ODD_MECH_TYPE_DRAWER;
50+
else
51+
return ODD_MECH_TYPE_UNSUPPORTED;
52+
}
53+
54+
static bool odd_can_poweroff(struct ata_device *ata_dev)
55+
{
56+
acpi_handle handle;
57+
acpi_status status;
58+
struct acpi_device *acpi_dev;
59+
60+
handle = ata_dev_acpi_handle(ata_dev);
61+
if (!handle)
62+
return false;
63+
64+
status = acpi_bus_get_device(handle, &acpi_dev);
65+
if (ACPI_FAILURE(status))
66+
return false;
67+
68+
return acpi_device_can_poweroff(acpi_dev);
69+
}
70+
71+
void zpodd_init(struct ata_device *dev)
72+
{
73+
enum odd_mech_type mech_type;
74+
struct zpodd *zpodd;
75+
76+
if (dev->zpodd)
77+
return;
78+
79+
if (!odd_can_poweroff(dev))
80+
return;
81+
82+
mech_type = zpodd_get_mech_type(dev);
83+
if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
84+
return;
85+
86+
zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
87+
if (!zpodd)
88+
return;
89+
90+
zpodd->mech_type = mech_type;
91+
92+
zpodd->dev = dev;
93+
dev->zpodd = zpodd;
94+
}
95+
96+
void zpodd_exit(struct ata_device *dev)
97+
{
98+
kfree(dev->zpodd);
99+
dev->zpodd = NULL;
100+
}

drivers/ata/libata.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,18 @@ static inline void ata_sff_exit(void)
230230
{ }
231231
#endif /* CONFIG_ATA_SFF */
232232

233+
/* libata-zpodd.c */
234+
#ifdef CONFIG_SATA_ZPODD
235+
void zpodd_init(struct ata_device *dev);
236+
void zpodd_exit(struct ata_device *dev);
237+
static inline bool zpodd_dev_enabled(struct ata_device *dev)
238+
{
239+
return dev->zpodd != NULL;
240+
}
241+
#else /* CONFIG_SATA_ZPODD */
242+
static inline void zpodd_init(struct ata_device *dev) {}
243+
static inline void zpodd_exit(struct ata_device *dev) {}
244+
static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; }
245+
#endif /* CONFIG_SATA_ZPODD */
246+
233247
#endif /* __LIBATA_H__ */

include/linux/libata.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,9 @@ struct ata_device {
620620
#ifdef CONFIG_ATA_ACPI
621621
union acpi_object *gtf_cache;
622622
unsigned int gtf_filter;
623+
#endif
624+
#ifdef CONFIG_SATA_ZPODD
625+
void *zpodd;
623626
#endif
624627
struct device tdev;
625628
/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */

include/uapi/linux/cdrom.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,5 +908,39 @@ struct mode_page_header {
908908
__be16 desc_length;
909909
};
910910

911+
/* removable medium feature descriptor */
912+
struct rm_feature_desc {
913+
__be16 feature_code;
914+
#if defined(__BIG_ENDIAN_BITFIELD)
915+
__u8 reserved1:2;
916+
__u8 feature_version:4;
917+
__u8 persistent:1;
918+
__u8 curr:1;
919+
#elif defined(__LITTLE_ENDIAN_BITFIELD)
920+
__u8 curr:1;
921+
__u8 persistent:1;
922+
__u8 feature_version:4;
923+
__u8 reserved1:2;
924+
#endif
925+
__u8 add_len;
926+
#if defined(__BIG_ENDIAN_BITFIELD)
927+
__u8 mech_type:3;
928+
__u8 load:1;
929+
__u8 eject:1;
930+
__u8 pvnt_jmpr:1;
931+
__u8 dbml:1;
932+
__u8 lock:1;
933+
#elif defined(__LITTLE_ENDIAN_BITFIELD)
934+
__u8 lock:1;
935+
__u8 dbml:1;
936+
__u8 pvnt_jmpr:1;
937+
__u8 eject:1;
938+
__u8 load:1;
939+
__u8 mech_type:3;
940+
#endif
941+
__u8 reserved2;
942+
__u8 reserved3;
943+
__u8 reserved4;
944+
};
911945

912946
#endif /* _UAPI_LINUX_CDROM_H */

0 commit comments

Comments
 (0)