linux/arch/arm/kernel/pmu.c
<<
>>
Prefs
   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 -EINVAL;
 101#endif
 102}
 103
 104static int
 105init_cpu_pmu(void)
 106{
 107        int i, irqs, err = 0;
 108        struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU];
 109
 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;
 121
 122        for (i = 0; i < irqs; ++i) {
 123                err = set_irq_affinity(platform_get_irq(pdev, i), i);
 124                if (err)
 125                        break;
 126        }
 127
 128        return err;
 129}
 130
 131int
 132init_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
 146        return err;
 147}
 148EXPORT_SYMBOL_GPL(init_pmu);
 149