base.c 20.2 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
#include <nvif/class.h>
30 31
#include <nvif/if0002.h>
#include <nvif/if0003.h>
32
#include <nvif/ioctl.h>
33
#include <nvif/unpack.h>
34

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

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

46
static u16
47 48
nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
{
49
	u16 signal_nr = 0;
50 51 52 53 54 55 56 57 58 59 60 61
	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 已提交
62
nvkm_perfdom_find(struct nvkm_pm *pm, int di)
63 64 65 66
{
	struct nvkm_perfdom *dom;
	int tmp = 0;

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

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

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

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

91 92 93 94 95 96 97 98 99 100 101 102
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;
}

103
static struct nvkm_perfsrc *
B
Ben Skeggs 已提交
104
nvkm_perfsrc_find(struct nvkm_pm *pm, struct nvkm_perfsig *sig, int si)
105 106 107 108 109 110 111 112 113 114 115 116 117 118
{
	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 已提交
119
		list_for_each_entry(src, &pm->sources, head) {
120 121 122 123 124 125 126 127
			if (tmp++ == si)
				return src;
		}
	}

	return NULL;
}

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

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

B
Ben Skeggs 已提交
146
			src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
147 148 149 150 151 152 153 154 155 156 157
			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 */
158
			nvkm_mask(device, src->addr, mask, value);
159 160 161
			nvkm_debug(subdev,
				   "enabled source %08x %08x %08x\n",
				   src->addr, mask, value);
162 163 164 165 166 167
		}
	}
	return 0;
}

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

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

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

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

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

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

217
	nvif_ioctl(object, "perfdom init size %d\n", size);
218
	if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
219
		nvif_ioctl(object, "perfdom init\n");
220 221 222
	} else
		return ret;

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

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

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

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

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

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

	return 0;
}

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

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

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

	if (!dom->clk)
283 284
		return -EAGAIN;

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

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

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

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

	return dom;
327 328 329
}

static int
B
Ben Skeggs 已提交
330 331 332
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)
333 334
{
	struct nvkm_perfctr *ctr;
335
	int i, j;
336 337 338 339 340 341 342 343

	if (!dom)
		return -EINVAL;

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

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

	return 0;
}

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

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

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

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

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

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

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

413
	if (!sdom)
414 415
		return -EINVAL;

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

422 423 424 425 426
	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];
427 428 429
	return 0;
}

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

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

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

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

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

		/* 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
482 483
nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon,
			       void *data, u32 size)
484 485 486 487
{
	union {
		struct nvif_perfmon_query_signal_v0 v0;
	} *args = data;
488 489 490
	struct nvkm_object *object = &perfmon->object;
	struct nvkm_pm *pm = perfmon->pm;
	struct nvkm_device *device = pm->engine.subdev.device;
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);
495
	int ret = -ENOSYS, si;
496

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

B
Ben Skeggs 已提交
506
	dom = nvkm_perfdom_find(pm, args->v0.domain);
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 534 535
	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
536 537
nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon,
			       void *data, u32 size)
538 539 540 541
{
	union {
		struct nvif_perfmon_query_source_v0 v0;
	} *args = data;
542 543
	struct nvkm_object *object = &perfmon->object;
	struct nvkm_pm *pm = perfmon->pm;
544 545 546 547
	struct nvkm_perfdom *dom = NULL;
	struct nvkm_perfsig *sig;
	struct nvkm_perfsrc *src;
	u8 source_nr = 0;
548
	int si, ret = -ENOSYS;
549

550
	nvif_ioctl(object, "perfmon query source size %d\n", size);
551
	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
552 553 554 555
		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);
556 557 558 559
		si = (args->v0.iter & 0xff) - 1;
	} else
		return ret;

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

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

	if (si >= 0) {
B
Ben Skeggs 已提交
569
		src = nvkm_perfsrc_find(pm, sig, sig->source[si]);
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
		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)
{
590
	struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
591 592
	switch (mthd) {
	case NVIF_PERFMON_V0_QUERY_DOMAIN:
593
		return nvkm_perfmon_mthd_query_domain(perfmon, data, size);
594
	case NVIF_PERFMON_V0_QUERY_SIGNAL:
595
		return nvkm_perfmon_mthd_query_signal(perfmon, data, size);
596
	case NVIF_PERFMON_V0_QUERY_SOURCE:
597
		return nvkm_perfmon_mthd_query_source(perfmon, data, size);
598 599 600 601 602 603
	default:
		break;
	}
	return -EINVAL;
}

604 605 606 607 608 609 610
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);
}
611 612

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

626
static void *
627
nvkm_perfmon_dtor(struct nvkm_object *object)
628
{
629
	struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
630 631 632 633 634 635 636
	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;
}
637

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

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

651 652 653 654 655 656
	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;
657 658
}

659 660 661 662
/*******************************************************************************
 * PPM engine/subdev functions
 ******************************************************************************/

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

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

674 675 676 677 678
	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);
679
	return ret;
680 681
}

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

690 691 692 693 694 695 696 697 698 699 700 701
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;
}

702
int
B
Ben Skeggs 已提交
703
nvkm_perfsrc_new(struct nvkm_pm *pm, struct nvkm_perfsig *sig,
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
		 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 已提交
724
			list_for_each_entry(src, &pm->sources, head) {
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
				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 已提交
746 747
				if (!src->name) {
					kfree(src);
748
					return -ENOMEM;
B
Ben Skeggs 已提交
749
				}
750 751 752
				snprintf(src->name, len, "%s_%s", ssrc->name,
					 smux->name);

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

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

	return 0;
}

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

	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 已提交
796
			list_add_tail(&dom->head, &pm->domains);
797 798 799 800 801 802 803
			INIT_LIST_HEAD(&dom->list);
			dom->func = sdom->func;
			dom->addr = addr;
			dom->signal_nr = sdom->signal_nr;

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

			addr += size_domain;
		}

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

	return 0;
}

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

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

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

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

849
	return pm;
850 851
}

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

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