aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2012-10-31 17:45:02 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2012-11-14 18:28:01 -0500
commit91e5687805885f9fceb60b95e950a3d3bdcf4764 (patch)
tree4bcff02107152a509a921d34929be77681790f6f
parenta42b9bfe959519772fd8d97557c760f7cda4d325 (diff)
ACPI: Add support for platform bus type
With ACPI 5 it is now possible to enumerate traditional SoC peripherals, like serial bus controllers and slave devices behind them. These devices are typically based on IP-blocks used in many existing SoC platforms and platform drivers for them may already be present in the kernel tree. To make driver "porting" more straightforward, add ACPI support to the platform bus type. Instead of writing ACPI "glue" drivers for the existing platform drivers, register the platform bus type with ACPI to create platform device objects for the drivers and bind the corresponding ACPI handles to those platform devices. This should allow us to reuse the existing platform drivers for the devices in question with the minimum amount of modifications. This changeset is based on Mika Westerberg's and Mathias Nyman's work. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: H. Peter Anvin <hpa@zytor.com> Acked-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/acpi_platform.c285
-rw-r--r--drivers/acpi/internal.h7
-rw-r--r--drivers/acpi/scan.c16
-rw-r--r--drivers/base/platform.c5
5 files changed, 313 insertions, 1 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 82422fe90f81..227c82bbe9a1 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -36,6 +36,7 @@ acpi-y += processor_core.o
36acpi-y += ec.o 36acpi-y += ec.o
37acpi-$(CONFIG_ACPI_DOCK) += dock.o 37acpi-$(CONFIG_ACPI_DOCK) += dock.o
38acpi-y += pci_root.o pci_link.o pci_irq.o pci_bind.o 38acpi-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
39acpi-y += acpi_platform.o
39acpi-y += power.o 40acpi-y += power.o
40acpi-y += event.o 41acpi-y += event.o
41acpi-y += sysfs.o 42acpi-y += sysfs.o
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
new file mode 100644
index 000000000000..a5a23462287d
--- /dev/null
+++ b/drivers/acpi/acpi_platform.c
@@ -0,0 +1,285 @@
1/*
2 * ACPI support for platform bus type.
3 *
4 * Copyright (C) 2012, Intel Corporation
5 * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
6 * Mathias Nyman <mathias.nyman@linux.intel.com>
7 * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/acpi.h>
15#include <linux/device.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19
20ACPI_MODULE_NAME("platform");
21
22struct resource_info {
23 struct device *dev;
24 struct resource *res;
25 size_t n, cur;
26};
27
28static acpi_status acpi_platform_count_resources(struct acpi_resource *res,
29 void *data)
30{
31 struct acpi_resource_extended_irq *acpi_xirq;
32 struct resource_info *ri = data;
33
34 switch (res->type) {
35 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
36 case ACPI_RESOURCE_TYPE_IRQ:
37 ri->n++;
38 break;
39 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
40 acpi_xirq = &res->data.extended_irq;
41 ri->n += acpi_xirq->interrupt_count;
42 break;
43 case ACPI_RESOURCE_TYPE_ADDRESS32:
44 if (res->data.address32.resource_type == ACPI_IO_RANGE)
45 ri->n++;
46 break;
47 }
48
49 return AE_OK;
50}
51
52static acpi_status acpi_platform_add_resources(struct acpi_resource *res,
53 void *data)
54{
55 struct acpi_resource_fixed_memory32 *acpi_mem;
56 struct acpi_resource_address32 *acpi_add32;
57 struct acpi_resource_extended_irq *acpi_xirq;
58 struct acpi_resource_irq *acpi_irq;
59 struct resource_info *ri = data;
60 struct resource *r;
61 int irq, i;
62
63 switch (res->type) {
64 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
65 acpi_mem = &res->data.fixed_memory32;
66 r = &ri->res[ri->cur++];
67
68 r->start = acpi_mem->address;
69 r->end = r->start + acpi_mem->address_length - 1;
70 r->flags = IORESOURCE_MEM;
71
72 dev_dbg(ri->dev, "Memory32Fixed %pR\n", r);
73 break;
74
75 case ACPI_RESOURCE_TYPE_ADDRESS32:
76 acpi_add32 = &res->data.address32;
77
78 if (acpi_add32->resource_type == ACPI_IO_RANGE) {
79 r = &ri->res[ri->cur++];
80 r->start = acpi_add32->minimum;
81 r->end = r->start + acpi_add32->address_length - 1;
82 r->flags = IORESOURCE_IO;
83 dev_dbg(ri->dev, "Address32 %pR\n", r);
84 }
85 break;
86
87 case ACPI_RESOURCE_TYPE_IRQ:
88 acpi_irq = &res->data.irq;
89 r = &ri->res[ri->cur++];
90
91 irq = acpi_register_gsi(ri->dev,
92 acpi_irq->interrupts[0],
93 acpi_irq->triggering,
94 acpi_irq->polarity);
95
96 r->start = r->end = irq;
97 r->flags = IORESOURCE_IRQ;
98
99 dev_dbg(ri->dev, "IRQ %pR\n", r);
100 break;
101
102 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
103 acpi_xirq = &res->data.extended_irq;
104
105 for (i = 0; i < acpi_xirq->interrupt_count; i++, ri->cur++) {
106 r = &ri->res[ri->cur];
107 irq = acpi_register_gsi(ri->dev,
108 acpi_xirq->interrupts[i],
109 acpi_xirq->triggering,
110 acpi_xirq->polarity);
111
112 r->start = r->end = irq;
113 r->flags = IORESOURCE_IRQ;
114
115 dev_dbg(ri->dev, "Interrupt %pR\n", r);
116 }
117 break;
118 }
119
120 return AE_OK;
121}
122
123static acpi_status acpi_platform_get_device_uid(struct acpi_device *adev,
124 int *uid)
125{
126 struct acpi_device_info *info;
127 acpi_status status;
128
129 status = acpi_get_object_info(adev->handle, &info);
130 if (ACPI_FAILURE(status))
131 return status;
132
133 status = AE_NOT_EXIST;
134 if ((info->valid & ACPI_VALID_UID) &&
135 !kstrtoint(info->unique_id.string, 0, uid))
136 status = AE_OK;
137
138 kfree(info);
139 return status;
140}
141
142/**
143 * acpi_create_platform_device - Create platform device for ACPI device node
144 * @adev: ACPI device node to create a platform device for.
145 *
146 * Check if the given @adev can be represented as a platform device and, if
147 * that's the case, create and register a platform device, populate its common
148 * resources and returns a pointer to it. Otherwise, return %NULL.
149 *
150 * The platform device's name will be taken from the @adev's _HID and _UID.
151 */
152struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
153{
154 struct platform_device *pdev = NULL;
155 struct acpi_device *acpi_parent;
156 struct device *parent = NULL;
157 struct resource_info ri;
158 acpi_status status;
159 int devid;
160
161 /* If the ACPI node already has a physical device attached, skip it. */
162 if (adev->physical_node_count)
163 return NULL;
164
165 /* Use the UID of the device as the new platform device id if found. */
166 status = acpi_platform_get_device_uid(adev, &devid);
167 if (ACPI_FAILURE(status))
168 devid = -1;
169
170 memset(&ri, 0, sizeof(ri));
171
172 /* First, count the resources. */
173 status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
174 acpi_platform_count_resources, &ri);
175 if (ACPI_FAILURE(status) || !ri.n)
176 return NULL;
177
178 /* Next, allocate memory for all the resources and populate it. */
179 ri.dev = &adev->dev;
180 ri.res = kzalloc(ri.n * sizeof(struct resource), GFP_KERNEL);
181 if (!ri.res) {
182 dev_err(&adev->dev,
183 "failed to allocate memory for resources\n");
184 return NULL;
185 }
186
187 status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
188 acpi_platform_add_resources, &ri);
189 if (ACPI_FAILURE(status)) {
190 dev_err(&adev->dev, "failed to walk resources\n");
191 goto out;
192 }
193
194 if (WARN_ON(ri.n != ri.cur))
195 goto out;
196
197 /*
198 * If the ACPI node has a parent and that parent has a physical device
199 * attached to it, that physical device should be the parent of the
200 * platform device we are about to create.
201 */
202 acpi_parent = adev->parent;
203 if (acpi_parent) {
204 struct acpi_device_physical_node *entry;
205 struct list_head *list;
206
207 mutex_lock(&acpi_parent->physical_node_lock);
208 list = &acpi_parent->physical_node_list;
209 if (!list_empty(list)) {
210 entry = list_first_entry(list,
211 struct acpi_device_physical_node,
212 node);
213 parent = entry->dev;
214 }
215 mutex_unlock(&acpi_parent->physical_node_lock);
216 }
217 pdev = platform_device_register_resndata(parent, acpi_device_hid(adev),
218 devid, ri.res, ri.n, NULL, 0);
219 if (IS_ERR(pdev)) {
220 dev_err(&adev->dev, "platform device creation failed: %ld\n",
221 PTR_ERR(pdev));
222 pdev = NULL;
223 } else {
224 dev_dbg(&adev->dev, "created platform device %s\n",
225 dev_name(&pdev->dev));
226 }
227
228 out:
229 kfree(ri.res);
230 return pdev;
231}
232
233static acpi_status acpi_platform_match(acpi_handle handle, u32 depth,
234 void *data, void **return_value)
235{
236 struct platform_device *pdev = data;
237 struct acpi_device *adev;
238 acpi_status status;
239
240 status = acpi_bus_get_device(handle, &adev);
241 if (ACPI_FAILURE(status))
242 return status;
243
244 /* Skip ACPI devices that have physical device attached */
245 if (adev->physical_node_count)
246 return AE_OK;
247
248 if (!strcmp(pdev->name, acpi_device_hid(adev))) {
249 int devid;
250
251 /* Check that both name and UID match if it exists */
252 status = acpi_platform_get_device_uid(adev, &devid);
253 if (ACPI_FAILURE(status))
254 devid = -1;
255
256 if (pdev->id != devid)
257 return AE_OK;
258
259 *(acpi_handle *)return_value = handle;
260 return AE_CTRL_TERMINATE;
261 }
262
263 return AE_OK;
264}
265
266static int acpi_platform_find_device(struct device *dev, acpi_handle *handle)
267{
268 struct platform_device *pdev = to_platform_device(dev);
269
270 *handle = NULL;
271 acpi_get_devices(pdev->name, acpi_platform_match, pdev, handle);
272
273 return *handle ? 0 : -ENODEV;
274}
275
276static struct acpi_bus_type acpi_platform_bus = {
277 .bus = &platform_bus_type,
278 .find_device = acpi_platform_find_device,
279};
280
281static int __init acpi_platform_init(void)
282{
283 return register_acpi_bus_type(&acpi_platform_bus);
284}
285arch_initcall(acpi_platform_init);
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index ca75b9ce0489..57d41f6e1441 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -93,4 +93,11 @@ static inline int suspend_nvs_save(void) { return 0; }
93static inline void suspend_nvs_restore(void) {} 93static inline void suspend_nvs_restore(void) {}
94#endif 94#endif
95 95
96/*--------------------------------------------------------------------------
97 Platform bus support
98 -------------------------------------------------------------------------- */
99struct platform_device;
100
101struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
102
96#endif /* _ACPI_INTERNAL_H_ */ 103#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index a0dfdffe54d4..d842569395a9 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -29,6 +29,15 @@ extern struct acpi_device *acpi_root;
29 29
30static const char *dummy_hid = "device"; 30static const char *dummy_hid = "device";
31 31
32/*
33 * The following ACPI IDs are known to be suitable for representing as
34 * platform devices.
35 */
36static const struct acpi_device_id acpi_platform_device_ids[] = {
37
38 { }
39};
40
32static LIST_HEAD(acpi_device_list); 41static LIST_HEAD(acpi_device_list);
33static LIST_HEAD(acpi_bus_id_list); 42static LIST_HEAD(acpi_bus_id_list);
34DEFINE_MUTEX(acpi_device_lock); 43DEFINE_MUTEX(acpi_device_lock);
@@ -1513,8 +1522,13 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
1513 */ 1522 */
1514 device = NULL; 1523 device = NULL;
1515 acpi_bus_get_device(handle, &device); 1524 acpi_bus_get_device(handle, &device);
1516 if (ops->acpi_op_add && !device) 1525 if (ops->acpi_op_add && !device) {
1517 acpi_add_single_object(&device, handle, type, sta, ops); 1526 acpi_add_single_object(&device, handle, type, sta, ops);
1527 /* Is the device a known good platform device? */
1528 if (device
1529 && !acpi_match_device_ids(device, acpi_platform_device_ids))
1530 acpi_create_platform_device(device);
1531 }
1518 1532
1519 if (!device) 1533 if (!device)
1520 return AE_CTRL_DEPTH; 1534 return AE_CTRL_DEPTH;
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 72c776f2a1f5..7de29ebfce7f 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -21,6 +21,7 @@
21#include <linux/slab.h> 21#include <linux/slab.h>
22#include <linux/pm_runtime.h> 22#include <linux/pm_runtime.h>
23#include <linux/idr.h> 23#include <linux/idr.h>
24#include <linux/acpi.h>
24 25
25#include "base.h" 26#include "base.h"
26#include "power/power.h" 27#include "power/power.h"
@@ -709,6 +710,10 @@ static int platform_match(struct device *dev, struct device_driver *drv)
709 if (of_driver_match_device(dev, drv)) 710 if (of_driver_match_device(dev, drv))
710 return 1; 711 return 1;
711 712
713 /* Then try ACPI style match */
714 if (acpi_driver_match_device(dev, drv))
715 return 1;
716
712 /* Then try to match against the id table */ 717 /* Then try to match against the id table */
713 if (pdrv->id_table) 718 if (pdrv->id_table)
714 return platform_match_id(pdrv->id_table, pdev) != NULL; 719 return platform_match_id(pdrv->id_table, pdev) != NULL;