aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/ata/Kconfig13
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/libata-core.c4
-rw-r--r--drivers/ata/libata-scsi.c2
-rw-r--r--drivers/ata/libata-zpodd.c100
-rw-r--r--drivers/ata/libata.h14
-rw-r--r--include/linux/libata.h3
-rw-r--r--include/uapi/linux/cdrom.h34
8 files changed, 170 insertions, 1 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index e08d322d01d7..996d16c9c6e5 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -58,6 +58,19 @@ config ATA_ACPI
58 You can disable this at kernel boot time by using the 58 You can disable this at kernel boot time by using the
59 option libata.noacpi=1 59 option libata.noacpi=1
60 60
61config 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
61config SATA_PMP 74config SATA_PMP
62 bool "SATA Port Multiplier support" 75 bool "SATA Port Multiplier support"
63 default y 76 default y
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 9329dafba91b..85e3de463ed1 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -107,3 +107,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o
107libata-$(CONFIG_ATA_SFF) += libata-sff.o 107libata-$(CONFIG_ATA_SFF) += libata-sff.o
108libata-$(CONFIG_SATA_PMP) += libata-pmp.o 108libata-$(CONFIG_SATA_PMP) += libata-pmp.o
109libata-$(CONFIG_ATA_ACPI) += libata-acpi.o 109libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
110libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 275941b576a8..c7ecd8492f1e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2401,8 +2401,10 @@ int ata_dev_configure(struct ata_device *dev)
2401 dma_dir_string = ", DMADIR"; 2401 dma_dir_string = ", DMADIR";
2402 } 2402 }
2403 2403
2404 if (ata_id_has_da(dev->id)) 2404 if (ata_id_has_da(dev->id)) {
2405 dev->flags |= ATA_DFLAG_DA; 2405 dev->flags |= ATA_DFLAG_DA;
2406 zpodd_init(dev);
2407 }
2406 2408
2407 /* print device info to dmesg */ 2409 /* print device info to dmesg */
2408 if (ata_msg_drv(ap) && print_info) 2410 if (ata_msg_drv(ap) && print_info)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 7c337e754dab..1ff018525e3b 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
3755 mutex_lock(&ap->scsi_host->scan_mutex); 3755 mutex_lock(&ap->scsi_host->scan_mutex);
3756 spin_lock_irqsave(ap->lock, flags); 3756 spin_lock_irqsave(ap->lock, flags);
3757 3757
3758 if (zpodd_dev_enabled(dev))
3759 zpodd_exit(dev);
3758 ata_acpi_unbind(dev); 3760 ata_acpi_unbind(dev);
3759 3761
3760 /* clearing dev->sdev is protected by host lock */ 3762 /* clearing dev->sdev is protected by host lock */
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
new file mode 100644
index 000000000000..27eed2f09a8a
--- /dev/null
+++ b/drivers/ata/libata-zpodd.c
@@ -0,0 +1,100 @@
1#include <linux/libata.h>
2#include <linux/cdrom.h>
3
4#include "libata.h"
5
6enum odd_mech_type {
7 ODD_MECH_TYPE_SLOT,
8 ODD_MECH_TYPE_DRAWER,
9 ODD_MECH_TYPE_UNSUPPORTED,
10};
11
12struct 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 */
18static 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
54static 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
71void 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
96void zpodd_exit(struct ata_device *dev)
97{
98 kfree(dev->zpodd);
99 dev->zpodd = NULL;
100}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 7148a58020b9..a21740b4ee11 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -230,4 +230,18 @@ static inline void ata_sff_exit(void)
230{ } 230{ }
231#endif /* CONFIG_ATA_SFF */ 231#endif /* CONFIG_ATA_SFF */
232 232
233/* libata-zpodd.c */
234#ifdef CONFIG_SATA_ZPODD
235void zpodd_init(struct ata_device *dev);
236void zpodd_exit(struct ata_device *dev);
237static inline bool zpodd_dev_enabled(struct ata_device *dev)
238{
239 return dev->zpodd != NULL;
240}
241#else /* CONFIG_SATA_ZPODD */
242static inline void zpodd_init(struct ata_device *dev) {}
243static inline void zpodd_exit(struct ata_device *dev) {}
244static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; }
245#endif /* CONFIG_SATA_ZPODD */
246
233#endif /* __LIBATA_H__ */ 247#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 7ae207eb29a0..65ff67e34b77 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -621,6 +621,9 @@ struct ata_device {
621 union acpi_object *gtf_cache; 621 union acpi_object *gtf_cache;
622 unsigned int gtf_filter; 622 unsigned int gtf_filter;
623#endif 623#endif
624#ifdef CONFIG_SATA_ZPODD
625 void *zpodd;
626#endif
624 struct device tdev; 627 struct device tdev;
625 /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ 628 /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
626 u64 n_sectors; /* size of device, if ATA */ 629 u64 n_sectors; /* size of device, if ATA */
diff --git a/include/uapi/linux/cdrom.h b/include/uapi/linux/cdrom.h
index 898b866b300c..bd17ad5aa06d 100644
--- a/include/uapi/linux/cdrom.h
+++ b/include/uapi/linux/cdrom.h
@@ -908,5 +908,39 @@ struct mode_page_header {
908 __be16 desc_length; 908 __be16 desc_length;
909}; 909};
910 910
911/* removable medium feature descriptor */
912struct 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};
911 945
912#endif /* _UAPI_LINUX_CDROM_H */ 946#endif /* _UAPI_LINUX_CDROM_H */