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: Andrew Bresticker <abrestic@chromium.org> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais 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: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
		
					parent
					
						
							
								079a460176
							
						
					
				
			
			
				commit
				
					
						a669efc4a3
					
				
			
		
					 9 changed files with 51 additions and 36 deletions
				
			
		| 
						 | 
					@ -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)
 | 
				
			||||||
	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;
 | 
				
			||||||
 * 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)
 | 
				
			||||||
		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];
 | 
				
			||||||
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)
 | 
				
			||||||
#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)
 | 
				
			||||||
	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)
 | 
				
			||||||
	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)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
	plat_perf_setup();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
		(est_freq % 1000000) * 100 / 1000000);
 | 
							(est_freq % 1000000) * 100 / 1000000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mips_scroll_message();
 | 
						mips_scroll_message();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	plat_perf_setup();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 @@
 | 
				
			||||||
#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)
 | 
				
			||||||
	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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	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 @@
 | 
				
			||||||
#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 = {
 | 
				
			||||||
	.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,
 | 
				
			||||||
	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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue