pmu.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. #define pr_fmt(fmt) "PMU: " fmt
  13. #include <linux/cpumask.h>
  14. #include <linux/err.h>
  15. #include <linux/interrupt.h>
  16. #include <linux/kernel.h>
  17. #include <linux/module.h>
  18. #include <linux/platform_device.h>
  19. #include <asm/pmu.h>
  20. static volatile long pmu_lock;
  21. static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES];
  22. static int __devinit pmu_register(struct platform_device *pdev,
  23. enum arm_pmu_type type)
  24. {
  25. if (type < 0 || type >= ARM_NUM_PMU_DEVICES) {
  26. pr_warning("received registration request for unknown "
  27. "device %d\n", type);
  28. return -EINVAL;
  29. }
  30. if (pmu_devices[type]) {
  31. pr_warning("rejecting duplicate registration of PMU device "
  32. "type %d.", type);
  33. return -ENOSPC;
  34. }
  35. pr_info("registered new PMU device of type %d\n", type);
  36. pmu_devices[type] = pdev;
  37. return 0;
  38. }
  39. static int __devinit armpmu_device_probe(struct platform_device *pdev)
  40. {
  41. return pmu_register(pdev, ARM_PMU_DEVICE_CPU);
  42. }
  43. static struct platform_driver armpmu_driver = {
  44. .driver = {
  45. .name = "arm-pmu",
  46. },
  47. .probe = armpmu_device_probe,
  48. };
  49. static int __init register_pmu_driver(void)
  50. {
  51. return platform_driver_register(&armpmu_driver);
  52. }
  53. device_initcall(register_pmu_driver);
  54. struct platform_device *
  55. reserve_pmu(enum arm_pmu_type device)
  56. {
  57. struct platform_device *pdev;
  58. if (test_and_set_bit_lock(device, &pmu_lock)) {
  59. pdev = ERR_PTR(-EBUSY);
  60. } else if (pmu_devices[device] == NULL) {
  61. clear_bit_unlock(device, &pmu_lock);
  62. pdev = ERR_PTR(-ENODEV);
  63. } else {
  64. pdev = pmu_devices[device];
  65. }
  66. return pdev;
  67. }
  68. EXPORT_SYMBOL_GPL(reserve_pmu);
  69. int
  70. release_pmu(enum arm_pmu_type device)
  71. {
  72. if (WARN_ON(!pmu_devices[device]))
  73. return -EINVAL;
  74. clear_bit_unlock(device, &pmu_lock);
  75. return 0;
  76. }
  77. EXPORT_SYMBOL_GPL(release_pmu);
  78. static int
  79. set_irq_affinity(int irq,
  80. unsigned int cpu)
  81. {
  82. #ifdef CONFIG_SMP
  83. int err = irq_set_affinity(irq, cpumask_of(cpu));
  84. if (err)
  85. pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
  86. irq, cpu);
  87. return err;
  88. #else
  89. return -EINVAL;
  90. #endif
  91. }
  92. static int
  93. init_cpu_pmu(void)
  94. {
  95. int i, irqs, err = 0;
  96. struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU];
  97. if (!pdev)
  98. return -ENODEV;
  99. irqs = pdev->num_resources;
  100. /*
  101. * If we have a single PMU interrupt that we can't shift, assume that
  102. * we're running on a uniprocessor machine and continue.
  103. */
  104. if (irqs == 1 && !irq_can_set_affinity(platform_get_irq(pdev, 0)))
  105. return 0;
  106. for (i = 0; i < irqs; ++i) {
  107. err = set_irq_affinity(platform_get_irq(pdev, i), i);
  108. if (err)
  109. break;
  110. }
  111. return err;
  112. }
  113. int
  114. init_pmu(enum arm_pmu_type device)
  115. {
  116. int err = 0;
  117. switch (device) {
  118. case ARM_PMU_DEVICE_CPU:
  119. err = init_cpu_pmu();
  120. break;
  121. default:
  122. pr_warning("attempt to initialise unknown device %d\n",
  123. device);
  124. err = -EINVAL;
  125. }
  126. return err;
  127. }
  128. EXPORT_SYMBOL_GPL(init_pmu);