base.c 19.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2013 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Ben Skeggs
 */
24
#include "priv.h"
25

26
#include <core/client.h>
27
#include <core/option.h>
28

29 30
#include <nvif/class.h>
#include <nvif/ioctl.h>
31
#include <nvif/unpack.h>
32

33
static u8
B
Ben Skeggs 已提交
34
nvkm_pm_count_perfdom(struct nvkm_pm *pm)
35 36 37 38
{
	struct nvkm_perfdom *dom;
	u8 domain_nr = 0;

B
Ben Skeggs 已提交
39
	list_for_each_entry(dom, &pm->domains, head)
40 41 42 43
		domain_nr++;
	return domain_nr;
}

44
static u16
45 46
nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
{
47
	u16 signal_nr = 0;
48 49 50 51 52 53 54 55 56 57 58 59
	int i;

	if (dom) {
		for (i = 0; i < dom->signal_nr; i++) {
			if (dom->signal[i].name)
				signal_nr++;
		}
	}
	return signal_nr;
}

static struct nvkm_perfdom *
B
Ben Skeggs 已提交
60
nvkm_perfdom_find(struct nvkm_pm *pm, int di)
61 62 63 64
{
	struct nvkm_perfdom *dom;
	int tmp = 0;

B
Ben Skeggs 已提交
65
	list_for_each_entry(dom, &pm->domains, head) {
66 67 68 69 70 71
		if (tmp++ == di)
			return dom;
	}
	return NULL;
}

72
struct nvkm_perfsig *
B
Ben Skeggs 已提交
73
nvkm_perfsig_find(struct nvkm_pm *pm, u8 di, u8 si, struct nvkm_perfdom **pdom)
74
{
75
	struct nvkm_perfdom *dom = *pdom;
76 77

	if (dom == NULL) {
B
Ben Skeggs 已提交
78
		dom = nvkm_perfdom_find(pm, di);
79 80 81
		if (dom == NULL)
			return NULL;
		*pdom = dom;
82 83
	}

84 85 86
	if (!dom->signal[si].name)
		return NULL;
	return &dom->signal[si];
87 88
}

89 90 91 92 93 94 95 96 97 98 99 100
static u8
nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig)
{
	u8 source_nr = 0, i;

	for (i = 0; i < ARRAY_SIZE(sig->source); i++) {
		if (sig->source[i])
			source_nr++;
	}
	return source_nr;
}

101
static struct nvkm_perfsrc *
B
Ben Skeggs 已提交
102
nvkm_perfsrc_find(struct nvkm_pm *pm, struct nvkm_perfsig *sig, int si)
103 104 105 106 107 108 109 110 111 112 113 114 115 116
{
	struct nvkm_perfsrc *src;
	bool found = false;
	int tmp = 1; /* Sources ID start from 1 */
	u8 i;

	for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) {
		if (sig->source[i] == si) {
			found = true;
			break;
		}
	}

	if (found) {
B
Ben Skeggs 已提交
117
		list_for_each_entry(src, &pm->sources, head) {
118 119 120 121 122 123 124 125
			if (tmp++ == si)
				return src;
		}
	}

	return NULL;
}

126
static int
B
Ben Skeggs 已提交
127
nvkm_perfsrc_enable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
128
{
129 130
	struct nvkm_subdev *subdev = &pm->engine.subdev;
	struct nvkm_device *device = subdev->device;
131 132 133 134 135 136
	struct nvkm_perfdom *dom = NULL;
	struct nvkm_perfsig *sig;
	struct nvkm_perfsrc *src;
	u32 mask, value;
	int i, j;

137
	for (i = 0; i < 4; i++) {
138
		for (j = 0; j < 8 && ctr->source[i][j]; j++) {
B
Ben Skeggs 已提交
139
			sig = nvkm_perfsig_find(pm, ctr->domain,
140 141 142 143
						ctr->signal[i], &dom);
			if (!sig)
				return -EINVAL;

B
Ben Skeggs 已提交
144
			src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
145 146 147 148 149 150 151 152 153 154 155
			if (!src)
				return -EINVAL;

			/* set enable bit if needed */
			mask = value = 0x00000000;
			if (src->enable)
				mask = value = 0x80000000;
			mask  |= (src->mask << src->shift);
			value |= ((ctr->source[i][j] >> 32) << src->shift);

			/* enable the source */
156
			nvkm_mask(device, src->addr, mask, value);
157 158 159
			nvkm_debug(subdev,
				   "enabled source %08x %08x %08x\n",
				   src->addr, mask, value);
160 161 162 163 164 165
		}
	}
	return 0;
}

static int
B
Ben Skeggs 已提交
166
nvkm_perfsrc_disable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
167
{
168 169
	struct nvkm_subdev *subdev = &pm->engine.subdev;
	struct nvkm_device *device = subdev->device;
170 171 172
	struct nvkm_perfdom *dom = NULL;
	struct nvkm_perfsig *sig;
	struct nvkm_perfsrc *src;
173
	u32 mask;
174 175
	int i, j;

176
	for (i = 0; i < 4; i++) {
177
		for (j = 0; j < 8 && ctr->source[i][j]; j++) {
B
Ben Skeggs 已提交
178
			sig = nvkm_perfsig_find(pm, ctr->domain,
179 180 181 182
						ctr->signal[i], &dom);
			if (!sig)
				return -EINVAL;

B
Ben Skeggs 已提交
183
			src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
184 185 186
			if (!src)
				return -EINVAL;

187 188 189 190 191 192
			/* unset enable bit if needed */
			mask = 0x00000000;
			if (src->enable)
				mask = 0x80000000;
			mask |= (src->mask << src->shift);

193
			/* disable the source */
194
			nvkm_mask(device, src->addr, mask, 0);
195 196
			nvkm_debug(subdev, "disabled source %08x %08x\n",
				   src->addr, mask);
197 198 199 200 201
		}
	}
	return 0;
}

202
/*******************************************************************************
203
 * Perfdom object classes
204
 ******************************************************************************/
205
static int
206
nvkm_perfdom_init(struct nvkm_perfdom *dom, void *data, u32 size)
207 208
{
	union {
209
		struct nvif_perfdom_init none;
210
	} *args = data;
211 212
	struct nvkm_object *object = &dom->object;
	struct nvkm_pm *pm = dom->perfmon->pm;
213
	int ret, i;
214

215
	nvif_ioctl(object, "perfdom init size %d\n", size);
216
	if (nvif_unvers(args->none)) {
217
		nvif_ioctl(object, "perfdom init\n");
218 219 220
	} else
		return ret;

221 222
	for (i = 0; i < 4; i++) {
		if (dom->ctr[i]) {
B
Ben Skeggs 已提交
223
			dom->func->init(pm, dom, dom->ctr[i]);
224

225
			/* enable sources */
B
Ben Skeggs 已提交
226
			nvkm_perfsrc_enable(pm, dom->ctr[i]);
227 228 229
		}
	}

230
	/* start next batch of counters for sampling */
B
Ben Skeggs 已提交
231
	dom->func->next(pm, dom);
232 233 234
	return 0;
}

235
static int
236
nvkm_perfdom_sample(struct nvkm_perfdom *dom, void *data, u32 size)
237
{
238
	union {
239
		struct nvif_perfdom_sample none;
240
	} *args = data;
241 242
	struct nvkm_object *object = &dom->object;
	struct nvkm_pm *pm = dom->perfmon->pm;
243
	int ret;
244

245
	nvif_ioctl(object, "perfdom sample size %d\n", size);
246
	if (nvif_unvers(args->none)) {
247
		nvif_ioctl(object, "perfdom sample\n");
248 249
	} else
		return ret;
B
Ben Skeggs 已提交
250
	pm->sequence++;
251

252
	/* sample previous batch of counters */
B
Ben Skeggs 已提交
253 254
	list_for_each_entry(dom, &pm->domains, head)
		dom->func->next(pm, dom);
255 256 257 258 259

	return 0;
}

