Root/drivers/amba/bus.c

1/*
2 * linux/arch/arm/common/amba.c
3 *
4 * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/module.h>
11#include <linux/init.h>
12#include <linux/device.h>
13#include <linux/string.h>
14#include <linux/slab.h>
15#include <linux/io.h>
16#include <linux/pm.h>
17#include <linux/pm_runtime.h>
18#include <linux/amba/bus.h>
19#include <linux/sizes.h>
20
21#include <asm/irq.h>
22
23#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
24
25static const struct amba_id *
26amba_lookup(const struct amba_id *table, struct amba_device *dev)
27{
28    int ret = 0;
29
30    while (table->mask) {
31        ret = (dev->periphid & table->mask) == table->id;
32        if (ret)
33            break;
34        table++;
35    }
36
37    return ret ? table : NULL;
38}
39
40static int amba_match(struct device *dev, struct device_driver *drv)
41{
42    struct amba_device *pcdev = to_amba_device(dev);
43    struct amba_driver *pcdrv = to_amba_driver(drv);
44
45    return amba_lookup(pcdrv->id_table, pcdev) != NULL;
46}
47
48#ifdef CONFIG_HOTPLUG
49static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
50{
51    struct amba_device *pcdev = to_amba_device(dev);
52    int retval = 0;
53
54    retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
55    if (retval)
56        return retval;
57
58    retval = add_uevent_var(env, "MODALIAS=amba:d%08X", pcdev->periphid);
59    return retval;
60}
61#else
62#define amba_uevent NULL
63#endif
64
65#define amba_attr_func(name,fmt,arg...) \
66static ssize_t name##_show(struct device *_dev, \
67               struct device_attribute *attr, char *buf) \
68{ \
69    struct amba_device *dev = to_amba_device(_dev); \
70    return sprintf(buf, fmt, arg); \
71}
72
73#define amba_attr(name,fmt,arg...) \
74amba_attr_func(name,fmt,arg) \
75static DEVICE_ATTR(name, S_IRUGO, name##_show, NULL)
76
77amba_attr_func(id, "%08x\n", dev->periphid);
78amba_attr(irq0, "%u\n", dev->irq[0]);
79amba_attr(irq1, "%u\n", dev->irq[1]);
80amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n",
81     (unsigned long long)dev->res.start, (unsigned long long)dev->res.end,
82     dev->res.flags);
83
84static struct device_attribute amba_dev_attrs[] = {
85    __ATTR_RO(id),
86    __ATTR_RO(resource),
87    __ATTR_NULL,
88};
89
90#ifdef CONFIG_PM_SLEEP
91
92static int amba_legacy_suspend(struct device *dev, pm_message_t mesg)
93{
94    struct amba_driver *adrv = to_amba_driver(dev->driver);
95    struct amba_device *adev = to_amba_device(dev);
96    int ret = 0;
97
98    if (dev->driver && adrv->suspend)
99        ret = adrv->suspend(adev, mesg);
100
101    return ret;
102}
103
104static int amba_legacy_resume(struct device *dev)
105{
106    struct amba_driver *adrv = to_amba_driver(dev->driver);
107    struct amba_device *adev = to_amba_device(dev);
108    int ret = 0;
109
110    if (dev->driver && adrv->resume)
111        ret = adrv->resume(adev);
112
113    return ret;
114}
115
116#endif /* CONFIG_PM_SLEEP */
117
118#ifdef CONFIG_SUSPEND
119
120static int amba_pm_suspend(struct device *dev)
121{
122    struct device_driver *drv = dev->driver;
123    int ret = 0;
124
125    if (!drv)
126        return 0;
127
128    if (drv->pm) {
129        if (drv->pm->suspend)
130            ret = drv->pm->suspend(dev);
131    } else {
132        ret = amba_legacy_suspend(dev, PMSG_SUSPEND);
133    }
134
135    return ret;
136}
137
138static int amba_pm_resume(struct device *dev)
139{
140    struct device_driver *drv = dev->driver;
141    int ret = 0;
142
143    if (!drv)
144        return 0;
145
146    if (drv->pm) {
147        if (drv->pm->resume)
148            ret = drv->pm->resume(dev);
149    } else {
150        ret = amba_legacy_resume(dev);
151    }
152
153    return ret;
154}
155
156#else /* !CONFIG_SUSPEND */
157
158#define amba_pm_suspend NULL
159#define amba_pm_resume NULL
160
161#endif /* !CONFIG_SUSPEND */
162
163#ifdef CONFIG_HIBERNATE_CALLBACKS
164
165static int amba_pm_freeze(struct device *dev)
166{
167    struct device_driver *drv = dev->driver;
168    int ret = 0;
169
170    if (!drv)
171        return 0;
172
173    if (drv->pm) {
174        if (drv->pm->freeze)
175            ret = drv->pm->freeze(dev);
176    } else {
177        ret = amba_legacy_suspend(dev, PMSG_FREEZE);
178    }
179
180    return ret;
181}
182
183static int amba_pm_thaw(struct device *dev)
184{
185    struct device_driver *drv = dev->driver;
186    int ret = 0;
187
188    if (!drv)
189        return 0;
190
191    if (drv->pm) {
192        if (drv->pm->thaw)
193            ret = drv->pm->thaw(dev);
194    } else {
195        ret = amba_legacy_resume(dev);
196    }
197
198    return ret;
199}
200
201static int amba_pm_poweroff(struct device *dev)
202{
203    struct device_driver *drv = dev->driver;
204    int ret = 0;
205
206    if (!drv)
207        return 0;
208
209    if (drv->pm) {
210        if (drv->pm->poweroff)
211            ret = drv->pm->poweroff(dev);
212    } else {
213        ret = amba_legacy_suspend(dev, PMSG_HIBERNATE);
214    }
215
216    return ret;
217}
218
219static int amba_pm_restore(struct device *dev)
220{
221    struct device_driver *drv = dev->driver;
222    int ret = 0;
223
224    if (!drv)
225        return 0;
226
227    if (drv->pm) {
228        if (drv->pm->restore)
229            ret = drv->pm->restore(dev);
230    } else {
231        ret = amba_legacy_resume(dev);
232    }
233
234    return ret;
235}
236
237#else /* !CONFIG_HIBERNATE_CALLBACKS */
238
239#define amba_pm_freeze NULL
240#define amba_pm_thaw NULL
241#define amba_pm_poweroff NULL
242#define amba_pm_restore NULL
243
244#endif /* !CONFIG_HIBERNATE_CALLBACKS */
245
246#ifdef CONFIG_PM_RUNTIME
247/*
248 * Hooks to provide runtime PM of the pclk (bus clock). It is safe to
249 * enable/disable the bus clock at runtime PM suspend/resume as this
250 * does not result in loss of context.
251 */
252static int amba_pm_runtime_suspend(struct device *dev)
253{
254    struct amba_device *pcdev = to_amba_device(dev);
255    int ret = pm_generic_runtime_suspend(dev);
256
257    if (ret == 0 && dev->driver)
258        clk_disable(pcdev->pclk);
259
260    return ret;
261}
262
263static int amba_pm_runtime_resume(struct device *dev)
264{
265    struct amba_device *pcdev = to_amba_device(dev);
266    int ret;
267
268    if (dev->driver) {
269        ret = clk_enable(pcdev->pclk);
270        /* Failure is probably fatal to the system, but... */
271        if (ret)
272            return ret;
273    }
274
275    return pm_generic_runtime_resume(dev);
276}
277#endif
278
279#ifdef CONFIG_PM
280
281static const struct dev_pm_ops amba_pm = {
282    .suspend = amba_pm_suspend,
283    .resume = amba_pm_resume,
284    .freeze = amba_pm_freeze,
285    .thaw = amba_pm_thaw,
286    .poweroff = amba_pm_poweroff,
287    .restore = amba_pm_restore,
288    SET_RUNTIME_PM_OPS(
289        amba_pm_runtime_suspend,
290        amba_pm_runtime_resume,
291        pm_generic_runtime_idle
292    )
293};
294
295#define AMBA_PM (&amba_pm)
296
297#else /* !CONFIG_PM */
298
299#define AMBA_PM NULL
300
301#endif /* !CONFIG_PM */
302
303/*
304 * Primecells are part of the Advanced Microcontroller Bus Architecture,
305 * so we call the bus "amba".
306 */
307struct bus_type amba_bustype = {
308    .name = "amba",
309    .dev_attrs = amba_dev_attrs,
310    .match = amba_match,
311    .uevent = amba_uevent,
312    .pm = AMBA_PM,
313};
314
315static int __init amba_init(void)
316{
317    return bus_register(&amba_bustype);
318}
319
320postcore_initcall(amba_init);
321
322static int amba_get_enable_pclk(struct amba_device *pcdev)
323{
324    struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk");
325    int ret;
326
327    pcdev->pclk = pclk;
328
329    if (IS_ERR(pclk))
330        return PTR_ERR(pclk);
331
332    ret = clk_prepare(pclk);
333    if (ret) {
334        clk_put(pclk);
335        return ret;
336    }
337
338    ret = clk_enable(pclk);
339    if (ret) {
340        clk_unprepare(pclk);
341        clk_put(pclk);
342    }
343
344    return ret;
345}
346
347static void amba_put_disable_pclk(struct amba_device *pcdev)
348{
349    struct clk *pclk = pcdev->pclk;
350
351    clk_disable(pclk);
352    clk_unprepare(pclk);
353    clk_put(pclk);
354}
355
356/*
357 * These are the device model conversion veneers; they convert the
358 * device model structures to our more specific structures.
359 */
360static int amba_probe(struct device *dev)
361{
362    struct amba_device *pcdev = to_amba_device(dev);
363    struct amba_driver *pcdrv = to_amba_driver(dev->driver);
364    const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
365    int ret;
366
367    do {
368        ret = amba_get_enable_pclk(pcdev);
369        if (ret)
370            break;
371
372        pm_runtime_get_noresume(dev);
373        pm_runtime_set_active(dev);
374        pm_runtime_enable(dev);
375
376        ret = pcdrv->probe(pcdev, id);
377        if (ret == 0)
378            break;
379
380        pm_runtime_disable(dev);
381        pm_runtime_set_suspended(dev);
382        pm_runtime_put_noidle(dev);
383
384        amba_put_disable_pclk(pcdev);
385    } while (0);
386
387    return ret;
388}
389
390static int amba_remove(struct device *dev)
391{
392    struct amba_device *pcdev = to_amba_device(dev);
393    struct amba_driver *drv = to_amba_driver(dev->driver);
394    int ret;
395
396    pm_runtime_get_sync(dev);
397    ret = drv->remove(pcdev);
398    pm_runtime_put_noidle(dev);
399
400    /* Undo the runtime PM settings in amba_probe() */
401    pm_runtime_disable(dev);
402    pm_runtime_set_suspended(dev);
403    pm_runtime_put_noidle(dev);
404
405    amba_put_disable_pclk(pcdev);
406
407    return ret;
408}
409
410static void amba_shutdown(struct device *dev)
411{
412    struct amba_driver *drv = to_amba_driver(dev->driver);
413    drv->shutdown(to_amba_device(dev));
414}
415
416/**
417 * amba_driver_register - register an AMBA device driver
418 * @drv: amba device driver structure
419 *
420 * Register an AMBA device driver with the Linux device model
421 * core. If devices pre-exist, the drivers probe function will
422 * be called.
423 */
424int amba_driver_register(struct amba_driver *drv)
425{
426    drv->drv.bus = &amba_bustype;
427
428#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
429    SETFN(probe);
430    SETFN(remove);
431    SETFN(shutdown);
432
433    return driver_register(&drv->drv);
434}
435
436/**
437 * amba_driver_unregister - remove an AMBA device driver
438 * @drv: AMBA device driver structure to remove
439 *
440 * Unregister an AMBA device driver from the Linux device
441 * model. The device model will call the drivers remove function
442 * for each device the device driver is currently handling.
443 */
444void amba_driver_unregister(struct amba_driver *drv)
445{
446    driver_unregister(&drv->drv);
447}
448
449
450static void amba_device_release(struct device *dev)
451{
452    struct amba_device *d = to_amba_device(dev);
453
454    if (d->res.parent)
455        release_resource(&d->res);
456    kfree(d);
457}
458
459/**
460 * amba_device_add - add a previously allocated AMBA device structure
461 * @dev: AMBA device allocated by amba_device_alloc
462 * @parent: resource parent for this devices resources
463 *
464 * Claim the resource, and read the device cell ID if not already
465 * initialized. Register the AMBA device with the Linux device
466 * manager.
467 */
468int amba_device_add(struct amba_device *dev, struct resource *parent)
469{
470    u32 size;
471    void __iomem *tmp;
472    int i, ret;
473
474    WARN_ON(dev->irq[0] == (unsigned int)-1);
475    WARN_ON(dev->irq[1] == (unsigned int)-1);
476
477    ret = request_resource(parent, &dev->res);
478    if (ret)
479        goto err_out;
480
481    /* Hard-coded primecell ID instead of plug-n-play */
482    if (dev->periphid != 0)
483        goto skip_probe;
484
485    /*
486     * Dynamically calculate the size of the resource
487     * and use this for iomap
488     */
489    size = resource_size(&dev->res);
490    tmp = ioremap(dev->res.start, size);
491    if (!tmp) {
492        ret = -ENOMEM;
493        goto err_release;
494    }
495
496    ret = amba_get_enable_pclk(dev);
497    if (ret == 0) {
498        u32 pid, cid;
499
500        /*
501         * Read pid and cid based on size of resource
502         * they are located at end of region
503         */
504        for (pid = 0, i = 0; i < 4; i++)
505            pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) <<
506                (i * 8);
507        for (cid = 0, i = 0; i < 4; i++)
508            cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) <<
509                (i * 8);
510
511        amba_put_disable_pclk(dev);
512
513        if (cid == AMBA_CID)
514            dev->periphid = pid;
515
516        if (!dev->periphid)
517            ret = -ENODEV;
518    }
519
520    iounmap(tmp);
521
522    if (ret)
523        goto err_release;
524
525 skip_probe:
526    ret = device_add(&dev->dev);
527    if (ret)
528        goto err_release;
529
530    if (dev->irq[0])
531        ret = device_create_file(&dev->dev, &dev_attr_irq0);
532    if (ret == 0 && dev->irq[1])
533        ret = device_create_file(&dev->dev, &dev_attr_irq1);
534    if (ret == 0)
535        return ret;
536
537    device_unregister(&dev->dev);
538
539 err_release:
540    release_resource(&dev->res);
541 err_out:
542    return ret;
543}
544EXPORT_SYMBOL_GPL(amba_device_add);
545
546static struct amba_device *
547amba_aphb_device_add(struct device *parent, const char *name,
548             resource_size_t base, size_t size, int irq1, int irq2,
549             void *pdata, unsigned int periphid, u64 dma_mask)
550{
551    struct amba_device *dev;
552    int ret;
553
554    dev = amba_device_alloc(name, base, size);
555    if (!dev)
556        return ERR_PTR(-ENOMEM);
557
558    dev->dma_mask = dma_mask;
559    dev->dev.coherent_dma_mask = dma_mask;
560    dev->irq[0] = irq1;
561    dev->irq[1] = irq2;
562    dev->periphid = periphid;
563    dev->dev.platform_data = pdata;
564    dev->dev.parent = parent;
565
566    ret = amba_device_add(dev, &iomem_resource);
567    if (ret) {
568        amba_device_put(dev);
569        return ERR_PTR(ret);
570    }
571
572    return dev;
573}
574
575struct amba_device *
576amba_apb_device_add(struct device *parent, const char *name,
577            resource_size_t base, size_t size, int irq1, int irq2,
578            void *pdata, unsigned int periphid)
579{
580    return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata,
581                    periphid, 0);
582}
583EXPORT_SYMBOL_GPL(amba_apb_device_add);
584
585struct amba_device *
586amba_ahb_device_add(struct device *parent, const char *name,
587            resource_size_t base, size_t size, int irq1, int irq2,
588            void *pdata, unsigned int periphid)
589{
590    return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata,
591                    periphid, ~0ULL);
592}
593EXPORT_SYMBOL_GPL(amba_ahb_device_add);
594
595static void amba_device_initialize(struct amba_device *dev, const char *name)
596{
597    device_initialize(&dev->dev);
598    if (name)
599        dev_set_name(&dev->dev, "%s", name);
600    dev->dev.release = amba_device_release;
601    dev->dev.bus = &amba_bustype;
602    dev->dev.dma_mask = &dev->dma_mask;
603    dev->res.name = dev_name(&dev->dev);
604}
605
606/**
607 * amba_device_alloc - allocate an AMBA device
608 * @name: sysfs name of the AMBA device
609 * @base: base of AMBA device
610 * @size: size of AMBA device
611 *
612 * Allocate and initialize an AMBA device structure. Returns %NULL
613 * on failure.
614 */
615struct amba_device *amba_device_alloc(const char *name, resource_size_t base,
616    size_t size)
617{
618    struct amba_device *dev;
619
620    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
621    if (dev) {
622        amba_device_initialize(dev, name);
623        dev->res.start = base;
624        dev->res.end = base + size - 1;
625        dev->res.flags = IORESOURCE_MEM;
626    }
627
628    return dev;
629}
630EXPORT_SYMBOL_GPL(amba_device_alloc);
631
632/**
633 * amba_device_register - register an AMBA device
634 * @dev: AMBA device to register
635 * @parent: parent memory resource
636 *
637 * Setup the AMBA device, reading the cell ID if present.
638 * Claim the resource, and register the AMBA device with
639 * the Linux device manager.
640 */
641int amba_device_register(struct amba_device *dev, struct resource *parent)
642{
643    amba_device_initialize(dev, dev->dev.init_name);
644    dev->dev.init_name = NULL;
645
646    if (!dev->dev.coherent_dma_mask && dev->dma_mask)
647        dev_warn(&dev->dev, "coherent dma mask is unset\n");
648
649    return amba_device_add(dev, parent);
650}
651
652/**
653 * amba_device_put - put an AMBA device
654 * @dev: AMBA device to put
655 */
656void amba_device_put(struct amba_device *dev)
657{
658    put_device(&dev->dev);
659}
660EXPORT_SYMBOL_GPL(amba_device_put);
661
662/**
663 * amba_device_unregister - unregister an AMBA device
664 * @dev: AMBA device to remove
665 *
666 * Remove the specified AMBA device from the Linux device
667 * manager. All files associated with this object will be
668 * destroyed, and device drivers notified that the device has
669 * been removed. The AMBA device's resources including
670 * the amba_device structure will be freed once all
671 * references to it have been dropped.
672 */
673void amba_device_unregister(struct amba_device *dev)
674{
675    device_unregister(&dev->dev);
676}
677
678
679struct find_data {
680    struct amba_device *dev;
681    struct device *parent;
682    const char *busid;
683    unsigned int id;
684    unsigned int mask;
685};
686
687static int amba_find_match(struct device *dev, void *data)
688{
689    struct find_data *d = data;
690    struct amba_device *pcdev = to_amba_device(dev);
691    int r;
692
693    r = (pcdev->periphid & d->mask) == d->id;
694    if (d->parent)
695        r &= d->parent == dev->parent;
696    if (d->busid)
697        r &= strcmp(dev_name(dev), d->busid) == 0;
698
699    if (r) {
700        get_device(dev);
701        d->dev = pcdev;
702    }
703
704    return r;
705}
706
707/**
708 * amba_find_device - locate an AMBA device given a bus id
709 * @busid: bus id for device (or NULL)
710 * @parent: parent device (or NULL)
711 * @id: peripheral ID (or 0)
712 * @mask: peripheral ID mask (or 0)
713 *
714 * Return the AMBA device corresponding to the supplied parameters.
715 * If no device matches, returns NULL.
716 *
717 * NOTE: When a valid device is found, its refcount is
718 * incremented, and must be decremented before the returned
719 * reference.
720 */
721struct amba_device *
722amba_find_device(const char *busid, struct device *parent, unsigned int id,
723         unsigned int mask)
724{
725    struct find_data data;
726
727    data.dev = NULL;
728    data.parent = parent;
729    data.busid = busid;
730    data.id = id;
731    data.mask = mask;
732
733    bus_for_each_dev(&amba_bustype, NULL, &data, amba_find_match);
734
735    return data.dev;
736}
737
738/**
739 * amba_request_regions - request all mem regions associated with device
740 * @dev: amba_device structure for device
741 * @name: name, or NULL to use driver name
742 */
743int amba_request_regions(struct amba_device *dev, const char *name)
744{
745    int ret = 0;
746    u32 size;
747
748    if (!name)
749        name = dev->dev.driver->name;
750
751    size = resource_size(&dev->res);
752
753    if (!request_mem_region(dev->res.start, size, name))
754        ret = -EBUSY;
755
756    return ret;
757}
758
759/**
760 * amba_release_regions - release mem regions associated with device
761 * @dev: amba_device structure for device
762 *
763 * Release regions claimed by a successful call to amba_request_regions.
764 */
765void amba_release_regions(struct amba_device *dev)
766{
767    u32 size;
768
769    size = resource_size(&dev->res);
770    release_mem_region(dev->res.start, size);
771}
772
773EXPORT_SYMBOL(amba_driver_register);
774EXPORT_SYMBOL(amba_driver_unregister);
775EXPORT_SYMBOL(amba_device_register);
776EXPORT_SYMBOL(amba_device_unregister);
777EXPORT_SYMBOL(amba_find_device);
778EXPORT_SYMBOL(amba_request_regions);
779EXPORT_SYMBOL(amba_release_regions);
780

Archive Download this file



interactive