1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2017 Etnaviv Project
4 * Copyright (C) 2017 Zodiac Inflight Innovations
5 */
6
7#include "common.xml.h"
8#include "etnaviv_gpu.h"
9#include "etnaviv_perfmon.h"
10#include "state_hi.xml.h"
11
12struct etnaviv_pm_domain;
13
14struct etnaviv_pm_signal {
15	char name[64];
16	u32 data;
17
18	u32 (*sample)(struct etnaviv_gpu *gpu,
19		      const struct etnaviv_pm_domain *domain,
20		      const struct etnaviv_pm_signal *signal);
21};
22
23struct etnaviv_pm_domain {
24	char name[64];
25
26	/* profile register */
27	u32 profile_read;
28	u32 profile_config;
29
30	u8 nr_signals;
31	const struct etnaviv_pm_signal *signal;
32};
33
34struct etnaviv_pm_domain_meta {
35	unsigned int feature;
36	const struct etnaviv_pm_domain *domains;
37	u32 nr_domains;
38};
39
40static u32 perf_reg_read(struct etnaviv_gpu *gpu,
41	const struct etnaviv_pm_domain *domain,
42	const struct etnaviv_pm_signal *signal)
43{
44	gpu_write(gpu, domain->profile_config, signal->data);
45
46	return gpu_read(gpu, domain->profile_read);
47}
48
49static inline void pipe_select(struct etnaviv_gpu *gpu, u32 clock, unsigned pipe)
50{
51	clock &= ~(VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK);
52	clock |= VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(pipe);
53
54	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
55}
56
57static u32 pipe_perf_reg_read(struct etnaviv_gpu *gpu,
58	const struct etnaviv_pm_domain *domain,
59	const struct etnaviv_pm_signal *signal)
60{
61	u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
62	u32 value = 0;
63	unsigned i;
64
65	for (i = 0; i < gpu->identity.pixel_pipes; i++) {
66		pipe_select(gpu, clock, i);
67		value += perf_reg_read(gpu, domain, signal);
68	}
69
70	/* switch back to pixel pipe 0 to prevent GPU hang */
71	pipe_select(gpu, clock, 0);
72
73	return value;
74}
75
76static u32 pipe_reg_read(struct etnaviv_gpu *gpu,
77	const struct etnaviv_pm_domain *domain,
78	const struct etnaviv_pm_signal *signal)
79{
80	u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
81	u32 value = 0;
82	unsigned i;
83
84	for (i = 0; i < gpu->identity.pixel_pipes; i++) {
85		pipe_select(gpu, clock, i);
86		value += gpu_read(gpu, signal->data);
87	}
88
89	/* switch back to pixel pipe 0 to prevent GPU hang */
90	pipe_select(gpu, clock, 0);
91
92	return value;
93}
94
95static u32 hi_total_cycle_read(struct etnaviv_gpu *gpu,
96	const struct etnaviv_pm_domain *domain,
97	const struct etnaviv_pm_signal *signal)
98{
99	u32 reg = VIVS_HI_PROFILE_TOTAL_CYCLES;
100
101	if (gpu->identity.model == chipModel_GC880 ||
102		gpu->identity.model == chipModel_GC2000 ||
103		gpu->identity.model == chipModel_GC2100)
104		reg = VIVS_MC_PROFILE_CYCLE_COUNTER;
105
106	return gpu_read(gpu, reg);
107}
108
109static u32 hi_total_idle_cycle_read(struct etnaviv_gpu *gpu,
110	const struct etnaviv_pm_domain *domain,
111	const struct etnaviv_pm_signal *signal)
112{
113	u32 reg = VIVS_HI_PROFILE_IDLE_CYCLES;
114
115	if (gpu->identity.model == chipModel_GC880 ||
116		gpu->identity.model == chipModel_GC2000 ||
117		gpu->identity.model == chipModel_GC2100)
118		reg = VIVS_HI_PROFILE_TOTAL_CYCLES;
119
120	return gpu_read(gpu, reg);
121}
122
123static const struct etnaviv_pm_domain doms_3d[] = {
124	{
125		.name = "HI",
126		.profile_read = VIVS_MC_PROFILE_HI_READ,
127		.profile_config = VIVS_MC_PROFILE_CONFIG2,
128		.nr_signals = 7,
129		.signal = (const struct etnaviv_pm_signal[]) {
130			{
131				"TOTAL_READ_BYTES8",
132				VIVS_HI_PROFILE_READ_BYTES8,
133				&pipe_reg_read,
134			},
135			{
136				"TOTAL_WRITE_BYTES8",
137				VIVS_HI_PROFILE_WRITE_BYTES8,
138				&pipe_reg_read,
139			},
140			{
141				"TOTAL_CYCLES",
142				0,
143				&hi_total_cycle_read
144			},
145			{
146				"IDLE_CYCLES",
147				0,
148				&hi_total_idle_cycle_read
149			},
150			{
151				"AXI_CYCLES_READ_REQUEST_STALLED",
152				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED,
153				&perf_reg_read
154			},
155			{
156				"AXI_CYCLES_WRITE_REQUEST_STALLED",
157				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED,
158				&perf_reg_read
159			},
160			{
161				"AXI_CYCLES_WRITE_DATA_STALLED",
162				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED,
163				&perf_reg_read
164			}
165		}
166	},
167	{
168		.name = "PE",
169		.profile_read = VIVS_MC_PROFILE_PE_READ,
170		.profile_config = VIVS_MC_PROFILE_CONFIG0,
171		.nr_signals = 4,
172		.signal = (const struct etnaviv_pm_signal[]) {
173			{
174				"PIXEL_COUNT_KILLED_BY_COLOR_PIPE",
175				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE,
176				&pipe_perf_reg_read
177			},
178			{
179				"PIXEL_COUNT_KILLED_BY_DEPTH_PIPE",
180				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE,
181				&pipe_perf_reg_read
182			},
183			{
184				"PIXEL_COUNT_DRAWN_BY_COLOR_PIPE",
185				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE,
186				&pipe_perf_reg_read
187			},
188			{
189				"PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE",
190				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE,
191				&pipe_perf_reg_read
192			}
193		}
194	},
195	{
196		.name = "SH",
197		.profile_read = VIVS_MC_PROFILE_SH_READ,
198		.profile_config = VIVS_MC_PROFILE_CONFIG0,
199		.nr_signals = 9,
200		.signal = (const struct etnaviv_pm_signal[]) {
201			{
202				"SHADER_CYCLES",
203				VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES,
204				&perf_reg_read
205			},
206			{
207				"PS_INST_COUNTER",
208				VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER,
209				&perf_reg_read
210			},
211			{
212				"RENDERED_PIXEL_COUNTER",
213				VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER,
214				&perf_reg_read
215			},
216			{
217				"VS_INST_COUNTER",
218				VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER,
219				&pipe_perf_reg_read
220			},
221			{
222				"RENDERED_VERTICE_COUNTER",
223				VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER,
224				&pipe_perf_reg_read
225			},
226			{
227				"VTX_BRANCH_INST_COUNTER",
228				VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER,
229				&pipe_perf_reg_read
230			},
231			{
232				"VTX_TEXLD_INST_COUNTER",
233				VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER,
234				&pipe_perf_reg_read
235			},
236			{
237				"PXL_BRANCH_INST_COUNTER",
238				VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER,
239				&pipe_perf_reg_read
240			},
241			{
242				"PXL_TEXLD_INST_COUNTER",
243				VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER,
244				&pipe_perf_reg_read
245			}
246		}
247	},
248	{
249		.name = "PA",
250		.profile_read = VIVS_MC_PROFILE_PA_READ,
251		.profile_config = VIVS_MC_PROFILE_CONFIG1,
252		.nr_signals = 6,
253		.signal = (const struct etnaviv_pm_signal[]) {
254			{
255				"INPUT_VTX_COUNTER",
256				VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER,
257				&perf_reg_read
258			},
259			{
260				"INPUT_PRIM_COUNTER",
261				VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER,
262				&perf_reg_read
263			},
264			{
265				"OUTPUT_PRIM_COUNTER",
266				VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER,
267				&perf_reg_read
268			},
269			{
270				"DEPTH_CLIPPED_COUNTER",
271				VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER,
272				&pipe_perf_reg_read
273			},
274			{
275				"TRIVIAL_REJECTED_COUNTER",
276				VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER,
277				&pipe_perf_reg_read
278			},
279			{
280				"CULLED_COUNTER",
281				VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER,
282				&pipe_perf_reg_read
283			}
284		}
285	},
286	{
287		.name = "SE",
288		.profile_read = VIVS_MC_PROFILE_SE_READ,
289		.profile_config = VIVS_MC_PROFILE_CONFIG1,
290		.nr_signals = 2,
291		.signal = (const struct etnaviv_pm_signal[]) {
292			{
293				"CULLED_TRIANGLE_COUNT",
294				VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT,
295				&perf_reg_read
296			},
297			{
298				"CULLED_LINES_COUNT",
299				VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT,
300				&perf_reg_read
301			}
302		}
303	},
304	{
305		.name = "RA",
306		.profile_read = VIVS_MC_PROFILE_RA_READ,
307		.profile_config = VIVS_MC_PROFILE_CONFIG1,
308		.nr_signals = 7,
309		.signal = (const struct etnaviv_pm_signal[]) {
310			{
311				"VALID_PIXEL_COUNT",
312				VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT,
313				&perf_reg_read
314			},
315			{
316				"TOTAL_QUAD_COUNT",
317				VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT,
318				&perf_reg_read
319			},
320			{
321				"VALID_QUAD_COUNT_AFTER_EARLY_Z",
322				VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z,
323				&perf_reg_read
324			},
325			{
326				"TOTAL_PRIMITIVE_COUNT",
327				VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT,
328				&perf_reg_read
329			},
330			{
331				"PIPE_CACHE_MISS_COUNTER",
332				VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER,
333				&perf_reg_read
334			},
335			{
336				"PREFETCH_CACHE_MISS_COUNTER",
337				VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER,
338				&perf_reg_read
339			},
340			{
341				"CULLED_QUAD_COUNT",
342				VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT,
343				&perf_reg_read
344			}
345		}
346	},
347	{
348		.name = "TX",
349		.profile_read = VIVS_MC_PROFILE_TX_READ,
350		.profile_config = VIVS_MC_PROFILE_CONFIG1,
351		.nr_signals = 9,
352		.signal = (const struct etnaviv_pm_signal[]) {
353			{
354				"TOTAL_BILINEAR_REQUESTS",
355				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS,
356				&perf_reg_read
357			},
358			{
359				"TOTAL_TRILINEAR_REQUESTS",
360				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS,
361				&perf_reg_read
362			},
363			{
364				"TOTAL_DISCARDED_TEXTURE_REQUESTS",
365				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS,
366				&perf_reg_read
367			},
368			{
369				"TOTAL_TEXTURE_REQUESTS",
370				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS,
371				&perf_reg_read
372			},
373			{
374				"MEM_READ_COUNT",
375				VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT,
376				&perf_reg_read
377			},
378			{
379				"MEM_READ_IN_8B_COUNT",
380				VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT,
381				&perf_reg_read
382			},
383			{
384				"CACHE_MISS_COUNT",
385				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT,
386				&perf_reg_read
387			},
388			{
389				"CACHE_HIT_TEXEL_COUNT",
390				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT,
391				&perf_reg_read
392			},
393			{
394				"CACHE_MISS_TEXEL_COUNT",
395				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT,
396				&perf_reg_read
397			}
398		}
399	},
400	{
401		.name = "MC",
402		.profile_read = VIVS_MC_PROFILE_MC_READ,
403		.profile_config = VIVS_MC_PROFILE_CONFIG2,
404		.nr_signals = 3,
405		.signal = (const struct etnaviv_pm_signal[]) {
406			{
407				"TOTAL_READ_REQ_8B_FROM_PIPELINE",
408				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE,
409				&perf_reg_read
410			},
411			{
412				"TOTAL_READ_REQ_8B_FROM_IP",
413				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP,
414				&perf_reg_read
415			},
416			{
417				"TOTAL_WRITE_REQ_8B_FROM_PIPELINE",
418				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE,
419				&perf_reg_read
420			}
421		}
422	}
423};
424
425static const struct etnaviv_pm_domain doms_2d[] = {
426	{
427		.name = "PE",
428		.profile_read = VIVS_MC_PROFILE_PE_READ,
429		.profile_config = VIVS_MC_PROFILE_CONFIG0,
430		.nr_signals = 1,
431		.signal = (const struct etnaviv_pm_signal[]) {
432			{
433				"PIXELS_RENDERED_2D",
434				VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D,
435				&pipe_perf_reg_read
436			}
437		}
438	}
439};
440
441static const struct etnaviv_pm_domain doms_vg[] = {
442};
443
444static const struct etnaviv_pm_domain_meta doms_meta[] = {
445	{
446		.feature = chipFeatures_PIPE_3D,
447		.nr_domains = ARRAY_SIZE(doms_3d),
448		.domains = &doms_3d[0]
449	},
450	{
451		.feature = chipFeatures_PIPE_2D,
452		.nr_domains = ARRAY_SIZE(doms_2d),
453		.domains = &doms_2d[0]
454	},
455	{
456		.feature = chipFeatures_PIPE_VG,
457		.nr_domains = ARRAY_SIZE(doms_vg),
458		.domains = &doms_vg[0]
459	}
460};
461
462static unsigned int num_pm_domains(const struct etnaviv_gpu *gpu)
463{
464	unsigned int num = 0, i;
465
466	for (i = 0; i < ARRAY_SIZE(doms_meta); i++) {
467		const struct etnaviv_pm_domain_meta *meta = &doms_meta[i];
468
469		if (gpu->identity.features & meta->feature)
470			num += meta->nr_domains;
471	}
472
473	return num;
474}
475
476static const struct etnaviv_pm_domain *pm_domain(const struct etnaviv_gpu *gpu,
477	unsigned int index)
478{
479	const struct etnaviv_pm_domain *domain = NULL;
480	unsigned int offset = 0, i;
481
482	for (i = 0; i < ARRAY_SIZE(doms_meta); i++) {
483		const struct etnaviv_pm_domain_meta *meta = &doms_meta[i];
484
485		if (!(gpu->identity.features & meta->feature))
486			continue;
487
488		if (index - offset >= meta->nr_domains) {
489			offset += meta->nr_domains;
490			continue;
491		}
492
493		domain = meta->domains + (index - offset);
494	}
495
496	return domain;
497}
498
499int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
500	struct drm_etnaviv_pm_domain *domain)
501{
502	const unsigned int nr_domains = num_pm_domains(gpu);
503	const struct etnaviv_pm_domain *dom;
504
505	if (domain->iter >= nr_domains)
506		return -EINVAL;
507
508	dom = pm_domain(gpu, domain->iter);
509	if (!dom)
510		return -EINVAL;
511
512	domain->id = domain->iter;
513	domain->nr_signals = dom->nr_signals;
514	strscpy_pad(domain->name, dom->name, sizeof(domain->name));
515
516	domain->iter++;
517	if (domain->iter == nr_domains)
518		domain->iter = 0xff;
519
520	return 0;
521}
522
523int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
524	struct drm_etnaviv_pm_signal *signal)
525{
526	const unsigned int nr_domains = num_pm_domains(gpu);
527	const struct etnaviv_pm_domain *dom;
528	const struct etnaviv_pm_signal *sig;
529
530	if (signal->domain >= nr_domains)
531		return -EINVAL;
532
533	dom = pm_domain(gpu, signal->domain);
534	if (!dom)
535		return -EINVAL;
536
537	if (signal->iter >= dom->nr_signals)
538		return -EINVAL;
539
540	sig = &dom->signal[signal->iter];
541
542	signal->id = signal->iter;
543	strscpy_pad(signal->name, sig->name, sizeof(signal->name));
544
545	signal->iter++;
546	if (signal->iter == dom->nr_signals)
547		signal->iter = 0xffff;
548
549	return 0;
550}
551
552int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
553	u32 exec_state)
554{
555	const struct etnaviv_pm_domain_meta *meta = &doms_meta[exec_state];
556	const struct etnaviv_pm_domain *dom;
557
558	if (r->domain >= meta->nr_domains)
559		return -EINVAL;
560
561	dom = meta->domains + r->domain;
562
563	if (r->signal >= dom->nr_signals)
564		return -EINVAL;
565
566	return 0;
567}
568
569void etnaviv_perfmon_process(struct etnaviv_gpu *gpu,
570	const struct etnaviv_perfmon_request *pmr, u32 exec_state)
571{
572	const struct etnaviv_pm_domain_meta *meta = &doms_meta[exec_state];
573	const struct etnaviv_pm_domain *dom;
574	const struct etnaviv_pm_signal *sig;
575	u32 *bo = pmr->bo_vma;
576	u32 val;
577
578	dom = meta->domains + pmr->domain;
579	sig = &dom->signal[pmr->signal];
580	val = sig->sample(gpu, dom, sig);
581
582	*(bo + pmr->offset) = val;
583}
584