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>
34 #define QUAD_MASK 0x0f
35 #define QUAD_FREE 0x01
38 nvkm_pm_count_perfdom(struct nvkm_pm
*ppm
)
40 struct nvkm_perfdom
*dom
;
43 list_for_each_entry(dom
, &ppm
->domains
, head
)
49 nvkm_perfdom_count_perfsig(struct nvkm_perfdom
*dom
)
55 for (i
= 0; i
< dom
->signal_nr
; i
++) {
56 if (dom
->signal
[i
].name
)
63 static struct nvkm_perfdom
*
64 nvkm_perfdom_find(struct nvkm_pm
*ppm
, int di
)
66 struct nvkm_perfdom
*dom
;
69 list_for_each_entry(dom
, &ppm
->domains
, head
) {
76 static struct nvkm_perfsig
*
77 nvkm_perfsig_find_(struct nvkm_perfdom
*dom
, const char *name
, u32 size
)
83 for (i
= 0; i
< dom
->signal_nr
; i
++) {
84 if ( dom
->signal
[i
].name
&&
85 !strncmp(name
, dom
->signal
[i
].name
, size
))
86 return &dom
->signal
[i
];
89 for (i
= 0; i
< dom
->signal_nr
; i
++) {
90 snprintf(path
, sizeof(path
), "/%s/%02x", dom
->name
, i
);
91 if (!strncmp(name
, path
, size
))
92 return &dom
->signal
[i
];
100 nvkm_perfsig_find(struct nvkm_pm
*ppm
, const char *name
, u32 size
,
101 struct nvkm_perfdom
**pdom
)
103 struct nvkm_perfdom
*dom
= *pdom
;
104 struct nvkm_perfsig
*sig
;
107 list_for_each_entry(dom
, &ppm
->domains
, head
) {
108 sig
= nvkm_perfsig_find_(dom
, name
, size
);
118 return nvkm_perfsig_find_(dom
, name
, size
);
121 /*******************************************************************************
122 * Perfmon object classes
123 ******************************************************************************/
125 nvkm_perfmon_mthd_query_domain(struct nvkm_object
*object
, void *data
, u32 size
)
128 struct nvif_perfmon_query_domain_v0 v0
;
130 struct nvkm_pm
*ppm
= (void *)object
->engine
;
131 struct nvkm_perfdom
*dom
;
135 nv_ioctl(object
, "perfmon query domain size %d\n", size
);
136 if (nvif_unpack(args
->v0
, 0, 0, false)) {
137 nv_ioctl(object
, "perfmon domain vers %d iter %02x\n",
138 args
->v0
.version
, args
->v0
.iter
);
139 di
= (args
->v0
.iter
& 0xff) - 1;
143 domain_nr
= nvkm_pm_count_perfdom(ppm
);
144 if (di
>= (int)domain_nr
)
148 dom
= nvkm_perfdom_find(ppm
, di
);
153 args
->v0
.signal_nr
= nvkm_perfdom_count_perfsig(dom
);
155 /* Currently only global counters (PCOUNTER) are implemented
156 * but this will be different for local counters (MP). */
157 args
->v0
.counter_nr
= 4;
160 if (++di
< domain_nr
) {
161 args
->v0
.iter
= ++di
;
165 args
->v0
.iter
= 0xff;
170 nvkm_perfmon_mthd_query_signal(struct nvkm_object
*object
, void *data
, u32 size
)
173 struct nvif_perfmon_query_signal_v0 v0
;
175 struct nvkm_device
*device
= nv_device(object
);
176 struct nvkm_pm
*ppm
= (void *)object
->engine
;
177 struct nvkm_perfdom
*dom
;
178 const bool all
= nvkm_boolopt(device
->cfgopt
, "NvPmShowAll", false);
179 const bool raw
= nvkm_boolopt(device
->cfgopt
, "NvPmUnnamed", all
);
183 nv_ioctl(object
, "perfmon query signal size %d\n", size
);
184 if (nvif_unpack(args
->v0
, 0, 0, false)) {
186 "perfmon query signal vers %d dom %d iter %04x\n",
187 args
->v0
.version
, args
->v0
.domain
, args
->v0
.iter
);
188 si
= (args
->v0
.iter
& 0xffff) - 1;
192 dom
= nvkm_perfdom_find(ppm
, args
->v0
.domain
);
193 if (dom
== NULL
|| si
>= (int)dom
->signal_nr
)
197 if (raw
|| !(name
= dom
->signal
[si
].name
)) {
198 snprintf(args
->v0
.name
, sizeof(args
->v0
.name
),
199 "/%s/%02x", dom
->name
, si
);
201 strncpy(args
->v0
.name
, name
, sizeof(args
->v0
.name
));
205 while (++si
< dom
->signal_nr
) {
206 if (all
|| dom
->signal
[si
].name
) {
207 args
->v0
.iter
= ++si
;
212 args
->v0
.iter
= 0xffff;
217 nvkm_perfmon_mthd(struct nvkm_object
*object
, u32 mthd
, void *data
, u32 size
)
220 case NVIF_PERFMON_V0_QUERY_DOMAIN
:
221 return nvkm_perfmon_mthd_query_domain(object
, data
, size
);
222 case NVIF_PERFMON_V0_QUERY_SIGNAL
:
223 return nvkm_perfmon_mthd_query_signal(object
, data
, size
);
230 static struct nvkm_ofuncs
231 nvkm_perfmon_ofuncs
= {
232 .ctor
= _nvkm_object_ctor
,
233 .dtor
= nvkm_object_destroy
,
234 .init
= nvkm_object_init
,
235 .fini
= nvkm_object_fini
,
236 .mthd
= nvkm_perfmon_mthd
,
239 /*******************************************************************************
240 * Perfctr object classes
241 ******************************************************************************/
243 nvkm_perfctr_sample(struct nvkm_object
*object
, void *data
, u32 size
)
246 struct nvif_perfctr_sample none
;
248 struct nvkm_pm
*ppm
= (void *)object
->engine
;
249 struct nvkm_perfctr
*ctr
, *tmp
;
250 struct nvkm_perfdom
*dom
;
253 nv_ioctl(object
, "perfctr sample size %d\n", size
);
254 if (nvif_unvers(args
->none
)) {
255 nv_ioctl(object
, "perfctr sample\n");
260 list_for_each_entry(dom
, &ppm
->domains
, head
) {
261 /* sample previous batch of counters */
262 if (dom
->quad
!= QUAD_MASK
) {
263 dom
->func
->next(ppm
, dom
);
265 while (!list_empty(&dom
->list
)) {
266 ctr
= list_first_entry(&dom
->list
,
268 if (ctr
->slot
< 0) break;
269 if ( tmp
&& tmp
== ctr
) break;
271 dom
->func
->read(ppm
, dom
, ctr
);
273 list_move_tail(&ctr
->head
, &dom
->list
);
277 dom
->quad
= QUAD_MASK
;
279 /* setup next batch of counters for sampling */
280 list_for_each_entry(ctr
, &dom
->list
, head
) {
281 ctr
->slot
= ffs(dom
->quad
) - 1;
284 dom
->quad
&= ~(QUAD_FREE
<< ctr
->slot
);
285 dom
->func
->init(ppm
, dom
, ctr
);
288 if (dom
->quad
!= QUAD_MASK
)
289 dom
->func
->next(ppm
, dom
);
296 nvkm_perfctr_read(struct nvkm_object
*object
, void *data
, u32 size
)
299 struct nvif_perfctr_read_v0 v0
;
301 struct nvkm_perfctr
*ctr
= (void *)object
;
304 nv_ioctl(object
, "perfctr read size %d\n", size
);
305 if (nvif_unpack(args
->v0
, 0, 0, false)) {
306 nv_ioctl(object
, "perfctr read vers %d\n", args
->v0
.version
);
313 args
->v0
.clk
= ctr
->clk
;
314 args
->v0
.ctr
= ctr
->ctr
;
319 nvkm_perfctr_mthd(struct nvkm_object
*object
, u32 mthd
, void *data
, u32 size
)
322 case NVIF_PERFCTR_V0_SAMPLE
:
323 return nvkm_perfctr_sample(object
, data
, size
);
324 case NVIF_PERFCTR_V0_READ
:
325 return nvkm_perfctr_read(object
, data
, size
);
333 nvkm_perfctr_dtor(struct nvkm_object
*object
)
335 struct nvkm_perfctr
*ctr
= (void *)object
;
337 list_del(&ctr
->head
);
338 nvkm_object_destroy(&ctr
->base
);
342 nvkm_perfctr_ctor(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
343 struct nvkm_oclass
*oclass
, void *data
, u32 size
,
344 struct nvkm_object
**pobject
)
347 struct nvif_perfctr_v0 v0
;
349 struct nvkm_pm
*ppm
= (void *)engine
;
350 struct nvkm_perfdom
*dom
= NULL
;
351 struct nvkm_perfsig
*sig
[4] = {};
352 struct nvkm_perfctr
*ctr
;
355 nv_ioctl(parent
, "create perfctr size %d\n", size
);
356 if (nvif_unpack(args
->v0
, 0, 0, false)) {
357 nv_ioctl(parent
, "create perfctr vers %d logic_op %04x\n",
358 args
->v0
.version
, args
->v0
.logic_op
);
362 for (i
= 0; i
< ARRAY_SIZE(args
->v0
.name
) && args
->v0
.name
[i
][0]; i
++) {
363 sig
[i
] = nvkm_perfsig_find(ppm
, args
->v0
.name
[i
],
364 strnlen(args
->v0
.name
[i
],
365 sizeof(args
->v0
.name
[i
])),
374 ret
= nvkm_object_create(parent
, engine
, oclass
, 0, &ctr
);
375 *pobject
= nv_object(ctr
);
380 ctr
->logic_op
= args
->v0
.logic_op
;
381 ctr
->signal
[0] = sig
[0];
382 ctr
->signal
[1] = sig
[1];
383 ctr
->signal
[2] = sig
[2];
384 ctr
->signal
[3] = sig
[3];
385 list_add_tail(&ctr
->head
, &dom
->list
);
389 static struct nvkm_ofuncs
390 nvkm_perfctr_ofuncs
= {
391 .ctor
= nvkm_perfctr_ctor
,
392 .dtor
= nvkm_perfctr_dtor
,
393 .init
= nvkm_object_init
,
394 .fini
= nvkm_object_fini
,
395 .mthd
= nvkm_perfctr_mthd
,
401 .handle
= NVIF_IOCTL_NEW_V0_PERFMON
,
402 .ofuncs
= &nvkm_perfmon_ofuncs
,
404 { .handle
= NVIF_IOCTL_NEW_V0_PERFCTR
,
405 .ofuncs
= &nvkm_perfctr_ofuncs
,
410 /*******************************************************************************
412 ******************************************************************************/
414 nvkm_perfctx_dtor(struct nvkm_object
*object
)
416 struct nvkm_pm
*ppm
= (void *)object
->engine
;
417 struct nvkm_perfctx
*ctx
= (void *)object
;
419 mutex_lock(&nv_subdev(ppm
)->mutex
);
420 nvkm_engctx_destroy(&ctx
->base
);
421 if (ppm
->context
== ctx
)
423 mutex_unlock(&nv_subdev(ppm
)->mutex
);
427 nvkm_perfctx_ctor(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
428 struct nvkm_oclass
*oclass
, void *data
, u32 size
,
429 struct nvkm_object
**pobject
)
431 struct nvkm_pm
*ppm
= (void *)engine
;
432 struct nvkm_perfctx
*ctx
;
435 ret
= nvkm_engctx_create(parent
, engine
, oclass
, NULL
, 0, 0, 0, &ctx
);
436 *pobject
= nv_object(ctx
);
440 mutex_lock(&nv_subdev(ppm
)->mutex
);
441 if (ppm
->context
== NULL
)
443 if (ctx
!= ppm
->context
)
445 mutex_unlock(&nv_subdev(ppm
)->mutex
);
452 .handle
= NV_ENGCTX(PM
, 0x00),
453 .ofuncs
= &(struct nvkm_ofuncs
) {
454 .ctor
= nvkm_perfctx_ctor
,
455 .dtor
= nvkm_perfctx_dtor
,
456 .init
= _nvkm_engctx_init
,
457 .fini
= _nvkm_engctx_fini
,
461 /*******************************************************************************
462 * PPM engine/subdev functions
463 ******************************************************************************/
465 nvkm_perfdom_new(struct nvkm_pm
*ppm
, const char *name
, u32 mask
,
466 u32 base
, u32 size_unit
, u32 size_domain
,
467 const struct nvkm_specdom
*spec
)
469 const struct nvkm_specdom
*sdom
;
470 const struct nvkm_specsig
*ssig
;
471 struct nvkm_perfdom
*dom
;
474 for (i
= 0; i
== 0 || mask
; i
++) {
475 u32 addr
= base
+ (i
* size_unit
);
476 if (i
&& !(mask
& (1 << i
)))
480 while (sdom
->signal_nr
) {
481 dom
= kzalloc(sizeof(*dom
) + sdom
->signal_nr
*
482 sizeof(*dom
->signal
), GFP_KERNEL
);
487 snprintf(dom
->name
, sizeof(dom
->name
),
488 "%s/%02x/%02x", name
, i
,
491 snprintf(dom
->name
, sizeof(dom
->name
),
492 "%s/%02x", name
, (int)(sdom
- spec
));
495 list_add_tail(&dom
->head
, &ppm
->domains
);
496 INIT_LIST_HEAD(&dom
->list
);
497 dom
->func
= sdom
->func
;
499 dom
->quad
= QUAD_MASK
;
500 dom
->signal_nr
= sdom
->signal_nr
;
502 ssig
= (sdom
++)->signal
;
504 dom
->signal
[ssig
->signal
].name
= ssig
->name
;
518 _nvkm_pm_fini(struct nvkm_object
*object
, bool suspend
)
520 struct nvkm_pm
*ppm
= (void *)object
;
521 return nvkm_engine_fini(&ppm
->base
, suspend
);
525 _nvkm_pm_init(struct nvkm_object
*object
)
527 struct nvkm_pm
*ppm
= (void *)object
;
528 return nvkm_engine_init(&ppm
->base
);
532 _nvkm_pm_dtor(struct nvkm_object
*object
)
534 struct nvkm_pm
*ppm
= (void *)object
;
535 struct nvkm_perfdom
*dom
, *tmp
;
537 list_for_each_entry_safe(dom
, tmp
, &ppm
->domains
, head
) {
538 list_del(&dom
->head
);
542 nvkm_engine_destroy(&ppm
->base
);
546 nvkm_pm_create_(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
547 struct nvkm_oclass
*oclass
, int length
, void **pobject
)
552 ret
= nvkm_engine_create_(parent
, engine
, oclass
, true, "PPM",
553 "pm", length
, pobject
);
558 INIT_LIST_HEAD(&ppm
->domains
);