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
37 static struct nvkm_perfsig
*
38 nvkm_perfsig_find_(struct nvkm_perfdom
*dom
, const char *name
, u32 size
)
44 for (i
= 0; i
< dom
->signal_nr
; i
++) {
45 if ( dom
->signal
[i
].name
&&
46 !strncmp(name
, dom
->signal
[i
].name
, size
))
47 return &dom
->signal
[i
];
50 for (i
= 0; i
< dom
->signal_nr
; i
++) {
51 snprintf(path
, sizeof(path
), "/%s/%02x", dom
->name
, i
);
52 if (!strncmp(name
, path
, size
))
53 return &dom
->signal
[i
];
61 nvkm_perfsig_find(struct nvkm_pm
*ppm
, const char *name
, u32 size
,
62 struct nvkm_perfdom
**pdom
)
64 struct nvkm_perfdom
*dom
= *pdom
;
65 struct nvkm_perfsig
*sig
;
68 list_for_each_entry(dom
, &ppm
->domains
, head
) {
69 sig
= nvkm_perfsig_find_(dom
, name
, size
);
79 return nvkm_perfsig_find_(dom
, name
, size
);
83 nvkm_perfsig_wrap(struct nvkm_pm
*ppm
, const char *name
,
84 struct nvkm_perfdom
**pdom
)
86 struct nvkm_perfsig
*sig
;
87 struct nvkm_perfctr
*ctr
;
89 sig
= nvkm_perfsig_find(ppm
, name
, strlen(name
), pdom
);
93 ctr
= kzalloc(sizeof(*ctr
), GFP_KERNEL
);
96 ctr
->logic_op
= 0xaaaa;
102 /*******************************************************************************
103 * Perfmon object classes
104 ******************************************************************************/
106 nvkm_perfctr_query(struct nvkm_object
*object
, void *data
, u32 size
)
109 struct nvif_perfctr_query_v0 v0
;
111 struct nvkm_device
*device
= nv_device(object
);
112 struct nvkm_pm
*ppm
= (void *)object
->engine
;
113 struct nvkm_perfdom
*dom
= NULL
, *chk
;
114 const bool all
= nvkm_boolopt(device
->cfgopt
, "NvPmShowAll", false);
115 const bool raw
= nvkm_boolopt(device
->cfgopt
, "NvPmUnnamed", all
);
120 nv_ioctl(object
, "perfctr query size %d\n", size
);
121 if (nvif_unpack(args
->v0
, 0, 0, false)) {
122 nv_ioctl(object
, "perfctr query vers %d iter %08x\n",
123 args
->v0
.version
, args
->v0
.iter
);
124 di
= (args
->v0
.iter
& 0xff000000) >> 24;
125 si
= (args
->v0
.iter
& 0x00ffffff) - 1;
129 list_for_each_entry(chk
, &ppm
->domains
, head
) {
136 if (dom
== NULL
|| si
>= (int)dom
->signal_nr
)
140 if (raw
|| !(name
= dom
->signal
[si
].name
)) {
141 snprintf(args
->v0
.name
, sizeof(args
->v0
.name
),
142 "/%s/%02x", dom
->name
, si
);
144 strncpy(args
->v0
.name
, name
, sizeof(args
->v0
.name
));
149 while (++si
< dom
->signal_nr
) {
150 if (all
|| dom
->signal
[si
].name
) {
151 args
->v0
.iter
= (di
<< 24) | ++si
;
157 dom
= list_entry(dom
->head
.next
, typeof(*dom
), head
);
158 } while (&dom
->head
!= &ppm
->domains
);
160 args
->v0
.iter
= 0xffffffff;
165 nvkm_perfctr_sample(struct nvkm_object
*object
, void *data
, u32 size
)
168 struct nvif_perfctr_sample none
;
170 struct nvkm_pm
*ppm
= (void *)object
->engine
;
171 struct nvkm_perfctr
*ctr
, *tmp
;
172 struct nvkm_perfdom
*dom
;
175 nv_ioctl(object
, "perfctr sample size %d\n", size
);
176 if (nvif_unvers(args
->none
)) {
177 nv_ioctl(object
, "perfctr sample\n");
182 list_for_each_entry(dom
, &ppm
->domains
, head
) {
183 /* sample previous batch of counters */
184 if (dom
->quad
!= QUAD_MASK
) {
185 dom
->func
->next(ppm
, dom
);
187 while (!list_empty(&dom
->list
)) {
188 ctr
= list_first_entry(&dom
->list
,
190 if (ctr
->slot
< 0) break;
191 if ( tmp
&& tmp
== ctr
) break;
193 dom
->func
->read(ppm
, dom
, ctr
);
195 list_move_tail(&ctr
->head
, &dom
->list
);
199 dom
->quad
= QUAD_MASK
;
201 /* setup next batch of counters for sampling */
202 list_for_each_entry(ctr
, &dom
->list
, head
) {
203 ctr
->slot
= ffs(dom
->quad
) - 1;
206 dom
->quad
&= ~(QUAD_FREE
<< ctr
->slot
);
207 dom
->func
->init(ppm
, dom
, ctr
);
210 if (dom
->quad
!= QUAD_MASK
)
211 dom
->func
->next(ppm
, dom
);
218 nvkm_perfctr_read(struct nvkm_object
*object
, void *data
, u32 size
)
221 struct nvif_perfctr_read_v0 v0
;
223 struct nvkm_perfctr
*ctr
= (void *)object
;
226 nv_ioctl(object
, "perfctr read size %d\n", size
);
227 if (nvif_unpack(args
->v0
, 0, 0, false)) {
228 nv_ioctl(object
, "perfctr read vers %d\n", args
->v0
.version
);
235 args
->v0
.clk
= ctr
->clk
;
236 args
->v0
.ctr
= ctr
->ctr
;
241 nvkm_perfctr_mthd(struct nvkm_object
*object
, u32 mthd
, void *data
, u32 size
)
244 case NVIF_PERFCTR_V0_QUERY
:
245 return nvkm_perfctr_query(object
, data
, size
);
246 case NVIF_PERFCTR_V0_SAMPLE
:
247 return nvkm_perfctr_sample(object
, data
, size
);
248 case NVIF_PERFCTR_V0_READ
:
249 return nvkm_perfctr_read(object
, data
, size
);
257 nvkm_perfctr_dtor(struct nvkm_object
*object
)
259 struct nvkm_perfctr
*ctr
= (void *)object
;
261 list_del(&ctr
->head
);
262 nvkm_object_destroy(&ctr
->base
);
266 nvkm_perfctr_ctor(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
267 struct nvkm_oclass
*oclass
, void *data
, u32 size
,
268 struct nvkm_object
**pobject
)
271 struct nvif_perfctr_v0 v0
;
273 struct nvkm_pm
*ppm
= (void *)engine
;
274 struct nvkm_perfdom
*dom
= NULL
;
275 struct nvkm_perfsig
*sig
[4] = {};
276 struct nvkm_perfctr
*ctr
;
279 nv_ioctl(parent
, "create perfctr size %d\n", size
);
280 if (nvif_unpack(args
->v0
, 0, 0, false)) {
281 nv_ioctl(parent
, "create perfctr vers %d logic_op %04x\n",
282 args
->v0
.version
, args
->v0
.logic_op
);
286 for (i
= 0; i
< ARRAY_SIZE(args
->v0
.name
) && args
->v0
.name
[i
][0]; i
++) {
287 sig
[i
] = nvkm_perfsig_find(ppm
, args
->v0
.name
[i
],
288 strnlen(args
->v0
.name
[i
],
289 sizeof(args
->v0
.name
[i
])),
295 ret
= nvkm_object_create(parent
, engine
, oclass
, 0, &ctr
);
296 *pobject
= nv_object(ctr
);
301 ctr
->logic_op
= args
->v0
.logic_op
;
302 ctr
->signal
[0] = sig
[0];
303 ctr
->signal
[1] = sig
[1];
304 ctr
->signal
[2] = sig
[2];
305 ctr
->signal
[3] = sig
[3];
307 list_add_tail(&ctr
->head
, &dom
->list
);
311 static struct nvkm_ofuncs
312 nvkm_perfctr_ofuncs
= {
313 .ctor
= nvkm_perfctr_ctor
,
314 .dtor
= nvkm_perfctr_dtor
,
315 .init
= nvkm_object_init
,
316 .fini
= nvkm_object_fini
,
317 .mthd
= nvkm_perfctr_mthd
,
322 { .handle
= NVIF_IOCTL_NEW_V0_PERFCTR
,
323 .ofuncs
= &nvkm_perfctr_ofuncs
,
328 /*******************************************************************************
330 ******************************************************************************/
332 nvkm_perfctx_dtor(struct nvkm_object
*object
)
334 struct nvkm_pm
*ppm
= (void *)object
->engine
;
335 mutex_lock(&nv_subdev(ppm
)->mutex
);
336 nvkm_engctx_destroy(&ppm
->context
->base
);
338 mutex_unlock(&nv_subdev(ppm
)->mutex
);
342 nvkm_perfctx_ctor(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
343 struct nvkm_oclass
*oclass
, void *data
, u32 size
,
344 struct nvkm_object
**pobject
)
346 struct nvkm_pm
*ppm
= (void *)engine
;
347 struct nvkm_perfctx
*ctx
;
350 ret
= nvkm_engctx_create(parent
, engine
, oclass
, NULL
, 0, 0, 0, &ctx
);
351 *pobject
= nv_object(ctx
);
355 mutex_lock(&nv_subdev(ppm
)->mutex
);
356 if (ppm
->context
== NULL
)
358 mutex_unlock(&nv_subdev(ppm
)->mutex
);
360 if (ctx
!= ppm
->context
)
368 .handle
= NV_ENGCTX(PM
, 0x00),
369 .ofuncs
= &(struct nvkm_ofuncs
) {
370 .ctor
= nvkm_perfctx_ctor
,
371 .dtor
= nvkm_perfctx_dtor
,
372 .init
= _nvkm_engctx_init
,
373 .fini
= _nvkm_engctx_fini
,
377 /*******************************************************************************
378 * PPM engine/subdev functions
379 ******************************************************************************/
381 nvkm_perfdom_new(struct nvkm_pm
*ppm
, const char *name
, u32 mask
,
382 u32 base
, u32 size_unit
, u32 size_domain
,
383 const struct nvkm_specdom
*spec
)
385 const struct nvkm_specdom
*sdom
;
386 const struct nvkm_specsig
*ssig
;
387 struct nvkm_perfdom
*dom
;
390 for (i
= 0; i
== 0 || mask
; i
++) {
391 u32 addr
= base
+ (i
* size_unit
);
392 if (i
&& !(mask
& (1 << i
)))
396 while (sdom
->signal_nr
) {
397 dom
= kzalloc(sizeof(*dom
) + sdom
->signal_nr
*
398 sizeof(*dom
->signal
), GFP_KERNEL
);
403 snprintf(dom
->name
, sizeof(dom
->name
),
404 "%s/%02x/%02x", name
, i
,
407 snprintf(dom
->name
, sizeof(dom
->name
),
408 "%s/%02x", name
, (int)(sdom
- spec
));
411 list_add_tail(&dom
->head
, &ppm
->domains
);
412 INIT_LIST_HEAD(&dom
->list
);
413 dom
->func
= sdom
->func
;
415 dom
->quad
= QUAD_MASK
;
416 dom
->signal_nr
= sdom
->signal_nr
;
418 ssig
= (sdom
++)->signal
;
420 dom
->signal
[ssig
->signal
].name
= ssig
->name
;
434 _nvkm_pm_fini(struct nvkm_object
*object
, bool suspend
)
436 struct nvkm_pm
*ppm
= (void *)object
;
437 return nvkm_engine_fini(&ppm
->base
, suspend
);
441 _nvkm_pm_init(struct nvkm_object
*object
)
443 struct nvkm_pm
*ppm
= (void *)object
;
444 return nvkm_engine_init(&ppm
->base
);
448 _nvkm_pm_dtor(struct nvkm_object
*object
)
450 struct nvkm_pm
*ppm
= (void *)object
;
451 struct nvkm_perfdom
*dom
, *tmp
;
453 list_for_each_entry_safe(dom
, tmp
, &ppm
->domains
, head
) {
454 list_del(&dom
->head
);
458 nvkm_engine_destroy(&ppm
->base
);
462 nvkm_pm_create_(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
463 struct nvkm_oclass
*oclass
, int length
, void **pobject
)
468 ret
= nvkm_engine_create_(parent
, engine
, oclass
, true, "PPM",
469 "pm", length
, pobject
);
474 INIT_LIST_HEAD(&ppm
->domains
);