blob: a4688e8e8f6dba740acb19345f59d5ebc8fd3a7f [file] [log] [blame]
Jamie Iles0f4f0672010-02-02 20:23:15 +01001/*
2 * linux/arch/arm/kernel/pmu.c
3 *
4 * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles
Will Deacon49c006b2010-04-29 17:13:24 +01005 * Copyright (C) 2010 ARM Ltd, Will Deacon
Jamie Iles0f4f0672010-02-02 20:23:15 +01006 *
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
Will Deacon49c006b2010-04-29 17:13:24 +010013#define pr_fmt(fmt) "PMU: " fmt
14
Jamie Iles0f4f0672010-02-02 20:23:15 +010015#include <linux/cpumask.h>
16#include <linux/err.h>
17#include <linux/interrupt.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
Will Deacon49c006b2010-04-29 17:13:24 +010020#include <linux/platform_device.h>
Jamie Iles0f4f0672010-02-02 20:23:15 +010021
22#include <asm/pmu.h>
23
Jamie Iles0f4f0672010-02-02 20:23:15 +010024static volatile long pmu_lock;
25
Will Deacon49c006b2010-04-29 17:13:24 +010026static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES];
27
28static int __devinit pmu_device_probe(struct platform_device *pdev)
Jamie Iles0f4f0672010-02-02 20:23:15 +010029{
Will Deacon49c006b2010-04-29 17:13:24 +010030
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);
43
44 pmu_devices[pdev->id] = pdev;
45 return 0;
46}
47
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048static struct platform_driver cpu_pmu_driver = {
Will Deacon49c006b2010-04-29 17:13:24 +010049 .driver = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050 .name = "cpu-arm-pmu",
Will Deacon49c006b2010-04-29 17:13:24 +010051 },
52 .probe = pmu_device_probe,
53};
54
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055static struct platform_driver l2_pmu_driver = {
56 .driver = {
57 .name = "l2-arm-pmu",
58 },
59 .probe = pmu_device_probe,
60};
61
62static struct platform_driver *pmu_drivers[] __initdata = {
63 &cpu_pmu_driver,
64 &l2_pmu_driver,
65};
66
Will Deacon49c006b2010-04-29 17:13:24 +010067static int __init register_pmu_driver(void)
68{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069 int i;
70 int err;
71
72 for (i = 0; i < ARM_NUM_PMU_DEVICES; i++) {
73 err = platform_driver_register(pmu_drivers[i]);
74 if (err) {
75 pr_err("%s: failed to register id:%d\n", __func__, i);
76 while (--i >= 0)
77 platform_driver_unregister(pmu_drivers[i]);
78 break;
79 }
80 }
81
82 return err;
Will Deacon49c006b2010-04-29 17:13:24 +010083}
84device_initcall(register_pmu_driver);
85
86struct platform_device *
87reserve_pmu(enum arm_pmu_type device)
88{
89 struct platform_device *pdev;
90
91 if (test_and_set_bit_lock(device, &pmu_lock)) {
92 pdev = ERR_PTR(-EBUSY);
93 } else if (pmu_devices[device] == NULL) {
94 clear_bit_unlock(device, &pmu_lock);
95 pdev = ERR_PTR(-ENODEV);
96 } else {
97 pdev = pmu_devices[device];
98 }
99
100 return pdev;
Jamie Iles0f4f0672010-02-02 20:23:15 +0100101}
102EXPORT_SYMBOL_GPL(reserve_pmu);
103
104int
Will Deacon49c006b2010-04-29 17:13:24 +0100105release_pmu(struct platform_device *pdev)
Jamie Iles0f4f0672010-02-02 20:23:15 +0100106{
Will Deacon49c006b2010-04-29 17:13:24 +0100107 if (WARN_ON(pdev != pmu_devices[pdev->id]))
Jamie Iles0f4f0672010-02-02 20:23:15 +0100108 return -EINVAL;
Will Deacon49c006b2010-04-29 17:13:24 +0100109 clear_bit_unlock(pdev->id, &pmu_lock);
Jamie Iles0f4f0672010-02-02 20:23:15 +0100110 return 0;
111}
112EXPORT_SYMBOL_GPL(release_pmu);
113
114static int
115set_irq_affinity(int irq,
116 unsigned int cpu)
117{
118#ifdef CONFIG_SMP
119 int err = irq_set_affinity(irq, cpumask_of(cpu));
120 if (err)
121 pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
122 irq, cpu);
123 return err;
124#else
Will Deacon71efb062011-02-18 16:21:06 +0100125 return -EINVAL;
Jamie Iles0f4f0672010-02-02 20:23:15 +0100126#endif
127}
128
Will Deacon49c006b2010-04-29 17:13:24 +0100129static int
130init_cpu_pmu(void)
Jamie Iles0f4f0672010-02-02 20:23:15 +0100131{
Will Deacon71efb062011-02-18 16:21:06 +0100132 int i, irqs, err = 0;
Will Deacon49c006b2010-04-29 17:13:24 +0100133 struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU];
Jamie Iles0f4f0672010-02-02 20:23:15 +0100134
Will Deacon71efb062011-02-18 16:21:06 +0100135 if (!pdev)
136 return -ENODEV;
Will Deacon49c006b2010-04-29 17:13:24 +0100137
Will Deacon71efb062011-02-18 16:21:06 +0100138 irqs = pdev->num_resources;
139
140 /*
141 * If we have a single PMU interrupt that we can't shift, assume that
142 * we're running on a uniprocessor machine and continue.
143 */
144 if (irqs == 1 && !irq_can_set_affinity(platform_get_irq(pdev, 0)))
145 return 0;
146
147 for (i = 0; i < irqs; ++i) {
Will Deacon49c006b2010-04-29 17:13:24 +0100148 err = set_irq_affinity(platform_get_irq(pdev, i), i);
Jamie Iles0f4f0672010-02-02 20:23:15 +0100149 if (err)
150 break;
151 }
152
Will Deacon49c006b2010-04-29 17:13:24 +0100153 return err;
154}
155
156int
157init_pmu(enum arm_pmu_type device)
158{
159 int err = 0;
160
161 switch (device) {
162 case ARM_PMU_DEVICE_CPU:
163 err = init_cpu_pmu();
164 break;
165 default:
166 pr_warning("attempt to initialise unknown device %d\n",
167 device);
168 err = -EINVAL;
169 }
170
Jamie Iles0f4f0672010-02-02 20:23:15 +0100171 return err;
172}
173EXPORT_SYMBOL_GPL(init_pmu);