static int
260
nvkm_perfdom_read(struct nvkm_perfdom *dom, void *data, u32 size)
261
{
262
	union {
263
		struct nvif_perfdom_read_v0 v0;
264
	} *args = data;
265 266
	struct nvkm_object *object = &dom->object;
	struct nvkm_pm *pm = dom->perfmon->pm;
267
	int ret, i;
268

269
	nvif_ioctl(object, "perfdom read size %d\n", size);
270
	if (nvif_unpack(args->v0, 0, 0, false)) {
271
		nvif_ioctl(object, "perfdom read vers %d\n", args->v0.version);
272 273
	} else
		return ret;
274

275 276
	for (i = 0; i < 4; i++) {
		if (dom->ctr[i])
B
Ben Skeggs 已提交
277
			dom->func->read(pm, dom, dom->ctr[i]);
278 279 280
	}

	if (!dom->clk)
281 282
		return -EAGAIN;

283 284 285 286
	for (i = 0; i < 4; i++)
		if (dom->ctr[i])
			args->v0.ctr[i] = dom->ctr[i]->ctr;
	args->v0.clk = dom->clk;
287 288 289
	return 0;
}

290
static int
291
nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
292
{
293
	struct nvkm_perfdom *dom = nvkm_perfdom(object);
294
	switch (mthd) {
295
	case NVIF_PERFDOM_V0_INIT:
296
		return nvkm_perfdom_init(dom, data, size);
297
	case NVIF_PERFDOM_V0_SAMPLE:
298
		return nvkm_perfdom_sample(dom, data, size);
299
	case NVIF_PERFDOM_V0_READ:
300
		return nvkm_perfdom_read(dom, data, size);
301 302 303 304 305 306
	default:
		break;
	}
	return -EINVAL;
}

307
static void *
308
nvkm_perfdom_dtor(struct nvkm_object *object)
309
{
310
	struct nvkm_perfdom *dom = nvkm_perfdom(object);
311
	struct nvkm_pm *pm = dom->perfmon->pm;
312 313 314 315
	int i;

	for (i = 0; i < 4; i++) {
		struct nvkm_perfctr *ctr = dom->ctr[i];
316
		if (ctr) {
B
Ben Skeggs 已提交
317
			nvkm_perfsrc_disable(pm, ctr);
318 319 320
			if (ctr->head.next)
				list_del(&ctr->head);
		}
321 322
		kfree(ctr);
	}
323 324

	return dom;
325 326 327
}

static int
B
Ben Skeggs 已提交
328 329 330
nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot, u8 domain,
		 struct nvkm_perfsig *signal[4], u64 source[4][8],
		 u16 logic_op, struct nvkm_perfctr **pctr)
331 332
{
	struct nvkm_perfctr *ctr;
333
	int i, j;
334 335 336 337 338 339 340 341

	if (!dom)
		return -EINVAL;

	ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
	if (!ctr)
		return -ENOMEM;

342
	ctr->domain   = domain;
343 344 345
	ctr->logic_op = logic_op;
	ctr->slot     = slot;
	for (i = 0; i < 4; i++) {
346
		if (signal[i]) {
347
			ctr->signal[i] = signal[i] - dom->signal;
348 349 350
			for (j = 0; j < 8; j++)
				ctr->source[i][j] = source[i][j];
		}
351 352 353 354 355 356
	}
	list_add_tail(&ctr->head, &dom->list);

	return 0;
}

357 358 359 360 361 362
static const struct nvkm_object_func
nvkm_perfdom = {
	.dtor = nvkm_perfdom_dtor,
	.mthd = nvkm_perfdom_mthd,
};

363
static int
364 365
nvkm_perfdom_new_(struct nvkm_perfmon *perfmon,
		  const struct nvkm_oclass *oclass, void *data, u32 size,
366
		  struct nvkm_object **pobject)
367
{
368
	union {
369
		struct nvif_perfdom_v0 v0;
370
	} *args = data;
371 372
	struct nvkm_pm *pm = perfmon->pm;
	struct nvkm_object *parent = oclass->parent;
373 374 375
	struct nvkm_perfdom *sdom = NULL;
	struct nvkm_perfctr *ctr[4] = {};
	struct nvkm_perfdom *dom;
376
	int c, s, m;
377
	int ret;
378

379
	nvif_ioctl(parent, "create perfdom size %d\n", size);
380
	if (nvif_unpack(args->v0, 0, 0, false)) {
381 382
		nvif_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
			   args->v0.version, args->v0.domain, args->v0.mode);
383 384
	} else
		return ret;
385

386 387
	for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
		struct nvkm_perfsig *sig[4] = {};
