1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/arch/arm/common/amba.c |
4 | * |
5 | * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved. |
6 | */ |
7 | #include <linux/module.h> |
8 | #include <linux/init.h> |
9 | #include <linux/device.h> |
10 | #include <linux/string.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/io.h> |
13 | #include <linux/pm.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <linux/pm_domain.h> |
16 | #include <linux/amba/bus.h> |
17 | #include <linux/sizes.h> |
18 | #include <linux/limits.h> |
19 | #include <linux/clk/clk-conf.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/property.h> |
22 | #include <linux/reset.h> |
23 | #include <linux/of_irq.h> |
24 | #include <linux/of_device.h> |
25 | #include <linux/acpi.h> |
26 | #include <linux/iommu.h> |
27 | #include <linux/dma-map-ops.h> |
28 | |
29 | #define to_amba_driver(d) container_of(d, struct amba_driver, drv) |
30 | |
31 | /* called on periphid match and class 0x9 coresight device. */ |
32 | static int |
33 | amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev) |
34 | { |
35 | int ret = 0; |
36 | struct amba_cs_uci_id *uci; |
37 | |
38 | uci = table->data; |
39 | |
40 | /* no table data or zero mask - return match on periphid */ |
41 | if (!uci || (uci->devarch_mask == 0)) |
42 | return 1; |
43 | |
44 | /* test against read devtype and masked devarch value */ |
45 | ret = (dev->uci.devtype == uci->devtype) && |
46 | ((dev->uci.devarch & uci->devarch_mask) == uci->devarch); |
47 | return ret; |
48 | } |
49 | |
50 | static const struct amba_id * |
51 | amba_lookup(const struct amba_id *table, struct amba_device *dev) |
52 | { |
53 | while (table->mask) { |
54 | if (((dev->periphid & table->mask) == table->id) && |
55 | ((dev->cid != CORESIGHT_CID) || |
56 | (amba_cs_uci_id_match(table, dev)))) |
57 | return table; |
58 | table++; |
59 | } |
60 | return NULL; |
61 | } |
62 | |
63 | static int amba_get_enable_pclk(struct amba_device *pcdev) |
64 | { |
65 | int ret; |
66 | |
67 | pcdev->pclk = clk_get(dev: &pcdev->dev, id: "apb_pclk" ); |
68 | if (IS_ERR(ptr: pcdev->pclk)) |
69 | return PTR_ERR(ptr: pcdev->pclk); |
70 | |
71 | ret = clk_prepare_enable(clk: pcdev->pclk); |
72 | if (ret) |
73 | clk_put(clk: pcdev->pclk); |
74 | |
75 | return ret; |
76 | } |
77 | |
78 | static void amba_put_disable_pclk(struct amba_device *pcdev) |
79 | { |
80 | clk_disable_unprepare(clk: pcdev->pclk); |
81 | clk_put(clk: pcdev->pclk); |
82 | } |
83 | |
84 | |
85 | static ssize_t driver_override_show(struct device *_dev, |
86 | struct device_attribute *attr, char *buf) |
87 | { |
88 | struct amba_device *dev = to_amba_device(_dev); |
89 | ssize_t len; |
90 | |
91 | device_lock(dev: _dev); |
92 | len = sprintf(buf, fmt: "%s\n" , dev->driver_override); |
93 | device_unlock(dev: _dev); |
94 | return len; |
95 | } |
96 | |
97 | static ssize_t driver_override_store(struct device *_dev, |
98 | struct device_attribute *attr, |
99 | const char *buf, size_t count) |
100 | { |
101 | struct amba_device *dev = to_amba_device(_dev); |
102 | int ret; |
103 | |
104 | ret = driver_set_override(dev: _dev, override: &dev->driver_override, s: buf, len: count); |
105 | if (ret) |
106 | return ret; |
107 | |
108 | return count; |
109 | } |
110 | static DEVICE_ATTR_RW(driver_override); |
111 | |
112 | #define amba_attr_func(name,fmt,arg...) \ |
113 | static ssize_t name##_show(struct device *_dev, \ |
114 | struct device_attribute *attr, char *buf) \ |
115 | { \ |
116 | struct amba_device *dev = to_amba_device(_dev); \ |
117 | return sprintf(buf, fmt, arg); \ |
118 | } \ |
119 | static DEVICE_ATTR_RO(name) |
120 | |
121 | amba_attr_func(id, "%08x\n" , dev->periphid); |
122 | amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n" , |
123 | (unsigned long long)dev->res.start, (unsigned long long)dev->res.end, |
124 | dev->res.flags); |
125 | |
126 | static struct attribute *amba_dev_attrs[] = { |
127 | &dev_attr_id.attr, |
128 | &dev_attr_resource.attr, |
129 | &dev_attr_driver_override.attr, |
130 | NULL, |
131 | }; |
132 | ATTRIBUTE_GROUPS(amba_dev); |
133 | |
134 | static int amba_read_periphid(struct amba_device *dev) |
135 | { |
136 | struct reset_control *rstc; |
137 | u32 size, pid, cid; |
138 | void __iomem *tmp; |
139 | int i, ret; |
140 | |
141 | ret = dev_pm_domain_attach(dev: &dev->dev, power_on: true); |
142 | if (ret) { |
143 | dev_dbg(&dev->dev, "can't get PM domain: %d\n" , ret); |
144 | goto err_out; |
145 | } |
146 | |
147 | ret = amba_get_enable_pclk(pcdev: dev); |
148 | if (ret) { |
149 | dev_dbg(&dev->dev, "can't get pclk: %d\n" , ret); |
150 | goto err_pm; |
151 | } |
152 | |
153 | /* |
154 | * Find reset control(s) of the amba bus and de-assert them. |
155 | */ |
156 | rstc = of_reset_control_array_get_optional_shared(node: dev->dev.of_node); |
157 | if (IS_ERR(ptr: rstc)) { |
158 | ret = PTR_ERR(ptr: rstc); |
159 | if (ret != -EPROBE_DEFER) |
160 | dev_err(&dev->dev, "can't get reset: %d\n" , ret); |
161 | goto err_clk; |
162 | } |
163 | reset_control_deassert(rstc); |
164 | reset_control_put(rstc); |
165 | |
166 | size = resource_size(res: &dev->res); |
167 | tmp = ioremap(offset: dev->res.start, size); |
168 | if (!tmp) { |
169 | ret = -ENOMEM; |
170 | goto err_clk; |
171 | } |
172 | |
173 | /* |
174 | * Read pid and cid based on size of resource |
175 | * they are located at end of region |
176 | */ |
177 | for (pid = 0, i = 0; i < 4; i++) |
178 | pid |= (readl(addr: tmp + size - 0x20 + 4 * i) & 255) << (i * 8); |
179 | for (cid = 0, i = 0; i < 4; i++) |
180 | cid |= (readl(addr: tmp + size - 0x10 + 4 * i) & 255) << (i * 8); |
181 | |
182 | if (cid == CORESIGHT_CID) { |
183 | /* set the base to the start of the last 4k block */ |
184 | void __iomem *csbase = tmp + size - 4096; |
185 | |
186 | dev->uci.devarch = readl(addr: csbase + UCI_REG_DEVARCH_OFFSET); |
187 | dev->uci.devtype = readl(addr: csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; |
188 | } |
189 | |
190 | if (cid == AMBA_CID || cid == CORESIGHT_CID) { |
191 | dev->periphid = pid; |
192 | dev->cid = cid; |
193 | } |
194 | |
195 | if (!dev->periphid) |
196 | ret = -ENODEV; |
197 | |
198 | iounmap(addr: tmp); |
199 | |
200 | err_clk: |
201 | amba_put_disable_pclk(pcdev: dev); |
202 | err_pm: |
203 | dev_pm_domain_detach(dev: &dev->dev, power_off: true); |
204 | err_out: |
205 | return ret; |
206 | } |
207 | |
208 | static int amba_match(struct device *dev, struct device_driver *drv) |
209 | { |
210 | struct amba_device *pcdev = to_amba_device(dev); |
211 | struct amba_driver *pcdrv = to_amba_driver(drv); |
212 | |
213 | mutex_lock(&pcdev->periphid_lock); |
214 | if (!pcdev->periphid) { |
215 | int ret = amba_read_periphid(dev: pcdev); |
216 | |
217 | /* |
218 | * Returning any error other than -EPROBE_DEFER from bus match |
219 | * can cause driver registration failure. So, if there's a |
220 | * permanent failure in reading pid and cid, simply map it to |
221 | * -EPROBE_DEFER. |
222 | */ |
223 | if (ret) { |
224 | mutex_unlock(lock: &pcdev->periphid_lock); |
225 | return -EPROBE_DEFER; |
226 | } |
227 | dev_set_uevent_suppress(dev, val: false); |
228 | kobject_uevent(kobj: &dev->kobj, action: KOBJ_ADD); |
229 | } |
230 | mutex_unlock(lock: &pcdev->periphid_lock); |
231 | |
232 | /* When driver_override is set, only bind to the matching driver */ |
233 | if (pcdev->driver_override) |
234 | return !strcmp(pcdev->driver_override, drv->name); |
235 | |
236 | return amba_lookup(table: pcdrv->id_table, dev: pcdev) != NULL; |
237 | } |
238 | |
239 | static int amba_uevent(const struct device *dev, struct kobj_uevent_env *env) |
240 | { |
241 | const struct amba_device *pcdev = to_amba_device(dev); |
242 | int retval = 0; |
243 | |
244 | retval = add_uevent_var(env, format: "AMBA_ID=%08x" , pcdev->periphid); |
245 | if (retval) |
246 | return retval; |
247 | |
248 | retval = add_uevent_var(env, format: "MODALIAS=amba:d%08X" , pcdev->periphid); |
249 | return retval; |
250 | } |
251 | |
252 | static int of_amba_device_decode_irq(struct amba_device *dev) |
253 | { |
254 | struct device_node *node = dev->dev.of_node; |
255 | int i, irq = 0; |
256 | |
257 | if (IS_ENABLED(CONFIG_OF_IRQ) && node) { |
258 | /* Decode the IRQs and address ranges */ |
259 | for (i = 0; i < AMBA_NR_IRQS; i++) { |
260 | irq = of_irq_get(dev: node, index: i); |
261 | if (irq < 0) { |
262 | if (irq == -EPROBE_DEFER) |
263 | return irq; |
264 | irq = 0; |
265 | } |
266 | |
267 | dev->irq[i] = irq; |
268 | } |
269 | } |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | /* |
275 | * These are the device model conversion veneers; they convert the |
276 | * device model structures to our more specific structures. |
277 | */ |
278 | static int amba_probe(struct device *dev) |
279 | { |
280 | struct amba_device *pcdev = to_amba_device(dev); |
281 | struct amba_driver *pcdrv = to_amba_driver(dev->driver); |
282 | const struct amba_id *id = amba_lookup(table: pcdrv->id_table, dev: pcdev); |
283 | int ret; |
284 | |
285 | do { |
286 | ret = of_amba_device_decode_irq(dev: pcdev); |
287 | if (ret) |
288 | break; |
289 | |
290 | ret = of_clk_set_defaults(node: dev->of_node, clk_supplier: false); |
291 | if (ret < 0) |
292 | break; |
293 | |
294 | ret = dev_pm_domain_attach(dev, power_on: true); |
295 | if (ret) |
296 | break; |
297 | |
298 | ret = amba_get_enable_pclk(pcdev); |
299 | if (ret) { |
300 | dev_pm_domain_detach(dev, power_off: true); |
301 | break; |
302 | } |
303 | |
304 | pm_runtime_get_noresume(dev); |
305 | pm_runtime_set_active(dev); |
306 | pm_runtime_enable(dev); |
307 | |
308 | ret = pcdrv->probe(pcdev, id); |
309 | if (ret == 0) |
310 | break; |
311 | |
312 | pm_runtime_disable(dev); |
313 | pm_runtime_set_suspended(dev); |
314 | pm_runtime_put_noidle(dev); |
315 | |
316 | amba_put_disable_pclk(pcdev); |
317 | dev_pm_domain_detach(dev, power_off: true); |
318 | } while (0); |
319 | |
320 | return ret; |
321 | } |
322 | |
323 | static void amba_remove(struct device *dev) |
324 | { |
325 | struct amba_device *pcdev = to_amba_device(dev); |
326 | struct amba_driver *drv = to_amba_driver(dev->driver); |
327 | |
328 | pm_runtime_get_sync(dev); |
329 | if (drv->remove) |
330 | drv->remove(pcdev); |
331 | pm_runtime_put_noidle(dev); |
332 | |
333 | /* Undo the runtime PM settings in amba_probe() */ |
334 | pm_runtime_disable(dev); |
335 | pm_runtime_set_suspended(dev); |
336 | pm_runtime_put_noidle(dev); |
337 | |
338 | amba_put_disable_pclk(pcdev); |
339 | dev_pm_domain_detach(dev, power_off: true); |
340 | } |
341 | |
342 | static void amba_shutdown(struct device *dev) |
343 | { |
344 | struct amba_driver *drv; |
345 | |
346 | if (!dev->driver) |
347 | return; |
348 | |
349 | drv = to_amba_driver(dev->driver); |
350 | if (drv->shutdown) |
351 | drv->shutdown(to_amba_device(dev)); |
352 | } |
353 | |
354 | static int amba_dma_configure(struct device *dev) |
355 | { |
356 | struct amba_driver *drv = to_amba_driver(dev->driver); |
357 | enum dev_dma_attr attr; |
358 | int ret = 0; |
359 | |
360 | if (dev->of_node) { |
361 | ret = of_dma_configure(dev, np: dev->of_node, force_dma: true); |
362 | } else if (has_acpi_companion(dev)) { |
363 | attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode)); |
364 | ret = acpi_dma_configure(dev, attr); |
365 | } |
366 | |
367 | if (!ret && !drv->driver_managed_dma) { |
368 | ret = iommu_device_use_default_domain(dev); |
369 | if (ret) |
370 | arch_teardown_dma_ops(dev); |
371 | } |
372 | |
373 | return ret; |
374 | } |
375 | |
376 | static void amba_dma_cleanup(struct device *dev) |
377 | { |
378 | struct amba_driver *drv = to_amba_driver(dev->driver); |
379 | |
380 | if (!drv->driver_managed_dma) |
381 | iommu_device_unuse_default_domain(dev); |
382 | } |
383 | |
384 | #ifdef CONFIG_PM |
385 | /* |
386 | * Hooks to provide runtime PM of the pclk (bus clock). It is safe to |
387 | * enable/disable the bus clock at runtime PM suspend/resume as this |
388 | * does not result in loss of context. |
389 | */ |
390 | static int amba_pm_runtime_suspend(struct device *dev) |
391 | { |
392 | struct amba_device *pcdev = to_amba_device(dev); |
393 | int ret = pm_generic_runtime_suspend(dev); |
394 | |
395 | if (ret == 0 && dev->driver) { |
396 | if (pm_runtime_is_irq_safe(dev)) |
397 | clk_disable(clk: pcdev->pclk); |
398 | else |
399 | clk_disable_unprepare(clk: pcdev->pclk); |
400 | } |
401 | |
402 | return ret; |
403 | } |
404 | |
405 | static int amba_pm_runtime_resume(struct device *dev) |
406 | { |
407 | struct amba_device *pcdev = to_amba_device(dev); |
408 | int ret; |
409 | |
410 | if (dev->driver) { |
411 | if (pm_runtime_is_irq_safe(dev)) |
412 | ret = clk_enable(clk: pcdev->pclk); |
413 | else |
414 | ret = clk_prepare_enable(clk: pcdev->pclk); |
415 | /* Failure is probably fatal to the system, but... */ |
416 | if (ret) |
417 | return ret; |
418 | } |
419 | |
420 | return pm_generic_runtime_resume(dev); |
421 | } |
422 | #endif /* CONFIG_PM */ |
423 | |
424 | static const struct dev_pm_ops amba_pm = { |
425 | SET_RUNTIME_PM_OPS( |
426 | amba_pm_runtime_suspend, |
427 | amba_pm_runtime_resume, |
428 | NULL |
429 | ) |
430 | }; |
431 | |
432 | /* |
433 | * Primecells are part of the Advanced Microcontroller Bus Architecture, |
434 | * so we call the bus "amba". |
435 | * DMA configuration for platform and AMBA bus is same. So here we reuse |
436 | * platform's DMA config routine. |
437 | */ |
438 | struct bus_type amba_bustype = { |
439 | .name = "amba" , |
440 | .dev_groups = amba_dev_groups, |
441 | .match = amba_match, |
442 | .uevent = amba_uevent, |
443 | .probe = amba_probe, |
444 | .remove = amba_remove, |
445 | .shutdown = amba_shutdown, |
446 | .dma_configure = amba_dma_configure, |
447 | .dma_cleanup = amba_dma_cleanup, |
448 | .pm = &amba_pm, |
449 | }; |
450 | EXPORT_SYMBOL_GPL(amba_bustype); |
451 | |
452 | static int __init amba_init(void) |
453 | { |
454 | return bus_register(bus: &amba_bustype); |
455 | } |
456 | |
457 | postcore_initcall(amba_init); |
458 | |
459 | static int amba_proxy_probe(struct amba_device *adev, |
460 | const struct amba_id *id) |
461 | { |
462 | WARN(1, "Stub driver should never match any device.\n" ); |
463 | return -ENODEV; |
464 | } |
465 | |
466 | static const struct amba_id amba_stub_drv_ids[] = { |
467 | { 0, 0 }, |
468 | }; |
469 | |
470 | static struct amba_driver amba_proxy_drv = { |
471 | .drv = { |
472 | .name = "amba-proxy" , |
473 | }, |
474 | .probe = amba_proxy_probe, |
475 | .id_table = amba_stub_drv_ids, |
476 | }; |
477 | |
478 | static int __init amba_stub_drv_init(void) |
479 | { |
480 | if (!IS_ENABLED(CONFIG_MODULES)) |
481 | return 0; |
482 | |
483 | /* |
484 | * The amba_match() function will get called only if there is at least |
485 | * one amba driver registered. If all amba drivers are modules and are |
486 | * only loaded based on uevents, then we'll hit a chicken-and-egg |
487 | * situation where amba_match() is waiting on drivers and drivers are |
488 | * waiting on amba_match(). So, register a stub driver to make sure |
489 | * amba_match() is called even if no amba driver has been registered. |
490 | */ |
491 | return amba_driver_register(drv: &amba_proxy_drv); |
492 | } |
493 | late_initcall_sync(amba_stub_drv_init); |
494 | |
495 | /** |
496 | * amba_driver_register - register an AMBA device driver |
497 | * @drv: amba device driver structure |
498 | * |
499 | * Register an AMBA device driver with the Linux device model |
500 | * core. If devices pre-exist, the drivers probe function will |
501 | * be called. |
502 | */ |
503 | int amba_driver_register(struct amba_driver *drv) |
504 | { |
505 | if (!drv->probe) |
506 | return -EINVAL; |
507 | |
508 | drv->drv.bus = &amba_bustype; |
509 | |
510 | return driver_register(drv: &drv->drv); |
511 | } |
512 | EXPORT_SYMBOL(amba_driver_register); |
513 | |
514 | /** |
515 | * amba_driver_unregister - remove an AMBA device driver |
516 | * @drv: AMBA device driver structure to remove |
517 | * |
518 | * Unregister an AMBA device driver from the Linux device |
519 | * model. The device model will call the drivers remove function |
520 | * for each device the device driver is currently handling. |
521 | */ |
522 | void amba_driver_unregister(struct amba_driver *drv) |
523 | { |
524 | driver_unregister(drv: &drv->drv); |
525 | } |
526 | EXPORT_SYMBOL(amba_driver_unregister); |
527 | |
528 | static void amba_device_release(struct device *dev) |
529 | { |
530 | struct amba_device *d = to_amba_device(dev); |
531 | |
532 | fwnode_handle_put(dev_fwnode(&d->dev)); |
533 | if (d->res.parent) |
534 | release_resource(new: &d->res); |
535 | mutex_destroy(lock: &d->periphid_lock); |
536 | kfree(objp: d); |
537 | } |
538 | |
539 | /** |
540 | * amba_device_add - add a previously allocated AMBA device structure |
541 | * @dev: AMBA device allocated by amba_device_alloc |
542 | * @parent: resource parent for this devices resources |
543 | * |
544 | * Claim the resource, and read the device cell ID if not already |
545 | * initialized. Register the AMBA device with the Linux device |
546 | * manager. |
547 | */ |
548 | int amba_device_add(struct amba_device *dev, struct resource *parent) |
549 | { |
550 | int ret; |
551 | |
552 | fwnode_handle_get(dev_fwnode(&dev->dev)); |
553 | |
554 | ret = request_resource(root: parent, new: &dev->res); |
555 | if (ret) |
556 | return ret; |
557 | |
558 | /* If primecell ID isn't hard-coded, figure it out */ |
559 | if (!dev->periphid) { |
560 | /* |
561 | * AMBA device uevents require reading its pid and cid |
562 | * registers. To do this, the device must be on, clocked and |
563 | * out of reset. However in some cases those resources might |
564 | * not yet be available. If that's the case, we suppress the |
565 | * generation of uevents until we can read the pid and cid |
566 | * registers. See also amba_match(). |
567 | */ |
568 | if (amba_read_periphid(dev)) |
569 | dev_set_uevent_suppress(dev: &dev->dev, val: true); |
570 | } |
571 | |
572 | ret = device_add(dev: &dev->dev); |
573 | if (ret) |
574 | release_resource(new: &dev->res); |
575 | |
576 | return ret; |
577 | } |
578 | EXPORT_SYMBOL_GPL(amba_device_add); |
579 | |
580 | static void amba_device_initialize(struct amba_device *dev, const char *name) |
581 | { |
582 | device_initialize(dev: &dev->dev); |
583 | if (name) |
584 | dev_set_name(dev: &dev->dev, name: "%s" , name); |
585 | dev->dev.release = amba_device_release; |
586 | dev->dev.bus = &amba_bustype; |
587 | dev->dev.dma_mask = &dev->dev.coherent_dma_mask; |
588 | dev->dev.dma_parms = &dev->dma_parms; |
589 | dev->res.name = dev_name(dev: &dev->dev); |
590 | mutex_init(&dev->periphid_lock); |
591 | } |
592 | |
593 | /** |
594 | * amba_device_alloc - allocate an AMBA device |
595 | * @name: sysfs name of the AMBA device |
596 | * @base: base of AMBA device |
597 | * @size: size of AMBA device |
598 | * |
599 | * Allocate and initialize an AMBA device structure. Returns %NULL |
600 | * on failure. |
601 | */ |
602 | struct amba_device *amba_device_alloc(const char *name, resource_size_t base, |
603 | size_t size) |
604 | { |
605 | struct amba_device *dev; |
606 | |
607 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
608 | if (dev) { |
609 | amba_device_initialize(dev, name); |
610 | dev->res.start = base; |
611 | dev->res.end = base + size - 1; |
612 | dev->res.flags = IORESOURCE_MEM; |
613 | } |
614 | |
615 | return dev; |
616 | } |
617 | EXPORT_SYMBOL_GPL(amba_device_alloc); |
618 | |
619 | /** |
620 | * amba_device_register - register an AMBA device |
621 | * @dev: AMBA device to register |
622 | * @parent: parent memory resource |
623 | * |
624 | * Setup the AMBA device, reading the cell ID if present. |
625 | * Claim the resource, and register the AMBA device with |
626 | * the Linux device manager. |
627 | */ |
628 | int amba_device_register(struct amba_device *dev, struct resource *parent) |
629 | { |
630 | amba_device_initialize(dev, name: dev->dev.init_name); |
631 | dev->dev.init_name = NULL; |
632 | |
633 | return amba_device_add(dev, parent); |
634 | } |
635 | EXPORT_SYMBOL(amba_device_register); |
636 | |
637 | /** |
638 | * amba_device_put - put an AMBA device |
639 | * @dev: AMBA device to put |
640 | */ |
641 | void amba_device_put(struct amba_device *dev) |
642 | { |
643 | put_device(dev: &dev->dev); |
644 | } |
645 | EXPORT_SYMBOL_GPL(amba_device_put); |
646 | |
647 | /** |
648 | * amba_device_unregister - unregister an AMBA device |
649 | * @dev: AMBA device to remove |
650 | * |
651 | * Remove the specified AMBA device from the Linux device |
652 | * manager. All files associated with this object will be |
653 | * destroyed, and device drivers notified that the device has |
654 | * been removed. The AMBA device's resources including |
655 | * the amba_device structure will be freed once all |
656 | * references to it have been dropped. |
657 | */ |
658 | void amba_device_unregister(struct amba_device *dev) |
659 | { |
660 | device_unregister(dev: &dev->dev); |
661 | } |
662 | EXPORT_SYMBOL(amba_device_unregister); |
663 | |
664 | /** |
665 | * amba_request_regions - request all mem regions associated with device |
666 | * @dev: amba_device structure for device |
667 | * @name: name, or NULL to use driver name |
668 | */ |
669 | int amba_request_regions(struct amba_device *dev, const char *name) |
670 | { |
671 | int ret = 0; |
672 | u32 size; |
673 | |
674 | if (!name) |
675 | name = dev->dev.driver->name; |
676 | |
677 | size = resource_size(res: &dev->res); |
678 | |
679 | if (!request_mem_region(dev->res.start, size, name)) |
680 | ret = -EBUSY; |
681 | |
682 | return ret; |
683 | } |
684 | EXPORT_SYMBOL(amba_request_regions); |
685 | |
686 | /** |
687 | * amba_release_regions - release mem regions associated with device |
688 | * @dev: amba_device structure for device |
689 | * |
690 | * Release regions claimed by a successful call to amba_request_regions. |
691 | */ |
692 | void amba_release_regions(struct amba_device *dev) |
693 | { |
694 | u32 size; |
695 | |
696 | size = resource_size(res: &dev->res); |
697 | release_mem_region(dev->res.start, size); |
698 | } |
699 | EXPORT_SYMBOL(amba_release_regions); |
700 | |