Commit 7be2958e authored by Jon Hunter's avatar Jon Hunter Committed by Will Deacon

ARM: PMU: Add runtime PM Support

Add runtime PM support to the ARM PMU driver so that devices such as OMAP
supporting dynamic PM can use the platform->runtime_* hooks to initialise
hardware at runtime. Without having these runtime PM hooks in place any
configuration of the PMU hardware would be lost when low power states are
entered and hence would prevent PMU from working.

This change also replaces the PMU platform functions enable_irq and disable_irq
added by Ming Lei with runtime_resume and runtime_suspend funtions. Ming had
added the enable_irq and disable_irq functions as a method to configure the
cross trigger interface on OMAP4 for routing the PMU interrupts. By adding
runtime PM support, we can move the code called by enable_irq and disable_irq
into the runtime PM callbacks runtime_resume and runtime_suspend.

Cc: Ming Lei <ming.lei@canonical.com>
Cc: Benoit Cousson <b-cousson@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Kevin Hilman <khilman@ti.com>
Signed-off-by: default avatarJon Hunter <jon-hunter@ti.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent fea7a08a
...@@ -31,18 +31,22 @@ enum arm_pmu_type { ...@@ -31,18 +31,22 @@ enum arm_pmu_type {
* interrupt and passed the address of the low level handler, * interrupt and passed the address of the low level handler,
* and can be used to implement any platform specific handling * and can be used to implement any platform specific handling
* before or after calling it. * before or after calling it.
* @enable_irq: an optional handler which will be called after * @runtime_resume: an optional handler which will be called by the
* request_irq and be used to handle some platform specific * runtime PM framework following a call to pm_runtime_get().
* irq enablement * Note that if pm_runtime_get() is called more than once in
* @disable_irq: an optional handler which will be called before * succession this handler will only be called once.
* free_irq and be used to handle some platform specific * @runtime_suspend: an optional handler which will be called by the
* irq disablement * runtime PM framework following a call to pm_runtime_put().
* Note that if pm_runtime_get() is called more than once in
* succession this handler will only be called following the
* final call to pm_runtime_put() that actually disables the
* hardware.
*/ */
struct arm_pmu_platdata { struct arm_pmu_platdata {
irqreturn_t (*handle_irq)(int irq, void *dev, irqreturn_t (*handle_irq)(int irq, void *dev,
irq_handler_t pmu_handler); irq_handler_t pmu_handler);
void (*enable_irq)(int irq); int (*runtime_resume)(struct device *dev);
void (*disable_irq)(int irq); int (*runtime_suspend)(struct device *dev);
}; };
#ifdef CONFIG_CPU_HAS_PMU #ifdef CONFIG_CPU_HAS_PMU
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/pm_runtime.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -364,8 +365,6 @@ armpmu_release_hardware(struct arm_pmu *armpmu) ...@@ -364,8 +365,6 @@ armpmu_release_hardware(struct arm_pmu *armpmu)
{ {
int i, irq, irqs; int i, irq, irqs;
struct platform_device *pmu_device = armpmu->plat_device; struct platform_device *pmu_device = armpmu->plat_device;
struct arm_pmu_platdata *plat =
dev_get_platdata(&pmu_device->dev);
irqs = min(pmu_device->num_resources, num_possible_cpus()); irqs = min(pmu_device->num_resources, num_possible_cpus());
...@@ -373,13 +372,11 @@ armpmu_release_hardware(struct arm_pmu *armpmu) ...@@ -373,13 +372,11 @@ armpmu_release_hardware(struct arm_pmu *armpmu)
if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs))
continue; continue;
irq = platform_get_irq(pmu_device, i); irq = platform_get_irq(pmu_device, i);
if (irq >= 0) { if (irq >= 0)
if (plat && plat->disable_irq)
plat->disable_irq(irq);
free_irq(irq, armpmu); free_irq(irq, armpmu);
}
} }
pm_runtime_put_sync(&pmu_device->dev);
release_pmu(armpmu->type); release_pmu(armpmu->type);
} }
...@@ -412,6 +409,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) ...@@ -412,6 +409,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)
return -ENODEV; return -ENODEV;
} }
pm_runtime_get_sync(&pmu_device->dev);
for (i = 0; i < irqs; ++i) { for (i = 0; i < irqs; ++i) {
err = 0; err = 0;
irq = platform_get_irq(pmu_device, i); irq = platform_get_irq(pmu_device, i);
...@@ -437,8 +436,7 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) ...@@ -437,8 +436,7 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)
irq); irq);
armpmu_release_hardware(armpmu); armpmu_release_hardware(armpmu);
return err; return err;
} else if (plat && plat->enable_irq) }
plat->enable_irq(irq);
cpumask_set_cpu(i, &armpmu->active_irqs); cpumask_set_cpu(i, &armpmu->active_irqs);
} }
...@@ -581,6 +579,28 @@ static void armpmu_disable(struct pmu *pmu) ...@@ -581,6 +579,28 @@ static void armpmu_disable(struct pmu *pmu)
armpmu->stop(); armpmu->stop();
} }
#ifdef CONFIG_PM_RUNTIME
static int armpmu_runtime_resume(struct device *dev)
{
struct arm_pmu_platdata *plat = dev_get_platdata(dev);
if (plat && plat->runtime_resume)
return plat->runtime_resume(dev);
return 0;
}
static int armpmu_runtime_suspend(struct device *dev)
{
struct arm_pmu_platdata *plat = dev_get_platdata(dev);
if (plat && plat->runtime_suspend)
return plat->runtime_suspend(dev);
return 0;
}
#endif
static void __init armpmu_init(struct arm_pmu *armpmu) static void __init armpmu_init(struct arm_pmu *armpmu)
{ {
atomic_set(&armpmu->active_events, 0); atomic_set(&armpmu->active_events, 0);
...@@ -647,9 +667,14 @@ static int __devinit armpmu_device_probe(struct platform_device *pdev) ...@@ -647,9 +667,14 @@ static int __devinit armpmu_device_probe(struct platform_device *pdev)
return 0; return 0;
} }
static const struct dev_pm_ops armpmu_dev_pm_ops = {
SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL)
};
static struct platform_driver armpmu_driver = { static struct platform_driver armpmu_driver = {
.driver = { .driver = {
.name = "arm-pmu", .name = "arm-pmu",
.pm = &armpmu_dev_pm_ops,
.of_match_table = armpmu_of_device_ids, .of_match_table = armpmu_of_device_ids,
}, },
.probe = armpmu_device_probe, .probe = armpmu_device_probe,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment