aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-zpodd.c
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/libata-zpodd.c
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/libata-zpodd.c')
-rw-r--r--drivers/ata/libata-zpodd.c100
1 files changed, 100 insertions, 0 deletions
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}