aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorAaron Lu <aaron.lu@intel.com>2013-01-15 04:20:58 -0500
committerJeff Garzik <jgarzik@redhat.com>2013-01-21 15:40:35 -0500
commitafe759511808cd5bb508b598007cf0c7b0ca8e08 (patch)
tree26073c5a74b5fe2183aa457da6d9b5cc3d9912bb /drivers/ata
parent1757d902b029a29dfcef63609964385cf8865b5a (diff)
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 <aaron.lu@intel.com> Acked-by: Tejun Heo <tj@kernel.org> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-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
6 files changed, 133 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__ */