Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fda0e18c authored by Russell King's avatar Russell King
Browse files

Merge branch 'devel-pmu' into devel

parents 98830bc9 d1e86d64
Loading
Loading
Loading
Loading
+2 −24
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ config ARM
	select RTC_LIB
	select SYS_SUPPORTS_APM_EMULATION
	select GENERIC_ATOMIC64 if (!CPU_32v6K)
	select HAVE_OPROFILE
	select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
	select HAVE_ARCH_KGDB
	select HAVE_KPROBES if (!XIP_KERNEL)
	select HAVE_KRETPROBES if (HAVE_KPROBES)
@@ -181,28 +181,6 @@ config ARM_L1_CACHE_SHIFT_6
	help
	  Setting ARM L1 cache line size to 64 Bytes.

if OPROFILE

config OPROFILE_ARMV6
	def_bool y
	depends on CPU_V6 && !SMP
	select OPROFILE_ARM11_CORE

config OPROFILE_MPCORE
	def_bool y
	depends on CPU_V6 && SMP
	select OPROFILE_ARM11_CORE

config OPROFILE_ARM11_CORE
	bool

config OPROFILE_ARMV7
	def_bool y
	depends on CPU_V7 && !SMP
	bool

endif

config VECTORS_BASE
	hex
	default 0xffff0000 if MMU || CPU_HIGH_VECTOR
@@ -1314,7 +1292,7 @@ config HIGHPTE

config HW_PERF_EVENTS
	bool "Enable hardware performance counter support for perf events"
	depends on PERF_EVENTS && CPU_HAS_PMU && (CPU_V6 || CPU_V7)
	depends on PERF_EVENTS && CPU_HAS_PMU
	default y
	help
	  Enable hardware performance counter support for perf events. If
+17 −0
Original line number Diff line number Diff line
@@ -28,4 +28,21 @@ set_perf_event_pending(void)
 * same indexes here for consistency. */
#define PERF_EVENT_INDEX_OFFSET 1

/* ARM perf PMU IDs for use by internal perf clients. */
enum arm_perf_pmu_ids {
	ARM_PERF_PMU_ID_XSCALE1	= 0,
	ARM_PERF_PMU_ID_XSCALE2,
	ARM_PERF_PMU_ID_V6,
	ARM_PERF_PMU_ID_V6MP,
	ARM_PERF_PMU_ID_CA8,
	ARM_PERF_PMU_ID_CA9,
	ARM_NUM_PMU_IDS,
};

extern enum arm_perf_pmu_ids
armpmu_get_pmu_id(void);

extern int
armpmu_get_max_events(void);

#endif /* __ARM_PERF_EVENT_H__ */
+12 −15
Original line number Diff line number Diff line
@@ -19,31 +19,26 @@ enum arm_pmu_type {

#ifdef CONFIG_CPU_HAS_PMU

struct pmu_irqs {
	const int   *irqs;
	int	    num_irqs;
};

/**
 * reserve_pmu() - reserve the hardware performance counters
 *
 * Reserve the hardware performance counters in the system for exclusive use.
 * The 'struct pmu_irqs' for the system is returned on success, ERR_PTR()
 * The platform_device for the system is returned on success, ERR_PTR()
 * encoded error on failure.
 */
extern const struct pmu_irqs *
reserve_pmu(void);
extern struct platform_device *
reserve_pmu(enum arm_pmu_type device);

/**
 * release_pmu() - Relinquish control of the performance counters
 *
 * Release the performance counters and allow someone else to use them.
 * Callers must have disabled the counters and released IRQs before calling
 * this. The 'struct pmu_irqs' returned from reserve_pmu() must be passed as
 * this. The platform_device returned from reserve_pmu() must be passed as
 * a cookie.
 */
extern int
release_pmu(const struct pmu_irqs *irqs);
release_pmu(struct platform_device *pdev);

/**
 * init_pmu() - Initialise the PMU.
@@ -53,24 +48,26 @@ release_pmu(const struct pmu_irqs *irqs);
 * the actual hardware initialisation.
 */
extern int
init_pmu(void);
init_pmu(enum arm_pmu_type device);

#else /* CONFIG_CPU_HAS_PMU */

static inline const struct pmu_irqs *
reserve_pmu(void)
#include <linux/err.h>

static inline struct platform_device *
reserve_pmu(enum arm_pmu_type device)
{
	return ERR_PTR(-ENODEV);
}

static inline int
release_pmu(const struct pmu_irqs *irqs)
release_pmu(struct platform_device *pdev)
{
	return -ENODEV;
}

static inline int
init_pmu(void)
init_pmu(enum arm_pmu_type device)
{
	return -ENODEV;
}
+892 −36

File changed.

Preview size limit exceeded, changes collapsed.

+83 −44
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
 *  linux/arch/arm/kernel/pmu.c
 *
 *  Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles
 *  Copyright (C) 2010 ARM Ltd, Will Deacon
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
@@ -9,65 +10,78 @@
 *
 */

#define pr_fmt(fmt) "PMU: " fmt

#include <linux/cpumask.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <asm/pmu.h>

/*
 * Define the IRQs for the system. We could use something like a platform
 * device but that seems fairly heavyweight for this. Also, the performance
 * counters can't be removed or hotplugged.
 *
 * Ordering is important: init_pmu() will use the ordering to set the affinity
 * to the corresponding core. e.g. the first interrupt will go to cpu 0, the
 * second goes to cpu 1 etc.
 */
static const int irqs[] = {
#if defined(CONFIG_ARCH_OMAP2)
	3,
#elif defined(CONFIG_ARCH_BCMRING)
	IRQ_PMUIRQ,
#elif defined(CONFIG_MACH_REALVIEW_EB)
	IRQ_EB11MP_PMU_CPU0,
	IRQ_EB11MP_PMU_CPU1,
	IRQ_EB11MP_PMU_CPU2,
	IRQ_EB11MP_PMU_CPU3,
#elif defined(CONFIG_ARCH_OMAP3)
	INT_34XX_BENCH_MPU_EMUL,
#elif defined(CONFIG_ARCH_IOP32X)
	IRQ_IOP32X_CORE_PMU,
#elif defined(CONFIG_ARCH_IOP33X)
	IRQ_IOP33X_CORE_PMU,
#elif defined(CONFIG_ARCH_PXA)
	IRQ_PMU,
#endif
};
static volatile long pmu_lock;

static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES];

static int __devinit pmu_device_probe(struct platform_device *pdev)
{

	if (pdev->id < 0 || pdev->id >= ARM_NUM_PMU_DEVICES) {
		pr_warning("received registration request for unknown "
				"device %d\n", pdev->id);
		return -EINVAL;
	}

	if (pmu_devices[pdev->id])
		pr_warning("registering new PMU device type %d overwrites "
				"previous registration!\n", pdev->id);
	else
		pr_info("registered new PMU device of type %d\n",
				pdev->id);

static const struct pmu_irqs pmu_irqs = {
	.irqs	    = irqs,
	.num_irqs   = ARRAY_SIZE(irqs),
	pmu_devices[pdev->id] = pdev;
	return 0;
}

static struct platform_driver pmu_driver = {
	.driver		= {
		.name	= "arm-pmu",
	},
	.probe		= pmu_device_probe,
};

static volatile long pmu_lock;
static int __init register_pmu_driver(void)
{
	return platform_driver_register(&pmu_driver);
}
device_initcall(register_pmu_driver);

const struct pmu_irqs *
reserve_pmu(void)
struct platform_device *
reserve_pmu(enum arm_pmu_type device)
{
	return test_and_set_bit_lock(0, &pmu_lock) ? ERR_PTR(-EBUSY) :
		&pmu_irqs;
	struct platform_device *pdev;

	if (test_and_set_bit_lock(device, &pmu_lock)) {
		pdev = ERR_PTR(-EBUSY);
	} else if (pmu_devices[device] == NULL) {
		clear_bit_unlock(device, &pmu_lock);
		pdev = ERR_PTR(-ENODEV);
	} else {
		pdev = pmu_devices[device];
	}

	return pdev;
}
EXPORT_SYMBOL_GPL(reserve_pmu);

int
release_pmu(const struct pmu_irqs *irqs)
release_pmu(struct platform_device *pdev)
{
	if (WARN_ON(irqs != &pmu_irqs))
	if (WARN_ON(pdev != pmu_devices[pdev->id]))
		return -EINVAL;
	clear_bit_unlock(0, &pmu_lock);
	clear_bit_unlock(pdev->id, &pmu_lock);
	return 0;
}
EXPORT_SYMBOL_GPL(release_pmu);
@@ -87,17 +101,42 @@ set_irq_affinity(int irq,
#endif
}

int
init_pmu(void)
static int
init_cpu_pmu(void)
{
	int i, err = 0;
	struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU];

	if (!pdev) {
		err = -ENODEV;
		goto out;
	}

	for (i = 0; i < pmu_irqs.num_irqs; ++i) {
		err = set_irq_affinity(pmu_irqs.irqs[i], i);
	for (i = 0; i < pdev->num_resources; ++i) {
		err = set_irq_affinity(platform_get_irq(pdev, i), i);
		if (err)
			break;
	}

out:
	return err;
}

int
init_pmu(enum arm_pmu_type device)
{
	int err = 0;

	switch (device) {
	case ARM_PMU_DEVICE_CPU:
		err = init_cpu_pmu();
		break;
	default:
		pr_warning("attempt to initialise unknown device %d\n",
				device);
		err = -EINVAL;
	}

	return err;
}
EXPORT_SYMBOL_GPL(init_pmu);
Loading