diff options
Diffstat (limited to 'arch/arm/kernel/perf_event.c')
| -rw-r--r-- | arch/arm/kernel/perf_event.c | 347 | 
1 files changed, 51 insertions, 296 deletions
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index ab243b87118..93971b1a4f0 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -12,68 +12,15 @@   */  #define pr_fmt(fmt) "hw perfevents: " fmt -#include <linux/bitmap.h> -#include <linux/interrupt.h>  #include <linux/kernel.h> -#include <linux/export.h> -#include <linux/perf_event.h>  #include <linux/platform_device.h> -#include <linux/spinlock.h> +#include <linux/pm_runtime.h>  #include <linux/uaccess.h> -#include <asm/cputype.h> -#include <asm/irq.h>  #include <asm/irq_regs.h>  #include <asm/pmu.h>  #include <asm/stacktrace.h> -/* - * ARMv6 supports a maximum of 3 events, starting from index 0. If we add - * another platform that supports more, we need to increase this to be the - * largest of all platforms. - * - * ARMv7 supports up to 32 events: - *  cycle counter CCNT + 31 events counters CNT0..30. - *  Cortex-A8 has 1+4 counters, Cortex-A9 has 1+6 counters. - */ -#define ARMPMU_MAX_HWEVENTS		32 - -static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); -static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); -static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); - -#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) - -/* Set at runtime when we know what CPU type we are. */ -static struct arm_pmu *cpu_pmu; - -const char *perf_pmu_name(void) -{ -	if (!cpu_pmu) -		return NULL; - -	return cpu_pmu->pmu.name; -} -EXPORT_SYMBOL_GPL(perf_pmu_name); - -int perf_num_counters(void) -{ -	int max_events = 0; - -	if (cpu_pmu != NULL) -		max_events = cpu_pmu->num_events; - -	return max_events; -} -EXPORT_SYMBOL_GPL(perf_num_counters); - -#define HW_OP_UNSUPPORTED		0xFFFF - -#define C(_x) \ -	PERF_COUNT_HW_CACHE_##_x - -#define CACHE_OP_UNSUPPORTED		0xFFFF -  static int  armpmu_map_cache_event(const unsigned (*cache_map)  				      [PERF_COUNT_HW_CACHE_MAX] @@ -104,7 +51,7 @@ armpmu_map_cache_event(const unsigned (*cache_map)  }  static int -armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) +armpmu_map_hw_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config)  {  	int mapping = (*event_map)[config];  	return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; @@ -116,19 +63,20 @@ armpmu_map_raw_event(u32 raw_event_mask, u64 config)  	return (int)(config & raw_event_mask);  } -static int map_cpu_event(struct perf_event *event, -			 const unsigned (*event_map)[PERF_COUNT_HW_MAX], -			 const unsigned (*cache_map) -					[PERF_COUNT_HW_CACHE_MAX] -					[PERF_COUNT_HW_CACHE_OP_MAX] -					[PERF_COUNT_HW_CACHE_RESULT_MAX], -			 u32 raw_event_mask) +int +armpmu_map_event(struct perf_event *event, +		 const unsigned (*event_map)[PERF_COUNT_HW_MAX], +		 const unsigned (*cache_map) +				[PERF_COUNT_HW_CACHE_MAX] +				[PERF_COUNT_HW_CACHE_OP_MAX] +				[PERF_COUNT_HW_CACHE_RESULT_MAX], +		 u32 raw_event_mask)  {  	u64 config = event->attr.config;  	switch (event->attr.type) {  	case PERF_TYPE_HARDWARE: -		return armpmu_map_event(event_map, config); +		return armpmu_map_hw_event(event_map, config);  	case PERF_TYPE_HW_CACHE:  		return armpmu_map_cache_event(cache_map, config);  	case PERF_TYPE_RAW: @@ -222,7 +170,6 @@ armpmu_stop(struct perf_event *event, int flags)  	 */  	if (!(hwc->state & PERF_HES_STOPPED)) {  		armpmu->disable(hwc, hwc->idx); -		barrier(); /* why? */  		armpmu_event_update(event, hwc, hwc->idx);  		hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;  	} @@ -350,99 +297,41 @@ validate_group(struct perf_event *event)  	return 0;  } -static irqreturn_t armpmu_platform_irq(int irq, void *dev) +static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)  {  	struct arm_pmu *armpmu = (struct arm_pmu *) dev;  	struct platform_device *plat_device = armpmu->plat_device;  	struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); -	return plat->handle_irq(irq, dev, armpmu->handle_irq); +	if (plat && plat->handle_irq) +		return plat->handle_irq(irq, dev, armpmu->handle_irq); +	else +		return armpmu->handle_irq(irq, dev);  }  static void  armpmu_release_hardware(struct arm_pmu *armpmu)  { -	int i, irq, irqs; -	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()); - -	for (i = 0; i < irqs; ++i) { -		if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) -			continue; -		irq = platform_get_irq(pmu_device, i); -		if (irq >= 0) { -			if (plat && plat->disable_irq) -				plat->disable_irq(irq); -			free_irq(irq, armpmu); -		} -	} - -	release_pmu(armpmu->type); +	armpmu->free_irq(); +	pm_runtime_put_sync(&armpmu->plat_device->dev);  }  static int  armpmu_reserve_hardware(struct arm_pmu *armpmu)  { -	struct arm_pmu_platdata *plat; -	irq_handler_t handle_irq; -	int i, err, irq, irqs; +	int err;  	struct platform_device *pmu_device = armpmu->plat_device;  	if (!pmu_device)  		return -ENODEV; -	err = reserve_pmu(armpmu->type); +	pm_runtime_get_sync(&pmu_device->dev); +	err = armpmu->request_irq(armpmu_dispatch_irq);  	if (err) { -		pr_warning("unable to reserve pmu\n"); +		armpmu_release_hardware(armpmu);  		return err;  	} -	plat = dev_get_platdata(&pmu_device->dev); -	if (plat && plat->handle_irq) -		handle_irq = armpmu_platform_irq; -	else -		handle_irq = armpmu->handle_irq; - -	irqs = min(pmu_device->num_resources, num_possible_cpus()); -	if (irqs < 1) { -		pr_err("no irqs for PMUs defined\n"); -		return -ENODEV; -	} - -	for (i = 0; i < irqs; ++i) { -		err = 0; -		irq = platform_get_irq(pmu_device, i); -		if (irq < 0) -			continue; - -		/* -		 * If we have a single PMU interrupt that we can't shift, -		 * assume that we're running on a uniprocessor machine and -		 * continue. Otherwise, continue without this interrupt. -		 */ -		if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { -			pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", -				    irq, i); -			continue; -		} - -		err = request_irq(irq, handle_irq, -				  IRQF_DISABLED | IRQF_NOBALANCING, -				  "arm-pmu", armpmu); -		if (err) { -			pr_err("unable to request IRQ%d for ARM PMU counters\n", -				irq); -			armpmu_release_hardware(armpmu); -			return err; -		} else if (plat && plat->enable_irq) -			plat->enable_irq(irq); - -		cpumask_set_cpu(i, &armpmu->active_irqs); -	} -  	return 0;  } @@ -581,6 +470,32 @@ static void armpmu_disable(struct pmu *pmu)  	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 + +const struct dev_pm_ops armpmu_dev_pm_ops = { +	SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL) +}; +  static void __init armpmu_init(struct arm_pmu *armpmu)  {  	atomic_set(&armpmu->active_events, 0); @@ -598,174 +513,14 @@ static void __init armpmu_init(struct arm_pmu *armpmu)  	};  } -int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) +int armpmu_register(struct arm_pmu *armpmu, char *name, int type)  {  	armpmu_init(armpmu); +	pr_info("enabled with %s PMU driver, %d counters available\n", +			armpmu->name, armpmu->num_events);  	return perf_pmu_register(&armpmu->pmu, name, type);  } -/* Include the PMU-specific implementations. */ -#include "perf_event_xscale.c" -#include "perf_event_v6.c" -#include "perf_event_v7.c" - -/* - * Ensure the PMU has sane values out of reset. - * This requires SMP to be available, so exists as a separate initcall. - */ -static int __init -cpu_pmu_reset(void) -{ -	if (cpu_pmu && cpu_pmu->reset) -		return on_each_cpu(cpu_pmu->reset, NULL, 1); -	return 0; -} -arch_initcall(cpu_pmu_reset); - -/* - * PMU platform driver and devicetree bindings. - */ -static struct of_device_id armpmu_of_device_ids[] = { -	{.compatible = "arm,cortex-a9-pmu"}, -	{.compatible = "arm,cortex-a8-pmu"}, -	{.compatible = "arm,arm1136-pmu"}, -	{.compatible = "arm,arm1176-pmu"}, -	{}, -}; - -static struct platform_device_id armpmu_plat_device_ids[] = { -	{.name = "arm-pmu"}, -	{}, -}; - -static int __devinit armpmu_device_probe(struct platform_device *pdev) -{ -	if (!cpu_pmu) -		return -ENODEV; - -	cpu_pmu->plat_device = pdev; -	return 0; -} - -static struct platform_driver armpmu_driver = { -	.driver		= { -		.name	= "arm-pmu", -		.of_match_table = armpmu_of_device_ids, -	}, -	.probe		= armpmu_device_probe, -	.id_table	= armpmu_plat_device_ids, -}; - -static int __init register_pmu_driver(void) -{ -	return platform_driver_register(&armpmu_driver); -} -device_initcall(register_pmu_driver); - -static struct pmu_hw_events *armpmu_get_cpu_events(void) -{ -	return &__get_cpu_var(cpu_hw_events); -} - -static void __init cpu_pmu_init(struct arm_pmu *armpmu) -{ -	int cpu; -	for_each_possible_cpu(cpu) { -		struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); -		events->events = per_cpu(hw_events, cpu); -		events->used_mask = per_cpu(used_mask, cpu); -		raw_spin_lock_init(&events->pmu_lock); -	} -	armpmu->get_hw_events = armpmu_get_cpu_events; -	armpmu->type = ARM_PMU_DEVICE_CPU; -} - -/* - * PMU hardware loses all context when a CPU goes offline. - * When a CPU is hotplugged back in, since some hardware registers are - * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading - * junk values out of them. - */ -static int __cpuinit pmu_cpu_notify(struct notifier_block *b, -					unsigned long action, void *hcpu) -{ -	if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) -		return NOTIFY_DONE; - -	if (cpu_pmu && cpu_pmu->reset) -		cpu_pmu->reset(NULL); - -	return NOTIFY_OK; -} - -static struct notifier_block __cpuinitdata pmu_cpu_notifier = { -	.notifier_call = pmu_cpu_notify, -}; - -/* - * CPU PMU identification and registration. - */ -static int __init -init_hw_perf_events(void) -{ -	unsigned long cpuid = read_cpuid_id(); -	unsigned long implementor = (cpuid & 0xFF000000) >> 24; -	unsigned long part_number = (cpuid & 0xFFF0); - -	/* ARM Ltd CPUs. */ -	if (0x41 == implementor) { -		switch (part_number) { -		case 0xB360:	/* ARM1136 */ -		case 0xB560:	/* ARM1156 */ -		case 0xB760:	/* ARM1176 */ -			cpu_pmu = armv6pmu_init(); -			break; -		case 0xB020:	/* ARM11mpcore */ -			cpu_pmu = armv6mpcore_pmu_init(); -			break; -		case 0xC080:	/* Cortex-A8 */ -			cpu_pmu = armv7_a8_pmu_init(); -			break; -		case 0xC090:	/* Cortex-A9 */ -			cpu_pmu = armv7_a9_pmu_init(); -			break; -		case 0xC050:	/* Cortex-A5 */ -			cpu_pmu = armv7_a5_pmu_init(); -			break; -		case 0xC0F0:	/* Cortex-A15 */ -			cpu_pmu = armv7_a15_pmu_init(); -			break; -		case 0xC070:	/* Cortex-A7 */ -			cpu_pmu = armv7_a7_pmu_init(); -			break; -		} -	/* Intel CPUs [xscale]. */ -	} else if (0x69 == implementor) { -		part_number = (cpuid >> 13) & 0x7; -		switch (part_number) { -		case 1: -			cpu_pmu = xscale1pmu_init(); -			break; -		case 2: -			cpu_pmu = xscale2pmu_init(); -			break; -		} -	} - -	if (cpu_pmu) { -		pr_info("enabled with %s PMU driver, %d counters available\n", -			cpu_pmu->name, cpu_pmu->num_events); -		cpu_pmu_init(cpu_pmu); -		register_cpu_notifier(&pmu_cpu_notifier); -		armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW); -	} else { -		pr_info("no hardware support available\n"); -	} - -	return 0; -} -early_initcall(init_hw_perf_events); -  /*   * Callchain handling code.   */  | 
