base.c 16.8 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/device.h>
28
#include <core/option.h>
29

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

34 35 36 37 38 39 40 41 42 43 44
static u8
nvkm_pm_count_perfdom(struct nvkm_pm *ppm)
{
	struct nvkm_perfdom *dom;
	u8 domain_nr = 0;

	list_for_each_entry(dom, &ppm->domains, head)
		domain_nr++;
	return domain_nr;
}

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

	list_for_each_entry(dom, &ppm->domains, head) {
		if (tmp++ == di)
			return dom;
	}
	return NULL;
}

73
struct nvkm_perfsig *
74
nvkm_perfsig_find(struct nvkm_pm *ppm, uint8_t di, uint8_t si,
75
		  struct nvkm_perfdom **pdom)
76
{
77
	struct nvkm_perfdom *dom = *pdom;
78 79

	if (dom == NULL) {
80 81 82 83
		dom = nvkm_perfdom_find(ppm, di);
		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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static struct nvkm_perfsrc *
nvkm_perfsrc_find(struct nvkm_pm *ppm, struct nvkm_perfsig *sig, int si)
{
	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) {
		list_for_each_entry(src, &ppm->sources, head) {
			if (tmp++ == si)
				return src;
		}
	}

	return NULL;
}

128 129 130
/*******************************************************************************
 * Perfmon object classes
 ******************************************************************************/
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
static int
nvkm_perfmon_mthd_query_domain(struct nvkm_object *object, void *data, u32 size)
{
	union {
		struct nvif_perfmon_query_domain_v0 v0;
	} *args = data;
	struct nvkm_pm *ppm = (void *)object->engine;
	struct nvkm_perfdom *dom;
	u8 domain_nr;
	int di, ret;

	nv_ioctl(object, "perfmon query domain size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_ioctl(object, "perfmon domain vers %d iter %02x\n",
			 args->v0.version, args->v0.iter);
		di = (args->v0.iter & 0xff) - 1;
	} else
		return ret;

	domain_nr = nvkm_pm_count_perfdom(ppm);
	if (di >= (int)domain_nr)
		return -EINVAL;

	if (di >= 0) {
		dom = nvkm_perfdom_find(ppm, di);
		if (dom == NULL)
			return -EINVAL;

		args->v0.id         = di;
		args->v0.signal_nr  = nvkm_perfdom_count_perfsig(dom);

		/* 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;
}

176
static int
177
nvkm_perfmon_mthd_query_signal(struct nvkm_object *object, void *data, u32 size)
178
{
179
	union {
180
		struct nvif_perfmon_query_signal_v0 v0;
181
	} *args = data;
182 183
	struct nvkm_device *device = nv_device(object);
	struct nvkm_pm *ppm = (void *)object->engine;
184
	struct nvkm_perfdom *dom;
185
	struct nvkm_perfsig *sig;
186 187
	const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
	const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all);
188
	int ret, si;
189

190
	nv_ioctl(object, "perfmon query signal size %d\n", size);
191
	if (nvif_unpack(args->v0, 0, 0, false)) {
192
		nv_ioctl(object,
193
			 "perfmon query signal vers %d dom %d iter %04x\n",
194
			 args->v0.version, args->v0.domain, args->v0.iter);
195
		si = (args->v0.iter & 0xffff) - 1;
196 197
	} else
		return ret;
198

199
	dom = nvkm_perfdom_find(ppm, args->v0.domain);
200 201 202 203
	if (dom == NULL || si >= (int)dom->signal_nr)
		return -EINVAL;

	if (si >= 0) {
204 205
		sig = &dom->signal[si];
		if (raw || !sig->name) {
206 207 208
			snprintf(args->v0.name, sizeof(args->v0.name),
				 "/%s/%02x", dom->name, si);
		} else {
209 210
			strncpy(args->v0.name, sig->name,
				sizeof(args->v0.name));
211
		}
212

213
		args->v0.signal = si;
214
		args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
215 216
	}

217 218 219 220
	while (++si < dom->signal_nr) {
		if (all || dom->signal[si].name) {
			args->v0.iter = ++si;
			return 0;
221
		}
222
	}
223

224
	args->v0.iter = 0xffff;
225 226 227
	return 0;
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
static int
nvkm_perfmon_mthd_query_source(struct nvkm_object *object, void *data, u32 size)
{
	union {
		struct nvif_perfmon_query_source_v0 v0;
	} *args = data;
	struct nvkm_pm *ppm = (void *)object->engine;
	struct nvkm_perfdom *dom = NULL;
	struct nvkm_perfsig *sig;
	struct nvkm_perfsrc *src;
	u8 source_nr = 0;
	int si, ret;

	nv_ioctl(object, "perfmon query source size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_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);
		si = (args->v0.iter & 0xff) - 1;
	} else
		return ret;

	sig = nvkm_perfsig_find(ppm, args->v0.domain, args->v0.signal, &dom);
	if (!sig)
		return -EINVAL;

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

	if (si >= 0) {
		src = nvkm_perfsrc_find(ppm, sig, sig->source[si]);
		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;
}

278 279 280 281
static int
nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
	switch (mthd) {
282 283
	case NVIF_PERFMON_V0_QUERY_DOMAIN:
		return nvkm_perfmon_mthd_query_domain(object, data, size);
284 285
	case NVIF_PERFMON_V0_QUERY_SIGNAL:
		return nvkm_perfmon_mthd_query_signal(object, data, size);
286 287
	case NVIF_PERFMON_V0_QUERY_SOURCE:
		return nvkm_perfmon_mthd_query_source(object, data, size);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	default:
		break;
	}
	return -EINVAL;
}

static struct nvkm_ofuncs
nvkm_perfmon_ofuncs = {
	.ctor = _nvkm_object_ctor,
	.dtor = nvkm_object_destroy,
	.init = nvkm_object_init,
	.fini = nvkm_object_fini,
	.mthd = nvkm_perfmon_mthd,
};

/*******************************************************************************
304
 * Perfdom object classes
305
 ******************************************************************************/
306
static int
307
nvkm_perfdom_init(struct nvkm_object *object, void *data, u32 size)
308 309
{
	union {
310
		struct nvif_perfdom_init none;
311 312
	} *args = data;
	struct nvkm_pm *ppm = (void *)object->engine;
313 314
	struct nvkm_perfdom *dom = (void *)object;
	int ret, i;
315

316
	nv_ioctl(object, "perfdom init size %d\n", size);
317
	if (nvif_unvers(args->none)) {
318
		nv_ioctl(object, "perfdom init\n");
319 320 321
	} else
		return ret;

322 323 324
	for (i = 0; i < 4; i++)
		if (dom->ctr[i])
			dom->func->init(ppm, dom, dom->ctr[i]);
325 326 327 328 329 330

	/* start next batch of counters for sampling */
	dom->func->next(ppm, dom);
	return 0;
}

331
static int
332
nvkm_perfdom_sample(struct nvkm_object *object, void *data, u32 size)
333
{
334
	union {
335
		struct nvif_perfdom_sample none;
336
	} *args = data;
337 338
	struct nvkm_pm *ppm = (void *)object->engine;
	struct nvkm_perfdom *dom;
339
	int ret;
340

341
	nv_ioctl(object, "perfdom sample size %d\n", size);
342
	if (nvif_unvers(args->none)) {
343
		nv_ioctl(object, "perfdom sample\n");
344 345
	} else
		return ret;
346 347
	ppm->sequence++;

348 349 350
	/* sample previous batch of counters */
	list_for_each_entry(dom, &ppm->domains, head)
		dom->func->next(ppm, dom);
351 352 353 354 355

	return 0;
}

static int
356
nvkm_perfdom_read(struct nvkm_object *object, void *data, u32 size)
357
{
358
	union {
359
		struct nvif_perfdom_read_v0 v0;
360
	} *args = data;
361 362 363
	struct nvkm_pm *ppm = (void *)object->engine;
	struct nvkm_perfdom *dom = (void *)object;
	int ret, i;
364

365
	nv_ioctl(object, "perfdom read size %d\n", size);
366
	if (nvif_unpack(args->v0, 0, 0, false)) {
367
		nv_ioctl(object, "perfdom read vers %d\n", args->v0.version);
368 369
	} else
		return ret;
370

371 372 373 374 375 376
	for (i = 0; i < 4; i++) {
		if (dom->ctr[i])
			dom->func->read(ppm, dom, dom->ctr[i]);
	}

	if (!dom->clk)
377 378
		return -EAGAIN;

379 380 381 382
	for (i = 0; i < 4; i++)
		if (dom->ctr[i])
			args->v0.ctr[i] = dom->ctr[i]->ctr;
	args->v0.clk = dom->clk;
383 384 385
	return 0;
}

386
static int
387
nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
388 389
{
	switch (mthd) {
390 391 392 393 394 395
	case NVIF_PERFDOM_V0_INIT:
		return nvkm_perfdom_init(object, data, size);
	case NVIF_PERFDOM_V0_SAMPLE:
		return nvkm_perfdom_sample(object, data, size);
	case NVIF_PERFDOM_V0_READ:
		return nvkm_perfdom_read(object, data, size);
396 397 398 399 400 401
	default:
		break;
	}
	return -EINVAL;
}

402
static void
403
nvkm_perfdom_dtor(struct nvkm_object *object)
404
{
405 406 407 408 409 410 411 412 413 414
	struct nvkm_perfdom *dom = (void *)object;
	int i;

	for (i = 0; i < 4; i++) {
		struct nvkm_perfctr *ctr = dom->ctr[i];
		if (ctr && ctr->head.next)
			list_del(&ctr->head);
		kfree(ctr);
	}
	nvkm_object_destroy(&dom->base);
415 416 417
}

static int
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot,
		 struct nvkm_perfsig *signal[4], uint16_t logic_op,
		 struct nvkm_perfctr **pctr)
{
	struct nvkm_perfctr *ctr;
	int i;

	if (!dom)
		return -EINVAL;

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

	ctr->logic_op = logic_op;
	ctr->slot     = slot;
	for (i = 0; i < 4; i++) {
		if (signal[i])
			ctr->signal[i] = signal[i] - dom->signal;
	}
	list_add_tail(&ctr->head, &dom->list);

	return 0;
}

static int
nvkm_perfdom_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
445 446
		  struct nvkm_oclass *oclass, void *data, u32 size,
		  struct nvkm_object **pobject)
