xref: /openbmc/linux/arch/sh/kernel/cpu/sh4/perf_event.c (revision 6ecc0a4d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Performance events support for SH7750-style performance counters
4  *
5  *  Copyright (C) 2009  Paul Mundt
6  */
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/io.h>
10 #include <linux/irq.h>
11 #include <linux/perf_event.h>
12 #include <asm/processor.h>
13 
14 #define PM_CR_BASE	0xff000084	/* 16-bit */
15 #define PM_CTR_BASE	0xff100004	/* 32-bit */
16 
17 #define PMCR(n)		(PM_CR_BASE + ((n) * 0x04))
18 #define PMCTRH(n)	(PM_CTR_BASE + 0x00 + ((n) * 0x08))
19 #define PMCTRL(n)	(PM_CTR_BASE + 0x04 + ((n) * 0x08))
20 
21 #define PMCR_PMM_MASK	0x0000003f
22 
23 #define PMCR_CLKF	0x00000100
24 #define PMCR_PMCLR	0x00002000
25 #define PMCR_PMST	0x00004000
26 #define PMCR_PMEN	0x00008000
27 
28 static struct sh_pmu sh7750_pmu;
29 
30 /*
31  * There are a number of events supported by each counter (33 in total).
32  * Since we have 2 counters, each counter will take the event code as it
33  * corresponds to the PMCR PMM setting. Each counter can be configured
34  * independently.
35  *
36  *	Event Code	Description
37  *	----------	-----------
38  *
39  *	0x01		Operand read access
40  *	0x02		Operand write access
41  *	0x03		UTLB miss
42  *	0x04		Operand cache read miss
43  *	0x05		Operand cache write miss
44  *	0x06		Instruction fetch (w/ cache)
45  *	0x07		Instruction TLB miss
46  *	0x08		Instruction cache miss
47  *	0x09		All operand accesses
48  *	0x0a		All instruction accesses
49  *	0x0b		OC RAM operand access
50  *	0x0d		On-chip I/O space access
51  *	0x0e		Operand access (r/w)
52  *	0x0f		Operand cache miss (r/w)
53  *	0x10		Branch instruction
54  *	0x11		Branch taken
55  *	0x12		BSR/BSRF/JSR
56  *	0x13		Instruction execution
57  *	0x14		Instruction execution in parallel
58  *	0x15		FPU Instruction execution
59  *	0x16		Interrupt
60  *	0x17		NMI
61  *	0x18		trapa instruction execution
62  *	0x19		UBCA match
63  *	0x1a		UBCB match
64  *	0x21		Instruction cache fill
65  *	0x22		Operand cache fill
66  *	0x23		Elapsed time
67  *	0x24		Pipeline freeze by I-cache miss
68  *	0x25		Pipeline freeze by D-cache miss
69  *	0x27		Pipeline freeze by branch instruction
70  *	0x28		Pipeline freeze by CPU register
71  *	0x29		Pipeline freeze by FPU
72  */
73 
74 static const int sh7750_general_events[] = {
75 	[PERF_COUNT_HW_CPU_CYCLES]		= 0x0023,
76 	[PERF_COUNT_HW_INSTRUCTIONS]		= 0x000a,
77 	[PERF_COUNT_HW_CACHE_REFERENCES]	= 0x0006,	/* I-cache */
78 	[PERF_COUNT_HW_CACHE_MISSES]		= 0x0008,	/* I-cache */
79 	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= 0x0010,
80 	[PERF_COUNT_HW_BRANCH_MISSES]		= -1,
81 	[PERF_COUNT_HW_BUS_CYCLES]		= -1,
82 };
83 
84 #define C(x)	PERF_COUNT_HW_CACHE_##x
85 
86 static const int sh7750_cache_events
87 			[PERF_COUNT_HW_CACHE_MAX]
88 			[PERF_COUNT_HW_CACHE_OP_MAX]
89 			[PERF_COUNT_HW_CACHE_RESULT_MAX] =
90 {
91 	[ C(L1D) ] = {
92 		[ C(OP_READ) ] = {
93 			[ C(RESULT_ACCESS) ] = 0x0001,
94 			[ C(RESULT_MISS)   ] = 0x0004,
95 		},
96 		[ C(OP_WRITE) ] = {
97 			[ C(RESULT_ACCESS) ] = 0x0002,
98 			[ C(RESULT_MISS)   ] = 0x0005,
99 		},
100 		[ C(OP_PREFETCH) ] = {
101 			[ C(RESULT_ACCESS) ] = 0,
102 			[ C(RESULT_MISS)   ] = 0,
103 		},
104 	},
105 
106 	[ C(L1I) ] = {
107 		[ C(OP_READ) ] = {
108 			[ C(RESULT_ACCESS) ] = 0x0006,
109 			[ C(RESULT_MISS)   ] = 0x0008,
110 		},
111 		[ C(OP_WRITE) ] = {
112 			[ C(RESULT_ACCESS) ] = -1,
113 			[ C(RESULT_MISS)   ] = -1,
114 		},
115 		[ C(OP_PREFETCH) ] = {
116 			[ C(RESULT_ACCESS) ] = 0,
117 			[ C(RESULT_MISS)   ] = 0,
118 		},
119 	},
120 
121 	[ C(LL) ] = {
122 		[ C(OP_READ) ] = {
123 			[ C(RESULT_ACCESS) ] = 0,
124 			[ C(RESULT_MISS)   ] = 0,
125 		},
126 		[ C(OP_WRITE) ] = {
127 			[ C(RESULT_ACCESS) ] = 0,
128 			[ C(RESULT_MISS)   ] = 0,
129 		},
130 		[ C(OP_PREFETCH) ] = {
131 			[ C(RESULT_ACCESS) ] = 0,
132 			[ C(RESULT_MISS)   ] = 0,
133 		},
134 	},
135 
136 	[ C(DTLB) ] = {
137 		[ C(OP_READ) ] = {
138 			[ C(RESULT_ACCESS) ] = 0,
139 			[ C(RESULT_MISS)   ] = 0x0003,
140 		},
141 		[ C(OP_WRITE) ] = {
142 			[ C(RESULT_ACCESS) ] = 0,
143 			[ C(RESULT_MISS)   ] = 0,
144 		},
145 		[ C(OP_PREFETCH) ] = {
146 			[ C(RESULT_ACCESS) ] = 0,
147 			[ C(RESULT_MISS)   ] = 0,
148 		},
149 	},
150 
151 	[ C(ITLB) ] = {
152 		[ C(OP_READ) ] = {
153 			[ C(RESULT_ACCESS) ] = 0,
154 			[ C(RESULT_MISS)   ] = 0x0007,
155 		},
156 		[ C(OP_WRITE) ] = {
157 			[ C(RESULT_ACCESS) ] = -1,
158 			[ C(RESULT_MISS)   ] = -1,
159 		},
160 		[ C(OP_PREFETCH) ] = {
161 			[ C(RESULT_ACCESS) ] = -1,
162 			[ C(RESULT_MISS)   ] = -1,
163 		},
164 	},
165 
166 	[ C(BPU) ] = {
167 		[ C(OP_READ) ] = {
168 			[ C(RESULT_ACCESS) ] = -1,
169 			[ C(RESULT_MISS)   ] = -1,
170 		},
171 		[ C(OP_WRITE) ] = {
172 			[ C(RESULT_ACCESS) ] = -1,
173 			[ C(RESULT_MISS)   ] = -1,
174 		},
175 		[ C(OP_PREFETCH) ] = {
176 			[ C(RESULT_ACCESS) ] = -1,
177 			[ C(RESULT_MISS)   ] = -1,
178 		},
179 	},
180 
181 	[ C(NODE) ] = {
182 		[ C(OP_READ) ] = {
183 			[ C(RESULT_ACCESS) ] = -1,
184 			[ C(RESULT_MISS)   ] = -1,
185 		},
186 		[ C(OP_WRITE) ] = {
187 			[ C(RESULT_ACCESS) ] = -1,
188 			[ C(RESULT_MISS)   ] = -1,
189 		},
190 		[ C(OP_PREFETCH) ] = {
191 			[ C(RESULT_ACCESS) ] = -1,
192 			[ C(RESULT_MISS)   ] = -1,
193 		},
194 	},
195 };
196 
sh7750_event_map(int event)197 static int sh7750_event_map(int event)
198 {
199 	return sh7750_general_events[event];
200 }
201 
sh7750_pmu_read(int idx)202 static u64 sh7750_pmu_read(int idx)
203 {
204 	return (u64)((u64)(__raw_readl(PMCTRH(idx)) & 0xffff) << 32) |
205 			   __raw_readl(PMCTRL(idx));
206 }
207 
sh7750_pmu_disable(struct hw_perf_event * hwc,int idx)208 static void sh7750_pmu_disable(struct hw_perf_event *hwc, int idx)
209 {
210 	unsigned int tmp;
211 
212 	tmp = __raw_readw(PMCR(idx));
213 	tmp &= ~(PMCR_PMM_MASK | PMCR_PMEN);
214 	__raw_writew(tmp, PMCR(idx));
215 }
216 
sh7750_pmu_enable(struct hw_perf_event * hwc,int idx)217 static void sh7750_pmu_enable(struct hw_perf_event *hwc, int idx)
218 {
219 	__raw_writew(__raw_readw(PMCR(idx)) | PMCR_PMCLR, PMCR(idx));
220 	__raw_writew(hwc->config | PMCR_PMEN | PMCR_PMST, PMCR(idx));
221 }
222 
sh7750_pmu_disable_all(void)223 static void sh7750_pmu_disable_all(void)
224 {
225 	int i;
226 
227 	for (i = 0; i < sh7750_pmu.num_events; i++)
228 		__raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i));
229 }
230 
sh7750_pmu_enable_all(void)231 static void sh7750_pmu_enable_all(void)
232 {
233 	int i;
234 
235 	for (i = 0; i < sh7750_pmu.num_events; i++)
236 		__raw_writew(__raw_readw(PMCR(i)) | PMCR_PMEN, PMCR(i));
237 }
238 
239 static struct sh_pmu sh7750_pmu = {
240 	.name		= "sh7750",
241 	.num_events	= 2,
242 	.event_map	= sh7750_event_map,
243 	.max_events	= ARRAY_SIZE(sh7750_general_events),
244 	.raw_event_mask	= PMCR_PMM_MASK,
245 	.cache_events	= &sh7750_cache_events,
246 	.read		= sh7750_pmu_read,
247 	.disable	= sh7750_pmu_disable,
248 	.enable		= sh7750_pmu_enable,
249 	.disable_all	= sh7750_pmu_disable_all,
250 	.enable_all	= sh7750_pmu_enable_all,
251 };
252 
sh7750_pmu_init(void)253 static int __init sh7750_pmu_init(void)
254 {
255 	/*
256 	 * Make sure this CPU actually has perf counters.
257 	 */
258 	if (!(boot_cpu_data.flags & CPU_HAS_PERF_COUNTER)) {
259 		pr_notice("HW perf events unsupported, software events only.\n");
260 		return -ENODEV;
261 	}
262 
263 	return register_sh_pmu(&sh7750_pmu);
264 }
265 early_initcall(sh7750_pmu_init);
266