drm/nouveau/pm: change signal iter to u16
[deliverable/linux.git] / drivers / gpu / drm / nouveau / nvkm / engine / pm / base.c
1 /*
2 * Copyright 2013 Red Hat Inc.
3 *
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:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
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.
21 *
22 * Authors: Ben Skeggs
23 */
24 #include "priv.h"
25
26 #include <core/client.h>
27 #include <core/device.h>
28 #include <core/option.h>
29
30 #include <nvif/class.h>
31 #include <nvif/ioctl.h>
32 #include <nvif/unpack.h>
33
34 #define QUAD_MASK 0x0f
35 #define QUAD_FREE 0x01
36
37 static u8
38 nvkm_pm_count_perfdom(struct nvkm_pm *ppm)
39 {
40 struct nvkm_perfdom *dom;
41 u8 domain_nr = 0;
42
43 list_for_each_entry(dom, &ppm->domains, head)
44 domain_nr++;
45 return domain_nr;
46 }
47
48 static u16
49 nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
50 {
51 u16 signal_nr = 0;
52 int i;
53
54 if (dom) {
55 for (i = 0; i < dom->signal_nr; i++) {
56 if (dom->signal[i].name)
57 signal_nr++;
58 }
59 }
60 return signal_nr;
61 }
62
63 static struct nvkm_perfdom *
64 nvkm_perfdom_find(struct nvkm_pm *ppm, int di)
65 {
66 struct nvkm_perfdom *dom;
67 int tmp = 0;
68
69 list_for_each_entry(dom, &ppm->domains, head) {
70 if (tmp++ == di)
71 return dom;
72 }
73 return NULL;
74 }
75
76 static struct nvkm_perfsig *
77 nvkm_perfsig_find_(struct nvkm_perfdom *dom, const char *name, u32 size)
78 {
79 char path[64];
80 int i;
81
82 if (name[0] != '/') {
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];
87 }
88 } else {
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];
93 }
94 }
95
96 return NULL;
97 }
98
99 struct nvkm_perfsig *
100 nvkm_perfsig_find(struct nvkm_pm *ppm, const char *name, u32 size,
101 struct nvkm_perfdom **pdom)
102 {
103 struct nvkm_perfdom *dom = *pdom;
104 struct nvkm_perfsig *sig;
105
106 if (dom == NULL) {
107 list_for_each_entry(dom, &ppm->domains, head) {
108 sig = nvkm_perfsig_find_(dom, name, size);
109 if (sig) {
110 *pdom = dom;
111 return sig;
112 }
113 }
114
115 return NULL;
116 }
117
118 return nvkm_perfsig_find_(dom, name, size);
119 }
120
121 /*******************************************************************************
122 * Perfmon object classes
123 ******************************************************************************/
124 static int
125 nvkm_perfmon_mthd_query_domain(struct nvkm_object *object, void *data, u32 size)
126 {
127 union {
128 struct nvif_perfmon_query_domain_v0 v0;
129 } *args = data;
130 struct nvkm_pm *ppm = (void *)object->engine;
131 struct nvkm_perfdom *dom;
132 u8 domain_nr;
133 int di, ret;
134
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;
140 } else
141 return ret;
142
143 domain_nr = nvkm_pm_count_perfdom(ppm);
144 if (di >= (int)domain_nr)
145 return -EINVAL;
146
147 if (di >= 0) {
148 dom = nvkm_perfdom_find(ppm, di);
149 if (dom == NULL)
150 return -EINVAL;
151
152 args->v0.id = di;
153 args->v0.signal_nr = nvkm_perfdom_count_perfsig(dom);
154
155 /* Currently only global counters (PCOUNTER) are implemented
156 * but this will be different for local counters (MP). */
157 args->v0.counter_nr = 4;
158 }
159
160 if (++di < domain_nr) {
161 args->v0.iter = ++di;
162 return 0;
163 }
164
165 args->v0.iter = 0xff;
166 return 0;
167 }
168
169 static int
170 nvkm_perfmon_mthd_query_signal(struct nvkm_object *object, void *data, u32 size)
171 {
172 union {
173 struct nvif_perfmon_query_signal_v0 v0;
174 } *args = data;
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);
180 const char *name;
181 int ret, si;
182
183 nv_ioctl(object, "perfmon query signal size %d\n", size);
184 if (nvif_unpack(args->v0, 0, 0, false)) {
185 nv_ioctl(object,
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;
189 } else
190 return ret;
191
192 dom = nvkm_perfdom_find(ppm, args->v0.domain);
193 if (dom == NULL || si >= (int)dom->signal_nr)
194 return -EINVAL;
195
196 if (si >= 0) {
197 if (raw || !(name = dom->signal[si].name)) {
198 snprintf(args->v0.name, sizeof(args->v0.name),
199 "/%s/%02x", dom->name, si);
200 } else {
201 strncpy(args->v0.name, name, sizeof(args->v0.name));
202 }
203 }
204
205 while (++si < dom->signal_nr) {
206 if (all || dom->signal[si].name) {
207 args->v0.iter = ++si;
208 return 0;
209 }
210 }
211
212 args->v0.iter = 0xffff;
213 return 0;
214 }
215
216 static int
217 nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
218 {
219 switch (mthd) {
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);
224 default:
225 break;
226 }
227 return -EINVAL;
228 }
229
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,
237 };
238
239 /*******************************************************************************
240 * Perfctr object classes
241 ******************************************************************************/
242 static int
243 nvkm_perfctr_sample(struct nvkm_object *object, void *data, u32 size)
244 {
245 union {
246 struct nvif_perfctr_sample none;
247 } *args = data;
248 struct nvkm_pm *ppm = (void *)object->engine;
249 struct nvkm_perfctr *ctr, *tmp;
250 struct nvkm_perfdom *dom;
251 int ret;
252
253 nv_ioctl(object, "perfctr sample size %d\n", size);
254 if (nvif_unvers(args->none)) {
255 nv_ioctl(object, "perfctr sample\n");
256 } else
257 return ret;
258 ppm->sequence++;
259
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);
264 tmp = NULL;
265 while (!list_empty(&dom->list)) {
266 ctr = list_first_entry(&dom->list,
267 typeof(*ctr), head);
268 if (ctr->slot < 0) break;
269 if ( tmp && tmp == ctr) break;
270 if (!tmp) tmp = ctr;
271 dom->func->read(ppm, dom, ctr);
272 ctr->slot = -1;
273 list_move_tail(&ctr->head, &dom->list);
274 }
275 }
276
277 dom->quad = QUAD_MASK;
278
279 /* setup next batch of counters for sampling */
280 list_for_each_entry(ctr, &dom->list, head) {
281 ctr->slot = ffs(dom->quad) - 1;
282 if (ctr->slot < 0)
283 break;
284 dom->quad &= ~(QUAD_FREE << ctr->slot);
285 dom->func->init(ppm, dom, ctr);
286 }
287
288 if (dom->quad != QUAD_MASK)
289 dom->func->next(ppm, dom);
290 }
291
292 return 0;
293 }
294
295 static int
296 nvkm_perfctr_read(struct nvkm_object *object, void *data, u32 size)
297 {
298 union {
299 struct nvif_perfctr_read_v0 v0;
300 } *args = data;
301 struct nvkm_perfctr *ctr = (void *)object;
302 int ret;
303
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);
307 } else
308 return ret;
309
310 if (!ctr->clk)
311 return -EAGAIN;
312
313 args->v0.clk = ctr->clk;
314 args->v0.ctr = ctr->ctr;
315 return 0;
316 }
317
318 static int
319 nvkm_perfctr_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
320 {
321 switch (mthd) {
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);
326 default:
327 break;
328 }
329 return -EINVAL;
330 }
331
332 static void
333 nvkm_perfctr_dtor(struct nvkm_object *object)
334 {
335 struct nvkm_perfctr *ctr = (void *)object;
336 if (ctr->head.next)
337 list_del(&ctr->head);
338 nvkm_object_destroy(&ctr->base);
339 }
340
341 static int
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)
345 {
346 union {
347 struct nvif_perfctr_v0 v0;
348 } *args = data;
349 struct nvkm_pm *ppm = (void *)engine;
350 struct nvkm_perfdom *dom = NULL;
351 struct nvkm_perfsig *sig[4] = {};
352 struct nvkm_perfctr *ctr;
353 int ret, i;
354
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);
359 } else
360 return ret;
361
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])),
366 &dom);
367 if (!sig[i])
368 return -EINVAL;
369 }
370
371 if (!dom)
372 return -EINVAL;
373
374 ret = nvkm_object_create(parent, engine, oclass, 0, &ctr);
375 *pobject = nv_object(ctr);
376 if (ret)
377 return ret;
378
379 ctr->slot = -1;
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);
386 return 0;
387 }
388
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,
396 };
397
398 struct nvkm_oclass
399 nvkm_pm_sclass[] = {
400 {
401 .handle = NVIF_IOCTL_NEW_V0_PERFMON,
402 .ofuncs = &nvkm_perfmon_ofuncs,
403 },
404 { .handle = NVIF_IOCTL_NEW_V0_PERFCTR,
405 .ofuncs = &nvkm_perfctr_ofuncs,
406 },
407 {},
408 };
409
410 /*******************************************************************************
411 * PPM context
412 ******************************************************************************/
413 static void
414 nvkm_perfctx_dtor(struct nvkm_object *object)
415 {
416 struct nvkm_pm *ppm = (void *)object->engine;
417 struct nvkm_perfctx *ctx = (void *)object;
418
419 mutex_lock(&nv_subdev(ppm)->mutex);
420 nvkm_engctx_destroy(&ctx->base);
421 if (ppm->context == ctx)
422 ppm->context = NULL;
423 mutex_unlock(&nv_subdev(ppm)->mutex);
424 }
425
426 static int
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)
430 {
431 struct nvkm_pm *ppm = (void *)engine;
432 struct nvkm_perfctx *ctx;
433 int ret;
434
435 ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx);
436 *pobject = nv_object(ctx);
437 if (ret)
438 return ret;
439
440 mutex_lock(&nv_subdev(ppm)->mutex);
441 if (ppm->context == NULL)
442 ppm->context = ctx;
443 if (ctx != ppm->context)
444 ret = -EBUSY;
445 mutex_unlock(&nv_subdev(ppm)->mutex);
446
447 return ret;
448 }
449
450 struct nvkm_oclass
451 nvkm_pm_cclass = {
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,
458 },
459 };
460
461 /*******************************************************************************
462 * PPM engine/subdev functions
463 ******************************************************************************/
464 int
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)
468 {
469 const struct nvkm_specdom *sdom;
470 const struct nvkm_specsig *ssig;
471 struct nvkm_perfdom *dom;
472 int i;
473
474 for (i = 0; i == 0 || mask; i++) {
475 u32 addr = base + (i * size_unit);
476 if (i && !(mask & (1 << i)))
477 continue;
478
479 sdom = spec;
480 while (sdom->signal_nr) {
481 dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
482 sizeof(*dom->signal), GFP_KERNEL);
483 if (!dom)
484 return -ENOMEM;
485
486 if (mask) {
487 snprintf(dom->name, sizeof(dom->name),
488 "%s/%02x/%02x", name, i,
489 (int)(sdom - spec));
490 } else {
491 snprintf(dom->name, sizeof(dom->name),
492 "%s/%02x", name, (int)(sdom - spec));
493 }
494
495 list_add_tail(&dom->head, &ppm->domains);
496 INIT_LIST_HEAD(&dom->list);
497 dom->func = sdom->func;
498 dom->addr = addr;
499 dom->quad = QUAD_MASK;
500 dom->signal_nr = sdom->signal_nr;
501
502 ssig = (sdom++)->signal;
503 while (ssig->name) {
504 dom->signal[ssig->signal].name = ssig->name;
505 ssig++;
506 }
507
508 addr += size_domain;
509 }
510
511 mask &= ~(1 << i);
512 }
513
514 return 0;
515 }
516
517 int
518 _nvkm_pm_fini(struct nvkm_object *object, bool suspend)
519 {
520 struct nvkm_pm *ppm = (void *)object;
521 return nvkm_engine_fini(&ppm->base, suspend);
522 }
523
524 int
525 _nvkm_pm_init(struct nvkm_object *object)
526 {
527 struct nvkm_pm *ppm = (void *)object;
528 return nvkm_engine_init(&ppm->base);
529 }
530
531 void
532 _nvkm_pm_dtor(struct nvkm_object *object)
533 {
534 struct nvkm_pm *ppm = (void *)object;
535 struct nvkm_perfdom *dom, *tmp;
536
537 list_for_each_entry_safe(dom, tmp, &ppm->domains, head) {
538 list_del(&dom->head);
539 kfree(dom);
540 }
541
542 nvkm_engine_destroy(&ppm->base);
543 }
544
545 int
546 nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
547 struct nvkm_oclass *oclass, int length, void **pobject)
548 {
549 struct nvkm_pm *ppm;
550 int ret;
551
552 ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM",
553 "pm", length, pobject);
554 ppm = *pobject;
555 if (ret)
556 return ret;
557
558 INIT_LIST_HEAD(&ppm->domains);
559 return 0;
560 }
This page took 0.043116 seconds and 5 git commands to generate.