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/option.h>
26 #include <core/class.h>
28 #include <subdev/clock.h>
32 #define QUAD_MASK 0x0f
33 #define QUAD_FREE 0x01
35 static struct nouveau_perfsig *
36 nouveau_perfsig_find_(struct nouveau_perfdom *dom, const char *name, u32 size)
42 for (i = 0; i < dom->signal_nr; i++) {
43 if ( dom->signal[i].name &&
44 !strncmp(name, dom->signal[i].name, size))
45 return &dom->signal[i];
48 for (i = 0; i < dom->signal_nr; i++) {
49 snprintf(path, sizeof(path), "/%s/%02x", dom->name, i);
50 if (!strncmp(name, path, size))
51 return &dom->signal[i];
58 struct nouveau_perfsig *
59 nouveau_perfsig_find(struct nouveau_perfmon *ppm, const char *name, u32 size,
60 struct nouveau_perfdom **pdom)
62 struct nouveau_perfdom *dom = *pdom;
63 struct nouveau_perfsig *sig;
66 list_for_each_entry(dom, &ppm->domains, head) {
67 sig = nouveau_perfsig_find_(dom, name, size);
77 return nouveau_perfsig_find_(dom, name, size);
80 struct nouveau_perfctr *
81 nouveau_perfsig_wrap(struct nouveau_perfmon *ppm, const char *name,
82 struct nouveau_perfdom **pdom)
84 struct nouveau_perfsig *sig;
85 struct nouveau_perfctr *ctr;
87 sig = nouveau_perfsig_find(ppm, name, strlen(name), pdom);
91 ctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
94 ctr->logic_op = 0xaaaa;
100 /*******************************************************************************
101 * Perfmon object classes
102 ******************************************************************************/
104 nouveau_perfctr_query(struct nouveau_object *object, u32 mthd,
105 void *data, u32 size)
107 struct nouveau_device *device = nv_device(object);
108 struct nouveau_perfmon *ppm = (void *)object->engine;
109 struct nouveau_perfdom *dom = NULL, *chk;
110 struct nv_perfctr_query *args = data;
111 const bool all = nouveau_boolopt(device->cfgopt, "NvPmShowAll", false);
112 const bool raw = nouveau_boolopt(device->cfgopt, "NvPmUnnamed", all);
117 if (size < sizeof(*args))
120 di = (args->iter & 0xff000000) >> 24;
121 si = (args->iter & 0x00ffffff) - 1;
123 list_for_each_entry(chk, &ppm->domains, head) {
130 if (dom == NULL || si >= (int)dom->signal_nr)
134 if (raw || !(name = dom->signal[si].name)) {
135 snprintf(path, sizeof(path), "/%s/%02x", dom->name, si);
140 strncpy(args->name, name, args->size);
141 args->size = strlen(name) + 1;
145 while (++si < dom->signal_nr) {
146 if (all || dom->signal[si].name) {
147 args->iter = (di << 24) | ++si;
153 dom = list_entry(dom->head.next, typeof(*dom), head);
154 } while (&dom->head != &ppm->domains);
156 args->iter = 0xffffffff;
161 nouveau_perfctr_sample(struct nouveau_object *object, u32 mthd,
162 void *data, u32 size)
164 struct nouveau_perfmon *ppm = (void *)object->engine;
165 struct nouveau_perfctr *ctr, *tmp;
166 struct nouveau_perfdom *dom;
167 struct nv_perfctr_sample *args = data;
169 if (size < sizeof(*args))
173 list_for_each_entry(dom, &ppm->domains, head) {
174 /* sample previous batch of counters */
175 if (dom->quad != QUAD_MASK) {
176 dom->func->next(ppm, dom);
178 while (!list_empty(&dom->list)) {
179 ctr = list_first_entry(&dom->list,
181 if (ctr->slot < 0) break;
182 if ( tmp && tmp == ctr) break;
184 dom->func->read(ppm, dom, ctr);
186 list_move_tail(&ctr->head, &dom->list);
190 dom->quad = QUAD_MASK;
192 /* setup next batch of counters for sampling */
193 list_for_each_entry(ctr, &dom->list, head) {
194 ctr->slot = ffs(dom->quad) - 1;
197 dom->quad &= ~(QUAD_FREE << ctr->slot);
198 dom->func->init(ppm, dom, ctr);
201 if (dom->quad != QUAD_MASK)
202 dom->func->next(ppm, dom);
209 nouveau_perfctr_read(struct nouveau_object *object, u32 mthd,
210 void *data, u32 size)
212 struct nouveau_perfctr *ctr = (void *)object;
213 struct nv_perfctr_read *args = data;
215 if (size < sizeof(*args))
220 args->clk = ctr->clk;
221 args->ctr = ctr->ctr;
226 nouveau_perfctr_dtor(struct nouveau_object *object)
228 struct nouveau_perfctr *ctr = (void *)object;
230 list_del(&ctr->head);
231 nouveau_object_destroy(&ctr->base);
235 nouveau_perfctr_ctor(struct nouveau_object *parent,
236 struct nouveau_object *engine,
237 struct nouveau_oclass *oclass, void *data, u32 size,
238 struct nouveau_object **pobject)
240 struct nouveau_perfmon *ppm = (void *)engine;
241 struct nouveau_perfdom *dom = NULL;
242 struct nouveau_perfsig *sig[4] = {};
243 struct nouveau_perfctr *ctr;
244 struct nv_perfctr_class *args = data;
247 if (size < sizeof(*args))
250 for (i = 0; i < ARRAY_SIZE(args->signal) && args->signal[i].name; i++) {
251 sig[i] = nouveau_perfsig_find(ppm, args->signal[i].name,
252 args->signal[i].size, &dom);
257 ret = nouveau_object_create(parent, engine, oclass, 0, &ctr);
258 *pobject = nv_object(ctr);
263 ctr->logic_op = args->logic_op;
264 ctr->signal[0] = sig[0];
265 ctr->signal[1] = sig[1];
266 ctr->signal[2] = sig[2];
267 ctr->signal[3] = sig[3];
269 list_add_tail(&ctr->head, &dom->list);
273 static struct nouveau_ofuncs
274 nouveau_perfctr_ofuncs = {
275 .ctor = nouveau_perfctr_ctor,
276 .dtor = nouveau_perfctr_dtor,
277 .init = nouveau_object_init,
278 .fini = nouveau_object_fini,
281 static struct nouveau_omthds
282 nouveau_perfctr_omthds[] = {
283 { NV_PERFCTR_QUERY, NV_PERFCTR_QUERY, nouveau_perfctr_query },
284 { NV_PERFCTR_SAMPLE, NV_PERFCTR_SAMPLE, nouveau_perfctr_sample },
285 { NV_PERFCTR_READ, NV_PERFCTR_READ, nouveau_perfctr_read },
289 struct nouveau_oclass
290 nouveau_perfmon_sclass[] = {
291 { .handle = NV_PERFCTR_CLASS,
292 .ofuncs = &nouveau_perfctr_ofuncs,
293 .omthds = nouveau_perfctr_omthds,
298 /*******************************************************************************
300 ******************************************************************************/
302 nouveau_perfctx_dtor(struct nouveau_object *object)
304 struct nouveau_perfmon *ppm = (void *)object->engine;
305 mutex_lock(&nv_subdev(ppm)->mutex);
307 mutex_unlock(&nv_subdev(ppm)->mutex);
311 nouveau_perfctx_ctor(struct nouveau_object *parent,
312 struct nouveau_object *engine,
313 struct nouveau_oclass *oclass, void *data, u32 size,
314 struct nouveau_object **pobject)
316 struct nouveau_perfmon *ppm = (void *)engine;
317 struct nouveau_perfctx *ctx;
320 ret = nouveau_engctx_create(parent, engine, oclass, NULL,
322 *pobject = nv_object(ctx);
326 mutex_lock(&nv_subdev(ppm)->mutex);
327 if (ppm->context == NULL)
329 mutex_unlock(&nv_subdev(ppm)->mutex);
331 if (ctx != ppm->context)
337 struct nouveau_oclass
338 nouveau_perfmon_cclass = {
339 .handle = NV_ENGCTX(PERFMON, 0x00),
340 .ofuncs = &(struct nouveau_ofuncs) {
341 .ctor = nouveau_perfctx_ctor,
342 .dtor = nouveau_perfctx_dtor,
343 .init = _nouveau_engctx_init,
344 .fini = _nouveau_engctx_fini,
348 /*******************************************************************************
349 * PPM engine/subdev functions
350 ******************************************************************************/
352 nouveau_perfdom_new(struct nouveau_perfmon *ppm, const char *name, u32 mask,
353 u32 base, u32 size_unit, u32 size_domain,
354 const struct nouveau_specdom *spec)
356 const struct nouveau_specdom *sdom;
357 const struct nouveau_specsig *ssig;
358 struct nouveau_perfdom *dom;
361 for (i = 0; i == 0 || mask; i++) {
362 u32 addr = base + (i * size_unit);
363 if (i && !(mask & (1 << i)))
367 while (sdom->signal_nr) {
368 dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
369 sizeof(*dom->signal), GFP_KERNEL);
374 snprintf(dom->name, sizeof(dom->name),
375 "%s/%02x/%02x", name, i,
378 snprintf(dom->name, sizeof(dom->name),
379 "%s/%02x", name, (int)(sdom - spec));
382 list_add_tail(&dom->head, &ppm->domains);
383 INIT_LIST_HEAD(&dom->list);
384 dom->func = sdom->func;
386 dom->quad = QUAD_MASK;
387 dom->signal_nr = sdom->signal_nr;
389 ssig = (sdom++)->signal;
391 dom->signal[ssig->signal].name = ssig->name;
405 _nouveau_perfmon_fini(struct nouveau_object *object, bool suspend)
407 struct nouveau_perfmon *ppm = (void *)object;
408 return nouveau_engine_fini(&ppm->base, suspend);
412 _nouveau_perfmon_init(struct nouveau_object *object)
414 struct nouveau_perfmon *ppm = (void *)object;
415 return nouveau_engine_init(&ppm->base);
419 _nouveau_perfmon_dtor(struct nouveau_object *object)
421 struct nouveau_perfmon *ppm = (void *)object;
422 struct nouveau_perfdom *dom, *tmp;
424 list_for_each_entry_safe(dom, tmp, &ppm->domains, head) {
425 list_del(&dom->head);
429 nouveau_engine_destroy(&ppm->base);
433 nouveau_perfmon_create_(struct nouveau_object *parent,
434 struct nouveau_object *engine,
435 struct nouveau_oclass *oclass,
436 int length, void **pobject)
438 struct nouveau_perfmon *ppm;
441 ret = nouveau_engine_create_(parent, engine, oclass, true, "PPM",
442 "perfmon", length, pobject);
447 INIT_LIST_HEAD(&ppm->domains);