Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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 struct nvkm_perfsig *
38 nvkm_perfsig_find_(struct nvkm_perfdom *dom, const char *name, u32 size)
39 {
40         char path[64];
41         int i;
42
43         if (name[0] != '/') {
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];
48                 }
49         } else {
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];
54                 }
55         }
56
57         return NULL;
58 }
59
60 struct nvkm_perfsig *
61 nvkm_perfsig_find(struct nvkm_pm *ppm, const char *name, u32 size,
62                   struct nvkm_perfdom **pdom)
63 {
64         struct nvkm_perfdom *dom = *pdom;
65         struct nvkm_perfsig *sig;
66
67         if (dom == NULL) {
68                 list_for_each_entry(dom, &ppm->domains, head) {
69                         sig = nvkm_perfsig_find_(dom, name, size);
70                         if (sig) {
71                                 *pdom = dom;
72                                 return sig;
73                         }
74                 }
75
76                 return NULL;
77         }
78
79         return nvkm_perfsig_find_(dom, name, size);
80 }
81
82 struct nvkm_perfctr *
83 nvkm_perfsig_wrap(struct nvkm_pm *ppm, const char *name,
84                   struct nvkm_perfdom **pdom)
85 {
86         struct nvkm_perfsig *sig;
87         struct nvkm_perfctr *ctr;
88
89         sig = nvkm_perfsig_find(ppm, name, strlen(name), pdom);
90         if (!sig)
91                 return NULL;
92
93         ctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
94         if (ctr) {
95                 ctr->signal[0] = sig;
96                 ctr->logic_op = 0xaaaa;
97         }
98
99         return ctr;
100 }
101
102 /*******************************************************************************
103  * Perfmon object classes
104  ******************************************************************************/
105 static int
106 nvkm_perfctr_query(struct nvkm_object *object, void *data, u32 size)
107 {
108         union {
109                 struct nvif_perfctr_query_v0 v0;
110         } *args = data;
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);
116         const char *name;
117         int tmp = 0, di, si;
118         int ret;
119
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;
126         } else
127                 return ret;
128
129         list_for_each_entry(chk, &ppm->domains, head) {
130                 if (tmp++ == di) {
131                         dom = chk;
132                         break;
133                 }
134         }
135
136         if (dom == NULL || si >= (int)dom->signal_nr)
137                 return -EINVAL;
138
139         if (si >= 0) {
140                 if (raw || !(name = dom->signal[si].name)) {
141                         snprintf(args->v0.name, sizeof(args->v0.name),
142                                  "/%s/%02x", dom->name, si);
143                 } else {
144                         strncpy(args->v0.name, name, sizeof(args->v0.name));
145                 }
146         }
147
148         do {
149                 while (++si < dom->signal_nr) {
150                         if (all || dom->signal[si].name) {
151                                 args->v0.iter = (di << 24) | ++si;
152                                 return 0;
153                         }
154                 }
155                 si = -1;
156                 di = di + 1;
157                 dom = list_entry(dom->head.next, typeof(*dom), head);
158         } while (&dom->head != &ppm->domains);
159
160         args->v0.iter = 0xffffffff;
161         return 0;
162 }
163
164 static int
165 nvkm_perfctr_sample(struct nvkm_object *object, void *data, u32 size)
166 {
167         union {
168                 struct nvif_perfctr_sample none;
169         } *args = data;
170         struct nvkm_pm *ppm = (void *)object->engine;
171         struct nvkm_perfctr *ctr, *tmp;
172         struct nvkm_perfdom *dom;
173         int ret;
174
175         nv_ioctl(object, "perfctr sample size %d\n", size);
176         if (nvif_unvers(args->none)) {
177                 nv_ioctl(object, "perfctr sample\n");
178         } else
179                 return ret;
180         ppm->sequence++;
181
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);
186                         tmp = NULL;
187                         while (!list_empty(&dom->list)) {
188                                 ctr = list_first_entry(&dom->list,
189                                                        typeof(*ctr), head);
190                                 if (ctr->slot < 0) break;
191                                 if ( tmp && tmp == ctr) break;
192                                 if (!tmp) tmp = ctr;
193                                 dom->func->read(ppm, dom, ctr);
194                                 ctr->slot  = -1;
195                                 list_move_tail(&ctr->head, &dom->list);
196                         }
197                 }
198
199                 dom->quad = QUAD_MASK;
200
201                 /* setup next batch of counters for sampling */
202                 list_for_each_entry(ctr, &dom->list, head) {
203                         ctr->slot = ffs(dom->quad) - 1;
204                         if (ctr->slot < 0)
205                                 break;
206                         dom->quad &= ~(QUAD_FREE << ctr->slot);
207                         dom->func->init(ppm, dom, ctr);
208                 }
209
210                 if (dom->quad != QUAD_MASK)
211                         dom->func->next(ppm, dom);
212         }
213
214         return 0;
215 }
216
217 static int
218 nvkm_perfctr_read(struct nvkm_object *object, void *data, u32 size)
219 {
220         union {
221                 struct nvif_perfctr_read_v0 v0;
222         } *args = data;
223         struct nvkm_perfctr *ctr = (void *)object;
224         int ret;
225
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);
229         } else
230                 return ret;
231
232         if (!ctr->clk)
233                 return -EAGAIN;
234
235         args->v0.clk = ctr->clk;
236         args->v0.ctr = ctr->ctr;
237         return 0;
238 }
239
240 static int
241 nvkm_perfctr_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
242 {
243         switch (mthd) {
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);
250         default:
251                 break;
252         }
253         return -EINVAL;
254 }
255
256 static void
257 nvkm_perfctr_dtor(struct nvkm_object *object)
258 {
259         struct nvkm_perfctr *ctr = (void *)object;
260         if (ctr->head.next)
261                 list_del(&ctr->head);
262         nvkm_object_destroy(&ctr->base);
263 }
264
265 static int
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)
269 {
270         union {
271                 struct nvif_perfctr_v0 v0;
272         } *args = data;
273         struct nvkm_pm *ppm = (void *)engine;
274         struct nvkm_perfdom *dom = NULL;
275         struct nvkm_perfsig *sig[4] = {};
276         struct nvkm_perfctr *ctr;
277         int ret, i;
278
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);
283         } else
284                 return ret;
285
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])),
290                                            &dom);
291                 if (!sig[i])
292                         return -EINVAL;
293         }
294
295         ret = nvkm_object_create(parent, engine, oclass, 0, &ctr);
296         *pobject = nv_object(ctr);
297         if (ret)
298                 return ret;
299
300         ctr->slot = -1;
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];
306         if (dom)
307                 list_add_tail(&ctr->head, &dom->list);
308         return 0;
309 }
310
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,
318 };
319
320 struct nvkm_oclass
321 nvkm_pm_sclass[] = {
322         { .handle = NVIF_IOCTL_NEW_V0_PERFCTR,
323           .ofuncs = &nvkm_perfctr_ofuncs,
324         },
325         {},
326 };
327
328 /*******************************************************************************
329  * PPM context
330  ******************************************************************************/
331 static void
332 nvkm_perfctx_dtor(struct nvkm_object *object)
333 {
334         struct nvkm_pm *ppm = (void *)object->engine;
335         mutex_lock(&nv_subdev(ppm)->mutex);
336         nvkm_engctx_destroy(&ppm->context->base);
337         ppm->context = NULL;
338         mutex_unlock(&nv_subdev(ppm)->mutex);
339 }
340
341 static int
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)
345 {
346         struct nvkm_pm *ppm = (void *)engine;
347         struct nvkm_perfctx *ctx;
348         int ret;
349
350         ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx);
351         *pobject = nv_object(ctx);
352         if (ret)
353                 return ret;
354
355         mutex_lock(&nv_subdev(ppm)->mutex);
356         if (ppm->context == NULL)
357                 ppm->context = ctx;
358         mutex_unlock(&nv_subdev(ppm)->mutex);
359
360         if (ctx != ppm->context)
361                 return -EBUSY;
362
363         return 0;
364 }
365
366 struct nvkm_oclass
367 nvkm_pm_cclass = {
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,
374         },
375 };
376
377 /*******************************************************************************
378  * PPM engine/subdev functions
379  ******************************************************************************/
380 int
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)
384 {
385         const struct nvkm_specdom *sdom;
386         const struct nvkm_specsig *ssig;
387         struct nvkm_perfdom *dom;
388         int i;
389
390         for (i = 0; i == 0 || mask; i++) {
391                 u32 addr = base + (i * size_unit);
392                 if (i && !(mask & (1 << i)))
393                         continue;
394
395                 sdom = spec;
396                 while (sdom->signal_nr) {
397                         dom = kzalloc(sizeof(*dom) + sdom->signal_nr *
398                                       sizeof(*dom->signal), GFP_KERNEL);
399                         if (!dom)
400                                 return -ENOMEM;
401
402                         if (mask) {
403                                 snprintf(dom->name, sizeof(dom->name),
404                                          "%s/%02x/%02x", name, i,
405                                          (int)(sdom - spec));
406                         } else {
407                                 snprintf(dom->name, sizeof(dom->name),
408                                          "%s/%02x", name, (int)(sdom - spec));
409                         }
410
411                         list_add_tail(&dom->head, &ppm->domains);
412                         INIT_LIST_HEAD(&dom->list);
413                         dom->func = sdom->func;
414                         dom->addr = addr;
415                         dom->quad = QUAD_MASK;
416                         dom->signal_nr = sdom->signal_nr;
417
418                         ssig = (sdom++)->signal;
419                         while (ssig->name) {
420                                 dom->signal[ssig->signal].name = ssig->name;
421                                 ssig++;
422                         }
423
424                         addr += size_domain;
425                 }
426
427                 mask &= ~(1 << i);
428         }
429
430         return 0;
431 }
432
433 int
434 _nvkm_pm_fini(struct nvkm_object *object, bool suspend)
435 {
436         struct nvkm_pm *ppm = (void *)object;
437         return nvkm_engine_fini(&ppm->base, suspend);
438 }
439
440 int
441 _nvkm_pm_init(struct nvkm_object *object)
442 {
443         struct nvkm_pm *ppm = (void *)object;
444         return nvkm_engine_init(&ppm->base);
445 }
446
447 void
448 _nvkm_pm_dtor(struct nvkm_object *object)
449 {
450         struct nvkm_pm *ppm = (void *)object;
451         struct nvkm_perfdom *dom, *tmp;
452
453         list_for_each_entry_safe(dom, tmp, &ppm->domains, head) {
454                 list_del(&dom->head);
455                 kfree(dom);
456         }
457
458         nvkm_engine_destroy(&ppm->base);
459 }
460
461 int
462 nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine,
463                 struct nvkm_oclass *oclass, int length, void **pobject)
464 {
465         struct nvkm_pm *ppm;
466         int ret;
467
468         ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM",
469                                   "pm", length, pobject);
470         ppm = *pobject;
471         if (ret)
472                 return ret;
473
474         INIT_LIST_HEAD(&ppm->domains);
475         return 0;
476 }