Commit | Line | Data |
---|---|---|
0f4f0672 JI |
1 | /* |
2 | * linux/arch/arm/kernel/pmu.c | |
3 | * | |
4 | * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles | |
49c006b9 | 5 | * Copyright (C) 2010 ARM Ltd, Will Deacon |
0f4f0672 JI |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | */ | |
12 | ||
49c006b9 WD |
13 | #define pr_fmt(fmt) "PMU: " fmt |
14 | ||
0f4f0672 JI |
15 | #include <linux/cpumask.h> |
16 | #include <linux/err.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/module.h> | |
49c006b9 | 20 | #include <linux/platform_device.h> |
0f4f0672 JI |
21 | |
22 | #include <asm/pmu.h> | |
23 | ||
49c006b9 WD |
24 | static volatile long pmu_lock; |
25 | ||
26 | static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES]; | |
27 | ||
28 | static int __devinit pmu_device_probe(struct platform_device *pdev) | |
29 | { | |
30 | ||
31 | if (pdev->id < 0 || pdev->id >= ARM_NUM_PMU_DEVICES) { | |
32 | pr_warning("received registration request for unknown " | |
33 | "device %d\n", pdev->id); | |
34 | return -EINVAL; | |
35 | } | |
36 | ||
37 | if (pmu_devices[pdev->id]) | |
38 | pr_warning("registering new PMU device type %d overwrites " | |
39 | "previous registration!\n", pdev->id); | |
40 | else | |
41 | pr_info("registered new PMU device of type %d\n", | |
42 | pdev->id); | |
0f4f0672 | 43 | |
49c006b9 WD |
44 | pmu_devices[pdev->id] = pdev; |
45 | return 0; | |
46 | } | |
47 | ||
48 | static struct platform_driver pmu_driver = { | |
49 | .driver = { | |
50 | .name = "arm-pmu", | |
51 | }, | |
52 | .probe = pmu_device_probe, | |
0f4f0672 JI |
53 | }; |
54 | ||
49c006b9 WD |
55 | static int __init register_pmu_driver(void) |
56 | { | |
57 | return platform_driver_register(&pmu_driver); | |
58 | } | |
59 | device_initcall(register_pmu_driver); | |
0f4f0672 | 60 | |
49c006b9 WD |
61 | struct platform_device * |
62 | reserve_pmu(enum arm_pmu_type device) | |
0f4f0672 | 63 | { |
49c006b9 WD |
64 | struct platform_device *pdev; |
65 | ||
66 | if (test_and_set_bit_lock(device, &pmu_lock)) { | |
67 | pdev = ERR_PTR(-EBUSY); | |
68 | } else if (pmu_devices[device] == NULL) { | |
69 | clear_bit_unlock(device, &pmu_lock); | |
70 | pdev = ERR_PTR(-ENODEV); | |
71 | } else { | |
72 | pdev = pmu_devices[device]; | |
73 | } | |
74 | ||
75 | return pdev; | |
0f4f0672 JI |
76 | } |
77 | EXPORT_SYMBOL_GPL(reserve_pmu); | |
78 | ||
79 | int | |
49c006b9 | 80 | release_pmu(struct platform_device *pdev) |
0f4f0672 | 81 | { |
49c006b9 | 82 | if (WARN_ON(pdev != pmu_devices[pdev->id])) |
0f4f0672 | 83 | return -EINVAL; |
49c006b9 | 84 | clear_bit_unlock(pdev->id, &pmu_lock); |
0f4f0672 JI |
85 | return 0; |
86 | } | |
87 | EXPORT_SYMBOL_GPL(release_pmu); | |
88 | ||
89 | static int | |
90 | set_irq_affinity(int irq, | |
91 | unsigned int cpu) | |
92 | { | |
93 | #ifdef CONFIG_SMP | |
94 | int err = irq_set_affinity(irq, cpumask_of(cpu)); | |
95 | if (err) | |
96 | pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", | |
97 | irq, cpu); | |
98 | return err; | |
99 | #else | |
71efb063 | 100 | return -EINVAL; |
0f4f0672 JI |
101 | #endif |
102 | } | |
103 | ||
49c006b9 WD |
104 | static int |
105 | init_cpu_pmu(void) | |
0f4f0672 | 106 | { |
71efb063 | 107 | int i, irqs, err = 0; |
49c006b9 WD |
108 | struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU]; |
109 | ||
71efb063 WD |
110 | if (!pdev) |
111 | return -ENODEV; | |
112 | ||
113 | irqs = pdev->num_resources; | |
114 | ||
115 | /* | |
116 | * If we have a single PMU interrupt that we can't shift, assume that | |
117 | * we're running on a uniprocessor machine and continue. | |
118 | */ | |
119 | if (irqs == 1 && !irq_can_set_affinity(platform_get_irq(pdev, 0))) | |
120 | return 0; | |
0f4f0672 | 121 | |
71efb063 | 122 | for (i = 0; i < irqs; ++i) { |
49c006b9 | 123 | err = set_irq_affinity(platform_get_irq(pdev, i), i); |
0f4f0672 JI |
124 | if (err) |
125 | break; | |
126 | } | |
127 | ||
49c006b9 WD |
128 | return err; |
129 | } | |
130 | ||
131 | int | |
132 | init_pmu(enum arm_pmu_type device) | |
133 | { | |
134 | int err = 0; | |
135 | ||
136 | switch (device) { | |
137 | case ARM_PMU_DEVICE_CPU: | |
138 | err = init_cpu_pmu(); | |
139 | break; | |
140 | default: | |
141 | pr_warning("attempt to initialise unknown device %d\n", | |
142 | device); | |
143 | err = -EINVAL; | |
144 | } | |
145 | ||
0f4f0672 JI |
146 | return err; |
147 | } | |
148 | EXPORT_SYMBOL_GPL(init_pmu); |