123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- /*
- * 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
- */
- #include "priv.h"
- #include <core/client.h>
- #include <core/option.h>
- #include <nvif/class.h>
- #include <nvif/if0002.h>
- #include <nvif/if0003.h>
- #include <nvif/ioctl.h>
- #include <nvif/unpack.h>
- static u8
- nvkm_pm_count_perfdom(struct nvkm_pm *pm)
- {
- struct nvkm_perfdom *dom;
- u8 domain_nr = 0;
- list_for_each_entry(dom, &pm->domains, head)
- domain_nr++;
- return domain_nr;
- }
- static u16
- nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
- {
- u16 signal_nr = 0;
- 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 *pm, int di)
- {
- struct nvkm_perfdom *dom;
- int tmp = 0;
- list_for_each_entry(dom, &pm->domains, head) {
- if (tmp++ == di)
- return dom;
- }
- return NULL;
- }
- static struct nvkm_perfsig *
- nvkm_perfsig_find(struct nvkm_pm *pm, u8 di, u8 si, struct nvkm_perfdom **pdom)
- {
- struct nvkm_perfdom *dom = *pdom;
- if (dom == NULL) {
- dom = nvkm_perfdom_find(pm, di);
- if (dom == NULL)
- return NULL;
- *pdom = dom;
- }
- if (!dom->signal[si].name)
- return NULL;
- return &dom->signal[si];
- }
- 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;
- }
- static struct nvkm_perfsrc *
- nvkm_perfsrc_find(struct nvkm_pm *pm, 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, &pm->sources, head) {
- if (tmp++ == si)
- return src;
- }
- }
- return NULL;
- }
- static int
- nvkm_perfsrc_enable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
- {
- struct nvkm_subdev *subdev = &pm->engine.subdev;
- struct nvkm_device *device = subdev->device;
- struct nvkm_perfdom *dom = NULL;
- struct nvkm_perfsig *sig;
- struct nvkm_perfsrc *src;
- u32 mask, value;
- int i, j;
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 8 && ctr->source[i][j]; j++) {
- sig = nvkm_perfsig_find(pm, ctr->domain,
- ctr->signal[i], &dom);
- if (!sig)
- return -EINVAL;
- src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
- 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 */
- nvkm_mask(device, src->addr, mask, value);
- nvkm_debug(subdev,
- "enabled source %08x %08x %08x\n",
- src->addr, mask, value);
- }
- }
- return 0;
- }
- static int
- nvkm_perfsrc_disable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
- {
- struct nvkm_subdev *subdev = &pm->engine.subdev;
- struct nvkm_device *device = subdev->device;
- struct nvkm_perfdom *dom = NULL;
- struct nvkm_perfsig *sig;
- struct nvkm_perfsrc *src;
- u32 mask;
- int i, j;
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 8 && ctr->source[i][j]; j++) {
- sig = nvkm_perfsig_find(pm, ctr->domain,
- ctr->signal[i], &dom);
- if (!sig)
- return -EINVAL;
- src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
- if (!src)
- return -EINVAL;
- /* unset enable bit if needed */
- mask = 0x00000000;
- if (src->enable)
- mask = 0x80000000;
- mask |= (src->mask << src->shift);
- /* disable the source */
- nvkm_mask(device, src->addr, mask, 0);
- nvkm_debug(subdev, "disabled source %08x %08x\n",
- src->addr, mask);
- }
- }
- return 0;
- }
- /*******************************************************************************
- * Perfdom object classes
- ******************************************************************************/
- static int
- nvkm_perfdom_init(struct nvkm_perfdom *dom, void *data, u32 size)
- {
- union {
- struct nvif_perfdom_init none;
- } *args = data;
- struct nvkm_object *object = &dom->object;
- struct nvkm_pm *pm = dom->perfmon->pm;
- int ret = -ENOSYS, i;
- nvif_ioctl(object, "perfdom init size %d\n", size);
- if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
- nvif_ioctl(object, "perfdom init\n");
- } else
- return ret;
- for (i = 0; i < 4; i++) {
- if (dom->ctr[i]) {
- dom->func->init(pm, dom, dom->ctr[i]);
- /* enable sources */
- nvkm_perfsrc_enable(pm, dom->ctr[i]);
- }
- }
- /* start next batch of counters for sampling */
- dom->func->next(pm, dom);
- return 0;
- }
- static int
- nvkm_perfdom_sample(struct nvkm_perfdom *dom, void *data, u32 size)
- {
- union {
- struct nvif_perfdom_sample none;
- } *args = data;
- struct nvkm_object *object = &dom->object;
- struct nvkm_pm *pm = dom->perfmon->pm;
- int ret = -ENOSYS;
- nvif_ioctl(object, "perfdom sample size %d\n", size);
- if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
- nvif_ioctl(object, "perfdom sample\n");
- } else
- return ret;
- pm->sequence++;
- /* sample previous batch of counters */
- list_for_each_entry(dom, &pm->domains, head)
- dom->func->next(pm, dom);
- return 0;
- }
- static int
- nvkm_perfdom_read(struct nvkm_perfdom *dom, void *data, u32 size)
- {
- union {
- struct nvif_perfdom_read_v0 v0;
- } *args = data;
- struct nvkm_object *object = &dom->object;
- struct nvkm_pm *pm = dom->perfmon->pm;
- int ret = -ENOSYS, i;
- nvif_ioctl(object, "perfdom read size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "perfdom read vers %d\n", args->v0.version);
- } else
- return ret;
- for (i = 0; i < 4; i++) {
- if (dom->ctr[i])
- dom->func->read(pm, dom, dom->ctr[i]);
- }
- if (!dom->clk)
- return -EAGAIN;
- for (i = 0; i < 4; i++)
- if (dom->ctr[i])
- args->v0.ctr[i] = dom->ctr[i]->ctr;
- args->v0.clk = dom->clk;
- return 0;
- }
- static int
- nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
- {
- struct nvkm_perfdom *dom = nvkm_perfdom(object);
- switch (mthd) {
- case NVIF_PERFDOM_V0_INIT:
- return nvkm_perfdom_init(dom, data, size);
- case NVIF_PERFDOM_V0_SAMPLE:
- return nvkm_perfdom_sample(dom, data, size);
- case NVIF_PERFDOM_V0_READ:
- return nvkm_perfdom_read(dom, data, size);
- default:
- break;
- }
- return -EINVAL;
- }
- static void *
- nvkm_perfdom_dtor(struct nvkm_object *object)
- {
- struct nvkm_perfdom *dom = nvkm_perfdom(object);
- struct nvkm_pm *pm = dom->perfmon->pm;
- int i;
- for (i = 0; i < 4; i++) {
- struct nvkm_perfctr *ctr = dom->ctr[i];
- if (ctr) {
- nvkm_perfsrc_disable(pm, ctr);
- if (ctr->head.next)
- list_del(&ctr->head);
- }
- kfree(ctr);
- }
- return dom;
- }
- static int
- 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)
- {
- struct nvkm_perfctr *ctr;
- int i, j;
- if (!dom)
- return -EINVAL;
- ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
- if (!ctr)
- return -ENOMEM;
- ctr->domain = domain;
- ctr->logic_op = logic_op;
- ctr->slot = slot;
- for (i = 0; i < 4; i++) {
- if (signal[i]) {
- ctr->signal[i] = signal[i] - dom->signal;
- for (j = 0; j < 8; j++)
- ctr->source[i][j] = source[i][j];
- }
- }
- list_add_tail(&ctr->head, &dom->list);
- return 0;
- }
- static const struct nvkm_object_func
- nvkm_perfdom = {
- .dtor = nvkm_perfdom_dtor,
- .mthd = nvkm_perfdom_mthd,
- };
- static int
- nvkm_perfdom_new_(struct nvkm_perfmon *perfmon,
- const struct nvkm_oclass *oclass, void *data, u32 size,
- struct nvkm_object **pobject)
- {
- union {
- struct nvif_perfdom_v0 v0;
- } *args = data;
- struct nvkm_pm *pm = perfmon->pm;
- struct nvkm_object *parent = oclass->parent;
- struct nvkm_perfdom *sdom = NULL;
- struct nvkm_perfctr *ctr[4] = {};
- struct nvkm_perfdom *dom;
- int c, s, m;
- int ret = -ENOSYS;
- nvif_ioctl(parent, "create perfdom size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
- args->v0.version, args->v0.domain, args->v0.mode);
- } else
- return ret;
- for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
- struct nvkm_perfsig *sig[4] = {};
- u64 src[4][8] = {};
- for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
- sig[s] = nvkm_perfsig_find(pm, args->v0.domain,
- args->v0.ctr[c].signal[s],
- &sdom);
- if (args->v0.ctr[c].signal[s] && !sig[s])
- return -EINVAL;
- for (m = 0; m < 8; m++) {
- src[s][m] = args->v0.ctr[c].source[s][m];
- if (src[s][m] && !nvkm_perfsrc_find(pm, sig[s],
- src[s][m]))
- return -EINVAL;
- }
- }
- ret = nvkm_perfctr_new(sdom, c, args->v0.domain, sig, src,
- args->v0.ctr[c].logic_op, &ctr[c]);
- if (ret)
- return ret;
- }
- if (!sdom)
- return -EINVAL;
- if (!(dom = kzalloc(sizeof(*dom), GFP_KERNEL)))
- return -ENOMEM;
- nvkm_object_ctor(&nvkm_perfdom, oclass, &dom->object);
- dom->perfmon = perfmon;
- *pobject = &dom->object;
- 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];
- return 0;
- }
- /*******************************************************************************
- * Perfmon object classes
- ******************************************************************************/
- static int
- nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon,
- void *data, u32 size)
- {
- union {
- struct nvif_perfmon_query_domain_v0 v0;
- } *args = data;
- struct nvkm_object *object = &perfmon->object;
- struct nvkm_pm *pm = perfmon->pm;
- struct nvkm_perfdom *dom;
- u8 domain_nr;
- int di, ret = -ENOSYS;
- nvif_ioctl(object, "perfmon query domain size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_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(pm);
- if (di >= (int)domain_nr)
- return -EINVAL;
- if (di >= 0) {
- dom = nvkm_perfdom_find(pm, di);
- if (dom == NULL)
- return -EINVAL;
- args->v0.id = di;
- args->v0.signal_nr = nvkm_perfdom_count_perfsig(dom);
- strncpy(args->v0.name, dom->name, sizeof(args->v0.name) - 1);
- /* 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
- nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon,
- void *data, u32 size)
- {
- union {
- struct nvif_perfmon_query_signal_v0 v0;
- } *args = data;
- struct nvkm_object *object = &perfmon->object;
- struct nvkm_pm *pm = perfmon->pm;
- struct nvkm_device *device = pm->engine.subdev.device;
- 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 = -ENOSYS, si;
- nvif_ioctl(object, "perfmon query signal size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object,
- "perfmon query signal vers %d dom %d iter %04x\n",
- args->v0.version, args->v0.domain, args->v0.iter);
- si = (args->v0.iter & 0xffff) - 1;
- } else
- return ret;
- dom = nvkm_perfdom_find(pm, args->v0.domain);
- 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) - 1);
- }
- 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
- nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon,
- void *data, u32 size)
- {
- union {
- struct nvif_perfmon_query_source_v0 v0;
- } *args = data;
- struct nvkm_object *object = &perfmon->object;
- struct nvkm_pm *pm = perfmon->pm;
- struct nvkm_perfdom *dom = NULL;
- struct nvkm_perfsig *sig;
- struct nvkm_perfsrc *src;
- u8 source_nr = 0;
- int si, ret = -ENOSYS;
- nvif_ioctl(object, "perfmon query source size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- 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);
- si = (args->v0.iter & 0xff) - 1;
- } else
- return ret;
- sig = nvkm_perfsig_find(pm, 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(pm, 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) - 1);
- }
- 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)
- {
- struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
- switch (mthd) {
- case NVIF_PERFMON_V0_QUERY_DOMAIN:
- return nvkm_perfmon_mthd_query_domain(perfmon, data, size);
- case NVIF_PERFMON_V0_QUERY_SIGNAL:
- return nvkm_perfmon_mthd_query_signal(perfmon, data, size);
- case NVIF_PERFMON_V0_QUERY_SOURCE:
- return nvkm_perfmon_mthd_query_source(perfmon, data, size);
- default:
- break;
- }
- return -EINVAL;
- }
- 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);
- }
- static int
- nvkm_perfmon_child_get(struct nvkm_object *object, int index,
- struct nvkm_oclass *oclass)
- {
- if (index == 0) {
- oclass->base.oclass = NVIF_CLASS_PERFDOM;
- oclass->base.minver = 0;
- oclass->base.maxver = 0;
- oclass->ctor = nvkm_perfmon_child_new;
- return 0;
- }
- return -EINVAL;
- }
- static void *
- nvkm_perfmon_dtor(struct nvkm_object *object)
- {
- struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
- 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;
- }
- static const struct nvkm_object_func
- nvkm_perfmon = {
- .dtor = nvkm_perfmon_dtor,
- .mthd = nvkm_perfmon_mthd,
- .sclass = nvkm_perfmon_child_get,
- };
- static int
- nvkm_perfmon_new(struct nvkm_pm *pm, const struct nvkm_oclass *oclass,
- void *data, u32 size, struct nvkm_object **pobject)
- {
- struct nvkm_perfmon *perfmon;
- 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;
- }
- /*******************************************************************************
- * PPM engine/subdev functions
- ******************************************************************************/
- static int
- nvkm_pm_oclass_new(struct nvkm_device *device, const struct nvkm_oclass *oclass,
- void *data, u32 size, struct nvkm_object **pobject)
- {
- struct nvkm_pm *pm = nvkm_pm(oclass->engine);
- int ret;
- ret = nvkm_perfmon_new(pm, oclass, data, size, pobject);
- if (ret)
- return ret;
- 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);
- return ret;
- }
- static const struct nvkm_device_oclass
- nvkm_pm_oclass = {
- .base.oclass = NVIF_CLASS_PERFMON,
- .base.minver = -1,
- .base.maxver = -1,
- .ctor = nvkm_pm_oclass_new,
- };
- 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;
- }
- static int
- nvkm_perfsrc_new(struct nvkm_pm *pm, 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, &pm->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) {
- kfree(src);
- return -ENOMEM;
- }
- snprintf(src->name, len, "%s_%s", ssrc->name,
- smux->name);
- list_add_tail(&src->head, &pm->sources);
- }
- sig->source[source_nr++] = source_id + 1;
- smux++;
- }
- ssrc++;
- }
- return 0;
- }
- int
- nvkm_perfdom_new(struct nvkm_pm *pm, const char *name, u32 mask,
- u32 base, u32 size_unit, u32 size_domain,
- const struct nvkm_specdom *spec)
- {
- const struct nvkm_specdom *sdom;
- const struct nvkm_specsig *ssig;
- struct nvkm_perfdom *dom;
- int ret, i;
- 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(struct_size(dom, signal, sdom->signal_nr),
- 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, &pm->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) {
- struct nvkm_perfsig *sig =
- &dom->signal[ssig->signal];
- sig->name = ssig->name;
- ret = nvkm_perfsrc_new(pm, sig, ssig->source);
- if (ret)
- return ret;
- ssig++;
- }
- addr += size_domain;
- }
- mask &= ~(1 << i);
- }
- return 0;
- }
- static int
- nvkm_pm_fini(struct nvkm_engine *engine, bool suspend)
- {
- struct nvkm_pm *pm = nvkm_pm(engine);
- if (pm->func->fini)
- pm->func->fini(pm);
- return 0;
- }
- static void *
- nvkm_pm_dtor(struct nvkm_engine *engine)
- {
- struct nvkm_pm *pm = nvkm_pm(engine);
- struct nvkm_perfdom *dom, *next_dom;
- struct nvkm_perfsrc *src, *next_src;
- list_for_each_entry_safe(dom, next_dom, &pm->domains, head) {
- list_del(&dom->head);
- kfree(dom);
- }
- list_for_each_entry_safe(src, next_src, &pm->sources, head) {
- list_del(&src->head);
- kfree(src->name);
- kfree(src);
- }
- return pm;
- }
- static const struct nvkm_engine_func
- nvkm_pm = {
- .dtor = nvkm_pm_dtor,
- .fini = nvkm_pm_fini,
- .base.sclass = nvkm_pm_oclass_get,
- };
- int
- nvkm_pm_ctor(const struct nvkm_pm_func *func, struct nvkm_device *device,
- int index, struct nvkm_pm *pm)
- {
- pm->func = func;
- INIT_LIST_HEAD(&pm->domains);
- INIT_LIST_HEAD(&pm->sources);
- return nvkm_engine_ctor(&nvkm_pm, device, index, true, &pm->engine);
- }
|