447
{
448
	union {
449
		struct nvif_perfdom_v0 v0;
450
	} *args = data;
451
	struct nvkm_pm *ppm = (void *)engine;
452 453 454 455 456
	struct nvkm_perfdom *sdom = NULL;
	struct nvkm_perfctr *ctr[4] = {};
	struct nvkm_perfdom *dom;
	int c, s;
	int ret;
457

458
	nv_ioctl(parent, "create perfdom size %d\n", size);
459
	if (nvif_unpack(args->v0, 0, 0, false)) {
460 461
		nv_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
			 args->v0.version, args->v0.domain, args->v0.mode);
462 463
	} else
		return ret;
464

465 466 467 468 469 470 471 472 473 474 475 476 477 478
	for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
		struct nvkm_perfsig *sig[4] = {};
		for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
			sig[s] = nvkm_perfsig_find(ppm, args->v0.domain,
						   args->v0.ctr[c].signal[s],
						   &sdom);
			if (args->v0.ctr[c].signal[s] && !sig[s])
				return -EINVAL;
		}

		ret = nvkm_perfctr_new(sdom, c, sig,
				       args->v0.ctr[c].logic_op, &ctr[c]);
		if (ret)
			return ret;
479 480
	}

481
	if (!sdom)
482 483
		return -EINVAL;

