2 * Copyright 2013 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include <core/client.h>
27 #include <core/device.h>
28 #include <core/option.h>
30 #include <nvif/class.h>
31 #include <nvif/ioctl.h>
32 #include <nvif/unpack.h>
35 nvkm_pm_count_perfdom(struct nvkm_pm *ppm)
37 struct nvkm_perfdom *dom;
40 list_for_each_entry(dom, &ppm->domains, head)
46 nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
52 for (i = 0; i < dom->signal_nr; i++) {
53 if (dom->signal[i].name)
60 static struct nvkm_perfdom *
61 nvkm_perfdom_find(struct nvkm_pm *ppm, int di)
63 struct nvkm_perfdom *dom;
66 list_for_each_entry(dom, &ppm->domains, head) {
74 nvkm_perfsig_find(struct nvkm_pm *ppm, uint8_t di, uint8_t si,
75 struct nvkm_perfdom **pdom)
77 struct nvkm_perfdom *dom = *pdom;
80 dom = nvkm_perfdom_find(ppm, di);
86 if (!dom->signal[si].name)
88 return &dom->signal[si];
92 nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig)
96 for (i = 0; i < ARRAY_SIZE(sig->source); i++) {
103 static struct nvkm_perfsrc *
104 nvkm_perfsrc_find(struct nvkm_pm *ppm, struct nvkm_perfsig *sig, int si)
106 struct nvkm_perfsrc *src;
108 int tmp = 1; /* Sources ID start from 1 */
111 for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) {
112 if (sig->source[i] == si) {
119 list_for_each_entry(src, &ppm->sources, head) {
128 /*******************************************************************************
129 * Perfmon object classes
130 ******************************************************************************/
132 nvkm_perfmon_mthd_query_domain(struct nvkm_object *object, void *data, u32 size)
135 struct nvif_perfmon_query_domain_v0 v0;
137 struct nvkm_pm *ppm = (void *)object->engine;
138 struct nvkm_perfdom *dom;
142 nv_ioctl(object, "perfmon query domain size %d\n", size);
143 if (nvif_unpack(args->v0, 0, 0, false)) {
144 nv_ioctl(object, "perfmon domain vers %d iter %02x\n",
145 args->v0.version, args->v0.iter);
146 di = (args->v0.iter & 0xff) - 1;
150 domain_nr = nvkm_pm_count_perfdom(ppm);
151 if (di >= (int)domain_nr)
155 dom = nvkm_perfdom_find(ppm, di);
160 args->v0.signal_nr = nvkm_perfdom_count_perfsig(dom);
162 /* Currently only global counters (PCOUNTER) are implemented
163 * but this will be different for local counters (MP). */
164 args->v0.counter_nr = 4;
167 if (++di < domain_nr) {
168 args->v0.iter = ++di;
172 args->v0.iter = 0xff;
177 nvkm_perfmon_mthd_query_signal(struct nvkm_object *object, void *data, u32 size)
180 struct nvif_perfmon_query_signal_v0 v0;
182 struct nvkm_device *device = nv_device(object);
183 struct nvkm_pm *ppm = (void *)object->engine;
184 struct nvkm_perfdom *dom;
185 struct nvkm_perfsig *sig;
186 const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
187 const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all);
190 nv_ioctl(object, "perfmon query signal size %d\n", size);
191 if (nvif_unpack(args->v0, 0, 0, false)) {
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;
199 dom = nvkm_perfdom_find(ppm, args->v0.domain);
200 if (dom == NULL || si >= (int)dom->signal_nr)
204 sig = &dom->signal[si];
205 if (raw || !sig->name) {
206 snprintf(args->v0.name, sizeof(args->v0.name),
207 "/%s/%02x", dom->name, si);
209 strncpy(args->v0.name, sig->name,
210 sizeof(args->v0.name));
213 args->v0.signal = si;
214 args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
217 while (++si < dom->signal_nr) {
218 if (all || dom->signal[si].name) {
219 args->v0.iter = ++si;
224 args->v0.iter = 0xffff;
229 nvkm_perfmon_mthd_query_source(struct nvkm_object *object, void *data, u32 size)
232 struct nvif_perfmon_query_source_v0 v0;
234 struct nvkm_pm *ppm = (void *)object->engine;
235 struct nvkm_perfdom *dom = NULL;
236 struct nvkm_perfsig *sig;
237 struct nvkm_perfsrc *src;
241 nv_ioctl(object, "perfmon query source size %d\n", size);
242 if (nvif_unpack(args->v0, 0, 0, false)) {
244 "perfmon source vers %d dom %d sig %02x iter %02x\n",
245 args->v0.version, args->v0.domain, args->v0.signal,
247 si = (args->v0.iter & 0xff) - 1;
251 sig = nvkm_perfsig_find(ppm, args->v0.domain, args->v0.signal, &dom);
255 source_nr = nvkm_perfsig_count_perfsrc(sig);
256 if (si >= (int)source_nr)
260 src = nvkm_perfsrc_find(ppm, sig, sig->source[si]);
264 args->v0.source = sig->source[si];
265 args->v0.mask = src->mask;
266 strncpy(args->v0.name, src->name, sizeof(args->v0.name));
269 if (++si < source_nr) {
270 args->v0.iter = ++si;
274 args->v0.iter = 0xff;
279 nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
282 case NVIF_PERFMON_V0_QUERY_DOMAIN:
283 return nvkm_perfmon_mthd_query_domain(object, data, size);
284 case NVIF_PERFMON_V0_QUERY_SIGNAL:
285 return nvkm_perfmon_mthd_query_signal(object, data, size);
286 case NVIF_PERFMON_V0_QUERY_SOURCE:
287 return nvkm_perfmon_mthd_query_source(object, data, size);
294 static struct nvkm_ofuncs
295 nvkm_perfmon_ofuncs = {
296 .ctor = _nvkm_object_ctor,
297 .dtor = nvkm_object_destroy,
298 .init = nvkm_object_init,
299 .fini = nvkm_object_fini,
300 .mthd = nvkm_perfmon_mthd,
303 /*******************************************************************************
304 * Perfdom object classes
305 ******************************************************************************/
307 nvkm_perfdom_init(struct nvkm_object *object, void *data, u32 size)
310 struct nvif_perfdom_init none;
312 struct nvkm_pm *ppm = (void *)object->engine;
313 struct nvkm_perfdom *dom = (void *)object;
316 nv_ioctl(object, "perfdom init size %d\n", size);
317 if (nvif_unvers(args->none)) {
318 nv_ioctl(object, "perfdom init\n");
322 for (i = 0; i < 4; i++)
324 dom->func->init(ppm, dom, dom->ctr[i]);
326 /* start next batch of counters for sampling */
327 dom->func->next(ppm, dom);
332 nvkm_perfdom_sample(struct nvkm_object *object, void *data, u32 size)
335 struct nvif_perfdom_sample none;
337 struct nvkm_pm *ppm = (void *)object->engine;
338 struct nvkm_perfdom *dom;
341 nv_ioctl(object, "perfdom sample size %d\n", size);
342 if (nvif_unvers(args->none)) {
343 nv_ioctl(object, "perfdom sample\n");
348 /* sample previous batch of counters */
349 list_for_each_entry(dom, &ppm->domains, head)
350 dom->func->next(ppm, dom);
356 nvkm_perfdom_read(struct nvkm_object *object, void *data, u32 size)
359 struct nvif_perfdom_read_v0 v0;
361 struct nvkm_pm *ppm = (void *)object->engine;
362 struct nvkm_perfdom *dom = (void *)object;
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);
371 for (i = 0; i < 4; i++) {
373 dom->func->read(ppm, dom, dom->ctr[i]);
379 for (i = 0; i < 4; i++)
381 args->v0.ctr[i] = dom->ctr[i]->ctr;
382 args->v0.clk = dom->clk;
387 nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
390 case NVIF_PERFDOM_V0_INIT:
391 return nvkm_perfdom_init(object, data, size);
392 case NVIF_PERFDOM_V0_SAMPLE:
393 return nvkm_perfdom_sample(object, data, size);
394 case NVIF_PERFDOM_V0_READ:
395 return nvkm_perfdom_read(object, data, size);
403 nvkm_perfdom_dtor(struct nvkm_object *object)
405 struct nvkm_perfdom *dom = (void *)object;
408 for (i = 0; i < 4; i++) {
409 struct nvkm_perfctr *ctr = dom->ctr[i];
410 if (ctr && ctr->head.next)
411 list_del(&ctr->head);
414 nvkm_object_destroy(&dom->base);
418 nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot,
419 struct nvkm_perfsig *signal[4], uint16_t logic_op,
420 struct nvkm_perfctr **pctr)
422 struct nvkm_perfctr *ctr;
428 ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
432 ctr->logic_op = logic_op;
434 for (i = 0; i < 4; i++) {
436 ctr->signal[i] = signal[i] - dom->signal;
438 list_add_tail(&ctr->head, &dom->list);
444 nvkm_perfdom_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
445 struct nvkm_oclass *oclass, void *data, u32 size,
446 struct nvkm_object **pobject)
449 struct nvif_perfdom_v0 v0;
451 struct nvkm_pm *ppm = (void *)engine;
452 struct nvkm_perfdom *sdom = NULL;
453 struct nvkm_perfctr *ctr[4] = {};
454 struct nvkm_perfdom *dom;
458 nv_ioctl(parent, "create perfdom size %d\n", size);
459 if (nvif_unpack(args->v0, 0, 0, false)) {
460 nv_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
461 args->v0.version, args->v0.domain, args->v0.mode);
465 for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
466 struct nvkm_perfsig *sig[4] = {};
467 for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
468 sig[s] = nvkm_perfsig_find(ppm, args->v0.domain,
469 args->v0.ctr[c].signal[s],
471 if (args->v0.ctr[c].signal[s] && !sig[s])
475 ret = nvkm_perfctr_new(sdom, c, sig,
476 args->v0.ctr[c].logic_op, &ctr[c]);
484 ret = nvkm_object_create(parent, engine, oclass, 0, &dom);
485 *pobject = nv_object(dom);
489 dom->func = sdom->func;
490 dom->addr = sdom->addr;
491 dom->mode = args->v0.mode;
492 for (c = 0; c < ARRAY_SIZE(ctr); c++)
493 dom->ctr[c] = ctr[c];
497 static struct nvkm_ofuncs
498 nvkm_perfdom_ofuncs = {
499 .ctor = nvkm_perfdom_ctor,
500 .dtor = nvkm_perfdom_dtor,
501 .init = nvkm_object_init,
502 .fini = nvkm_object_fini,
503 .mthd = nvkm_perfdom_mthd,
509 .handle = NVIF_IOCTL_NEW_V0_PERFMON,
510 .ofuncs = &nvkm_perfmon_ofuncs,
512 { .handle = NVIF_IOCTL_NEW_V0_PERFDOM,
513 .ofuncs = &nvkm_perfdom_ofuncs,
518 /*******************************************************************************
520 ******************************************************************************/
522 nvkm_perfctx_dtor(struct nvkm_object *object)
524 struct nvkm_pm *ppm = (void *)object->engine;
525 struct nvkm_perfctx *ctx = (void *)object;
527 mutex_lock(&nv_subdev(ppm)->mutex);
528 nvkm_engctx_destroy(&ctx->base);
529 if (ppm->context == ctx)
531 mutex_unlock(&nv_subdev(ppm)->mutex);
535 nvkm_perfctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
536 struct nvkm_oclass *oclass, void *data, u32 size,
537 struct nvkm_object **pobject)
539 struct nvkm_pm *ppm = (void *)engine;
540 struct nvkm_perfctx *ctx;
543 ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx);
544 *pobject = nv_object(ctx);
548 mutex_lock(&nv_subdev(ppm)->mutex);
549 if (ppm->context == NULL)
551 if (ctx != ppm->context)
553 mutex_unlock(&nv_subdev(ppm)->mutex);
560 .handle = NV_ENGCTX(PM, 0x00),
561 .ofuncs = &(struct nvkm_ofuncs) {
562 .ctor = nvkm_perfctx_ctor,
563 .dtor = nvkm_perfctx_dtor,
564 .init = _nvkm_engctx_init,
565 .fini = _nvkm_engctx_fini,
569 /*******************************************************************************
570 * PPM engine/subdev functions
571 ******************************************************************************/
573 nvkm_perfsrc_new(struct nvkm_pm *ppm, struct nvkm_perfsig *sig,
574 const struct nvkm_specsrc *spec)
576 const struct nvkm_specsrc *ssrc;
577 const struct nvkm_specmux *smux;
578 struct nvkm_perfsrc *src;
582 /* No sources are defined for this signal. */
594 list_for_each_entry(src, &ppm->sources, head) {
595 if (src->addr == ssrc->addr &&
596 src->shift == smux->shift) {
604 src = kzalloc(sizeof(*src), GFP_KERNEL);
608 src->addr = ssrc->addr;
609 src->mask = smux->mask;
610 src->shift = smux->shift;
611 src->enable = smux->enable;
613 len = strlen(ssrc->name) +
614 strlen(smux->name) + 2;
615 src->name = kzalloc(len, GFP_KERNEL);
618 snprintf(src->name, len, "%s_%s", ssrc->name,
621 list_add_tail(&src->head, &ppm->sources);
624 sig->source[source_nr++] = source_id + 1;
634 nvkm_perfdom_new(struct nvkm_pm *ppm, const char *name, u32 mask,
635 u32 base, u32 size_unit, u32 size_domain,
636 const struct nvkm_specdom *spec)
638 const struct nvkm_specdom *sdom;
639 const struct nvkm_specsig *ssig;
640 struct nvkm_perfdom *dom;
643 for (i = 0; i == 0 || mask; i++) {
644 u32 addr = base + (i * size_unit);
645 if (i && !(mask & (1 << i)))
649 while (sdom->signal_nr) {
650 dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
651 sizeof(*dom->signal), GFP_KERNEL);
656 snprintf(dom->name, sizeof(dom->name),
657 "%s/%02x/%02x", name, i,
660 snprintf(dom->name, sizeof(dom->name),
661 "%s/%02x", name, (int)(sdom - spec));
664 list_add_tail(&dom->head, &ppm->domains);
665 INIT_LIST_HEAD(&dom->list);
666 dom->func = sdom->func;
668 dom->signal_nr = sdom->signal_nr;
670 ssig = (sdom++)->signal;
672 struct nvkm_perfsig *sig =
673 &dom->signal[ssig->signal];
674 sig->name = ssig->name;
675 ret = nvkm_perfsrc_new(ppm, sig, ssig->source);
691 _nvkm_pm_fini(struct nvkm_object *object, bool suspend)
693 struct nvkm_pm *ppm = (void *)object;
694 return nvkm_engine_fini(&ppm->base, suspend);
698 _nvkm_pm_init(struct nvkm_object *object)
700 struct nvkm_pm *ppm = (void *)object;
701 return nvkm_engine_init(&ppm->base);
705 _nvkm_pm_dtor(struct nvkm_object *object)
707 struct nvkm_pm *ppm = (void *)object;
708 struct nvkm_perfdom *dom, *next_dom;
709 struct nvkm_perfsrc *src, *next_src;
711 list_for_each_entry_safe(dom, next_dom, &ppm->domains, head) {
712 list_del(&dom->head);
716 list_for_each_entry_safe(src, next_src, &ppm->sources, head) {
717 list_del(&src->head);
722 nvkm_engine_destroy(&ppm->base);
726 nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
727 struct nvkm_oclass *oclass, int length, void **pobject)
732 ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM",
733 "pm", length, pobject);
738 INIT_LIST_HEAD(&ppm->domains);
739 INIT_LIST_HEAD(&ppm->sources);