cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

base.c (20758B)


      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/if0002.h>
     31#include <nvif/if0003.h>
     32#include <nvif/ioctl.h>
     33#include <nvif/unpack.h>
     34
     35static u8
     36nvkm_pm_count_perfdom(struct nvkm_pm *pm)
     37{
     38	struct nvkm_perfdom *dom;
     39	u8 domain_nr = 0;
     40
     41	list_for_each_entry(dom, &pm->domains, head)
     42		domain_nr++;
     43	return domain_nr;
     44}
     45
     46static u16
     47nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
     48{
     49	u16 signal_nr = 0;
     50	int i;
     51
     52	if (dom) {
     53		for (i = 0; i < dom->signal_nr; i++) {
     54			if (dom->signal[i].name)
     55				signal_nr++;
     56		}
     57	}
     58	return signal_nr;
     59}
     60
     61static struct nvkm_perfdom *
     62nvkm_perfdom_find(struct nvkm_pm *pm, int di)
     63{
     64	struct nvkm_perfdom *dom;
     65	int tmp = 0;
     66
     67	list_for_each_entry(dom, &pm->domains, head) {
     68		if (tmp++ == di)
     69			return dom;
     70	}
     71	return NULL;
     72}
     73
     74static struct nvkm_perfsig *
     75nvkm_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) {
     80		dom = nvkm_perfdom_find(pm, di);
     81		if (dom == NULL)
     82			return NULL;
     83		*pdom = dom;
     84	}
     85
     86	if (!dom->signal[si].name)
     87		return NULL;
     88	return &dom->signal[si];
     89}
     90
     91static u8
     92nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig)
     93{
     94	u8 source_nr = 0, i;
     95
     96	for (i = 0; i < ARRAY_SIZE(sig->source); i++) {
     97		if (sig->source[i])
     98			source_nr++;
     99	}
    100	return source_nr;
    101}
    102
    103static struct nvkm_perfsrc *
    104nvkm_perfsrc_find(struct nvkm_pm *pm, struct nvkm_perfsig *sig, int si)
    105{
    106	struct nvkm_perfsrc *src;
    107	bool found = false;
    108	int tmp = 1; /* Sources ID start from 1 */
    109	u8 i;
    110
    111	for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) {
    112		if (sig->source[i] == si) {
    113			found = true;
    114			break;
    115		}
    116	}
    117
    118	if (found) {
    119		list_for_each_entry(src, &pm->sources, head) {
    120			if (tmp++ == si)
    121				return src;
    122		}
    123	}
    124
    125	return NULL;
    126}
    127
    128static int
    129nvkm_perfsrc_enable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
    130{
    131	struct nvkm_subdev *subdev = &pm->engine.subdev;
    132	struct nvkm_device *device = subdev->device;
    133	struct nvkm_perfdom *dom = NULL;
    134	struct nvkm_perfsig *sig;
    135	struct nvkm_perfsrc *src;
    136	u32 mask, value;
    137	int i, j;
    138
    139	for (i = 0; i < 4; i++) {
    140		for (j = 0; j < 8 && ctr->source[i][j]; j++) {
    141			sig = nvkm_perfsig_find(pm, ctr->domain,
    142						ctr->signal[i], &dom);
    143			if (!sig)
    144				return -EINVAL;
    145
    146			src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
    147			if (!src)
    148				return -EINVAL;
    149
    150			/* set enable bit if needed */
    151			mask = value = 0x00000000;
    152			if (src->enable)
    153				mask = value = 0x80000000;
    154			mask  |= (src->mask << src->shift);
    155			value |= ((ctr->source[i][j] >> 32) << src->shift);
    156
    157			/* enable the source */
    158			nvkm_mask(device, src->addr, mask, value);
    159			nvkm_debug(subdev,
    160				   "enabled source %08x %08x %08x\n",
    161				   src->addr, mask, value);
    162		}
    163	}
    164	return 0;
    165}
    166
    167static int
    168nvkm_perfsrc_disable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
    169{
    170	struct nvkm_subdev *subdev = &pm->engine.subdev;
    171	struct nvkm_device *device = subdev->device;
    172	struct nvkm_perfdom *dom = NULL;
    173	struct nvkm_perfsig *sig;
    174	struct nvkm_perfsrc *src;
    175	u32 mask;
    176	int i, j;
    177
    178	for (i = 0; i < 4; i++) {
    179		for (j = 0; j < 8 && ctr->source[i][j]; j++) {
    180			sig = nvkm_perfsig_find(pm, ctr->domain,
    181						ctr->signal[i], &dom);
    182			if (!sig)
    183				return -EINVAL;
    184
    185			src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
    186			if (!src)
    187				return -EINVAL;
    188
    189			/* unset enable bit if needed */
    190			mask = 0x00000000;
    191			if (src->enable)
    192				mask = 0x80000000;
    193			mask |= (src->mask << src->shift);
    194
    195			/* disable the source */
    196			nvkm_mask(device, src->addr, mask, 0);
    197			nvkm_debug(subdev, "disabled source %08x %08x\n",
    198				   src->addr, mask);
    199		}
    200	}
    201	return 0;
    202}
    203
    204/*******************************************************************************
    205 * Perfdom object classes
    206 ******************************************************************************/
    207static int
    208nvkm_perfdom_init(struct nvkm_perfdom *dom, void *data, u32 size)
    209{
    210	union {
    211		struct nvif_perfdom_init none;
    212	} *args = data;
    213	struct nvkm_object *object = &dom->object;
    214	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	} else
    221		return ret;
    222
    223	for (i = 0; i < 4; i++) {
    224		if (dom->ctr[i]) {
    225			dom->func->init(pm, dom, dom->ctr[i]);
    226
    227			/* enable sources */
    228			nvkm_perfsrc_enable(pm, dom->ctr[i]);
    229		}
    230	}
    231
    232	/* start next batch of counters for sampling */
    233	dom->func->next(pm, dom);
    234	return 0;
    235}
    236
    237static int
    238nvkm_perfdom_sample(struct nvkm_perfdom *dom, void *data, u32 size)
    239{
    240	union {
    241		struct nvif_perfdom_sample none;
    242	} *args = data;
    243	struct nvkm_object *object = &dom->object;
    244	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	} else
    251		return ret;
    252	pm->sequence++;
    253
    254	/* sample previous batch of counters */
    255	list_for_each_entry(dom, &pm->domains, head)
    256		dom->func->next(pm, dom);
    257
    258	return 0;
    259}
    260
    261static int
    262nvkm_perfdom_read(struct nvkm_perfdom *dom, void *data, u32 size)
    263{
    264	union {
    265		struct nvif_perfdom_read_v0 v0;
    266	} *args = data;
    267	struct nvkm_object *object = &dom->object;
    268	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	} else
    275		return ret;
    276
    277	for (i = 0; i < 4; i++) {
    278		if (dom->ctr[i])
    279			dom->func->read(pm, dom, dom->ctr[i]);
    280	}
    281
    282	if (!dom->clk)
    283		return -EAGAIN;
    284
    285	for (i = 0; i < 4; i++)
    286		if (dom->ctr[i])
    287			args->v0.ctr[i] = dom->ctr[i]->ctr;
    288	args->v0.clk = dom->clk;
    289	return 0;
    290}
    291
    292static int
    293nvkm_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	default:
    304		break;
    305	}
    306	return -EINVAL;
    307}
    308
    309static void *
    310nvkm_perfdom_dtor(struct nvkm_object *object)
    311{
    312	struct nvkm_perfdom *dom = nvkm_perfdom(object);
    313	struct nvkm_pm *pm = dom->perfmon->pm;
    314	int i;
    315
    316	for (i = 0; i < 4; i++) {
    317		struct nvkm_perfctr *ctr = dom->ctr[i];
    318		if (ctr) {
    319			nvkm_perfsrc_disable(pm, ctr);
    320			if (ctr->head.next)
    321				list_del(&ctr->head);
    322		}
    323		kfree(ctr);
    324	}
    325
    326	return dom;
    327}
    328
    329static int
    330nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot, u8 domain,
    331		 struct nvkm_perfsig *signal[4], u64 source[4][8],
    332		 u16 logic_op, struct nvkm_perfctr **pctr)
    333{
    334	struct nvkm_perfctr *ctr;
    335	int i, j;
    336
    337	if (!dom)
    338		return -EINVAL;
    339
    340	ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
    341	if (!ctr)
    342		return -ENOMEM;
    343
    344	ctr->domain   = domain;
    345	ctr->logic_op = logic_op;
    346	ctr->slot     = slot;
    347	for (i = 0; i < 4; i++) {
    348		if (signal[i]) {
    349			ctr->signal[i] = signal[i] - dom->signal;
    350			for (j = 0; j < 8; j++)
    351				ctr->source[i][j] = source[i][j];
    352		}
    353	}
    354	list_add_tail(&ctr->head, &dom->list);
    355
    356	return 0;
    357}
    358
    359static const struct nvkm_object_func
    360nvkm_perfdom = {
    361	.dtor = nvkm_perfdom_dtor,
    362	.mthd = nvkm_perfdom_mthd,
    363};
    364
    365static int
    366nvkm_perfdom_new_(struct nvkm_perfmon *perfmon,
    367		  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	struct nvkm_pm *pm = perfmon->pm;
    374	struct nvkm_object *parent = oclass->parent;
    375	struct nvkm_perfdom *sdom = NULL;
    376	struct nvkm_perfctr *ctr[4] = {};
    377	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		nvif_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
    384			   args->v0.version, args->v0.domain, args->v0.mode);
    385	} else
    386		return ret;
    387
    388	for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
    389		struct nvkm_perfsig *sig[4] = {};
    390		u64 src[4][8] = {};
    391
    392		for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
    393			sig[s] = nvkm_perfsig_find(pm, args->v0.domain,
    394						   args->v0.ctr[c].signal[s],
    395						   &sdom);
    396			if (args->v0.ctr[c].signal[s] && !sig[s])
    397				return -EINVAL;
    398
    399			for (m = 0; m < 8; m++) {
    400				src[s][m] = args->v0.ctr[c].source[s][m];
    401				if (src[s][m] && !nvkm_perfsrc_find(pm, sig[s],
    402							            src[s][m]))
    403					return -EINVAL;
    404			}
    405		}
    406
    407		ret = nvkm_perfctr_new(sdom, c, args->v0.domain, sig, src,
    408				       args->v0.ctr[c].logic_op, &ctr[c]);
    409		if (ret)
    410			return ret;
    411	}
    412
    413	if (!sdom)
    414		return -EINVAL;
    415
    416	if (!(dom = kzalloc(sizeof(*dom), GFP_KERNEL)))
    417		return -ENOMEM;
    418	nvkm_object_ctor(&nvkm_perfdom, oclass, &dom->object);
    419	dom->perfmon = perfmon;
    420	*pobject = &dom->object;
    421
    422	dom->func = sdom->func;
    423	dom->addr = sdom->addr;
    424	dom->mode = args->v0.mode;
    425	for (c = 0; c < ARRAY_SIZE(ctr); c++)
    426		dom->ctr[c] = ctr[c];
    427	return 0;
    428}
    429
    430/*******************************************************************************
    431 * Perfmon object classes
    432 ******************************************************************************/
    433static int
    434nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon,
    435			       void *data, u32 size)
    436{
    437	union {
    438		struct nvif_perfmon_query_domain_v0 v0;
    439	} *args = data;
    440	struct nvkm_object *object = &perfmon->object;
    441	struct nvkm_pm *pm = perfmon->pm;
    442	struct nvkm_perfdom *dom;
    443	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		nvif_ioctl(object, "perfmon domain vers %d iter %02x\n",
    449			   args->v0.version, args->v0.iter);
    450		di = (args->v0.iter & 0xff) - 1;
    451	} else
    452		return ret;
    453
    454	domain_nr = nvkm_pm_count_perfdom(pm);
    455	if (di >= (int)domain_nr)
    456		return -EINVAL;
    457
    458	if (di >= 0) {
    459		dom = nvkm_perfdom_find(pm, di);
    460		if (dom == NULL)
    461			return -EINVAL;
    462
    463		args->v0.id         = di;
    464		args->v0.signal_nr  = nvkm_perfdom_count_perfsig(dom);
    465		strncpy(args->v0.name, dom->name, sizeof(args->v0.name) - 1);
    466
    467		/* Currently only global counters (PCOUNTER) are implemented
    468		 * but this will be different for local counters (MP). */
    469		args->v0.counter_nr = 4;
    470	}
    471
    472	if (++di < domain_nr) {
    473		args->v0.iter = ++di;
    474		return 0;
    475	}
    476
    477	args->v0.iter = 0xff;
    478	return 0;
    479}
    480
    481static int
    482nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon,
    483			       void *data, u32 size)
    484{
    485	union {
    486		struct nvif_perfmon_query_signal_v0 v0;
    487	} *args = data;
    488	struct nvkm_object *object = &perfmon->object;
    489	struct nvkm_pm *pm = perfmon->pm;
    490	struct nvkm_device *device = pm->engine.subdev.device;
    491	struct nvkm_perfdom *dom;
    492	struct nvkm_perfsig *sig;
    493	const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
    494	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		nvif_ioctl(object,
    500			   "perfmon query signal vers %d dom %d iter %04x\n",
    501			   args->v0.version, args->v0.domain, args->v0.iter);
    502		si = (args->v0.iter & 0xffff) - 1;
    503	} else
    504		return ret;
    505
    506	dom = nvkm_perfdom_find(pm, args->v0.domain);
    507	if (dom == NULL || si >= (int)dom->signal_nr)
    508		return -EINVAL;
    509
    510	if (si >= 0) {
    511		sig = &dom->signal[si];
    512		if (raw || !sig->name) {
    513			snprintf(args->v0.name, sizeof(args->v0.name),
    514				 "/%s/%02x", dom->name, si);
    515		} else {
    516			strncpy(args->v0.name, sig->name,
    517				sizeof(args->v0.name) - 1);
    518		}
    519
    520		args->v0.signal = si;
    521		args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
    522	}
    523
    524	while (++si < dom->signal_nr) {
    525		if (all || dom->signal[si].name) {
    526			args->v0.iter = ++si;
    527			return 0;
    528		}
    529	}
    530
    531	args->v0.iter = 0xffff;
    532	return 0;
    533}
    534
    535static int
    536nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon,
    537			       void *data, u32 size)
    538{
    539	union {
    540		struct nvif_perfmon_query_source_v0 v0;
    541	} *args = data;
    542	struct nvkm_object *object = &perfmon->object;
    543	struct nvkm_pm *pm = perfmon->pm;
    544	struct nvkm_perfdom *dom = NULL;
    545	struct nvkm_perfsig *sig;
    546	struct nvkm_perfsrc *src;
    547	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		nvif_ioctl(object,
    553			   "perfmon source vers %d dom %d sig %02x iter %02x\n",
    554			   args->v0.version, args->v0.domain, args->v0.signal,
    555			   args->v0.iter);
    556		si = (args->v0.iter & 0xff) - 1;
    557	} else
    558		return ret;
    559
    560	sig = nvkm_perfsig_find(pm, args->v0.domain, args->v0.signal, &dom);
    561	if (!sig)
    562		return -EINVAL;
    563
    564	source_nr = nvkm_perfsig_count_perfsrc(sig);
    565	if (si >= (int)source_nr)
    566		return -EINVAL;
    567
    568	if (si >= 0) {
    569		src = nvkm_perfsrc_find(pm, sig, sig->source[si]);
    570		if (!src)
    571			return -EINVAL;
    572
    573		args->v0.source = sig->source[si];
    574		args->v0.mask   = src->mask;
    575		strncpy(args->v0.name, src->name, sizeof(args->v0.name) - 1);
    576	}
    577
    578	if (++si < source_nr) {
    579		args->v0.iter = ++si;
    580		return 0;
    581	}
    582
    583	args->v0.iter = 0xff;
    584	return 0;
    585}
    586
    587static int
    588nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
    589{
    590	struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
    591	switch (mthd) {
    592	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	default:
    599		break;
    600	}
    601	return -EINVAL;
    602}
    603
    604static int
    605nvkm_perfmon_child_new(const struct nvkm_oclass *oclass, void *data, u32 size,
    606		       struct nvkm_object **pobject)
    607{
    608	struct nvkm_perfmon *perfmon = nvkm_perfmon(oclass->parent);
    609	return nvkm_perfdom_new_(perfmon, oclass, data, size, pobject);
    610}
    611
    612static int
    613nvkm_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		oclass->base.minver = 0;
    619		oclass->base.maxver = 0;
    620		oclass->ctor = nvkm_perfmon_child_new;
    621		return 0;
    622	}
    623	return -EINVAL;
    624}
    625
    626static void *
    627nvkm_perfmon_dtor(struct nvkm_object *object)
    628{
    629	struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
    630	struct nvkm_pm *pm = perfmon->pm;
    631	spin_lock(&pm->client.lock);
    632	if (pm->client.object == &perfmon->object)
    633		pm->client.object = NULL;
    634	spin_unlock(&pm->client.lock);
    635	return perfmon;
    636}
    637
    638static const struct nvkm_object_func
    639nvkm_perfmon = {
    640	.dtor = nvkm_perfmon_dtor,
    641	.mthd = nvkm_perfmon_mthd,
    642	.sclass = nvkm_perfmon_child_get,
    643};
    644
    645static int
    646nvkm_perfmon_new(struct nvkm_pm *pm, const struct nvkm_oclass *oclass,
    647		 void *data, u32 size, struct nvkm_object **pobject)
    648{
    649	struct nvkm_perfmon *perfmon;
    650
    651	if (!(perfmon = kzalloc(sizeof(*perfmon), GFP_KERNEL)))
    652		return -ENOMEM;
    653	nvkm_object_ctor(&nvkm_perfmon, oclass, &perfmon->object);
    654	perfmon->pm = pm;
    655	*pobject = &perfmon->object;
    656	return 0;
    657}
    658
    659/*******************************************************************************
    660 * PPM engine/subdev functions
    661 ******************************************************************************/
    662
    663static int
    664nvkm_pm_oclass_new(struct nvkm_device *device, const struct nvkm_oclass *oclass,
    665		   void *data, u32 size, struct nvkm_object **pobject)
    666{
    667	struct nvkm_pm *pm = nvkm_pm(oclass->engine);
    668	int ret;
    669
    670	ret = nvkm_perfmon_new(pm, oclass, data, size, pobject);
    671	if (ret)
    672		return ret;
    673
    674	spin_lock(&pm->client.lock);
    675	if (pm->client.object == NULL)
    676		pm->client.object = *pobject;
    677	ret = (pm->client.object == *pobject) ? 0 : -EBUSY;
    678	spin_unlock(&pm->client.lock);
    679	return ret;
    680}
    681
    682static const struct nvkm_device_oclass
    683nvkm_pm_oclass = {
    684	.base.oclass = NVIF_CLASS_PERFMON,
    685	.base.minver = -1,
    686	.base.maxver = -1,
    687	.ctor = nvkm_pm_oclass_new,
    688};
    689
    690static int
    691nvkm_pm_oclass_get(struct nvkm_oclass *oclass, int index,
    692		   const struct nvkm_device_oclass **class)
    693{
    694	if (index == 0) {
    695		oclass->base = nvkm_pm_oclass.base;
    696		*class = &nvkm_pm_oclass;
    697		return index;
    698	}
    699	return 1;
    700}
    701
    702static int
    703nvkm_perfsrc_new(struct nvkm_pm *pm, struct nvkm_perfsig *sig,
    704		 const struct nvkm_specsrc *spec)
    705{
    706	const struct nvkm_specsrc *ssrc;
    707	const struct nvkm_specmux *smux;
    708	struct nvkm_perfsrc *src;
    709	u8 source_nr = 0;
    710
    711	if (!spec) {
    712		/* No sources are defined for this signal. */
    713		return 0;
    714	}
    715
    716	ssrc = spec;
    717	while (ssrc->name) {
    718		smux = ssrc->mux;
    719		while (smux->name) {
    720			bool found = false;
    721			u8 source_id = 0;
    722			u32 len;
    723
    724			list_for_each_entry(src, &pm->sources, head) {
    725				if (src->addr == ssrc->addr &&
    726				    src->shift == smux->shift) {
    727					found = true;
    728					break;
    729				}
    730				source_id++;
    731			}
    732
    733			if (!found) {
    734				src = kzalloc(sizeof(*src), GFP_KERNEL);
    735				if (!src)
    736					return -ENOMEM;
    737
    738				src->addr   = ssrc->addr;
    739				src->mask   = smux->mask;
    740				src->shift  = smux->shift;
    741				src->enable = smux->enable;
    742
    743				len = strlen(ssrc->name) +
    744				      strlen(smux->name) + 2;
    745				src->name = kzalloc(len, GFP_KERNEL);
    746				if (!src->name) {
    747					kfree(src);
    748					return -ENOMEM;
    749				}
    750				snprintf(src->name, len, "%s_%s", ssrc->name,
    751					 smux->name);
    752
    753				list_add_tail(&src->head, &pm->sources);
    754			}
    755
    756			sig->source[source_nr++] = source_id + 1;
    757			smux++;
    758		}
    759		ssrc++;
    760	}
    761
    762	return 0;
    763}
    764
    765int
    766nvkm_perfdom_new(struct nvkm_pm *pm, const char *name, u32 mask,
    767		 u32 base, u32 size_unit, u32 size_domain,
    768		 const struct nvkm_specdom *spec)
    769{
    770	const struct nvkm_specdom *sdom;
    771	const struct nvkm_specsig *ssig;
    772	struct nvkm_perfdom *dom;
    773	int ret, i;
    774
    775	for (i = 0; i == 0 || mask; i++) {
    776		u32 addr = base + (i * size_unit);
    777		if (i && !(mask & (1 << i)))
    778			continue;
    779
    780		sdom = spec;
    781		while (sdom->signal_nr) {
    782			dom = kzalloc(struct_size(dom, signal, sdom->signal_nr),
    783				      GFP_KERNEL);
    784			if (!dom)
    785				return -ENOMEM;
    786
    787			if (mask) {
    788				snprintf(dom->name, sizeof(dom->name),
    789					 "%s/%02x/%02x", name, i,
    790					 (int)(sdom - spec));
    791			} else {
    792				snprintf(dom->name, sizeof(dom->name),
    793					 "%s/%02x", name, (int)(sdom - spec));
    794			}
    795
    796			list_add_tail(&dom->head, &pm->domains);
    797			INIT_LIST_HEAD(&dom->list);
    798			dom->func = sdom->func;
    799			dom->addr = addr;
    800			dom->signal_nr = sdom->signal_nr;
    801
    802			ssig = (sdom++)->signal;
    803			while (ssig->name) {
    804				struct nvkm_perfsig *sig =
    805					&dom->signal[ssig->signal];
    806				sig->name = ssig->name;
    807				ret = nvkm_perfsrc_new(pm, sig, ssig->source);
    808				if (ret)
    809					return ret;
    810				ssig++;
    811			}
    812
    813			addr += size_domain;
    814		}
    815
    816		mask &= ~(1 << i);
    817	}
    818
    819	return 0;
    820}
    821
    822static int
    823nvkm_pm_fini(struct nvkm_engine *engine, bool suspend)
    824{
    825	struct nvkm_pm *pm = nvkm_pm(engine);
    826	if (pm->func->fini)
    827		pm->func->fini(pm);
    828	return 0;
    829}
    830
    831static void *
    832nvkm_pm_dtor(struct nvkm_engine *engine)
    833{
    834	struct nvkm_pm *pm = nvkm_pm(engine);
    835	struct nvkm_perfdom *dom, *next_dom;
    836	struct nvkm_perfsrc *src, *next_src;
    837
    838	list_for_each_entry_safe(dom, next_dom, &pm->domains, head) {
    839		list_del(&dom->head);
    840		kfree(dom);
    841	}
    842
    843	list_for_each_entry_safe(src, next_src, &pm->sources, head) {
    844		list_del(&src->head);
    845		kfree(src->name);
    846		kfree(src);
    847	}
    848
    849	return pm;
    850}
    851
    852static const struct nvkm_engine_func
    853nvkm_pm = {
    854	.dtor = nvkm_pm_dtor,
    855	.fini = nvkm_pm_fini,
    856	.base.sclass = nvkm_pm_oclass_get,
    857};
    858
    859int
    860nvkm_pm_ctor(const struct nvkm_pm_func *func, struct nvkm_device *device,
    861	     enum nvkm_subdev_type type, int inst, struct nvkm_pm *pm)
    862{
    863	pm->func = func;
    864	INIT_LIST_HEAD(&pm->domains);
    865	INIT_LIST_HEAD(&pm->sources);
    866	spin_lock_init(&pm->client.lock);
    867	return nvkm_engine_ctor(&nvkm_pm, device, type, inst, true, &pm->engine);
    868}