484 485
	ret = nvkm_object_create(parent, engine, oclass, 0, &dom);
	*pobject = nv_object(dom);
486 487 488
	if (ret)
		return ret;

489 490 491 492 493
	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];
494 495 496
	return 0;
}

497
static struct nvkm_ofuncs
498 499 500
nvkm_perfdom_ofuncs = {
	.ctor = nvkm_perfdom_ctor,
	.dtor = nvkm_perfdom_dtor,
501 502
	.init = nvkm_object_init,
	.fini = nvkm_object_fini,
503
	.mthd = nvkm_perfdom_mthd,
504 505
};

506 507
struct nvkm_oclass
nvkm_pm_sclass[] = {
508 509 510 511
	{
	  .handle = NVIF_IOCTL_NEW_V0_PERFMON,
	  .ofuncs = &nvkm_perfmon_ofuncs,
	},
512 513
	{ .handle = NVIF_IOCTL_NEW_V0_PERFDOM,
	  .ofuncs = &nvkm_perfdom_ofuncs,
514 515 516 517 518 519 520 521
	},
	{},
};

/*******************************************************************************
 * PPM context
 ******************************************************************************/
static void
522
nvkm_perfctx_dtor(struct nvkm_object *object)
523
{
524
	struct nvkm_pm *ppm = (void *)object->engine;
525 526
	struct nvkm_perfctx *ctx = (void *)object;

527
	mutex_lock(&nv_subdev(ppm)->mutex);
528 529 530
	nvkm_engctx_destroy(&ctx->base);
	if (ppm->context == ctx)
		ppm->context = NULL;
531 532 533 534
	mutex_unlock(&nv_subdev(ppm)->mutex);
}

