1/* 2 * linux/arch/arm/kernel/pmu.c 3 * 4 * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles 5 * Copyright (C) 2010 ARM Ltd, Will Deacon 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 13#define pr_fmt(fmt) "PMU: " fmt 14 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> 20#include <linux/platform_device.h> 21 22#include <asm/pmu.h> 23 24static volatile long pmu_lock; 25 26static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES]; 27 28static 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); 43 44 pmu_devices[pdev->id] = pdev; 45 return 0; 46} 47 48static struct platform_driver pmu_driver = { 49 .driver = { 50 .name = "arm-pmu", 51 }, 52 .probe = pmu_device_probe, 53}; 54 55static int __init register_pmu_driver(void) 56{ 57 return platform_driver_register(&pmu_driver); 58} 59device_initcall(register_pmu_driver); 60 61struct platform_device * 62reserve_pmu(enum arm_pmu_type device) 63{ 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; 76} 77EXPORT_SYMBOL_GPL(reserve_pmu); 78 79int 80release_pmu(struct platform_device *pdev) 81{ 82 if (WARN_ON(pdev != pmu_devices[pdev->id])) 83 return -EINVAL; 84 clear_bit_unlock(pdev->id, &pmu_lock); 85 return 0; 86} 87EXPORT_SYMBOL_GPL(release_pmu); 88 89static int 90set_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 100 return 0; 101#endif 102} 103 104static int 105init_cpu_pmu(void) 106{ 107 int i, err = 0; 108 struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU]; 109 110 if (!pdev) { 111 err = -ENODEV; 112 goto out; 113 } 114 115 for (i = 0; i < pdev->num_resources; ++i) { 116 err = set_irq_affinity(platform_get_irq(pdev, i), i); 117 if (err) 118 break; 119 } 120 121out: 122 return err; 123} 124 125int 126init_pmu(enum arm_pmu_type device) 127{ 128 int err = 0; 129 130 switch (device) { 131 case ARM_PMU_DEVICE_CPU: 132 err = init_cpu_pmu(); 133 break; 134 default: 135 pr_warning("attempt to initialise unknown device %d\n", 136 device); 137 err = -EINVAL; 138 } 139 140 return err; 141} 142EXPORT_SYMBOL_GPL(init_pmu); 143