388
		u64 src[4][8] = {};
389

390
		for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
B
Ben Skeggs 已提交
391
			sig[s] = nvkm_perfsig_find(pm, args->v0.domain,
392 393 394 395
						   args->v0.ctr[c].signal[s],
						   &sdom);
			if (args->v0.ctr[c].signal[s] && !sig[s])
				return -EINVAL;
396 397 398

			for (m = 0; m < 8; m++) {
				src[s][m] = args->v0.ctr[c].source[s][m];
B
Ben Skeggs 已提交
399
				if (src[s][m] && !nvkm_perfsrc_find(pm, sig[s],
400 401 402
							            src[s][m]))
					return -EINVAL;
			}
403 404
		}

405
		ret = nvkm_perfctr_new(sdom, c, args->v0.domain, sig, src,
406 407 408
				       args->v0.ctr[c].logic_op, &ctr[c]);
		if (ret)
			return ret;
409 410
	}

411
	if (!sdom)
412 413
		return -EINVAL;

414 415 416 417 418
	if (!(dom = kzalloc(sizeof(*dom), GFP_KERNEL)))
		return -ENOMEM;
	nvkm_object_ctor(&nvkm_perfdom, oclass, &dom->object);
	dom->perfmon = perfmon;
	*pobject = &dom->object;
419

420 421 422 423 424
	dom->func = sdom->func;
	dom->addr = sdom->addr;
	dom->mode = args->v0.mode;
	for (c = 0; c < ARRAY_SIZE(ctr); c++)
		dom->ctr[c] = ctr[c];
425 426 427
	return 0;
}

428 429 430 431
/*******************************************************************************
 * Perfmon object classes
 ******************************************************************************/
static int
432 433
nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon,
			       void *data, u32 size)
434 435 436 437
{
	union {
		struct nvif_perfmon_query_domain_v0 v0;
	} *args = data;
438 439
	struct nvkm_object *object = &perfmon->object;
	struct nvkm_pm *pm = perfmon->pm;
440 441 442 443
	struct nvkm_perfdom *dom;
	u8 domain_nr;
	int di, ret;

444
	nvif_ioctl(object, "perfmon query domain size %d\n", size);
445
	if (nvif_unpack(args->v0, 0, 0, false)) {
446 447
		nvif_ioctl(object, "perfmon domain vers %d iter %02x\n",
			   args->v0.version, args->v0.iter);
448 449 450 451
		di = (args->v0.iter & 0xff) - 1;
	} else
		return ret;

B
Ben Skeggs 已提交
452
	domain_nr = nvkm_pm_count_perfdom(pm);
453 454 455 456
	if (di >= (int)domain_nr)
		return -EINVAL;

	if (di >= 0) {
B
Ben Skeggs 已提交
457
		dom = nvkm_perfdom_find(pm, di);
458 459 460 461 462
		if (dom == NULL)
			return -EINVAL;

		args->v0.id         = di;
		args->v0.signal_nr  = nvkm_perfdom_count_perfsig(dom);
463
		strncpy(args->v0.name, dom->name, sizeof(args->v0.name));
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

		/* Currently only global counters (PCOUNTER) are implemented
		 * but this will be different for local counters (MP). */
		args->v0.counter_nr = 4;
	}

	if (++di < domain_nr) {
		args->v0.iter = ++di;
		return 0;
	}

	args->v0.iter = 0xff;
	return 0;
}

static int
480 481
nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon,
			       void *data, u32 size)
482 483 484 485
{
	union {
		struct nvif_perfmon_query_signal_v0 v0;
	} *args = data;
486 487 488
	struct nvkm_object *object = &perfmon->object;
	struct nvkm_pm *pm = perfmon->pm;
	struct nvkm_device *device = pm->engine.subdev.device;
489 490 491 492 493 494
	struct nvkm_perfdom *dom;
	struct nvkm_perfsig *sig;
	const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
	const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all);
	int ret, si;

495
	nvif_ioctl(object, "perfmon query signal size %d\n", size);
496
	if (nvif_unpack(args->v0, 0, 0, false)) {
497 498 499
		nvif_ioctl(object,
			   "perfmon query signal vers %d dom %d iter %04x\n",
			   args->v0.version, args->v0.domain, args->v0.iter);
500 501 502 503
		si = (args->v0.iter & 0xffff) - 1;
	} else
		return ret;

B
Ben Skeggs 已提交
504
	dom = nvkm_perfdom_find(pm, args->v0.domain);
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
	if (dom == NULL || si >= (int)dom->signal_nr)
		return -EINVAL;

	if (si >= 0) {
		sig = &dom->signal[si];
		if (raw || !sig->name) {
			snprintf(args->v0.name, sizeof(args->v0.name),
				 "/%s/%02x", dom->name, si);
		} else {
			strncpy(args->v0.name, sig->name,
				sizeof(args->v0.name));
		}

		args->v0.signal = si;
		args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
	}

	while (++si < dom->signal_nr) {
		if (all || dom->signal[si].name) {
			args->v0.iter = ++si;
			return 0;
		}
	}

	args->v0.iter = 0xffff;
	return 0;
}

static int
534 535
nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon,
			       void *data, u32 size)
536 537 538 539
{
	union {
		struct nvif_perfmon_query_source_v0 v0;
	} *args = data;
540 541
	struct nvkm_object *object = &perfmon->object;
	struct nvkm_pm *pm = perfmon->pm;
542 543 544 545 546 547
	struct nvkm_perfdom *dom = NULL;
	struct nvkm_perfsig *sig;
	struct nvkm_perfsrc *src;
	u8 source_nr = 0;
	int si, ret;

548
	nvif_ioctl(object, "perfmon query source size %d\n", size);
549
	if (nvif_unpack(args->v0, 0, 0, false)) {
550 551 552 553
		nvif_ioctl(object,
			   "perfmon source vers %d dom %d sig %02x iter %02x\n",
			   args->v0.version, args->v0.domain, args->v0.signal,
			   args->v0.iter);
554 555 556 557
		si = (args->v0.iter & 0xff) - 1;
	} else
		return ret;

B
Ben Skeggs 已提交
558
	sig = nvkm_perfsig_find(pm, args->v0.domain, args->v0.signal, &dom);
559 560 561 562 563 564 565 566
	if (!sig)
		return -EINVAL;

	source_nr = nvkm_perfsig_count_perfsrc(sig);
	if (si >= (int)source_nr)
		return -EINVAL;

	if (si >= 0) {
B
Ben Skeggs 已提交
567
		src = nvkm_perfsrc_find(pm, sig, sig->source[si]);
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
		if (!src)
			return -EINVAL;

		args->v0.source = sig->source[si];
		args->v0.mask   = src->mask;
		strncpy(args->v0.name, src->name, sizeof(args->v0.name));
	}

	if (++si < source_nr) {
		args->v0.iter = ++si;
		return 0;
	}

	args->v0.iter = 0xff;
	return 0;
}

static int
nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
588
	struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
589 590
	switch (mthd) {
	case NVIF_PERFMON_V0_QUERY_DOMAIN:
591
		return nvkm_perfmon_mthd_query_domain(perfmon, data, size);
592
	case NVIF_PERFMON_V0_QUERY_SIGNAL:
593
		return nvkm_perfmon_mthd_query_signal(perfmon, data, size);
594
	case NVIF_PERFMON_V0_QUERY_SOURCE:
595
		return nvkm_perfmon_mthd_query_source(perfmon, data, size);
596 597 598 599 600 601
	default:
		break;
	}
	return -EINVAL;
}

602 603 604 605 606 607 608
static int
nvkm_perfmon_child_new(const struct nvkm_oclass *oclass, void *data, u32 size,
		       struct nvkm_object **pobject)
{
	struct nvkm_perfmon *perfmon = nvkm_perfmon(oclass->parent);
	return nvkm_perfdom_new_(perfmon, oclass, data, size, pobject);
}
609 610

static int
611
nvkm_perfmon_child_get(struct nvkm_object *object, int index,
612
		       struct nvkm_oclass *oclass)
613
{
614 615 616 617 618 619 620 621
	if (index == 0) {
		oclass->base.oclass = NVIF_IOCTL_NEW_V0_PERFDOM;
		oclass->base.minver = 0;
		oclass->base.maxver = 0;
		oclass->ctor = nvkm_perfmon_child_new;
		return 0;
	}
	return -EINVAL;
622 623
}

624
static void *
625
nvkm_perfmon_dtor(struct nvkm_object *object)
626
{
627
	struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
628 629 630 631 632 633 634
	struct nvkm_pm *pm = perfmon->pm;
	mutex_lock(&pm->engine.subdev.mutex);
	if (pm->perfmon == &perfmon->object)
		pm->perfmon = NULL;
	mutex_unlock(&pm->engine.subdev.mutex);
	return perfmon;
}
635

636 637 638 639 640
static struct nvkm_object_func
nvkm_perfmon = {
	.dtor = nvkm_perfmon_dtor,
	.mthd = nvkm_perfmon_mthd,
	.sclass = nvkm_perfmon_child_get,
641 642
};

643 644 645
static int
nvkm_perfmon_new(struct nvkm_pm *pm, const struct nvkm_oclass *oclass,
		 void *data, u32 size, struct nvkm_object **pobject)
646
{
647
	struct nvkm_perfmon *perfmon;
648

649 650 651 652 653 654
	if (!(perfmon = kzalloc(sizeof(*perfmon), GFP_KERNEL)))
		return -ENOMEM;
	nvkm_object_ctor(&nvkm_perfmon, oclass, &perfmon->object);
	perfmon->pm = pm;
	*pobject = &perfmon->object;
	return 0;
655 656
}

657 658 659 660
/*******************************************************************************
 * PPM engine/subdev functions
 ******************************************************************************/

661
static int
662 663
nvkm_pm_oclass_new(struct nvkm_device *device, const struct nvkm_oclass *oclass,
		   void *data, u32 size, struct nvkm_object **pobject)
664
{
665
	struct nvkm_pm *pm = nvkm_pm(oclass->engine);
666 667
	int ret;

668
	ret = nvkm_perfmon_new(pm, oclass, data, size, pobject);
669 670 671
	if (ret)
		return ret;

672 673 674 675 676
	mutex_lock(&pm->engine.subdev.mutex);
	if (pm->perfmon == NULL)
		pm->perfmon = *pobject;
	ret = (pm->perfmon == *pobject) ? 0 : -EBUSY;
	mutex_unlock(&pm->engine.subdev.mutex);
677
	return ret;
678 679
}

680 681 682 683 684 685
static const struct nvkm_device_oclass
nvkm_pm_oclass = {
	.base.oclass = NVIF_IOCTL_NEW_V0_PERFMON,
	.base.minver = -1,
	.base.maxver = -1,
	.ctor = nvkm_pm_oclass_new,
686 687
};

688 689 690 691 692 693 694 695 696 697 698 699
static int
nvkm_pm_oclass_get(struct nvkm_oclass *oclass, int index,
		   const struct nvkm_device_oclass **class)
{
	if (index == 0) {
		oclass->base = nvkm_pm_oclass.base;
		*class = &nvkm_pm_oclass;
		return index;
	}
	return 1;
}

700
int
B
Ben Skeggs 已提交
701
nvkm_perfsrc_new(struct nvkm_pm *pm, struct nvkm_perfsig *sig,
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
		 const struct nvkm_specsrc *spec)
{
	const struct nvkm_specsrc *ssrc;
	const struct nvkm_specmux *smux;
	struct nvkm_perfsrc *src;
	u8 source_nr = 0;

	if (!spec) {
		/* No sources are defined for this signal. */
		return 0;
	}

	ssrc = spec;
	while (ssrc->name) {
		smux = ssrc->mux;
		while (smux->name) {
			bool found = false;
			u8 source_id = 0;
			u32 len;

B
Ben Skeggs 已提交
722
			list_for_each_entry(src, &pm->sources, head) {
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
				if (src->addr == ssrc->addr &&
				    src->shift == smux->shift) {
					found = true;
					break;
				}
				source_id++;
			}

			if (!found) {
				src = kzalloc(sizeof(*src), GFP_KERNEL);
				if (!src)
					return -ENOMEM;

				src->addr   = ssrc->addr;
				src->mask   = smux->mask;
				src->shift  = smux->shift;
				src->enable = smux->enable;

				len = strlen(ssrc->name) +
				      strlen(smux->name) + 2;
				src->name = kzalloc(len, GFP_KERNEL);
B
Ben Skeggs 已提交
744 745
				if (!src->name) {
					kfree(src);
746
					return -ENOMEM;
B
Ben Skeggs 已提交
747
				}
748 749 750
				snprintf(src->name, len, "%s_%s", ssrc->name,
					 smux->name);

B
Ben Skeggs 已提交
751
				list_add_tail(&src->head, &pm->sources);
752 753 754 755 756 757 758 759 760 761 762
			}

			sig->source[source_nr++] = source_id + 1;
			smux++;
		}
		ssrc++;
	}

	return 0;
}

