diff options
Diffstat (limited to 'drivers/base/platform.c')
-rw-r--r-- | drivers/base/platform.c | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c new file mode 100644 index 000000000000..996cbb4d5087 --- /dev/null +++ b/drivers/base/platform.c | |||
@@ -0,0 +1,350 @@ | |||
1 | /* | ||
2 | * platform.c - platform 'pseudo' bus for legacy devices | ||
3 | * | ||
4 | * Copyright (c) 2002-3 Patrick Mochel | ||
5 | * Copyright (c) 2002-3 Open Source Development Labs | ||
6 | * | ||
7 | * This file is released under the GPLv2 | ||
8 | * | ||
9 | * Please see Documentation/driver-model/platform.txt for more | ||
10 | * information. | ||
11 | */ | ||
12 | |||
13 | #include <linux/device.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | #include <linux/bootmem.h> | ||
18 | #include <linux/err.h> | ||
19 | |||
20 | struct device platform_bus = { | ||
21 | .bus_id = "platform", | ||
22 | }; | ||
23 | |||
24 | /** | ||
25 | * platform_get_resource - get a resource for a device | ||
26 | * @dev: platform device | ||
27 | * @type: resource type | ||
28 | * @num: resource index | ||
29 | */ | ||
30 | struct resource * | ||
31 | platform_get_resource(struct platform_device *dev, unsigned int type, | ||
32 | unsigned int num) | ||
33 | { | ||
34 | int i; | ||
35 | |||
36 | for (i = 0; i < dev->num_resources; i++) { | ||
37 | struct resource *r = &dev->resource[i]; | ||
38 | |||
39 | if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| | ||
40 | IORESOURCE_IRQ|IORESOURCE_DMA)) | ||
41 | == type) | ||
42 | if (num-- == 0) | ||
43 | return r; | ||
44 | } | ||
45 | return NULL; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * platform_get_irq - get an IRQ for a device | ||
50 | * @dev: platform device | ||
51 | * @num: IRQ number index | ||
52 | */ | ||
53 | int platform_get_irq(struct platform_device *dev, unsigned int num) | ||
54 | { | ||
55 | struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); | ||
56 | |||
57 | return r ? r->start : 0; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * platform_get_resource_byname - get a resource for a device by name | ||
62 | * @dev: platform device | ||
63 | * @type: resource type | ||
64 | * @name: resource name | ||
65 | */ | ||
66 | struct resource * | ||
67 | platform_get_resource_byname(struct platform_device *dev, unsigned int type, | ||
68 | char *name) | ||
69 | { | ||
70 | int i; | ||
71 | |||
72 | for (i = 0; i < dev->num_resources; i++) { | ||
73 | struct resource *r = &dev->resource[i]; | ||
74 | |||
75 | if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| | ||
76 | IORESOURCE_IRQ|IORESOURCE_DMA)) == type) | ||
77 | if (!strcmp(r->name, name)) | ||
78 | return r; | ||
79 | } | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * platform_get_irq - get an IRQ for a device | ||
85 | * @dev: platform device | ||
86 | * @name: IRQ name | ||
87 | */ | ||
88 | int platform_get_irq_byname(struct platform_device *dev, char *name) | ||
89 | { | ||
90 | struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); | ||
91 | |||
92 | return r ? r->start : 0; | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * platform_add_devices - add a numbers of platform devices | ||
97 | * @devs: array of platform devices to add | ||
98 | * @num: number of platform devices in array | ||
99 | */ | ||
100 | int platform_add_devices(struct platform_device **devs, int num) | ||
101 | { | ||
102 | int i, ret = 0; | ||
103 | |||
104 | for (i = 0; i < num; i++) { | ||
105 | ret = platform_device_register(devs[i]); | ||
106 | if (ret) { | ||
107 | while (--i >= 0) | ||
108 | platform_device_unregister(devs[i]); | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * platform_device_register - add a platform-level device | ||
118 | * @dev: platform device we're adding | ||
119 | * | ||
120 | */ | ||
121 | int platform_device_register(struct platform_device * pdev) | ||
122 | { | ||
123 | int i, ret = 0; | ||
124 | |||
125 | if (!pdev) | ||
126 | return -EINVAL; | ||
127 | |||
128 | if (!pdev->dev.parent) | ||
129 | pdev->dev.parent = &platform_bus; | ||
130 | |||
131 | pdev->dev.bus = &platform_bus_type; | ||
132 | |||
133 | if (pdev->id != -1) | ||
134 | snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); | ||
135 | else | ||
136 | strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); | ||
137 | |||
138 | for (i = 0; i < pdev->num_resources; i++) { | ||
139 | struct resource *p, *r = &pdev->resource[i]; | ||
140 | |||
141 | if (r->name == NULL) | ||
142 | r->name = pdev->dev.bus_id; | ||
143 | |||
144 | p = r->parent; | ||
145 | if (!p) { | ||
146 | if (r->flags & IORESOURCE_MEM) | ||
147 | p = &iomem_resource; | ||
148 | else if (r->flags & IORESOURCE_IO) | ||
149 | p = &ioport_resource; | ||
150 | } | ||
151 | |||
152 | if (p && request_resource(p, r)) { | ||
153 | printk(KERN_ERR | ||
154 | "%s: failed to claim resource %d\n", | ||
155 | pdev->dev.bus_id, i); | ||
156 | ret = -EBUSY; | ||
157 | goto failed; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | pr_debug("Registering platform device '%s'. Parent at %s\n", | ||
162 | pdev->dev.bus_id, pdev->dev.parent->bus_id); | ||
163 | |||
164 | ret = device_register(&pdev->dev); | ||
165 | if (ret == 0) | ||
166 | return ret; | ||
167 | |||
168 | failed: | ||
169 | while (--i >= 0) | ||
170 | if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) | ||
171 | release_resource(&pdev->resource[i]); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * platform_device_unregister - remove a platform-level device | ||
177 | * @dev: platform device we're removing | ||
178 | * | ||
179 | * Note that this function will also release all memory- and port-based | ||
180 | * resources owned by the device (@dev->resource). | ||
181 | */ | ||
182 | void platform_device_unregister(struct platform_device * pdev) | ||
183 | { | ||
184 | int i; | ||
185 | |||
186 | if (pdev) { | ||
187 | for (i = 0; i < pdev->num_resources; i++) { | ||
188 | struct resource *r = &pdev->resource[i]; | ||
189 | if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO)) | ||
190 | release_resource(r); | ||
191 | } | ||
192 | |||
193 | device_unregister(&pdev->dev); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | struct platform_object { | ||
198 | struct platform_device pdev; | ||
199 | struct resource resources[0]; | ||
200 | }; | ||
201 | |||
202 | static void platform_device_release_simple(struct device *dev) | ||
203 | { | ||
204 | struct platform_device *pdev = to_platform_device(dev); | ||
205 | |||
206 | kfree(container_of(pdev, struct platform_object, pdev)); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * platform_device_register_simple | ||
211 | * @name: base name of the device we're adding | ||
212 | * @id: instance id | ||
213 | * @res: set of resources that needs to be allocated for the device | ||
214 | * @num: number of resources | ||
215 | * | ||
216 | * This function creates a simple platform device that requires minimal | ||
217 | * resource and memory management. Canned release function freeing | ||
218 | * memory allocated for the device allows drivers using such devices | ||
219 | * to be unloaded iwithout waiting for the last reference to the device | ||
220 | * to be dropped. | ||
221 | */ | ||
222 | struct platform_device *platform_device_register_simple(char *name, unsigned int id, | ||
223 | struct resource *res, unsigned int num) | ||
224 | { | ||
225 | struct platform_object *pobj; | ||
226 | int retval; | ||
227 | |||
228 | pobj = kmalloc(sizeof(struct platform_object) + sizeof(struct resource) * num, GFP_KERNEL); | ||
229 | if (!pobj) { | ||
230 | retval = -ENOMEM; | ||
231 | goto error; | ||
232 | } | ||
233 | |||
234 | memset(pobj, 0, sizeof(*pobj)); | ||
235 | pobj->pdev.name = name; | ||
236 | pobj->pdev.id = id; | ||
237 | pobj->pdev.dev.release = platform_device_release_simple; | ||
238 | |||
239 | if (num) { | ||
240 | memcpy(pobj->resources, res, sizeof(struct resource) * num); | ||
241 | pobj->pdev.resource = pobj->resources; | ||
242 | pobj->pdev.num_resources = num; | ||
243 | } | ||
244 | |||
245 | retval = platform_device_register(&pobj->pdev); | ||
246 | if (retval) | ||
247 | goto error; | ||
248 | |||
249 | return &pobj->pdev; | ||
250 | |||
251 | error: | ||
252 | kfree(pobj); | ||
253 | return ERR_PTR(retval); | ||
254 | } | ||
255 | |||
256 | |||
257 | /** | ||
258 | * platform_match - bind platform device to platform driver. | ||
259 | * @dev: device. | ||
260 | * @drv: driver. | ||
261 | * | ||
262 | * Platform device IDs are assumed to be encoded like this: | ||
263 | * "<name><instance>", where <name> is a short description of the | ||
264 | * type of device, like "pci" or "floppy", and <instance> is the | ||
265 | * enumerated instance of the device, like '0' or '42'. | ||
266 | * Driver IDs are simply "<name>". | ||
267 | * So, extract the <name> from the platform_device structure, | ||
268 | * and compare it against the name of the driver. Return whether | ||
269 | * they match or not. | ||
270 | */ | ||
271 | |||
272 | static int platform_match(struct device * dev, struct device_driver * drv) | ||
273 | { | ||
274 | struct platform_device *pdev = container_of(dev, struct platform_device, dev); | ||
275 | |||
276 | return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); | ||
277 | } | ||
278 | |||
279 | static int platform_suspend(struct device * dev, pm_message_t state) | ||
280 | { | ||
281 | int ret = 0; | ||
282 | |||
283 | if (dev->driver && dev->driver->suspend) { | ||
284 | ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); | ||
285 | if (ret == 0) | ||
286 | ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); | ||
287 | if (ret == 0) | ||
288 | ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); | ||
289 | } | ||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | static int platform_resume(struct device * dev) | ||
294 | { | ||
295 | int ret = 0; | ||
296 | |||
297 | if (dev->driver && dev->driver->resume) { | ||
298 | ret = dev->driver->resume(dev, RESUME_POWER_ON); | ||
299 | if (ret == 0) | ||
300 | ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); | ||
301 | if (ret == 0) | ||
302 | ret = dev->driver->resume(dev, RESUME_ENABLE); | ||
303 | } | ||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | struct bus_type platform_bus_type = { | ||
308 | .name = "platform", | ||
309 | .match = platform_match, | ||
310 | .suspend = platform_suspend, | ||
311 | .resume = platform_resume, | ||
312 | }; | ||
313 | |||
314 | int __init platform_bus_init(void) | ||
315 | { | ||
316 | device_register(&platform_bus); | ||
317 | return bus_register(&platform_bus_type); | ||
318 | } | ||
319 | |||
320 | #ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK | ||
321 | u64 dma_get_required_mask(struct device *dev) | ||
322 | { | ||
323 | u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT); | ||
324 | u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT)); | ||
325 | u64 mask; | ||
326 | |||
327 | if (!high_totalram) { | ||
328 | /* convert to mask just covering totalram */ | ||
329 | low_totalram = (1 << (fls(low_totalram) - 1)); | ||
330 | low_totalram += low_totalram - 1; | ||
331 | mask = low_totalram; | ||
332 | } else { | ||
333 | high_totalram = (1 << (fls(high_totalram) - 1)); | ||
334 | high_totalram += high_totalram - 1; | ||
335 | mask = (((u64)high_totalram) << 32) + 0xffffffff; | ||
336 | } | ||
337 | return mask & *dev->dma_mask; | ||
338 | } | ||
339 | EXPORT_SYMBOL_GPL(dma_get_required_mask); | ||
340 | #endif | ||
341 | |||
342 | EXPORT_SYMBOL_GPL(platform_bus); | ||
343 | EXPORT_SYMBOL_GPL(platform_bus_type); | ||
344 | EXPORT_SYMBOL_GPL(platform_device_register); | ||
345 | EXPORT_SYMBOL_GPL(platform_device_register_simple); | ||
346 | EXPORT_SYMBOL_GPL(platform_device_unregister); | ||
347 | EXPORT_SYMBOL_GPL(platform_get_irq); | ||
348 | EXPORT_SYMBOL_GPL(platform_get_resource); | ||
349 | EXPORT_SYMBOL_GPL(platform_get_irq_byname); | ||
350 | EXPORT_SYMBOL_GPL(platform_get_resource_byname); | ||