1 /*
2  * Copyright 2013 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 #include "priv.h"
25 
26 #include <core/client.h>
27 #include <core/option.h>
28 
29 #include <nvif/class.h>
30 #include <nvif/ioctl.h>
31 #include <nvif/unpack.h>
32 
33 static u8
34 nvkm_pm_count_perfdom(struct nvkm_pm *ppm)
35 {
36 	struct nvkm_perfdom *dom;
37 	u8 domain_nr = 0;
38 
39 	list_for_each_entry(dom, &ppm->domains, head)
40 		domain_nr++;
41 	return domain_nr;
42 }
43 
44 static u16
45 nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
46 {
47 	u16 signal_nr = 0;
48 	int i;
49 
50 	if (dom) {
51 		for (i = 0; i < dom->signal_nr; i++) {
52 			if (dom->signal[i].name)
53 				signal_nr++;
54 		}
55 	}
56 	return signal_nr;
57 }
58 
59 static struct nvkm_perfdom *
60 nvkm_perfdom_find(struct nvkm_pm *ppm, int di)
61 {
62 	struct nvkm_perfdom *dom;
63 	int tmp = 0;
64 
65 	list_for_each_entry(dom, &ppm->domains, head) {
66 		if (tmp++ == di)
67 			return dom;
68 	}
69 	return NULL;
70 }
71 
72 struct nvkm_perfsig *
73 nvkm_perfsig_find(struct nvkm_pm *ppm, uint8_t di, uint8_t si,
74 		  struct nvkm_perfdom **pdom)
75 {
76 	struct nvkm_perfdom *dom = *pdom;
77 
78 	if (dom == NULL) {
79 		dom = nvkm_perfdom_find(ppm, di);
80 		if (dom == NULL)
81 			return NULL;
82 		*pdom = dom;
83 	}
84 
85 	if (!dom->signal[si].name)
86 		return NULL;
87 	return &dom->signal[si];
88 }
89 
90 static u8
91 nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig)
92 {
93 	u8 source_nr = 0, i;
94 
95 	for (i = 0; i < ARRAY_SIZE(sig->source); i++) {
96 		if (sig->source[i])
97 			source_nr++;
98 	}
99 	return source_nr;
100 }
101 
102 static struct nvkm_perfsrc *
103 nvkm_perfsrc_find(struct nvkm_pm *ppm, struct nvkm_perfsig *sig, int si)
104 {
105 	struct nvkm_perfsrc *src;
106 	bool found = false;
107 	int tmp = 1; /* Sources ID start from 1 */
108 	u8 i;
109 
110 	for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) {
111 		if (sig->source[i] == si) {
112 			found = true;
113 			break;
114 		}
115 	}
116 
117 	if (found) {
118 		list_for_each_entry(src, &ppm->sources, head) {
119 			if (tmp++ == si)
120 				return src;
121 		}
122 	}
123 
124 	return NULL;
125 }
126 
127 static int
128 nvkm_perfsrc_enable(struct nvkm_pm *ppm, struct nvkm_perfctr *ctr)
129 {
130 	struct nvkm_perfdom *dom = NULL;
131 	struct nvkm_perfsig *sig;
132 	struct nvkm_perfsrc *src;
133 	u32 mask, value;
134 	int i, j;
135 
136 	for (i = 0; i < 4; i++) {
137 		for (j = 0; j < 8 && ctr->source[i][j]; j++) {
138 			sig = nvkm_perfsig_find(ppm, ctr->domain,
139 						ctr->signal[i], &dom);
140 			if (!sig)
141 				return -EINVAL;
142 
143 			src = nvkm_perfsrc_find(ppm, sig, ctr->source[i][j]);
144 			if (!src)
145 				return -EINVAL;
146 
147 			/* set enable bit if needed */
148 			mask = value = 0x00000000;
149 			if (src->enable)
150 				mask = value = 0x80000000;
151 			mask  |= (src->mask << src->shift);
152 			value |= ((ctr->source[i][j] >> 32) << src->shift);
153 
154 			/* enable the source */
155 			nv_mask(ppm, src->addr, mask, value);
156 			nv_debug(ppm, "enabled source 0x%08x 0x%08x 0x%08x\n",
157 				 src->addr, mask, value);
158 		}
159 	}
160 	return 0;
161 }
162 
163 static int
164 nvkm_perfsrc_disable(struct nvkm_pm *ppm, struct nvkm_perfctr *ctr)
165 {
166 	struct nvkm_perfdom *dom = NULL;
167 	struct nvkm_perfsig *sig;
168 	struct nvkm_perfsrc *src;
169 	u32 mask;
170 	int i, j;
171 
172 	for (i = 0; i < 4; i++) {
173 		for (j = 0; j < 8 && ctr->source[i][j]; j++) {
174 			sig = nvkm_perfsig_find(ppm, ctr->domain,
175 						ctr->signal[i], &dom);
176 			if (!sig)
177 				return -EINVAL;
178 
179 			src = nvkm_perfsrc_find(ppm, sig, ctr->source[i][j]);
180 			if (!src)
181 				return -EINVAL;
182 
183 			/* unset enable bit if needed */
184 			mask = 0x00000000;
185 			if (src->enable)
186 				mask = 0x80000000;
187 			mask |= (src->mask << src->shift);
188 
189 			/* disable the source */
190 			nv_mask(ppm, src->addr, mask, 0);
191 			nv_debug(ppm, "disabled source 0x%08x 0x%08x\n",
192 				 src->addr, mask);
193 		}
194 	}
195 	return 0;
196 }
197 
198 /*******************************************************************************
199  * Perfdom object classes
200  ******************************************************************************/
201 static int
202 nvkm_perfdom_init(struct nvkm_object *object, void *data, u32 size)
203 {
204 	union {
205 		struct nvif_perfdom_init none;
206 	} *args = data;
207 	struct nvkm_pm *ppm = (void *)object->engine;
208 	struct nvkm_perfdom *dom = (void *)object;
209 	int ret, i;
210 
211 	nv_ioctl(object, "perfdom init size %d\n", size);
212 	if (nvif_unvers(args->none)) {
213 		nv_ioctl(object, "perfdom init\n");
214 	} else
215 		return ret;
216 
217 	for (i = 0; i < 4; i++) {
218 		if (dom->ctr[i]) {
219 			dom->func->init(ppm, dom, dom->ctr[i]);
220 
221 			/* enable sources */
222 			nvkm_perfsrc_enable(ppm, dom->ctr[i]);
223 		}
224 	}
225 
226 	/* start next batch of counters for sampling */
227 	dom->func->next(ppm, dom);
228 	return 0;
229 }
230 
231 static int
232 nvkm_perfdom_sample(struct nvkm_object *object, void *data, u32 size)
233 {
234 	union {
235 		struct nvif_perfdom_sample none;
236 	} *args = data;
237 	struct nvkm_pm *ppm = (void *)object->engine;
238 	struct nvkm_perfdom *dom;
239 	int ret;
240 
241 	nv_ioctl(object, "perfdom sample size %d\n", size);
242 	if (nvif_unvers(args->none)) {
243 		nv_ioctl(object, "perfdom sample\n");
244 	} else
245 		return ret;
246 	ppm->sequence++;
247 
248 	/* sample previous batch of counters */
249 	list_for_each_entry(dom, &ppm->domains, head)
250 		dom->func->next(ppm, dom);
251 
252 	return 0;
253 }
254 
255 static int
256 nvkm_perfdom_read(struct nvkm_object *object, void *data, u32 size)
257 {
258 	union {
259 		struct nvif_perfdom_read_v0 v0;
260 	} *args = data;
261 	struct nvkm_pm *ppm = (void *)object->engine;
262 	struct nvkm_perfdom *dom = (void *)object;
263 	int ret, i;
264 
265 	nv_ioctl(object, "perfdom read size %d\n", size);
266 	if (nvif_unpack(args->v0, 0, 0, false)) {
267 		nv_ioctl(object, "perfdom read vers %d\n", args->v0.version);
268 	} else
269 		return ret;
270 
271 	for (i = 0; i < 4; i++) {
272 		if (dom->ctr[i])
273 			dom->func->read(ppm, dom, dom->ctr[i]);
274 	}
275 
276 	if (!dom->clk)
277 		return -EAGAIN;
278 
279 	for (i = 0; i < 4; i++)
280 		if (dom->ctr[i])
281 			args->v0.ctr[i] = dom->ctr[i]->ctr;
282 	args->v0.clk = dom->clk;
283 	return 0;
284 }
285 
286 static int
287 nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
288 {
289 	switch (mthd) {
290 	case NVIF_PERFDOM_V0_INIT:
291 		return nvkm_perfdom_init(object, data, size);
292 	case NVIF_PERFDOM_V0_SAMPLE:
293 		return nvkm_perfdom_sample(object, data, size);
294 	case NVIF_PERFDOM_V0_READ:
295 		return nvkm_perfdom_read(object, data, size);
296 	default:
297 		break;
298 	}
299 	return -EINVAL;
300 }
301 
302 static void
303 nvkm_perfdom_dtor(struct nvkm_object *object)
304 {
305 	struct nvkm_pm *ppm = (void *)object->engine;
306 	struct nvkm_perfdom *dom = (void *)object;
307 	int i;
308 
309 	for (i = 0; i < 4; i++) {
310 		struct nvkm_perfctr *ctr = dom->ctr[i];
311 		if (ctr) {
312 			nvkm_perfsrc_disable(ppm, ctr);
313 			if (ctr->head.next)
314 				list_del(&ctr->head);
315 		}
316 		kfree(ctr);
317 	}
318 	nvkm_object_destroy(&dom->base);
319 }
320 
321 static int
322 nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot, uint8_t domain,
323 		 struct nvkm_perfsig *signal[4], uint64_t source[4][8],
324 		 uint16_t logic_op, struct nvkm_perfctr **pctr)
325 {
326 	struct nvkm_perfctr *ctr;
327 	int i, j;
328 
329 	if (!dom)
330 		return -EINVAL;
331 
332 	ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
333 	if (!ctr)
334 		return -ENOMEM;
335 
336 	ctr->domain   = domain;
337 	ctr->logic_op = logic_op;
338 	ctr->slot     = slot;
339 	for (i = 0; i < 4; i++) {
340 		if (signal[i]) {
341 			ctr->signal[i] = signal[i] - dom->signal;
342 			for (j = 0; j < 8; j++)
343 				ctr->source[i][j] = source[i][j];
344 		}
345 	}
346 	list_add_tail(&ctr->head, &dom->list);
347 
348 	return 0;
349 }
350 
351 static int
352 nvkm_perfdom_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
353 		  struct nvkm_oclass *oclass, void *data, u32 size,
354 		  struct nvkm_object **pobject)
355 {
356 	union {
357 		struct nvif_perfdom_v0 v0;
358 	} *args = data;
359 	struct nvkm_pm *ppm = (void *)engine;
360 	struct nvkm_perfdom *sdom = NULL;
361 	struct nvkm_perfctr *ctr[4] = {};
362 	struct nvkm_perfdom *dom;
363 	int c, s, m;
364 	int ret;
365 
366 	nv_ioctl(parent, "create perfdom size %d\n", size);
367 	if (nvif_unpack(args->v0, 0, 0, false)) {
368 		nv_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
369 			 args->v0.version, args->v0.domain, args->v0.mode);
370 	} else
371 		return ret;
372 
373 	for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
374 		struct nvkm_perfsig *sig[4] = {};
375 		u64 src[4][8] = {};
376 
377 		for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
378 			sig[s] = nvkm_perfsig_find(ppm, args->v0.domain,
379 						   args->v0.ctr[c].signal[s],
380 						   &sdom);
381 			if (args->v0.ctr[c].signal[s] && !sig[s])
382 				return -EINVAL;
383 
384 			for (m = 0; m < 8; m++) {
385 				src[s][m] = args->v0.ctr[c].source[s][m];
386 				if (src[s][m] && !nvkm_perfsrc_find(ppm, sig[s],
387 							            src[s][m]))
388 					return -EINVAL;
389 			}
390 		}
391 
392 		ret = nvkm_perfctr_new(sdom, c, args->v0.domain, sig, src,
393 				       args->v0.ctr[c].logic_op, &ctr[c]);
394 		if (ret)
395 			return ret;
396 	}
397 
398 	if (!sdom)
399 		return -EINVAL;
400 
401 	ret = nvkm_object_create(parent, engine, oclass, 0, &dom);
402 	*pobject = nv_object(dom);
403 	if (ret)
404 		return ret;
405 
406 	dom->func = sdom->func;
407 	dom->addr = sdom->addr;
408 	dom->mode = args->v0.mode;
409 	for (c = 0; c < ARRAY_SIZE(ctr); c++)
410 		dom->ctr[c] = ctr[c];
411 	return 0;
412 }
413 
414 static struct nvkm_ofuncs
415 nvkm_perfdom_ofuncs = {
416 	.ctor = nvkm_perfdom_ctor,
417 	.dtor = nvkm_perfdom_dtor,
418 	.init = nvkm_object_init,
419 	.fini = nvkm_object_fini,
420 	.mthd = nvkm_perfdom_mthd,
421 };
422 
423 /*******************************************************************************
424  * Perfmon object classes
425  ******************************************************************************/
426 static int
427 nvkm_perfmon_mthd_query_domain(struct nvkm_object *object, void *data, u32 size)
428 {
429 	union {
430 		struct nvif_perfmon_query_domain_v0 v0;
431 	} *args = data;
432 	struct nvkm_pm *ppm = (void *)object->engine;
433 	struct nvkm_perfdom *dom;
434 	u8 domain_nr;
435 	int di, ret;
436 
437 	nv_ioctl(object, "perfmon query domain size %d\n", size);
438 	if (nvif_unpack(args->v0, 0, 0, false)) {
439 		nv_ioctl(object, "perfmon domain vers %d iter %02x\n",
440 			 args->v0.version, args->v0.iter);
441 		di = (args->v0.iter & 0xff) - 1;
442 	} else
443 		return ret;
444 
445 	domain_nr = nvkm_pm_count_perfdom(ppm);
446 	if (di >= (int)domain_nr)
447 		return -EINVAL;
448 
449 	if (di >= 0) {
450 		dom = nvkm_perfdom_find(ppm, di);
451 		if (dom == NULL)
452 			return -EINVAL;
453 
454 		args->v0.id         = di;
455 		args->v0.signal_nr  = nvkm_perfdom_count_perfsig(dom);
456 		strncpy(args->v0.name, dom->name, sizeof(args->v0.name));
457 
458 		/* Currently only global counters (PCOUNTER) are implemented
459 		 * but this will be different for local counters (MP). */
460 		args->v0.counter_nr = 4;
461 	}
462 
463 	if (++di < domain_nr) {
464 		args->v0.iter = ++di;
465 		return 0;
466 	}
467 
468 	args->v0.iter = 0xff;
469 	return 0;
470 }
471 
472 static int
473 nvkm_perfmon_mthd_query_signal(struct nvkm_object *object, void *data, u32 size)
474 {
475 	union {
476 		struct nvif_perfmon_query_signal_v0 v0;
477 	} *args = data;
478 	struct nvkm_device *device = nv_device(object);
479 	struct nvkm_pm *ppm = (void *)object->engine;
480 	struct nvkm_perfdom *dom;
481 	struct nvkm_perfsig *sig;
482 	const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
483 	const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all);
484 	int ret, si;
485 
486 	nv_ioctl(object, "perfmon query signal size %d\n", size);
487 	if (nvif_unpack(args->v0, 0, 0, false)) {
488 		nv_ioctl(object,
489 			 "perfmon query signal vers %d dom %d iter %04x\n",
490 			 args->v0.version, args->v0.domain, args->v0.iter);
491 		si = (args->v0.iter & 0xffff) - 1;
492 	} else
493 		return ret;
494 
495 	dom = nvkm_perfdom_find(ppm, args->v0.domain);
496 	if (dom == NULL || si >= (int)dom->signal_nr)
497 		return -EINVAL;
498 
499 	if (si >= 0) {
500 		sig = &dom->signal[si];
501 		if (raw || !sig->name) {
502 			snprintf(args->v0.name, sizeof(args->v0.name),
503 				 "/%s/%02x", dom->name, si);
504 		} else {
505 			strncpy(args->v0.name, sig->name,
506 				sizeof(args->v0.name));
507 		}
508 
509 		args->v0.signal = si;
510 		args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
511 	}
512 
513 	while (++si < dom->signal_nr) {
514 		if (all || dom->signal[si].name) {
515 			args->v0.iter = ++si;
516 			return 0;
517 		}
518 	}
519 
520 	args->v0.iter = 0xffff;
521 	return 0;
522 }
523 
524 static int
525 nvkm_perfmon_mthd_query_source(struct nvkm_object *object, void *data, u32 size)
526 {
527 	union {
528 		struct nvif_perfmon_query_source_v0 v0;
529 	} *args = data;
530 	struct nvkm_pm *ppm = (void *)object->engine;
531 	struct nvkm_perfdom *dom = NULL;
532 	struct nvkm_perfsig *sig;
533 	struct nvkm_perfsrc *src;
534 	u8 source_nr = 0;
535 	int si, ret;
536 
537 	nv_ioctl(object, "perfmon query source size %d\n", size);
538 	if (nvif_unpack(args->v0, 0, 0, false)) {
539 		nv_ioctl(object,
540 			 "perfmon source vers %d dom %d sig %02x iter %02x\n",
541 			 args->v0.version, args->v0.domain, args->v0.signal,
542 			 args->v0.iter);
543 		si = (args->v0.iter & 0xff) - 1;
544 	} else
545 		return ret;
546 
547 	sig = nvkm_perfsig_find(ppm, args->v0.domain, args->v0.signal, &dom);
548 	if (!sig)
549 		return -EINVAL;
550 
551 	source_nr = nvkm_perfsig_count_perfsrc(sig);
552 	if (si >= (int)source_nr)
553 		return -EINVAL;
554 
555 	if (si >= 0) {
556 		src = nvkm_perfsrc_find(ppm, sig, sig->source[si]);
557 		if (!src)
558 			return -EINVAL;
559 
560 		args->v0.source = sig->source[si];
561 		args->v0.mask   = src->mask;
562 		strncpy(args->v0.name, src->name, sizeof(args->v0.name));
563 	}
564 
565 	if (++si < source_nr) {
566 		args->v0.iter = ++si;
567 		return 0;
568 	}
569 
570 	args->v0.iter = 0xff;
571 	return 0;
572 }
573 
574 static int
575 nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
576 {
577 	switch (mthd) {
578 	case NVIF_PERFMON_V0_QUERY_DOMAIN:
579 		return nvkm_perfmon_mthd_query_domain(object, data, size);
580 	case NVIF_PERFMON_V0_QUERY_SIGNAL:
581 		return nvkm_perfmon_mthd_query_signal(object, data, size);
582 	case NVIF_PERFMON_V0_QUERY_SOURCE:
583 		return nvkm_perfmon_mthd_query_source(object, data, size);
584 	default:
585 		break;
586 	}
587 	return -EINVAL;
588 }
589 
590 static struct nvkm_oclass
591 nvkm_perfmon_sclass[] = {
592 	{ .handle = NVIF_IOCTL_NEW_V0_PERFDOM,
593 	  .ofuncs = &nvkm_perfdom_ofuncs,
594 	},
595 	{}
596 };
597 
598 static int
599 nvkm_perfmon_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
600 		  struct nvkm_oclass *oclass, void *data, u32 size,
601 		  struct nvkm_object **pobject)
602 {
603 	struct nvkm_parent *perfmon;
604 	int ret = nvkm_parent_create(parent, engine, oclass, 0,
605 				     nvkm_perfmon_sclass, 0, &perfmon);
606 	*pobject = perfmon ? &perfmon->object : NULL;
607 	return ret;
608 }
609 
610 static struct nvkm_ofuncs
611 nvkm_perfmon_ofuncs = {
612 	.ctor = nvkm_perfmon_ctor,
613 	.dtor = _nvkm_parent_dtor,
614 	.init = _nvkm_parent_init,
615 	.fini = _nvkm_parent_fini,
616 	.mthd = nvkm_perfmon_mthd,
617 };
618 
619 struct nvkm_oclass
620 nvkm_pm_sclass[] = {
621 	{
622 	  .handle = NVIF_IOCTL_NEW_V0_PERFMON,
623 	  .ofuncs = &nvkm_perfmon_ofuncs,
624 	},
625 	{},
626 };
627 
628 /*******************************************************************************
629  * PPM context
630  ******************************************************************************/
631 static void
632 nvkm_perfctx_dtor(struct nvkm_object *object)
633 {
634 	struct nvkm_pm *ppm = (void *)object->engine;
635 	struct nvkm_perfctx *ctx = (void *)object;
636 
637 	mutex_lock(&nv_subdev(ppm)->mutex);
638 	nvkm_engctx_destroy(&ctx->base);
639 	if (ppm->context == ctx)
640 		ppm->context = NULL;
641 	mutex_unlock(&nv_subdev(ppm)->mutex);
642 }
643 
644 static int
645 nvkm_perfctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
646 		  struct nvkm_oclass *oclass, void *data, u32 size,
647 		  struct nvkm_object **pobject)
648 {
649 	struct nvkm_pm *ppm = (void *)engine;
650 	struct nvkm_perfctx *ctx;
651 	int ret;
652 
653 	/* no context needed for perfdom objects... */
654 	if (nv_mclass(parent) != NV_DEVICE) {
655 		atomic_inc(&parent->refcount);
656 		*pobject = parent;
657 		return 1;
658 	}
659 
660 	ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx);
661 	*pobject = nv_object(ctx);
662 	if (ret)
663 		return ret;
664 
665 	mutex_lock(&nv_subdev(ppm)->mutex);
666 	if (ppm->context == NULL)
667 		ppm->context = ctx;
668 	if (ctx != ppm->context)
669 		ret = -EBUSY;
670 	mutex_unlock(&nv_subdev(ppm)->mutex);
671 
672 	return ret;
673 }
674 
675 struct nvkm_oclass
676 nvkm_pm_cclass = {
677 	.handle = NV_ENGCTX(PM, 0x00),
678 	.ofuncs = &(struct nvkm_ofuncs) {
679 		.ctor = nvkm_perfctx_ctor,
680 		.dtor = nvkm_perfctx_dtor,
681 		.init = _nvkm_engctx_init,
682 		.fini = _nvkm_engctx_fini,
683 	},
684 };
685 
686 /*******************************************************************************
687  * PPM engine/subdev functions
688  ******************************************************************************/
689 int
690 nvkm_perfsrc_new(struct nvkm_pm *ppm, struct nvkm_perfsig *sig,
691 		 const struct nvkm_specsrc *spec)
692 {
693 	const struct nvkm_specsrc *ssrc;
694 	const struct nvkm_specmux *smux;
695 	struct nvkm_perfsrc *src;
696 	u8 source_nr = 0;
697 
698 	if (!spec) {
699 		/* No sources are defined for this signal. */
700 		return 0;
701 	}
702 
703 	ssrc = spec;
704 	while (ssrc->name) {
705 		smux = ssrc->mux;
706 		while (smux->name) {
707 			bool found = false;
708 			u8 source_id = 0;
709 			u32 len;
710 
711 			list_for_each_entry(src, &ppm->sources, head) {
712 				if (src->addr == ssrc->addr &&
713 				    src->shift == smux->shift) {
714 					found = true;
715 					break;
716 				}
717 				source_id++;
718 			}
719 
720 			if (!found) {
721 				src = kzalloc(sizeof(*src), GFP_KERNEL);
722 				if (!src)
723 					return -ENOMEM;
724 
725 				src->addr   = ssrc->addr;
726 				src->mask   = smux->mask;
727 				src->shift  = smux->shift;
728 				src->enable = smux->enable;
729 
730 				len = strlen(ssrc->name) +
731 				      strlen(smux->name) + 2;
732 				src->name = kzalloc(len, GFP_KERNEL);
733 				if (!src->name)
734 					return -ENOMEM;
735 				snprintf(src->name, len, "%s_%s", ssrc->name,
736 					 smux->name);
737 
738 				list_add_tail(&src->head, &ppm->sources);
739 			}
740 
741 			sig->source[source_nr++] = source_id + 1;
742 			smux++;
743 		}
744 		ssrc++;
745 	}
746 
747 	return 0;
748 }
749 
750 int
751 nvkm_perfdom_new(struct nvkm_pm *ppm, const char *name, u32 mask,
752 		 u32 base, u32 size_unit, u32 size_domain,
753 		 const struct nvkm_specdom *spec)
754 {
755 	const struct nvkm_specdom *sdom;
756 	const struct nvkm_specsig *ssig;
757 	struct nvkm_perfdom *dom;
758 	int ret, i;
759 
760 	for (i = 0; i == 0 || mask; i++) {
761 		u32 addr = base + (i * size_unit);
762 		if (i && !(mask & (1 << i)))
763 			continue;
764 
765 		sdom = spec;
766 		while (sdom->signal_nr) {
767 			dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
768 				      sizeof(*dom->signal), GFP_KERNEL);
769 			if (!dom)
770 				return -ENOMEM;
771 
772 			if (mask) {
773 				snprintf(dom->name, sizeof(dom->name),
774 					 "%s/%02x/%02x", name, i,
775 					 (int)(sdom - spec));
776 			} else {
777 				snprintf(dom->name, sizeof(dom->name),
778 					 "%s/%02x", name, (int)(sdom - spec));
779 			}
780 
781 			list_add_tail(&dom->head, &ppm->domains);
782 			INIT_LIST_HEAD(&dom->list);
783 			dom->func = sdom->func;
784 			dom->addr = addr;
785 			dom->signal_nr = sdom->signal_nr;
786 
787 			ssig = (sdom++)->signal;
788 			while (ssig->name) {
789 				struct nvkm_perfsig *sig =
790 					&dom->signal[ssig->signal];
791 				sig->name = ssig->name;
792 				ret = nvkm_perfsrc_new(ppm, sig, ssig->source);
793 				if (ret)
794 					return ret;
795 				ssig++;
796 			}
797 
798 			addr += size_domain;
799 		}
800 
801 		mask &= ~(1 << i);
802 	}
803 
804 	return 0;
805 }
806 
807 int
808 _nvkm_pm_fini(struct nvkm_object *object, bool suspend)
809 {
810 	struct nvkm_pm *ppm = (void *)object;
811 	return nvkm_engine_fini(&ppm->base, suspend);
812 }
813 
814 int
815 _nvkm_pm_init(struct nvkm_object *object)
816 {
817 	struct nvkm_pm *ppm = (void *)object;
818 	return nvkm_engine_init(&ppm->base);
819 }
820 
821 void
822 _nvkm_pm_dtor(struct nvkm_object *object)
823 {
824 	struct nvkm_pm *ppm = (void *)object;
825 	struct nvkm_perfdom *dom, *next_dom;
826 	struct nvkm_perfsrc *src, *next_src;
827 
828 	list_for_each_entry_safe(dom, next_dom, &ppm->domains, head) {
829 		list_del(&dom->head);
830 		kfree(dom);
831 	}
832 
833 	list_for_each_entry_safe(src, next_src, &ppm->sources, head) {
834 		list_del(&src->head);
835 		kfree(src->name);
836 		kfree(src);
837 	}
838 
839 	nvkm_engine_destroy(&ppm->base);
840 }
841 
842 int
843 nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
844 		struct nvkm_oclass *oclass, int length, void **pobject)
845 {
846 	struct nvkm_pm *ppm;
847 	int ret;
848 
849 	ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM",
850 				  "pm", length, pobject);
851 	ppm = *pobject;
852 	if (ret)
853 		return ret;
854 
855 	INIT_LIST_HEAD(&ppm->domains);
856 	INIT_LIST_HEAD(&ppm->sources);
857 	return 0;
858 }
859