763
int
B
Ben Skeggs 已提交
764
nvkm_perfdom_new(struct nvkm_pm *pm, const char *name, u32 mask,
765 766
		 u32 base, u32 size_unit, u32 size_domain,
		 const struct nvkm_specdom *spec)
767
{
768 769 770
	const struct nvkm_specdom *sdom;
	const struct nvkm_specsig *ssig;
	struct nvkm_perfdom *dom;
771
	int ret, i;
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793

	for (i = 0; i == 0 || mask; i++) {
		u32 addr = base + (i * size_unit);
		if (i && !(mask & (1 << i)))
			continue;

		sdom = spec;
		while (sdom->signal_nr) {
			dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
				      sizeof(*dom->signal), GFP_KERNEL);
			if (!dom)
				return -ENOMEM;

			if (mask) {
				snprintf(dom->name, sizeof(dom->name),
					 "%s/%02x/%02x", name, i,
					 (int)(sdom - spec));
			} else {
				snprintf(dom->name, sizeof(dom->name),
					 "%s/%02x", name, (int)(sdom - spec));
			}

B
Ben Skeggs 已提交
794
			list_add_tail(&dom->head, &pm->domains);
795 796 797 798 799 800 801
			INIT_LIST_HEAD(&dom->list);
			dom->func = sdom->func;
			dom->addr = addr;
			dom->signal_nr = sdom->signal_nr;

			ssig = (sdom++)->signal;
			while (ssig->name) {
802 803 804
				struct nvkm_perfsig *sig =
					&dom->signal[ssig->signal];
				sig->name = ssig->name;
B
Ben Skeggs 已提交
805
				ret = nvkm_perfsrc_new(pm, sig, ssig->source);
806 807
				if (ret)
					return ret;
808 809 810 811 812 813 814 815 816 817 818 819
				ssig++;
			}

			addr += size_domain;
		}

		mask &= ~(1 << i);
	}

	return 0;
}

820 821
static int
nvkm_pm_fini(struct nvkm_engine *engine, bool suspend)
822
{
823 824 825 826
	struct nvkm_pm *pm = nvkm_pm(engine);
	if (pm->func->fini)
		pm->func->fini(pm);
	return 0;
827 828
}

829 830
static void *
nvkm_pm_dtor(struct nvkm_engine *engine)
831
{
832
	struct nvkm_pm *pm = nvkm_pm(engine);
833 834
	struct nvkm_perfdom *dom, *next_dom;
	struct nvkm_perfsrc *src, *next_src;
835

B
Ben Skeggs 已提交
836
	list_for_each_entry_safe(dom, next_dom, &pm->domains, head) {
837 838 839 840
		list_del(&dom->head);
		kfree(dom);
	}

B
Ben Skeggs 已提交
841
	list_for_each_entry_safe(src, next_src, &pm->sources, head) {
842 843 844 845 846
		list_del(&src->head);
		kfree(src->name);
		kfree(src);
	}

847
	return pm;
848 849
}

850 851
static const struct nvkm_engine_func
nvkm_pm = {
852 853
	.dtor = nvkm_pm_dtor,
	.fini = nvkm_pm_fini,
854 855 856
	.base.sclass = nvkm_pm_oclass_get,
};

857
int
858 859
nvkm_pm_ctor(const struct nvkm_pm_func *func, struct nvkm_device *device,
	     int index, struct nvkm_pm *pm)
860
{
861
	pm->func = func;
B
Ben Skeggs 已提交
862 863
	INIT_LIST_HEAD(&pm->domains);
	INIT_LIST_HEAD(&pm->sources);
864
	return nvkm_engine_ctor(&nvkm_pm, device, index, 0, true, &pm->engine);
865
}