brintos

brintos / linux-shallow public Read only

0
0
Text · 18.9 KiB · 638fea9 Raw
750 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ARMv5 [xscale] Performance counter handling code.4 *5 * Copyright (C) 2010, ARM Ltd., Will Deacon <will.deacon@arm.com>6 *7 * Based on the previous xscale OProfile code.8 *9 * There are two variants of the xscale PMU that we support:10 * 	- xscale1pmu: 2 event counters and a cycle counter11 * 	- xscale2pmu: 4 event counters and a cycle counter12 * The two variants share event definitions, but have different13 * PMU structures.14 */15 16#include <asm/cputype.h>17#include <asm/irq_regs.h>18 19#include <linux/of.h>20#include <linux/perf/arm_pmu.h>21#include <linux/platform_device.h>22 23enum xscale_perf_types {24	XSCALE_PERFCTR_ICACHE_MISS		= 0x00,25	XSCALE_PERFCTR_ICACHE_NO_DELIVER	= 0x01,26	XSCALE_PERFCTR_DATA_STALL		= 0x02,27	XSCALE_PERFCTR_ITLB_MISS		= 0x03,28	XSCALE_PERFCTR_DTLB_MISS		= 0x04,29	XSCALE_PERFCTR_BRANCH			= 0x05,30	XSCALE_PERFCTR_BRANCH_MISS		= 0x06,31	XSCALE_PERFCTR_INSTRUCTION		= 0x07,32	XSCALE_PERFCTR_DCACHE_FULL_STALL	= 0x08,33	XSCALE_PERFCTR_DCACHE_FULL_STALL_CONTIG	= 0x09,34	XSCALE_PERFCTR_DCACHE_ACCESS		= 0x0A,35	XSCALE_PERFCTR_DCACHE_MISS		= 0x0B,36	XSCALE_PERFCTR_DCACHE_WRITE_BACK	= 0x0C,37	XSCALE_PERFCTR_PC_CHANGED		= 0x0D,38	XSCALE_PERFCTR_BCU_REQUEST		= 0x10,39	XSCALE_PERFCTR_BCU_FULL			= 0x11,40	XSCALE_PERFCTR_BCU_DRAIN		= 0x12,41	XSCALE_PERFCTR_BCU_ECC_NO_ELOG		= 0x14,42	XSCALE_PERFCTR_BCU_1_BIT_ERR		= 0x15,43	XSCALE_PERFCTR_RMW			= 0x16,44	/* XSCALE_PERFCTR_CCNT is not hardware defined */45	XSCALE_PERFCTR_CCNT			= 0xFE,46	XSCALE_PERFCTR_UNUSED			= 0xFF,47};48 49enum xscale_counters {50	XSCALE_CYCLE_COUNTER	= 0,51	XSCALE_COUNTER0,52	XSCALE_COUNTER1,53	XSCALE_COUNTER2,54	XSCALE_COUNTER3,55};56#define XSCALE1_NUM_COUNTERS	357#define XSCALE2_NUM_COUNTERS	558 59static const unsigned xscale_perf_map[PERF_COUNT_HW_MAX] = {60	PERF_MAP_ALL_UNSUPPORTED,61	[PERF_COUNT_HW_CPU_CYCLES]		= XSCALE_PERFCTR_CCNT,62	[PERF_COUNT_HW_INSTRUCTIONS]		= XSCALE_PERFCTR_INSTRUCTION,63	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= XSCALE_PERFCTR_BRANCH,64	[PERF_COUNT_HW_BRANCH_MISSES]		= XSCALE_PERFCTR_BRANCH_MISS,65	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND]	= XSCALE_PERFCTR_ICACHE_NO_DELIVER,66};67 68static const unsigned xscale_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]69					   [PERF_COUNT_HW_CACHE_OP_MAX]70					   [PERF_COUNT_HW_CACHE_RESULT_MAX] = {71	PERF_CACHE_MAP_ALL_UNSUPPORTED,72 73	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= XSCALE_PERFCTR_DCACHE_ACCESS,74	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= XSCALE_PERFCTR_DCACHE_MISS,75	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= XSCALE_PERFCTR_DCACHE_ACCESS,76	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= XSCALE_PERFCTR_DCACHE_MISS,77 78	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= XSCALE_PERFCTR_ICACHE_MISS,79 80	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= XSCALE_PERFCTR_DTLB_MISS,81	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= XSCALE_PERFCTR_DTLB_MISS,82 83	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= XSCALE_PERFCTR_ITLB_MISS,84	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= XSCALE_PERFCTR_ITLB_MISS,85};86 87#define	XSCALE_PMU_ENABLE	0x00188#define XSCALE_PMN_RESET	0x00289#define	XSCALE_CCNT_RESET	0x00490#define	XSCALE_PMU_RESET	(CCNT_RESET | PMN_RESET)91#define XSCALE_PMU_CNT64	0x00892 93#define XSCALE1_OVERFLOWED_MASK	0x70094#define XSCALE1_CCOUNT_OVERFLOW	0x40095#define XSCALE1_COUNT0_OVERFLOW	0x10096#define XSCALE1_COUNT1_OVERFLOW	0x20097#define XSCALE1_CCOUNT_INT_EN	0x04098#define XSCALE1_COUNT0_INT_EN	0x01099#define XSCALE1_COUNT1_INT_EN	0x020100#define XSCALE1_COUNT0_EVT_SHFT	12101#define XSCALE1_COUNT0_EVT_MASK	(0xff << XSCALE1_COUNT0_EVT_SHFT)102#define XSCALE1_COUNT1_EVT_SHFT	20103#define XSCALE1_COUNT1_EVT_MASK	(0xff << XSCALE1_COUNT1_EVT_SHFT)104 105static inline u32106xscale1pmu_read_pmnc(void)107{108	u32 val;109	asm volatile("mrc p14, 0, %0, c0, c0, 0" : "=r" (val));110	return val;111}112 113static inline void114xscale1pmu_write_pmnc(u32 val)115{116	/* upper 4bits and 7, 11 are write-as-0 */117	val &= 0xffff77f;118	asm volatile("mcr p14, 0, %0, c0, c0, 0" : : "r" (val));119}120 121static inline int122xscale1_pmnc_counter_has_overflowed(unsigned long pmnc,123					enum xscale_counters counter)124{125	int ret = 0;126 127	switch (counter) {128	case XSCALE_CYCLE_COUNTER:129		ret = pmnc & XSCALE1_CCOUNT_OVERFLOW;130		break;131	case XSCALE_COUNTER0:132		ret = pmnc & XSCALE1_COUNT0_OVERFLOW;133		break;134	case XSCALE_COUNTER1:135		ret = pmnc & XSCALE1_COUNT1_OVERFLOW;136		break;137	default:138		WARN_ONCE(1, "invalid counter number (%d)\n", counter);139	}140 141	return ret;142}143 144static irqreturn_t145xscale1pmu_handle_irq(struct arm_pmu *cpu_pmu)146{147	unsigned long pmnc;148	struct perf_sample_data data;149	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);150	struct pt_regs *regs;151	int idx;152 153	/*154	 * NOTE: there's an A stepping erratum that states if an overflow155	 *       bit already exists and another occurs, the previous156	 *       Overflow bit gets cleared. There's no workaround.157	 *	 Fixed in B stepping or later.158	 */159	pmnc = xscale1pmu_read_pmnc();160 161	/*162	 * Write the value back to clear the overflow flags. Overflow163	 * flags remain in pmnc for use below. We also disable the PMU164	 * while we process the interrupt.165	 */166	xscale1pmu_write_pmnc(pmnc & ~XSCALE_PMU_ENABLE);167 168	if (!(pmnc & XSCALE1_OVERFLOWED_MASK))169		return IRQ_NONE;170 171	regs = get_irq_regs();172 173	for_each_set_bit(idx, cpu_pmu->cntr_mask, XSCALE1_NUM_COUNTERS) {174		struct perf_event *event = cpuc->events[idx];175		struct hw_perf_event *hwc;176 177		if (!event)178			continue;179 180		if (!xscale1_pmnc_counter_has_overflowed(pmnc, idx))181			continue;182 183		hwc = &event->hw;184		armpmu_event_update(event);185		perf_sample_data_init(&data, 0, hwc->last_period);186		if (!armpmu_event_set_period(event))187			continue;188 189		if (perf_event_overflow(event, &data, regs))190			cpu_pmu->disable(event);191	}192 193	irq_work_run();194 195	/*196	 * Re-enable the PMU.197	 */198	pmnc = xscale1pmu_read_pmnc() | XSCALE_PMU_ENABLE;199	xscale1pmu_write_pmnc(pmnc);200 201	return IRQ_HANDLED;202}203 204static void xscale1pmu_enable_event(struct perf_event *event)205{206	unsigned long val, mask, evt;207	struct hw_perf_event *hwc = &event->hw;208	int idx = hwc->idx;209 210	switch (idx) {211	case XSCALE_CYCLE_COUNTER:212		mask = 0;213		evt = XSCALE1_CCOUNT_INT_EN;214		break;215	case XSCALE_COUNTER0:216		mask = XSCALE1_COUNT0_EVT_MASK;217		evt = (hwc->config_base << XSCALE1_COUNT0_EVT_SHFT) |218			XSCALE1_COUNT0_INT_EN;219		break;220	case XSCALE_COUNTER1:221		mask = XSCALE1_COUNT1_EVT_MASK;222		evt = (hwc->config_base << XSCALE1_COUNT1_EVT_SHFT) |223			XSCALE1_COUNT1_INT_EN;224		break;225	default:226		WARN_ONCE(1, "invalid counter number (%d)\n", idx);227		return;228	}229 230	val = xscale1pmu_read_pmnc();231	val &= ~mask;232	val |= evt;233	xscale1pmu_write_pmnc(val);234}235 236static void xscale1pmu_disable_event(struct perf_event *event)237{238	unsigned long val, mask, evt;239	struct hw_perf_event *hwc = &event->hw;240	int idx = hwc->idx;241 242	switch (idx) {243	case XSCALE_CYCLE_COUNTER:244		mask = XSCALE1_CCOUNT_INT_EN;245		evt = 0;246		break;247	case XSCALE_COUNTER0:248		mask = XSCALE1_COUNT0_INT_EN | XSCALE1_COUNT0_EVT_MASK;249		evt = XSCALE_PERFCTR_UNUSED << XSCALE1_COUNT0_EVT_SHFT;250		break;251	case XSCALE_COUNTER1:252		mask = XSCALE1_COUNT1_INT_EN | XSCALE1_COUNT1_EVT_MASK;253		evt = XSCALE_PERFCTR_UNUSED << XSCALE1_COUNT1_EVT_SHFT;254		break;255	default:256		WARN_ONCE(1, "invalid counter number (%d)\n", idx);257		return;258	}259 260	val = xscale1pmu_read_pmnc();261	val &= ~mask;262	val |= evt;263	xscale1pmu_write_pmnc(val);264}265 266static int267xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc,268				struct perf_event *event)269{270	struct hw_perf_event *hwc = &event->hw;271	if (XSCALE_PERFCTR_CCNT == hwc->config_base) {272		if (test_and_set_bit(XSCALE_CYCLE_COUNTER, cpuc->used_mask))273			return -EAGAIN;274 275		return XSCALE_CYCLE_COUNTER;276	} else {277		if (!test_and_set_bit(XSCALE_COUNTER1, cpuc->used_mask))278			return XSCALE_COUNTER1;279 280		if (!test_and_set_bit(XSCALE_COUNTER0, cpuc->used_mask))281			return XSCALE_COUNTER0;282 283		return -EAGAIN;284	}285}286 287static void xscalepmu_clear_event_idx(struct pmu_hw_events *cpuc,288				     struct perf_event *event)289{290	clear_bit(event->hw.idx, cpuc->used_mask);291}292 293static void xscale1pmu_start(struct arm_pmu *cpu_pmu)294{295	unsigned long val;296 297	val = xscale1pmu_read_pmnc();298	val |= XSCALE_PMU_ENABLE;299	xscale1pmu_write_pmnc(val);300}301 302static void xscale1pmu_stop(struct arm_pmu *cpu_pmu)303{304	unsigned long val;305 306	val = xscale1pmu_read_pmnc();307	val &= ~XSCALE_PMU_ENABLE;308	xscale1pmu_write_pmnc(val);309}310 311static inline u64 xscale1pmu_read_counter(struct perf_event *event)312{313	struct hw_perf_event *hwc = &event->hw;314	int counter = hwc->idx;315	u32 val = 0;316 317	switch (counter) {318	case XSCALE_CYCLE_COUNTER:319		asm volatile("mrc p14, 0, %0, c1, c0, 0" : "=r" (val));320		break;321	case XSCALE_COUNTER0:322		asm volatile("mrc p14, 0, %0, c2, c0, 0" : "=r" (val));323		break;324	case XSCALE_COUNTER1:325		asm volatile("mrc p14, 0, %0, c3, c0, 0" : "=r" (val));326		break;327	}328 329	return val;330}331 332static inline void xscale1pmu_write_counter(struct perf_event *event, u64 val)333{334	struct hw_perf_event *hwc = &event->hw;335	int counter = hwc->idx;336 337	switch (counter) {338	case XSCALE_CYCLE_COUNTER:339		asm volatile("mcr p14, 0, %0, c1, c0, 0" : : "r" (val));340		break;341	case XSCALE_COUNTER0:342		asm volatile("mcr p14, 0, %0, c2, c0, 0" : : "r" (val));343		break;344	case XSCALE_COUNTER1:345		asm volatile("mcr p14, 0, %0, c3, c0, 0" : : "r" (val));346		break;347	}348}349 350static int xscale_map_event(struct perf_event *event)351{352	return armpmu_map_event(event, &xscale_perf_map,353				&xscale_perf_cache_map, 0xFF);354}355 356static int xscale1pmu_init(struct arm_pmu *cpu_pmu)357{358	cpu_pmu->name		= "armv5_xscale1";359	cpu_pmu->handle_irq	= xscale1pmu_handle_irq;360	cpu_pmu->enable		= xscale1pmu_enable_event;361	cpu_pmu->disable	= xscale1pmu_disable_event;362	cpu_pmu->read_counter	= xscale1pmu_read_counter;363	cpu_pmu->write_counter	= xscale1pmu_write_counter;364	cpu_pmu->get_event_idx	= xscale1pmu_get_event_idx;365	cpu_pmu->clear_event_idx = xscalepmu_clear_event_idx;366	cpu_pmu->start		= xscale1pmu_start;367	cpu_pmu->stop		= xscale1pmu_stop;368	cpu_pmu->map_event	= xscale_map_event;369 370	bitmap_set(cpu_pmu->cntr_mask, 0, XSCALE1_NUM_COUNTERS);371 372	return 0;373}374 375#define XSCALE2_OVERFLOWED_MASK	0x01f376#define XSCALE2_CCOUNT_OVERFLOW	0x001377#define XSCALE2_COUNT0_OVERFLOW	0x002378#define XSCALE2_COUNT1_OVERFLOW	0x004379#define XSCALE2_COUNT2_OVERFLOW	0x008380#define XSCALE2_COUNT3_OVERFLOW	0x010381#define XSCALE2_CCOUNT_INT_EN	0x001382#define XSCALE2_COUNT0_INT_EN	0x002383#define XSCALE2_COUNT1_INT_EN	0x004384#define XSCALE2_COUNT2_INT_EN	0x008385#define XSCALE2_COUNT3_INT_EN	0x010386#define XSCALE2_COUNT0_EVT_SHFT	0387#define XSCALE2_COUNT0_EVT_MASK	(0xff << XSCALE2_COUNT0_EVT_SHFT)388#define XSCALE2_COUNT1_EVT_SHFT	8389#define XSCALE2_COUNT1_EVT_MASK	(0xff << XSCALE2_COUNT1_EVT_SHFT)390#define XSCALE2_COUNT2_EVT_SHFT	16391#define XSCALE2_COUNT2_EVT_MASK	(0xff << XSCALE2_COUNT2_EVT_SHFT)392#define XSCALE2_COUNT3_EVT_SHFT	24393#define XSCALE2_COUNT3_EVT_MASK	(0xff << XSCALE2_COUNT3_EVT_SHFT)394 395static inline u32396xscale2pmu_read_pmnc(void)397{398	u32 val;399	asm volatile("mrc p14, 0, %0, c0, c1, 0" : "=r" (val));400	/* bits 1-2 and 4-23 are read-unpredictable */401	return val & 0xff000009;402}403 404static inline void405xscale2pmu_write_pmnc(u32 val)406{407	/* bits 4-23 are write-as-0, 24-31 are write ignored */408	val &= 0xf;409	asm volatile("mcr p14, 0, %0, c0, c1, 0" : : "r" (val));410}411 412static inline u32413xscale2pmu_read_overflow_flags(void)414{415	u32 val;416	asm volatile("mrc p14, 0, %0, c5, c1, 0" : "=r" (val));417	return val;418}419 420static inline void421xscale2pmu_write_overflow_flags(u32 val)422{423	asm volatile("mcr p14, 0, %0, c5, c1, 0" : : "r" (val));424}425 426static inline u32427xscale2pmu_read_event_select(void)428{429	u32 val;430	asm volatile("mrc p14, 0, %0, c8, c1, 0" : "=r" (val));431	return val;432}433 434static inline void435xscale2pmu_write_event_select(u32 val)436{437	asm volatile("mcr p14, 0, %0, c8, c1, 0" : : "r"(val));438}439 440static inline u32441xscale2pmu_read_int_enable(void)442{443	u32 val;444	asm volatile("mrc p14, 0, %0, c4, c1, 0" : "=r" (val));445	return val;446}447 448static void449xscale2pmu_write_int_enable(u32 val)450{451	asm volatile("mcr p14, 0, %0, c4, c1, 0" : : "r" (val));452}453 454static inline int455xscale2_pmnc_counter_has_overflowed(unsigned long of_flags,456					enum xscale_counters counter)457{458	int ret = 0;459 460	switch (counter) {461	case XSCALE_CYCLE_COUNTER:462		ret = of_flags & XSCALE2_CCOUNT_OVERFLOW;463		break;464	case XSCALE_COUNTER0:465		ret = of_flags & XSCALE2_COUNT0_OVERFLOW;466		break;467	case XSCALE_COUNTER1:468		ret = of_flags & XSCALE2_COUNT1_OVERFLOW;469		break;470	case XSCALE_COUNTER2:471		ret = of_flags & XSCALE2_COUNT2_OVERFLOW;472		break;473	case XSCALE_COUNTER3:474		ret = of_flags & XSCALE2_COUNT3_OVERFLOW;475		break;476	default:477		WARN_ONCE(1, "invalid counter number (%d)\n", counter);478	}479 480	return ret;481}482 483static irqreturn_t484xscale2pmu_handle_irq(struct arm_pmu *cpu_pmu)485{486	unsigned long pmnc, of_flags;487	struct perf_sample_data data;488	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);489	struct pt_regs *regs;490	int idx;491 492	/* Disable the PMU. */493	pmnc = xscale2pmu_read_pmnc();494	xscale2pmu_write_pmnc(pmnc & ~XSCALE_PMU_ENABLE);495 496	/* Check the overflow flag register. */497	of_flags = xscale2pmu_read_overflow_flags();498	if (!(of_flags & XSCALE2_OVERFLOWED_MASK))499		return IRQ_NONE;500 501	/* Clear the overflow bits. */502	xscale2pmu_write_overflow_flags(of_flags);503 504	regs = get_irq_regs();505 506	for_each_set_bit(idx, cpu_pmu->cntr_mask, XSCALE2_NUM_COUNTERS) {507		struct perf_event *event = cpuc->events[idx];508		struct hw_perf_event *hwc;509 510		if (!event)511			continue;512 513		if (!xscale2_pmnc_counter_has_overflowed(of_flags, idx))514			continue;515 516		hwc = &event->hw;517		armpmu_event_update(event);518		perf_sample_data_init(&data, 0, hwc->last_period);519		if (!armpmu_event_set_period(event))520			continue;521 522		if (perf_event_overflow(event, &data, regs))523			cpu_pmu->disable(event);524	}525 526	irq_work_run();527 528	/*529	 * Re-enable the PMU.530	 */531	pmnc = xscale2pmu_read_pmnc() | XSCALE_PMU_ENABLE;532	xscale2pmu_write_pmnc(pmnc);533 534	return IRQ_HANDLED;535}536 537static void xscale2pmu_enable_event(struct perf_event *event)538{539	unsigned long ien, evtsel;540	struct hw_perf_event *hwc = &event->hw;541	int idx = hwc->idx;542 543	ien = xscale2pmu_read_int_enable();544	evtsel = xscale2pmu_read_event_select();545 546	switch (idx) {547	case XSCALE_CYCLE_COUNTER:548		ien |= XSCALE2_CCOUNT_INT_EN;549		break;550	case XSCALE_COUNTER0:551		ien |= XSCALE2_COUNT0_INT_EN;552		evtsel &= ~XSCALE2_COUNT0_EVT_MASK;553		evtsel |= hwc->config_base << XSCALE2_COUNT0_EVT_SHFT;554		break;555	case XSCALE_COUNTER1:556		ien |= XSCALE2_COUNT1_INT_EN;557		evtsel &= ~XSCALE2_COUNT1_EVT_MASK;558		evtsel |= hwc->config_base << XSCALE2_COUNT1_EVT_SHFT;559		break;560	case XSCALE_COUNTER2:561		ien |= XSCALE2_COUNT2_INT_EN;562		evtsel &= ~XSCALE2_COUNT2_EVT_MASK;563		evtsel |= hwc->config_base << XSCALE2_COUNT2_EVT_SHFT;564		break;565	case XSCALE_COUNTER3:566		ien |= XSCALE2_COUNT3_INT_EN;567		evtsel &= ~XSCALE2_COUNT3_EVT_MASK;568		evtsel |= hwc->config_base << XSCALE2_COUNT3_EVT_SHFT;569		break;570	default:571		WARN_ONCE(1, "invalid counter number (%d)\n", idx);572		return;573	}574 575	xscale2pmu_write_event_select(evtsel);576	xscale2pmu_write_int_enable(ien);577}578 579static void xscale2pmu_disable_event(struct perf_event *event)580{581	unsigned long ien, evtsel, of_flags;582	struct hw_perf_event *hwc = &event->hw;583	int idx = hwc->idx;584 585	ien = xscale2pmu_read_int_enable();586	evtsel = xscale2pmu_read_event_select();587 588	switch (idx) {589	case XSCALE_CYCLE_COUNTER:590		ien &= ~XSCALE2_CCOUNT_INT_EN;591		of_flags = XSCALE2_CCOUNT_OVERFLOW;592		break;593	case XSCALE_COUNTER0:594		ien &= ~XSCALE2_COUNT0_INT_EN;595		evtsel &= ~XSCALE2_COUNT0_EVT_MASK;596		evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT0_EVT_SHFT;597		of_flags = XSCALE2_COUNT0_OVERFLOW;598		break;599	case XSCALE_COUNTER1:600		ien &= ~XSCALE2_COUNT1_INT_EN;601		evtsel &= ~XSCALE2_COUNT1_EVT_MASK;602		evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT1_EVT_SHFT;603		of_flags = XSCALE2_COUNT1_OVERFLOW;604		break;605	case XSCALE_COUNTER2:606		ien &= ~XSCALE2_COUNT2_INT_EN;607		evtsel &= ~XSCALE2_COUNT2_EVT_MASK;608		evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT2_EVT_SHFT;609		of_flags = XSCALE2_COUNT2_OVERFLOW;610		break;611	case XSCALE_COUNTER3:612		ien &= ~XSCALE2_COUNT3_INT_EN;613		evtsel &= ~XSCALE2_COUNT3_EVT_MASK;614		evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT3_EVT_SHFT;615		of_flags = XSCALE2_COUNT3_OVERFLOW;616		break;617	default:618		WARN_ONCE(1, "invalid counter number (%d)\n", idx);619		return;620	}621 622	xscale2pmu_write_event_select(evtsel);623	xscale2pmu_write_int_enable(ien);624	xscale2pmu_write_overflow_flags(of_flags);625}626 627static int628xscale2pmu_get_event_idx(struct pmu_hw_events *cpuc,629				struct perf_event *event)630{631	int idx = xscale1pmu_get_event_idx(cpuc, event);632	if (idx >= 0)633		goto out;634 635	if (!test_and_set_bit(XSCALE_COUNTER3, cpuc->used_mask))636		idx = XSCALE_COUNTER3;637	else if (!test_and_set_bit(XSCALE_COUNTER2, cpuc->used_mask))638		idx = XSCALE_COUNTER2;639out:640	return idx;641}642 643static void xscale2pmu_start(struct arm_pmu *cpu_pmu)644{645	unsigned long val;646 647	val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64;648	val |= XSCALE_PMU_ENABLE;649	xscale2pmu_write_pmnc(val);650}651 652static void xscale2pmu_stop(struct arm_pmu *cpu_pmu)653{654	unsigned long val;655 656	val = xscale2pmu_read_pmnc();657	val &= ~XSCALE_PMU_ENABLE;658	xscale2pmu_write_pmnc(val);659}660 661static inline u64 xscale2pmu_read_counter(struct perf_event *event)662{663	struct hw_perf_event *hwc = &event->hw;664	int counter = hwc->idx;665	u32 val = 0;666 667	switch (counter) {668	case XSCALE_CYCLE_COUNTER:669		asm volatile("mrc p14, 0, %0, c1, c1, 0" : "=r" (val));670		break;671	case XSCALE_COUNTER0:672		asm volatile("mrc p14, 0, %0, c0, c2, 0" : "=r" (val));673		break;674	case XSCALE_COUNTER1:675		asm volatile("mrc p14, 0, %0, c1, c2, 0" : "=r" (val));676		break;677	case XSCALE_COUNTER2:678		asm volatile("mrc p14, 0, %0, c2, c2, 0" : "=r" (val));679		break;680	case XSCALE_COUNTER3:681		asm volatile("mrc p14, 0, %0, c3, c2, 0" : "=r" (val));682		break;683	}684 685	return val;686}687 688static inline void xscale2pmu_write_counter(struct perf_event *event, u64 val)689{690	struct hw_perf_event *hwc = &event->hw;691	int counter = hwc->idx;692 693	switch (counter) {694	case XSCALE_CYCLE_COUNTER:695		asm volatile("mcr p14, 0, %0, c1, c1, 0" : : "r" (val));696		break;697	case XSCALE_COUNTER0:698		asm volatile("mcr p14, 0, %0, c0, c2, 0" : : "r" (val));699		break;700	case XSCALE_COUNTER1:701		asm volatile("mcr p14, 0, %0, c1, c2, 0" : : "r" (val));702		break;703	case XSCALE_COUNTER2:704		asm volatile("mcr p14, 0, %0, c2, c2, 0" : : "r" (val));705		break;706	case XSCALE_COUNTER3:707		asm volatile("mcr p14, 0, %0, c3, c2, 0" : : "r" (val));708		break;709	}710}711 712static int xscale2pmu_init(struct arm_pmu *cpu_pmu)713{714	cpu_pmu->name		= "armv5_xscale2";715	cpu_pmu->handle_irq	= xscale2pmu_handle_irq;716	cpu_pmu->enable		= xscale2pmu_enable_event;717	cpu_pmu->disable	= xscale2pmu_disable_event;718	cpu_pmu->read_counter	= xscale2pmu_read_counter;719	cpu_pmu->write_counter	= xscale2pmu_write_counter;720	cpu_pmu->get_event_idx	= xscale2pmu_get_event_idx;721	cpu_pmu->clear_event_idx = xscalepmu_clear_event_idx;722	cpu_pmu->start		= xscale2pmu_start;723	cpu_pmu->stop		= xscale2pmu_stop;724	cpu_pmu->map_event	= xscale_map_event;725 726	bitmap_set(cpu_pmu->cntr_mask, 0, XSCALE2_NUM_COUNTERS);727 728	return 0;729}730 731static const struct pmu_probe_info xscale_pmu_probe_table[] = {732	XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V1, xscale1pmu_init),733	XSCALE_PMU_PROBE(ARM_CPU_XSCALE_ARCH_V2, xscale2pmu_init),734	{ /* sentinel value */ }735};736 737static int xscale_pmu_device_probe(struct platform_device *pdev)738{739	return arm_pmu_device_probe(pdev, NULL, xscale_pmu_probe_table);740}741 742static struct platform_driver xscale_pmu_driver = {743	.driver		= {744		.name	= "xscale-pmu",745	},746	.probe		= xscale_pmu_device_probe,747};748 749builtin_platform_driver(xscale_pmu_driver);750