Hi, I am trying to get perf_event to work properly on my OMAP4 Pandabaord running the 3.2.0 kernel. I am the developer on libpfm4 and a regular contributor to the perf_event subsystem and perf tool. I want to use a Pandaboard to test libpfm4 ARM support. I have been talking with Will Deacon and he suggested I post to this list. I know that the off-the-shelf 3.2.0 does not have working PMU interrupts. I have integrated additional patches from both Ming Lei and Linaro. It seems some parts of those patches were integrated into 3.2.0. I have attached my 3.2.0 changes to this message. With that, I get perf_event to register PMU interrupts 33 and 34. However, they don't fire when I count therefore the counts are bogus (counters wrap around). I suspect I am missing something in the patch I put together. But I don't know what's missing. I am not an ARM platform expert. I am hoping someone on this list may shed some light on this problem. It would be really nice to have perf_event running out of the box on Pandaboard for 3.3. It would go a long way into making performance monitoring usable on ARM. Thanks. --- diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index 0bda22c..b5a5be2 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -27,13 +27,22 @@ enum arm_pmu_type { /* * struct arm_pmu_platdata - ARM PMU platform data * - * @handle_irq: an optional handler which will be called from the interrupt and - * passed the address of the low level handler, and can be used to implement - * any platform specific handling before or after calling it. + * @handle_irq: an optional handler which will be called from the + * interrupt and passed the address of the low level handler, + * and can be used to implement any platform specific handling + * before or after calling it. + * @enable_irq: an optional handler which will be called after + * request_irq and be used to handle some platform specific + * irq enablement + * @disable_irq: an optional handler which will be called before + * free_irq and be used to handle some platform specific + * irq disablement */ struct arm_pmu_platdata { irqreturn_t (*handle_irq)(int irq, void *dev, irq_handler_t pmu_handler); + void (*enable_irq)(int irq); + void (*disable_irq)(int irq); }; #ifdef CONFIG_CPU_HAS_PMU diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 88b0941..24e001f 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -380,6 +380,8 @@ 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()); @@ -387,8 +389,11 @@ armpmu_release_hardware(struct arm_pmu *armpmu) if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) continue; irq = platform_get_irq(pmu_device, i); - if (irq >= 0) + if (irq >= 0) { + if (plat->disable_irq) + plat->disable_irq(irq); free_irq(irq, armpmu); + } } release_pmu(armpmu->type); @@ -449,6 +454,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) armpmu_release_hardware(armpmu); return err; } + if (plat->enable_irq) + plat->enable_irq(irq); cpumask_set_cpu(i, &armpmu->active_irqs); } diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index c15cfad..6caf31a 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -17,12 +17,14 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <mach/hardware.h> #include <mach/irqs.h> #include <asm/mach-types.h> #include <asm/mach/map.h> #include <asm/pmu.h> +#include <asm/cti.h> #include <plat/tc.h> #include <plat/board.h> @@ -404,14 +406,136 @@ static struct platform_device omap_pmu_device = { .num_resources = 1, }; -static void omap_init_pmu(void) +static struct arm_pmu_platdata omap4_pmu_data; +static struct cti omap4_cti[2]; +static struct platform_device *pmu_dev; + +static void omap4_enable_cti(int irq) { - if (cpu_is_omap24xx()) + pm_runtime_get_sync(&pmu_dev->dev); + + if (irq == OMAP44XX_IRQ_CTI0) + cti_enable(&omap4_cti[0]); + else if (irq == OMAP44XX_IRQ_CTI1) + cti_enable(&omap4_cti[1]); +} + +static void omap4_disable_cti(int irq) +{ + if (irq == OMAP44XX_IRQ_CTI0) + cti_disable(&omap4_cti[0]); + else if (irq == OMAP44XX_IRQ_CTI1) + cti_disable(&omap4_cti[1]); + pm_runtime_put(&pmu_dev->dev); +} + +static irqreturn_t omap4_pmu_handler(int irq, void *dev, irq_handler_t handler) +{ + if (irq == OMAP44XX_IRQ_CTI0) + cti_irq_ack(&omap4_cti[0]); + else if (irq == OMAP44XX_IRQ_CTI1) + cti_irq_ack(&omap4_cti[1]); + + return handler(irq, dev); +} + +static void omap4_configure_pmu_irq(void) +{ + void __iomem *base0; + void __iomem *base1; + + base0 = ioremap(OMAP44XX_CTI0_BASE, SZ_4K); + base1 = ioremap(OMAP44XX_CTI1_BASE, SZ_4K); + if (!base0 && !base1) { + pr_err("ioremap for OMAP4 CTI failed\n"); + return; + } + + /*configure CTI0 for pmu irq routing*/ + cti_init(&omap4_cti[0], base0, OMAP44XX_IRQ_CTI0, 6); + cti_unlock(&omap4_cti[0]); + cti_map_trigger(&omap4_cti[0], 1, 6, 2); + + /*configure CTI1 for pmu irq routing*/ + cti_init(&omap4_cti[1], base1, OMAP44XX_IRQ_CTI1, 6); + cti_unlock(&omap4_cti[1]); + cti_map_trigger(&omap4_cti[1], 1, 6, 2); + + omap4_pmu_data.handle_irq = omap4_pmu_handler; + omap4_pmu_data.enable_irq = omap4_enable_cti; + omap4_pmu_data.disable_irq = omap4_disable_cti; +} + +static struct arm_pmu_platdata omap4_pmu_data; +static struct omap_device_pm_latency omap_pmu_latency[] = { + [0] = { + .deactivate_func = omap_device_idle_hwmods, + .activate_func = omap_device_enable_hwmods, + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST, + }, +}; + +static struct platform_device* __init omap4_init_pmu(void) +{ + int id = -1; + const char *hw; + struct platform_device *pd; + struct omap_hwmod* oh[3]; + char *dev_name = "arm-pmu"; + + hw = "l3_main_3"; + oh[0] = omap_hwmod_lookup(hw); + if (!oh[0]) { + pr_err("Could not look up %s hwmod\n", hw); + return NULL; + } + hw = "l3_instr"; + oh[1] = omap_hwmod_lookup(hw); + if (!oh[1]) { + pr_err("Could not look up %s hwmod\n", hw); + return NULL; + } + hw = "emu"; + oh[2] = omap_hwmod_lookup(hw); + if (!oh[2]) { + pr_err("Could not look up %s hwmod\n", hw); + return NULL; + } + + pd = omap_device_build_ss(dev_name, id, oh, 3, &omap4_pmu_data, + sizeof(omap4_pmu_data), + omap_pmu_latency, + ARRAY_SIZE(omap_pmu_latency), 0); + WARN(IS_ERR(pd), "Can't build omap_device for %s.\n", + dev_name); + return pd; +} + +static void __init omap_init_pmu(void) +{ + if (cpu_is_omap24xx()) { omap_pmu_device.resource = &omap2_pmu_resource; - else if (cpu_is_omap34xx()) + } else if (cpu_is_omap34xx()) { omap_pmu_device.resource = &omap3_pmu_resource; - else + } else if (cpu_is_omap44xx()) { + struct platform_device *pd; + + pd = omap4_init_pmu(); + if (!pd) + return; +{ int i; + for (i = 0 ; i < 2; i++) + printk("RES:%d IRQ:%d\n", i, platform_get_irq(pd, i)); +} + pmu_dev = pd; + pm_runtime_enable(&pd->dev); + pm_runtime_get_sync(&pd->dev); + omap4_configure_pmu_irq(); + pm_runtime_put(&pd->dev); + return; + } else { return; + } platform_device_register(&omap_pmu_device); } diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index daaf165..dd4e1c2 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -5276,6 +5276,30 @@ static struct omap_hwmod omap44xx_wd_timer3_hwmod = { .slaves_cnt = ARRAY_SIZE(omap44xx_wd_timer3_slaves), }; +static struct omap_hwmod_class omap44xx_emu_hwmod_class = { + .name = "emu", +}; + +static struct omap_hwmod_irq_info omap44xx_emu_irqs[] = { + { .name = "cti0", .irq = 1 + OMAP44XX_IRQ_GIC_START }, + { .name = "cti1", .irq = 2 + OMAP44XX_IRQ_GIC_START }, + { .irq = -1 } +}; + +/*emu hwmod*/ +static struct omap_hwmod omap44xx_emu_hwmod = { + .name = "emu", + .class = &omap44xx_emu_hwmod_class, + .clkdm_name = "emu_sys_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_EMU_CLKSTCTRL_OFFSET, + .modulemode = MODULEMODE_HWCTRL, + }, + }, + .mpu_irqs = omap44xx_emu_irqs, +}; + static __initdata struct omap_hwmod *omap44xx_hwmods[] = { /* dmm class */ @@ -5422,6 +5446,9 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = { &omap44xx_wd_timer2_hwmod, &omap44xx_wd_timer3_hwmod, + /*emu class*/ + &omap44xx_emu_hwmod, + NULL, }; diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h index ea2b8a6..b127a16 100644 --- a/arch/arm/plat-omap/include/plat/omap44xx.h +++ b/arch/arm/plat-omap/include/plat/omap44xx.h @@ -57,5 +57,7 @@ #define OMAP44XX_HSUSB_OHCI_BASE (L4_44XX_BASE + 0x64800) #define OMAP44XX_HSUSB_EHCI_BASE (L4_44XX_BASE + 0x64C00) +#define OMAP44XX_CTI0_BASE 0x54148000 +#define OMAP44XX_CTI1_BASE 0x54149000 #endif /* __ASM_ARCH_OMAP44XX_H */ -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html