Commit a669efc4 authored by Andrew Bresticker's avatar Andrew Bresticker Committed by Ralf Baechle

MIPS: Add hook to get C0 performance counter interrupt

The hardware perf event driver and oprofile interpret the global
cp0_perfcount_irq differently: in the hardware perf event driver
it is an offset from MIPS_CPU_IRQ_BASE and in oprofile it is the
actual IRQ number.  This still works most of the time since
MIPS_CPU_IRQ_BASE is usually 0, but is clearly wrong.  Since the
performance counter interrupt may vary from platform to platform
like the C0 timer interrupt, add the optional get_c0_perfcount_int
hook which returns the IRQ number of the performance counter.
The hook should return < 0 if the performance counter interrupt is
shared with the timer.  If the hook is not present, the CPU vector
reported in C0_IntCtl (cp0_perfcount_irq) is used.
Signed-off-by: default avatarAndrew Bresticker <abrestic@chromium.org>
Reviewed-by: default avatarQais Yousef <qais.yousef@imgtec.com>
Tested-by: default avatarQais Yousef <qais.yousef@imgtec.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Andrew Bresticker <abrestic@chromium.org>
Cc: Jeffrey Deans <jeffrey.deans@imgtec.com>
Cc: Markos Chandras <markos.chandras@imgtec.com>
Cc: Paul Burton <paul.burton@imgtec.com>
Cc: Qais Yousef <qais.yousef@imgtec.com>
Cc: Jonas Gorski <jogo@openwrt.org>
Cc: John Crispin <blogic@openwrt.org>
Cc: David Daney <ddaney.cavm@gmail.com>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/7805/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 079a4601
...@@ -359,7 +359,6 @@ void __init arch_init_irq(void) ...@@ -359,7 +359,6 @@ void __init arch_init_irq(void)
BUG(); BUG();
} }
cp0_perfcount_irq = ATH79_MISC_IRQ(5);
mips_cpu_irq_init(); mips_cpu_irq_init();
ath79_misc_irq_init(); ath79_misc_irq_init();
......
...@@ -182,6 +182,11 @@ const char *get_system_type(void) ...@@ -182,6 +182,11 @@ const char *get_system_type(void)
return ath79_sys_type; return ath79_sys_type;
} }
int get_c0_perfcount_int(void)
{
return ATH79_MISC_IRQ(5);
}
unsigned int get_c0_compare_int(void) unsigned int get_c0_compare_int(void)
{ {
return CP0_LEGACY_COMPARE_IRQ; return CP0_LEGACY_COMPARE_IRQ;
......
...@@ -46,6 +46,7 @@ extern unsigned int mips_hpt_frequency; ...@@ -46,6 +46,7 @@ extern unsigned int mips_hpt_frequency;
* so it lives here. * so it lives here.
*/ */
extern int (*perf_irq)(void); extern int (*perf_irq)(void);
extern int __weak get_c0_perfcount_int(void);
/* /*
* Initialize the calling CPU's compare interrupt as clockevent device * Initialize the calling CPU's compare interrupt as clockevent device
......
...@@ -1613,22 +1613,13 @@ init_hw_perf_events(void) ...@@ -1613,22 +1613,13 @@ init_hw_perf_events(void)
counters = counters_total_to_per_cpu(counters); counters = counters_total_to_per_cpu(counters);
#endif #endif
#ifdef MSC01E_INT_BASE if (get_c0_perfcount_int)
if (cpu_has_veic) { irq = get_c0_perfcount_int();
/* else if ((cp0_perfcount_irq >= 0) &&
* Using platform specific interrupt controller defines.
*/
irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
} else {
#endif
if ((cp0_perfcount_irq >= 0) &&
(cp0_compare_irq != cp0_perfcount_irq)) (cp0_compare_irq != cp0_perfcount_irq))
irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
else else
irq = -1; irq = -1;
#ifdef MSC01E_INT_BASE
}
#endif
mipspmu.map_raw_event = mipsxx_pmu_map_raw_event; mipspmu.map_raw_event = mipsxx_pmu_map_raw_event;
......
...@@ -70,6 +70,7 @@ static struct resource ltq_eiu_irq[MAX_EIU]; ...@@ -70,6 +70,7 @@ static struct resource ltq_eiu_irq[MAX_EIU];
static void __iomem *ltq_icu_membase[MAX_IM]; static void __iomem *ltq_icu_membase[MAX_IM];
static void __iomem *ltq_eiu_membase; static void __iomem *ltq_eiu_membase;
static struct irq_domain *ltq_domain; static struct irq_domain *ltq_domain;
static int ltq_perfcount_irq;
int ltq_eiu_get_irq(int exin) int ltq_eiu_get_irq(int exin)
{ {
...@@ -449,7 +450,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) ...@@ -449,7 +450,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
#endif #endif
/* tell oprofile which irq to use */ /* tell oprofile which irq to use */
cp0_perfcount_irq = irq_create_mapping(ltq_domain, LTQ_PERF_IRQ); ltq_perfcount_irq = irq_create_mapping(ltq_domain, LTQ_PERF_IRQ);
/* /*
* if the timer irq is not one of the mips irqs we need to * if the timer irq is not one of the mips irqs we need to
...@@ -461,6 +462,11 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) ...@@ -461,6 +462,11 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
return 0; return 0;
} }
int get_c0_perfcount_int(void)
{
return ltq_perfcount_irq;
}
unsigned int get_c0_compare_int(void) unsigned int get_c0_compare_int(void)
{ {
return MIPS_CPU_TIMER_IRQ; return MIPS_CPU_TIMER_IRQ;
......
...@@ -121,22 +121,20 @@ void read_persistent_clock(struct timespec *ts) ...@@ -121,22 +121,20 @@ void read_persistent_clock(struct timespec *ts)
ts->tv_nsec = 0; ts->tv_nsec = 0;
} }
static void __init plat_perf_setup(void) int get_c0_perfcount_int(void)
{ {
#ifdef MSC01E_INT_BASE
if (cpu_has_veic) { if (cpu_has_veic) {
set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch); set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch);
mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR; mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
} else } else if (cp0_perfcount_irq >= 0) {
#endif
if (cp0_perfcount_irq >= 0) {
if (cpu_has_vint) if (cpu_has_vint)
set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch); set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch);
mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
#ifdef CONFIG_SMP } else {
irq_set_handler(mips_cpu_perf_irq, handle_percpu_irq); mips_cpu_perf_irq = -1;
#endif
} }
return mips_cpu_perf_irq;
} }
unsigned int get_c0_compare_int(void) unsigned int get_c0_compare_int(void)
...@@ -201,6 +199,4 @@ void __init plat_time_init(void) ...@@ -201,6 +199,4 @@ void __init plat_time_init(void)
#endif #endif
} }
#endif #endif
plat_perf_setup();
} }
...@@ -81,13 +81,16 @@ void read_persistent_clock(struct timespec *ts) ...@@ -81,13 +81,16 @@ void read_persistent_clock(struct timespec *ts)
ts->tv_nsec = 0; ts->tv_nsec = 0;
} }
static void __init plat_perf_setup(void) int get_c0_perfcount_int(void)
{ {
if (cp0_perfcount_irq >= 0) { if (cp0_perfcount_irq >= 0) {
if (cpu_has_vint) if (cpu_has_vint)
set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch); set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch);
mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
} else {
mips_cpu_perf_irq = -1;
} }
return mips_cpu_perf_irq;
} }
unsigned int get_c0_compare_int(void) unsigned int get_c0_compare_int(void)
...@@ -108,6 +111,4 @@ void __init plat_time_init(void) ...@@ -108,6 +111,4 @@ void __init plat_time_init(void)
(est_freq % 1000000) * 100 / 1000000); (est_freq % 1000000) * 100 / 1000000);
mips_scroll_message(); mips_scroll_message();
plat_perf_setup();
} }
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
#include <asm/time.h>
#include "op_impl.h" #include "op_impl.h"
...@@ -35,6 +36,7 @@ ...@@ -35,6 +36,7 @@
#define M_PERFCTL_COUNT_ALL_THREADS (1UL << 13) #define M_PERFCTL_COUNT_ALL_THREADS (1UL << 13)
static int (*save_perf_irq)(void); static int (*save_perf_irq)(void);
static int perfcount_irq;
/* /*
* XLR has only one set of counters per core. Designate the * XLR has only one set of counters per core. Designate the
...@@ -431,8 +433,16 @@ static int __init mipsxx_init(void) ...@@ -431,8 +433,16 @@ static int __init mipsxx_init(void)
save_perf_irq = perf_irq; save_perf_irq = perf_irq;
perf_irq = mipsxx_perfcount_handler; perf_irq = mipsxx_perfcount_handler;
if ((cp0_perfcount_irq >= 0) && (cp0_compare_irq != cp0_perfcount_irq)) if (get_c0_perfcount_int)
return request_irq(cp0_perfcount_irq, mipsxx_perfcount_int, perfcount_irq = get_c0_perfcount_int();
else if ((cp0_perfcount_irq >= 0) &&
(cp0_compare_irq != cp0_perfcount_irq))
perfcount_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
else
perfcount_irq = -1;
if (perfcount_irq >= 0)
return request_irq(perfcount_irq, mipsxx_perfcount_int,
0, "Perfcounter", save_perf_irq); 0, "Perfcounter", save_perf_irq);
return 0; return 0;
...@@ -442,8 +452,8 @@ static void mipsxx_exit(void) ...@@ -442,8 +452,8 @@ static void mipsxx_exit(void)
{ {
int counters = op_model_mipsxx_ops.num_counters; int counters = op_model_mipsxx_ops.num_counters;
if ((cp0_perfcount_irq >= 0) && (cp0_compare_irq != cp0_perfcount_irq)) if (perfcount_irq >= 0)
free_irq(cp0_perfcount_irq, save_perf_irq); free_irq(perfcount_irq, save_perf_irq);
counters = counters_per_cpu_to_total(counters); counters = counters_per_cpu_to_total(counters);
on_each_cpu(reset_counters, (void *)(long)counters, 1); on_each_cpu(reset_counters, (void *)(long)counters, 1);
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9) #define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9)
static void __iomem *rt_intc_membase; static void __iomem *rt_intc_membase;
static int rt_perfcount_irq;
static inline void rt_intc_w32(u32 val, unsigned reg) static inline void rt_intc_w32(u32 val, unsigned reg)
{ {
...@@ -73,6 +74,11 @@ static struct irq_chip ralink_intc_irq_chip = { ...@@ -73,6 +74,11 @@ static struct irq_chip ralink_intc_irq_chip = {
.irq_mask_ack = ralink_intc_irq_mask, .irq_mask_ack = ralink_intc_irq_mask,
}; };
int get_c0_perfcount_int(void)
{
return rt_perfcount_irq;
}
unsigned int get_c0_compare_int(void) unsigned int get_c0_compare_int(void)
{ {
return CP0_LEGACY_COMPARE_IRQ; return CP0_LEGACY_COMPARE_IRQ;
...@@ -167,7 +173,7 @@ static int __init intc_of_init(struct device_node *node, ...@@ -167,7 +173,7 @@ static int __init intc_of_init(struct device_node *node,
irq_set_handler_data(irq, domain); irq_set_handler_data(irq, domain);
/* tell the kernel which irq is used for performance monitoring */ /* tell the kernel which irq is used for performance monitoring */
cp0_perfcount_irq = irq_create_mapping(domain, 9); rt_perfcount_irq = irq_create_mapping(domain, 9);
return 0; return 0;
} }
......
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