static int
535 536 537
nvkm_perfctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
		  struct nvkm_oclass *oclass, void *data, u32 size,
		  struct nvkm_object **pobject)
538
{
539 540
	struct nvkm_pm *ppm = (void *)engine;
	struct nvkm_perfctx *ctx;
541 542
	int ret;

543
	ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx);
544 545 546 547 548 549 550 551
	*pobject = nv_object(ctx);
	if (ret)
		return ret;

	mutex_lock(&nv_subdev(ppm)->mutex);
	if (ppm->context == NULL)
		ppm->context = ctx;
	if (ctx != ppm->context)
552 553
		ret = -EBUSY;
	mutex_unlock(&nv_subdev(ppm)->mutex);
554

555
	return ret;
556 557
}

558 559
struct nvkm_oclass
nvkm_pm_cclass = {
560
	.handle = NV_ENGCTX(PM, 0x00),
561 562 563 564 565
	.ofuncs = &(struct nvkm_ofuncs) {
		.ctor = nvkm_perfctx_ctor,
		.dtor = nvkm_perfctx_dtor,
		.init = _nvkm_engctx_init,
		.fini = _nvkm_engctx_fini,
566 567 568 569 570 571
	},
};

/*******************************************************************************
 * PPM engine/subdev functions
 ******************************************************************************/
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
int
nvkm_perfsrc_new(struct nvkm_pm *ppm, struct nvkm_perfsig *sig,
		 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;

			list_for_each_entry(src, &ppm->sources, head) {
				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);
				if (!src->name)
					return -ENOMEM;
				snprintf(src->name, len, "%s_%s", ssrc->name,
					 smux->name);

				list_add_tail(&src->head, &ppm->sources);
			}

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

	return 0;
}

633
int
634 635 636
nvkm_perfdom_new(struct nvkm_pm *ppm, const char *name, u32 mask,
		 u32 base, u32 size_unit, u32 size_domain,
		 const struct nvkm_specdom *spec)
637
{
638 639 640
	const struct nvkm_specdom *sdom;
	const struct nvkm_specsig *ssig;
	struct nvkm_perfdom *dom;
641
	int ret, i;
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671

	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));
			}

			list_add_tail(&dom->head, &ppm->domains);
			INIT_LIST_HEAD(&dom->list);
			dom->func = sdom->func;
			dom->addr = addr;
			dom->signal_nr = sdom->signal_nr;

			ssig = (sdom++)->signal;
			while (ssig->name) {
672 673 674 675 676 677
				struct nvkm_perfsig *sig =
					&dom->signal[ssig->signal];
				sig->name = ssig->name;
				ret = nvkm_perfsrc_new(ppm, sig, ssig->source);
				if (ret)
					return ret;
678 679 680 681 682 683 684 685 686 687 688 689 690
				ssig++;
			}

			addr += size_domain;
		}

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

	return 0;
}

int
691
_nvkm_pm_fini(struct nvkm_object *object, bool suspend)
692
{
693 694
	struct nvkm_pm *ppm = (void *)object;
	return nvkm_engine_fini(&ppm->base, suspend);
695 696 697
}

int
698
_nvkm_pm_init(struct nvkm_object *object)
699
{
700 701
	struct nvkm_pm *ppm = (void *)object;
	return nvkm_engine_init(&ppm->base);
702 703 704
}

void
705
_nvkm_pm_dtor(struct nvkm_object *object)
706
{
707
	struct nvkm_pm *ppm = (void *)object;
708 709
	struct nvkm_perfdom *dom, *next_dom;
	struct nvkm_perfsrc *src, *next_src;
710

711
	list_for_each_entry_safe(dom, next_dom, &ppm->domains, head) {
712 713 714 715
		list_del(&dom->head);
		kfree(dom);
	}

716 717 718 719 720 721
	list_for_each_entry_safe(src, next_src, &ppm->sources, head) {
		list_del(&src->head);
		kfree(src->name);
		kfree(src);
	}

722
	nvkm_engine_destroy(&ppm->base);
723 724 725
}

int
726 727
nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
		struct nvkm_oclass *oclass, int length, void **pobject)
728
{
729
	struct nvkm_pm *ppm;
730 731
	int ret;

732 733
	ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM",
				  "pm", length, pobject);
734 735 736 737 738
	ppm = *pobject;
	if (ret)
		return ret;

	INIT_LIST_HEAD(&ppm->domains);
739
	INIT_LIST_HEAD(&ppm->sources);
740 741
	return 0;
}