diff options
author | Quinn Jensen <quinn.jensen@freescale.com> | 2007-10-24 21:25:55 -0600 |
---|---|---|
committer | Quinn Jensen <quinn.jensen@freescale.com> | 2007-10-24 21:25:55 -0600 |
commit | 52735b4549f9a8f6b6c9a49e91bb993e252bd9bb (patch) | |
tree | b0c5d37ab9ca57754e295344f25c6b5869d0e516 | |
parent | d1ac43b861f163ae029de46059972802586ecb68 (diff) |
CR ENGR00048072 oprofile: updates to support CCNT/EVTMON on 2.6.22 kernel
Patch for CR ENGR00048072 oprofile: updates to support CCNT/EVTMON on
2.6.22 kernel for ARM-11 processors. Added Oprofile CCNT counter fix to
support newly added ARM11 PMU kernel driver in 2.6.22 kernel. Add new
file for ARM11 L2 cache EVTMON support.
http://www.bitshrine.org/gpp/linux-2.6.22-mx-CR-ENGR00048072-oprofile-updates-to-suppor.patch
-rw-r--r-- | arch/arm/mm/cache-l2x0.c | 50 | ||||
-rw-r--r-- | arch/arm/oprofile/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/oprofile/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/oprofile/evtmon_regs.h | 29 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_core.c | 44 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_core.h | 13 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_evtmon.c | 188 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_v6.c | 19 | ||||
-rw-r--r-- | arch/arm/plat-mxc/Kconfig | 5 |
9 files changed, 351 insertions, 2 deletions
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b074dbb75dc9..ce35d733ea71 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -23,6 +23,13 @@ #include <asm/hardware/cache-l2x0.h> #define CACHE_LINE_SIZE 32 +#ifdef CONFIG_OPROFILE_ARM11_EVTMON +#include <linux/module.h> +#define L2_ENABLE_BIT 0x1 +#define L2_EVTBUS_BIT 0x100000 +#define L2_CTL_REG (l2x0_base + L2X0_CTRL) +#define L2_AUX_REG (l2x0_base + L2X0_AUX_CTRL) +#endif static void __iomem *l2x0_base; @@ -94,6 +101,49 @@ static void l2x0_flush_range(unsigned long start, unsigned long end) cache_sync(); } +#ifdef CONFIG_OPROFILE_ARM11_EVTMON +/*! + * Enable the EVTBUS to monitor L2 cache events + */ +void l2x0_evtbus_enable(void) +{ + unsigned int flags; + + local_irq_save(flags); + /* If L2 cache is enabled then disable L2 cache, enable L2 evtbus, + re-enable L2 cache */ + if ((readl(L2_CTL_REG) & L2_ENABLE_BIT) != 0) { + writel(0, L2_CTL_REG); + writel((readl(L2_AUX_REG)| L2_EVTBUS_BIT), L2_AUX_REG); + writel(L2_ENABLE_BIT, L2_CTL_REG); + } else { + writel((readl(L2_AUX_REG)| L2_EVTBUS_BIT), L2_AUX_REG); + } + local_irq_restore(flags); +} + +/*! + * Disable the EVTBUS + */ +void l2x0_evtbus_disable(void) +{ + unsigned int flags; + + local_irq_save(flags); + /* If L2 cache is enabled then disable L2 cache, disable L2 evtbus, + re-enable L2 cache */ + if ((readl(L2_CTL_REG) & L2_ENABLE_BIT) != 0) { + writel(0, L2_CTL_REG); + writel((readl(L2_AUX_REG)& ~L2_EVTBUS_BIT), L2_AUX_REG); + writel(L2_ENABLE_BIT, L2_CTL_REG); + } else { + writel((readl(L2_AUX_REG)& ~L2_EVTBUS_BIT), L2_AUX_REG); + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(l2x0_evtbus_enable); +EXPORT_SYMBOL(l2x0_evtbus_disable); +#endif void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) { __u32 aux; diff --git a/arch/arm/oprofile/Kconfig b/arch/arm/oprofile/Kconfig index afd93ad02feb..578322ba3ae2 100644 --- a/arch/arm/oprofile/Kconfig +++ b/arch/arm/oprofile/Kconfig @@ -26,6 +26,7 @@ config OPROFILE_ARMV6 depends on CPU_V6 && !SMP default y select OPROFILE_ARM11_CORE + select OPROFILE_ARM11_EVTMON if ARCH_HAS_EVTMON config OPROFILE_MPCORE bool @@ -36,6 +37,9 @@ config OPROFILE_MPCORE config OPROFILE_ARM11_CORE bool +config OPROFILE_ARM11_EVTMON + bool + endif endmenu diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile index e61d0cc520b7..95cecb75e644 100644 --- a/arch/arm/oprofile/Makefile +++ b/arch/arm/oprofile/Makefile @@ -9,5 +9,6 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprofile-y := $(DRIVER_OBJS) common.o backtrace.o oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o +oprofile-$(CONFIG_OPROFILE_ARM11_EVTMON) += op_model_arm11_evtmon.o oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o diff --git a/arch/arm/oprofile/evtmon_regs.h b/arch/arm/oprofile/evtmon_regs.h index 13edbfc06491..701a3dc165fc 100644 --- a/arch/arm/oprofile/evtmon_regs.h +++ b/arch/arm/oprofile/evtmon_regs.h @@ -52,4 +52,33 @@ #define ECT_CTI_INTACK (ECT_CTI_BASE + 0x10) #define ECT_CTI_TRIGOUTSTATUS (ECT_CTI_BASE + 0x134) +#define ENABLE_L2CACHE 0x1 +#define EVTMON_ENABLE 0x001 /* Enable EVTMON */ +#define EVT_UNUSED 0x100 +#define MAX_PMUCOUNTERS 3 + +#ifdef CONFIG_OPROFILE_ARM11_EVTMON +#define ECT_WORKAROUND +#endif + +#ifdef ECT_WORKAROUND +#define ENABLE_CTI_CLOCK 0x00000020 +#define UNLOCK_ECT_CODE 0x0ACCE550 +#define ECT_CTI_CHAN_2 0x4 +#define ECT_CTI_CHAN_3 0x8 +#define ECT_CTI_TRIGIN_1 1 +#define ECT_CTI_TRIGIN_7 7 +#define ECT_CTI_TRIGOUT_2 2 +#define ECT_CTI_TRIGOUT_6 6 +#define ENABLE_ECT 0x1 +#define ACK_TRIG_OUT_2 0x4 +#define EM_SET_INT L2EM_ENABLE_CNTINCRINT +#define EVENT_OVERFLOW_INT INT_ECT +#else +#define EVENT_OVERFLOW_INT ARM11_PMU_IRQ +#define EM_SET_INT L2EM_ENABLE_OVERFLOWINT +#endif + +int l2em_configure_counter(int nr, int type); +void write_l2counter(int nr, u32 val); #endif diff --git a/arch/arm/oprofile/op_model_arm11_core.c b/arch/arm/oprofile/op_model_arm11_core.c index ad80752cb9fb..3a4c7bf8ef77 100644 --- a/arch/arm/oprofile/op_model_arm11_core.c +++ b/arch/arm/oprofile/op_model_arm11_core.c @@ -13,7 +13,12 @@ #include "op_counter.h" #include "op_arm_model.h" #include "op_model_arm11_core.h" +#include "evtmon_regs.h" +#define NEED_OPROFILE_CCNT_FIX +#ifdef NEED_OPROFILE_CCNT_FIX /* Workaround to correctly map from user space counters to kernel space counters */ +struct op_counter_config tmp; +#endif /* * ARM11 PMU support */ @@ -63,6 +68,12 @@ int arm11_setup_pmu(void) arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | PMCR_C | PMCR_P); +#ifdef NEED_OPROFILE_CCNT_FIX /* Workaround to correctly map from user space counters to kernel space counters */ + tmp = counter_config[PMN0]; + counter_config[PMN0] = counter_config[PMN1]; + counter_config[PMN1] = counter_config[CCNT]; + counter_config[CCNT] = tmp; +#endif for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { unsigned long event; @@ -119,16 +130,49 @@ static irqreturn_t arm11_pmu_interrupt(int irq, void *arg) unsigned int cnt; u32 pmnc; +#ifdef ECT_WORKAROUND + /* Disable L2_EVTMON */ + __raw_writel((__raw_readl(L2EM_CTRL) & ~EVTMON_ENABLE), L2EM_CTRL); + + /* Disable ARM11 PMU while retaining interrupts and overflow bits */ + pmnc = arm11_read_pmnc(); + pmnc &= ~(PMU_ENABLE | PMU_OVERFLOWBIT_MASK); + arm11_write_pmnc(pmnc); + + while (__raw_readl(ECT_CTI_TRIGOUTSTATUS) & ECT_CTI_CHAN_2) + __raw_writel(ACK_TRIG_OUT_2, ECT_CTI_INTACK); + + pmnc = arm11_read_pmnc(); + + /* Re-enable ARM11 PMU */ + pmnc |= PMU_ENABLE; + arm11_write_pmnc(pmnc); + + /* Re-enable L2_EVTMON */ + __raw_writel((__raw_readl(L2EM_CTRL) | L2EM_ENABLE_MASK), L2EM_CTRL); +#else pmnc = arm11_read_pmnc(); +#endif for (cnt = PMN0; cnt <= CCNT; cnt++) { if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) { arm11_reset_counter(cnt); +#ifdef NEED_OPROFILE_CCNT_FIX /* Workaround to correctly map from user space counters to kernel space counters */ + if (cnt == PMN0) + oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), PMN1)); + else if (cnt == PMN1) + oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), CCNT)); + else if (cnt == CCNT) + oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), PMN0)); +#else oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt)); +#endif } } +#ifndef ECT_WORKAROUND /* Clear counter flag(s) */ arm11_write_pmnc(pmnc); +#endif return IRQ_HANDLED; } diff --git a/arch/arm/oprofile/op_model_arm11_core.h b/arch/arm/oprofile/op_model_arm11_core.h index 6f8538e5a960..b59d15107d8d 100644 --- a/arch/arm/oprofile/op_model_arm11_core.h +++ b/arch/arm/oprofile/op_model_arm11_core.h @@ -35,6 +35,8 @@ #define CCNT 2 #define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter)) +#define PMU_ENABLE 0x001 /* Enable counters */ +#define PMU_OVERFLOWBIT_MASK 0x700 int arm11_setup_pmu(void); int arm11_start_pmu(void); @@ -42,4 +44,15 @@ int arm11_stop_pmu(void); int arm11_request_interrupts(int *, int); void arm11_release_interrupts(int *, int); +#ifdef CONFIG_OPROFILE_ARM11_EVTMON +extern int arm11_evtmon_setup_ctrs(void); +extern void arm11_evtmon_stop(void); +extern int arm11_evtmon_start(void); +extern int arm11_evtmon_detect(void); +#else +#define arm11_evtmon_setup_ctrs() do {} while(0) +#define arm11_evtmon_stop() do {} while(0) +#define arm11_evtmon_start() do {} while(0) +#define arm11_evtmon_detect() do {} while(0) +#endif #endif diff --git a/arch/arm/oprofile/op_model_arm11_evtmon.c b/arch/arm/oprofile/op_model_arm11_evtmon.c new file mode 100644 index 000000000000..a7e579b8b990 --- /dev/null +++ b/arch/arm/oprofile/op_model_arm11_evtmon.c @@ -0,0 +1,188 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup MXC_Oprofile ARM11 EVTMON Driver for Oprofile + */ + +/*! + * @file op_model_arm11_evtmon.c + * + *Based on the op_model_xscale.c driver by author Zwane Mwaikambo + * + * @ingroup MXC_Oprofile + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/system.h> + +#include "op_counter.h" +#include "op_arm_model.h" +#include "evtmon_regs.h" + +struct pmu_counter { + volatile unsigned long ovf; + unsigned long reset_counter; +}; + +enum { EMC0 = MAX_PMUCOUNTERS, EMC1, EMC2, EMC3, MAX_L2COUNTERS }; + +static struct pmu_counter results[MAX_L2COUNTERS]; + +struct pmu_type { + int id; + char *name; + int num_counters; + unsigned int int_enable; + unsigned int cnt_ovf[MAX_L2COUNTERS]; + unsigned int int_mask[MAX_L2COUNTERS]; +}; + +static struct pmu_type pmu_parms[] = { + { + .id = 0, + .int_mask = {[EMC0] = 0x800,[EMC1] = 0x400,[EMC2] = + 0x200,[EMC3] = 0x100}, + .cnt_ovf = {[EMC0] = 0x1,[EMC1] = 0x2,[EMC2] = 0x4,[EMC3] = 0x8}, + }, +}; + +static struct pmu_type *pmu; + +extern void l2x0_evtbus_enable(void); +extern void l2x0_evtbus_disable(void); + +/*! + * function is used to write the EVTMON counter configuration register. + */ +int l2em_configure_counter(int nr, int type) +{ + /* Configure the counter event source */ + __raw_writel(((type << 2) & 0x7c), L2EM_CC(nr)); + if (type) + __raw_writel((__raw_readl(L2EM_CC(nr)) | EM_SET_INT), + L2EM_CC(nr)); + + return 0; +} + +/*! + * function is used to write the EVTMON counters + */ +void write_l2counter(int nr, u32 val) +{ + __raw_writel(val, L2EM_CNT(nr)); +} + +/*! + * function is used to check the status of the ARM11 evtmon counters + */ +int arm11_evtmon_setup_ctrs(void) +{ + int i; + + for (i = EMC0; i < MAX_L2COUNTERS; i++) { + if (counter_config[i].enabled) + continue; + counter_config[i].event = EVT_UNUSED; + } + + for (i = EMC0; i < MAX_L2COUNTERS; i++) { + if (counter_config[i].event == EVT_UNUSED) { + counter_config[i].event = 0; + pmu->int_enable &= ~pmu->int_mask[i]; + pr_debug + ("arm11_setup_ctrs: The counter event is %lu for counter%d\n", + counter_config[i].event, i); + continue; + } + + results[i].reset_counter = counter_config[i].count; + write_l2counter(i - EMC0, -(u32) counter_config[i].count); + l2em_configure_counter(i - EMC0, counter_config[i].event); + + pmu->int_enable |= pmu->int_mask[i]; + pr_debug + ("arm11_setup_ctrs: The values of int mask and enables are %x, %x\n", + pmu->int_mask[i], pmu->int_enable); + + } + + return 0; +} + +/*! + * function used to start the ARM11 evtmon counters + */ +void arm11_evtmon_stop(void) +{ + + /* Disable the EVTMON */ + __raw_writel((__raw_readl(L2EM_CTRL) & ~EVTMON_ENABLE), L2EM_CTRL); + + /* Disable the EVTBUS */ + l2x0_evtbus_disable(); + +} + +/*! + * function used to start the ARM11 evtmon counters + */ +int arm11_evtmon_start(void) +{ + + /* Enable the EVTBUS */ + l2x0_evtbus_enable(); + +#ifdef ECT_WORKAROUND + __raw_writel(ENABLE_CTI_CLOCK, CLKCTL_SET_CTRL); + /* Unlock the AHB Interface */ + __raw_writel(UNLOCK_ECT_CODE, ECT_CTI_LOCK); + /* Trigger to Channel Mapping */ + __raw_writel(ECT_CTI_CHAN_2, ECT_CTI_INEN(ECT_CTI_TRIGIN_1)); + /* Channel to triggers mapping */ + __raw_writel(ECT_CTI_CHAN_2, ECT_CTI_OUTEN(ECT_CTI_TRIGOUT_2)); + /* Trigger to Channel Mapping */ + __raw_writel(ECT_CTI_CHAN_3, ECT_CTI_INEN(ECT_CTI_TRIGIN_7)); + /* Channel to triggers mapping */ + __raw_writel(ECT_CTI_CHAN_3, ECT_CTI_OUTEN(ECT_CTI_TRIGOUT_6)); + /* Enable CTI Logic */ + __raw_writel(ENABLE_ECT, ECT_CTI_CONTROL); +#endif + + /* Enable EVTMON with Edge triggered interrupt of one Clock Cycle */ + __raw_writel((__raw_readl(L2EM_CTRL) | + (L2EM_INT_EDGE | L2EM_INT_CLK_CYCLES)), L2EM_CTRL); + __raw_writel((__raw_readl(L2EM_CTRL) | L2EM_ENABLE_MASK), L2EM_CTRL); + + return 0; +} + +/*! + * function detect the ARM11 evtmon counters + */ +int arm11_evtmon_detect(void) +{ + int ret = 0; + + pmu = &pmu_parms[0]; + op_armv6_spec.num_counters = MAX_L2COUNTERS; + + return ret; +} diff --git a/arch/arm/oprofile/op_model_v6.c b/arch/arm/oprofile/op_model_v6.c index fe581383d3e2..28b6fbef840d 100644 --- a/arch/arm/oprofile/op_model_v6.c +++ b/arch/arm/oprofile/op_model_v6.c @@ -28,16 +28,21 @@ #include "op_counter.h" #include "op_arm_model.h" #include "op_model_arm11_core.h" +#include "evtmon_regs.h" static int irqs[] = { #ifdef CONFIG_ARCH_OMAP2 3, #endif +#ifdef CONFIG_ARCH_MXC + EVENT_OVERFLOW_INT, +#endif }; static void armv6_pmu_stop(void) { arm11_stop_pmu(); + arm11_evtmon_stop(); arm11_release_interrupts(irqs, ARRAY_SIZE(irqs)); } @@ -46,21 +51,31 @@ static int armv6_pmu_start(void) int ret; ret = arm11_request_interrupts(irqs, ARRAY_SIZE(irqs)); - if (ret >= 0) + if (ret >= 0) { ret = arm11_start_pmu(); + arm11_evtmon_start(); + } return ret; } +static int armv6_setup_pmu(void) +{ + arm11_setup_pmu(); + arm11_evtmon_setup_ctrs(); + return 0; +} + static int armv6_detect_pmu(void) { + arm11_evtmon_detect(); return 0; } struct op_arm_model_spec op_armv6_spec = { .init = armv6_detect_pmu, .num_counters = 3, - .setup_ctrs = arm11_setup_pmu, + .setup_ctrs = armv6_setup_pmu, .start = armv6_pmu_start, .stop = armv6_pmu_stop, .name = "arm/armv6", diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index b2b8889d9ab4..976631da6ef2 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -11,6 +11,7 @@ config ARCH_MX3 select CPU_V6 select CACHE_L2X0 select USB_ARCH_HAS_EHCI + select ARCH_HAS_EVTMON help This enables support for systems based on Freescale i.MX31 @@ -30,6 +31,10 @@ source "arch/arm/mach-mx3/Kconfig" endmenu +config ARCH_HAS_EVTMON + bool + depends on ARCH_MXC + config MXC_EMMA bool depends on ARCH_MXC |