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.
25 #include <core/client.h>
26 #include <core/option.h>
27 #include <nvif/unpack.h>
28 #include <nvif/class.h>
29 #include <nvif/ioctl.h>
31 #include <subdev/clock.h>
35 #define QUAD_MASK 0x0f
36 #define QUAD_FREE 0x01
38 static struct nouveau_perfsig
*
39 nouveau_perfsig_find_(struct nouveau_perfdom
*dom
, const char *name
, u32 size
)
45 for (i
= 0; i
< dom
->signal_nr
; i
++) {
46 if ( dom
->signal
[i
].name
&&
47 !strncmp(name
, dom
->signal
[i
].name
, size
))
48 return &dom
->signal
[i
];
51 for (i
= 0; i
< dom
->signal_nr
; i
++) {
52 snprintf(path
, sizeof(path
), "/%s/%02x", dom
->name
, i
);
53 if (!strncmp(name
, path
, size
))
54 return &dom
->signal
[i
];
61 struct nouveau_perfsig
*
62 nouveau_perfsig_find(struct nouveau_perfmon
*ppm
, const char *name
, u32 size
,
63 struct nouveau_perfdom
**pdom
)
65 struct nouveau_perfdom
*dom
= *pdom
;
66 struct nouveau_perfsig
*sig
;
69 list_for_each_entry(dom
, &ppm
->domains
, head
) {
70 sig
= nouveau_perfsig_find_(dom
, name
, size
);
80 return nouveau_perfsig_find_(dom
, name
, size
);
83 struct nouveau_perfctr
*
84 nouveau_perfsig_wrap(struct nouveau_perfmon
*ppm
, const char *name
,
85 struct nouveau_perfdom
**pdom
)
87 struct nouveau_perfsig
*sig
;
88 struct nouveau_perfctr
*ctr
;
90 sig
= nouveau_perfsig_find(ppm
, name
, strlen(name
), pdom
);
94 ctr
= kzalloc(sizeof(*ctr
), GFP_KERNEL
);
97 ctr
->logic_op
= 0xaaaa;
103 /*******************************************************************************
104 * Perfmon object classes
105 ******************************************************************************/
107 nouveau_perfctr_query(struct nouveau_object
*object
, void *data
, u32 size
)
110 struct nvif_perfctr_query_v0 v0
;
112 struct nouveau_device
*device
= nv_device(object
);
113 struct nouveau_perfmon
*ppm
= (void *)object
->engine
;
114 struct nouveau_perfdom
*dom
= NULL
, *chk
;
115 const bool all
= nouveau_boolopt(device
->cfgopt
, "NvPmShowAll", false);
116 const bool raw
= nouveau_boolopt(device
->cfgopt
, "NvPmUnnamed", all
);
121 nv_ioctl(object
, "perfctr query size %d\n", size
);
122 if (nvif_unpack(args
->v0
, 0, 0, false)) {
123 nv_ioctl(object
, "perfctr query vers %d iter %08x\n",
124 args
->v0
.version
, args
->v0
.iter
);
125 di
= (args
->v0
.iter
& 0xff000000) >> 24;
126 si
= (args
->v0
.iter
& 0x00ffffff) - 1;
130 list_for_each_entry(chk
, &ppm
->domains
, head
) {
137 if (dom
== NULL
|| si
>= (int)dom
->signal_nr
)
141 if (raw
|| !(name
= dom
->signal
[si
].name
)) {
142 snprintf(args
->v0
.name
, sizeof(args
->v0
.name
),
143 "/%s/%02x", dom
->name
, si
);
145 strncpy(args
->v0
.name
, name
, sizeof(args
->v0
.name
));
150 while (++si
< dom
->signal_nr
) {
151 if (all
|| dom
->signal
[si
].name
) {
152 args
->v0
.iter
= (di
<< 24) | ++si
;
158 dom
= list_entry(dom
->head
.next
, typeof(*dom
), head
);
159 } while (&dom
->head
!= &ppm
->domains
);
161 args
->v0
.iter
= 0xffffffff;
166 nouveau_perfctr_sample(struct nouveau_object
*object
, void *data
, u32 size
)
169 struct nvif_perfctr_sample none
;
171 struct nouveau_perfmon
*ppm
= (void *)object
->engine
;
172 struct nouveau_perfctr
*ctr
, *tmp
;
173 struct nouveau_perfdom
*dom
;
176 nv_ioctl(object
, "perfctr sample size %d\n", size
);
177 if (nvif_unvers(args
->none
)) {
178 nv_ioctl(object
, "perfctr sample\n");
183 list_for_each_entry(dom
, &ppm
->domains
, head
) {
184 /* sample previous batch of counters */
185 if (dom
->quad
!= QUAD_MASK
) {
186 dom
->func
->next(ppm
, dom
);
188 while (!list_empty(&dom
->list
)) {
189 ctr
= list_first_entry(&dom
->list
,
191 if (ctr
->slot
< 0) break;
192 if ( tmp
&& tmp
== ctr
) break;
194 dom
->func
->read(ppm
, dom
, ctr
);
196 list_move_tail(&ctr
->head
, &dom
->list
);
200 dom
->quad
= QUAD_MASK
;
202 /* setup next batch of counters for sampling */
203 list_for_each_entry(ctr
, &dom
->list
, head
) {
204 ctr
->slot
= ffs(dom
->quad
) - 1;
207 dom
->quad
&= ~(QUAD_FREE
<< ctr
->slot
);
208 dom
->func
->init(ppm
, dom
, ctr
);
211 if (dom
->quad
!= QUAD_MASK
)
212 dom
->func
->next(ppm
, dom
);
219 nouveau_perfctr_read(struct nouveau_object
*object
, void *data
, u32 size
)
222 struct nvif_perfctr_read_v0 v0
;
224 struct nouveau_perfctr
*ctr
= (void *)object
;
227 nv_ioctl(object
, "perfctr read size %d\n", size
);
228 if (nvif_unpack(args
->v0
, 0, 0, false)) {
229 nv_ioctl(object
, "perfctr read vers %d\n", args
->v0
.version
);
236 args
->v0
.clk
= ctr
->clk
;
237 args
->v0
.ctr
= ctr
->ctr
;
242 nouveau_perfctr_mthd(struct nouveau_object
*object
, u32 mthd
,
243 void *data
, u32 size
)
246 case NVIF_PERFCTR_V0_QUERY
:
247 return nouveau_perfctr_query(object
, data
, size
);
248 case NVIF_PERFCTR_V0_SAMPLE
:
249 return nouveau_perfctr_sample(object
, data
, size
);
250 case NVIF_PERFCTR_V0_READ
:
251 return nouveau_perfctr_read(object
, data
, size
);
259 nouveau_perfctr_dtor(struct nouveau_object
*object
)
261 struct nouveau_perfctr
*ctr
= (void *)object
;
263 list_del(&ctr
->head
);
264 nouveau_object_destroy(&ctr
->base
);
268 nouveau_perfctr_ctor(struct nouveau_object
*parent
,
269 struct nouveau_object
*engine
,
270 struct nouveau_oclass
*oclass
, void *data
, u32 size
,
271 struct nouveau_object
**pobject
)
274 struct nvif_perfctr_v0 v0
;
276 struct nouveau_perfmon
*ppm
= (void *)engine
;
277 struct nouveau_perfdom
*dom
= NULL
;
278 struct nouveau_perfsig
*sig
[4] = {};
279 struct nouveau_perfctr
*ctr
;
282 nv_ioctl(parent
, "create perfctr size %d\n", size
);
283 if (nvif_unpack(args
->v0
, 0, 0, false)) {
284 nv_ioctl(parent
, "create perfctr vers %d logic_op %04x\n",
285 args
->v0
.version
, args
->v0
.logic_op
);
289 for (i
= 0; i
< ARRAY_SIZE(args
->v0
.name
) && args
->v0
.name
[i
][0]; i
++) {
290 sig
[i
] = nouveau_perfsig_find(ppm
, args
->v0
.name
[i
],
291 strnlen(args
->v0
.name
[i
],
292 sizeof(args
->v0
.name
[i
])),
298 ret
= nouveau_object_create(parent
, engine
, oclass
, 0, &ctr
);
299 *pobject
= nv_object(ctr
);
304 ctr
->logic_op
= args
->v0
.logic_op
;
305 ctr
->signal
[0] = sig
[0];
306 ctr
->signal
[1] = sig
[1];
307 ctr
->signal
[2] = sig
[2];
308 ctr
->signal
[3] = sig
[3];
310 list_add_tail(&ctr
->head
, &dom
->list
);
314 static struct nouveau_ofuncs
315 nouveau_perfctr_ofuncs
= {
316 .ctor
= nouveau_perfctr_ctor
,
317 .dtor
= nouveau_perfctr_dtor
,
318 .init
= nouveau_object_init
,
319 .fini
= nouveau_object_fini
,
320 .mthd
= nouveau_perfctr_mthd
,
323 struct nouveau_oclass
324 nouveau_perfmon_sclass
[] = {
325 { .handle
= NVIF_IOCTL_NEW_V0_PERFCTR
,
326 .ofuncs
= &nouveau_perfctr_ofuncs
,
331 /*******************************************************************************
333 ******************************************************************************/
335 nouveau_perfctx_dtor(struct nouveau_object
*object
)
337 struct nouveau_perfmon
*ppm
= (void *)object
->engine
;
338 mutex_lock(&nv_subdev(ppm
)->mutex
);
339 nouveau_engctx_destroy(&ppm
->context
->base
);
341 mutex_unlock(&nv_subdev(ppm
)->mutex
);
345 nouveau_perfctx_ctor(struct nouveau_object
*parent
,
346 struct nouveau_object
*engine
,
347 struct nouveau_oclass
*oclass
, void *data
, u32 size
,
348 struct nouveau_object
**pobject
)
350 struct nouveau_perfmon
*ppm
= (void *)engine
;
351 struct nouveau_perfctx
*ctx
;
354 ret
= nouveau_engctx_create(parent
, engine
, oclass
, NULL
,
356 *pobject
= nv_object(ctx
);
360 mutex_lock(&nv_subdev(ppm
)->mutex
);
361 if (ppm
->context
== NULL
)
363 mutex_unlock(&nv_subdev(ppm
)->mutex
);
365 if (ctx
!= ppm
->context
)
371 struct nouveau_oclass
372 nouveau_perfmon_cclass
= {
373 .handle
= NV_ENGCTX(PERFMON
, 0x00),
374 .ofuncs
= &(struct nouveau_ofuncs
) {
375 .ctor
= nouveau_perfctx_ctor
,
376 .dtor
= nouveau_perfctx_dtor
,
377 .init
= _nouveau_engctx_init
,
378 .fini
= _nouveau_engctx_fini
,
382 /*******************************************************************************
383 * PPM engine/subdev functions
384 ******************************************************************************/
386 nouveau_perfdom_new(struct nouveau_perfmon
*ppm
, const char *name
, u32 mask
,
387 u32 base
, u32 size_unit
, u32 size_domain
,
388 const struct nouveau_specdom
*spec
)
390 const struct nouveau_specdom
*sdom
;
391 const struct nouveau_specsig
*ssig
;
392 struct nouveau_perfdom
*dom
;
395 for (i
= 0; i
== 0 || mask
; i
++) {
396 u32 addr
= base
+ (i
* size_unit
);
397 if (i
&& !(mask
& (1 << i
)))
401 while (sdom
->signal_nr
) {
402 dom
= kzalloc(sizeof(*dom
) + sdom
->signal_nr
*
403 sizeof(*dom
->signal
), GFP_KERNEL
);
408 snprintf(dom
->name
, sizeof(dom
->name
),
409 "%s/%02x/%02x", name
, i
,
412 snprintf(dom
->name
, sizeof(dom
->name
),
413 "%s/%02x", name
, (int)(sdom
- spec
));
416 list_add_tail(&dom
->head
, &ppm
->domains
);
417 INIT_LIST_HEAD(&dom
->list
);
418 dom
->func
= sdom
->func
;
420 dom
->quad
= QUAD_MASK
;
421 dom
->signal_nr
= sdom
->signal_nr
;
423 ssig
= (sdom
++)->signal
;
425 dom
->signal
[ssig
->signal
].name
= ssig
->name
;
439 _nouveau_perfmon_fini(struct nouveau_object
*object
, bool suspend
)
441 struct nouveau_perfmon
*ppm
= (void *)object
;
442 return nouveau_engine_fini(&ppm
->base
, suspend
);
446 _nouveau_perfmon_init(struct nouveau_object
*object
)
448 struct nouveau_perfmon
*ppm
= (void *)object
;
449 return nouveau_engine_init(&ppm
->base
);
453 _nouveau_perfmon_dtor(struct nouveau_object
*object
)
455 struct nouveau_perfmon
*ppm
= (void *)object
;
456 struct nouveau_perfdom
*dom
, *tmp
;
458 list_for_each_entry_safe(dom
, tmp
, &ppm
->domains
, head
) {
459 list_del(&dom
->head
);
463 nouveau_engine_destroy(&ppm
->base
);
467 nouveau_perfmon_create_(struct nouveau_object
*parent
,
468 struct nouveau_object
*engine
,
469 struct nouveau_oclass
*oclass
,
470 int length
, void **pobject
)
472 struct nouveau_perfmon
*ppm
;
475 ret
= nouveau_engine_create_(parent
, engine
, oclass
, true, "PPM",
476 "perfmon", length
, pobject
);
481 INIT_LIST_HEAD(&ppm
->domains
);