root/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nvkm_pm_count_perfdom
  2. nvkm_perfdom_count_perfsig
  3. nvkm_perfdom_find
  4. nvkm_perfsig_find
  5. nvkm_perfsig_count_perfsrc
  6. nvkm_perfsrc_find
  7. nvkm_perfsrc_enable
  8. nvkm_perfsrc_disable
  9. nvkm_perfdom_init
  10. nvkm_perfdom_sample
  11. nvkm_perfdom_read
  12. nvkm_perfdom_mthd
  13. nvkm_perfdom_dtor
  14. nvkm_perfctr_new
  15. nvkm_perfdom_new_
  16. nvkm_perfmon_mthd_query_domain
  17. nvkm_perfmon_mthd_query_signal
  18. nvkm_perfmon_mthd_query_source
  19. nvkm_perfmon_mthd
  20. nvkm_perfmon_child_new
  21. nvkm_perfmon_child_get
  22. nvkm_perfmon_dtor
  23. nvkm_perfmon_new
  24. nvkm_pm_oclass_new
  25. nvkm_pm_oclass_get
  26. nvkm_perfsrc_new
  27. nvkm_perfdom_new
  28. nvkm_pm_fini
  29. nvkm_pm_dtor
  30. nvkm_pm_ctor

   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/option.h>
  28 
  29 #include <nvif/class.h>
  30 #include <nvif/if0002.h>
  31 #include <nvif/if0003.h>
  32 #include <nvif/ioctl.h>
  33 #include <nvif/unpack.h>
  34 
  35 static u8
  36 nvkm_pm_count_perfdom(struct nvkm_pm *pm)
  37 {
  38         struct nvkm_perfdom *dom;
  39         u8 domain_nr = 0;
  40 
  41         list_for_each_entry(dom, &pm->domains, head)
  42                 domain_nr++;
  43         return domain_nr;
  44 }
  45 
  46 static u16
  47 nvkm_perfdom_count_perfsig(struct nvkm_perfdom *dom)
  48 {
  49         u16 signal_nr = 0;
  50         int i;
  51 
  52         if (dom) {
  53                 for (i = 0; i < dom->signal_nr; i++) {
  54                         if (dom->signal[i].name)
  55                                 signal_nr++;
  56                 }
  57         }
  58         return signal_nr;
  59 }
  60 
  61 static struct nvkm_perfdom *
  62 nvkm_perfdom_find(struct nvkm_pm *pm, int di)
  63 {
  64         struct nvkm_perfdom *dom;
  65         int tmp = 0;
  66 
  67         list_for_each_entry(dom, &pm->domains, head) {
  68                 if (tmp++ == di)
  69                         return dom;
  70         }
  71         return NULL;
  72 }
  73 
  74 static struct nvkm_perfsig *
  75 nvkm_perfsig_find(struct nvkm_pm *pm, u8 di, u8 si, struct nvkm_perfdom **pdom)
  76 {
  77         struct nvkm_perfdom *dom = *pdom;
  78 
  79         if (dom == NULL) {
  80                 dom = nvkm_perfdom_find(pm, di);
  81                 if (dom == NULL)
  82                         return NULL;
  83                 *pdom = dom;
  84         }
  85 
  86         if (!dom->signal[si].name)
  87                 return NULL;
  88         return &dom->signal[si];
  89 }
  90 
  91 static u8
  92 nvkm_perfsig_count_perfsrc(struct nvkm_perfsig *sig)
  93 {
  94         u8 source_nr = 0, i;
  95 
  96         for (i = 0; i < ARRAY_SIZE(sig->source); i++) {
  97                 if (sig->source[i])
  98                         source_nr++;
  99         }
 100         return source_nr;
 101 }
 102 
 103 static struct nvkm_perfsrc *
 104 nvkm_perfsrc_find(struct nvkm_pm *pm, struct nvkm_perfsig *sig, int si)
 105 {
 106         struct nvkm_perfsrc *src;
 107         bool found = false;
 108         int tmp = 1; /* Sources ID start from 1 */
 109         u8 i;
 110 
 111         for (i = 0; i < ARRAY_SIZE(sig->source) && sig->source[i]; i++) {
 112                 if (sig->source[i] == si) {
 113                         found = true;
 114                         break;
 115                 }
 116         }
 117 
 118         if (found) {
 119                 list_for_each_entry(src, &pm->sources, head) {
 120                         if (tmp++ == si)
 121                                 return src;
 122                 }
 123         }
 124 
 125         return NULL;
 126 }
 127 
 128 static int
 129 nvkm_perfsrc_enable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
 130 {
 131         struct nvkm_subdev *subdev = &pm->engine.subdev;
 132         struct nvkm_device *device = subdev->device;
 133         struct nvkm_perfdom *dom = NULL;
 134         struct nvkm_perfsig *sig;
 135         struct nvkm_perfsrc *src;
 136         u32 mask, value;
 137         int i, j;
 138 
 139         for (i = 0; i < 4; i++) {
 140                 for (j = 0; j < 8 && ctr->source[i][j]; j++) {
 141                         sig = nvkm_perfsig_find(pm, ctr->domain,
 142                                                 ctr->signal[i], &dom);
 143                         if (!sig)
 144                                 return -EINVAL;
 145 
 146                         src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
 147                         if (!src)
 148                                 return -EINVAL;
 149 
 150                         /* set enable bit if needed */
 151                         mask = value = 0x00000000;
 152                         if (src->enable)
 153                                 mask = value = 0x80000000;
 154                         mask  |= (src->mask << src->shift);
 155                         value |= ((ctr->source[i][j] >> 32) << src->shift);
 156 
 157                         /* enable the source */
 158                         nvkm_mask(device, src->addr, mask, value);
 159                         nvkm_debug(subdev,
 160                                    "enabled source %08x %08x %08x\n",
 161                                    src->addr, mask, value);
 162                 }
 163         }
 164         return 0;
 165 }
 166 
 167 static int
 168 nvkm_perfsrc_disable(struct nvkm_pm *pm, struct nvkm_perfctr *ctr)
 169 {
 170         struct nvkm_subdev *subdev = &pm->engine.subdev;
 171         struct nvkm_device *device = subdev->device;
 172         struct nvkm_perfdom *dom = NULL;
 173         struct nvkm_perfsig *sig;
 174         struct nvkm_perfsrc *src;
 175         u32 mask;
 176         int i, j;
 177 
 178         for (i = 0; i < 4; i++) {
 179                 for (j = 0; j < 8 && ctr->source[i][j]; j++) {
 180                         sig = nvkm_perfsig_find(pm, ctr->domain,
 181                                                 ctr->signal[i], &dom);
 182                         if (!sig)
 183                                 return -EINVAL;
 184 
 185                         src = nvkm_perfsrc_find(pm, sig, ctr->source[i][j]);
 186                         if (!src)
 187                                 return -EINVAL;
 188 
 189                         /* unset enable bit if needed */
 190                         mask = 0x00000000;
 191                         if (src->enable)
 192                                 mask = 0x80000000;
 193                         mask |= (src->mask << src->shift);
 194 
 195                         /* disable the source */
 196                         nvkm_mask(device, src->addr, mask, 0);
 197                         nvkm_debug(subdev, "disabled source %08x %08x\n",
 198                                    src->addr, mask);
 199                 }
 200         }
 201         return 0;
 202 }
 203 
 204 /*******************************************************************************
 205  * Perfdom object classes
 206  ******************************************************************************/
 207 static int
 208 nvkm_perfdom_init(struct nvkm_perfdom *dom, void *data, u32 size)
 209 {
 210         union {
 211                 struct nvif_perfdom_init none;
 212         } *args = data;
 213         struct nvkm_object *object = &dom->object;
 214         struct nvkm_pm *pm = dom->perfmon->pm;
 215         int ret = -ENOSYS, i;
 216 
 217         nvif_ioctl(object, "perfdom init size %d\n", size);
 218         if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
 219                 nvif_ioctl(object, "perfdom init\n");
 220         } else
 221                 return ret;
 222 
 223         for (i = 0; i < 4; i++) {
 224                 if (dom->ctr[i]) {
 225                         dom->func->init(pm, dom, dom->ctr[i]);
 226 
 227                         /* enable sources */
 228                         nvkm_perfsrc_enable(pm, dom->ctr[i]);
 229                 }
 230         }
 231 
 232         /* start next batch of counters for sampling */
 233         dom->func->next(pm, dom);
 234         return 0;
 235 }
 236 
 237 static int
 238 nvkm_perfdom_sample(struct nvkm_perfdom *dom, void *data, u32 size)
 239 {
 240         union {
 241                 struct nvif_perfdom_sample none;
 242         } *args = data;
 243         struct nvkm_object *object = &dom->object;
 244         struct nvkm_pm *pm = dom->perfmon->pm;
 245         int ret = -ENOSYS;
 246 
 247         nvif_ioctl(object, "perfdom sample size %d\n", size);
 248         if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
 249                 nvif_ioctl(object, "perfdom sample\n");
 250         } else
 251                 return ret;
 252         pm->sequence++;
 253 
 254         /* sample previous batch of counters */
 255         list_for_each_entry(dom, &pm->domains, head)
 256                 dom->func->next(pm, dom);
 257 
 258         return 0;
 259 }
 260 
 261 static int
 262 nvkm_perfdom_read(struct nvkm_perfdom *dom, void *data, u32 size)
 263 {
 264         union {
 265                 struct nvif_perfdom_read_v0 v0;
 266         } *args = data;
 267         struct nvkm_object *object = &dom->object;
 268         struct nvkm_pm *pm = dom->perfmon->pm;
 269         int ret = -ENOSYS, i;
 270 
 271         nvif_ioctl(object, "perfdom read size %d\n", size);
 272         if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 273                 nvif_ioctl(object, "perfdom read vers %d\n", args->v0.version);
 274         } else
 275                 return ret;
 276 
 277         for (i = 0; i < 4; i++) {
 278                 if (dom->ctr[i])
 279                         dom->func->read(pm, dom, dom->ctr[i]);
 280         }
 281 
 282         if (!dom->clk)
 283                 return -EAGAIN;
 284 
 285         for (i = 0; i < 4; i++)
 286                 if (dom->ctr[i])
 287                         args->v0.ctr[i] = dom->ctr[i]->ctr;
 288         args->v0.clk = dom->clk;
 289         return 0;
 290 }
 291 
 292 static int
 293 nvkm_perfdom_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
 294 {
 295         struct nvkm_perfdom *dom = nvkm_perfdom(object);
 296         switch (mthd) {
 297         case NVIF_PERFDOM_V0_INIT:
 298                 return nvkm_perfdom_init(dom, data, size);
 299         case NVIF_PERFDOM_V0_SAMPLE:
 300                 return nvkm_perfdom_sample(dom, data, size);
 301         case NVIF_PERFDOM_V0_READ:
 302                 return nvkm_perfdom_read(dom, data, size);
 303         default:
 304                 break;
 305         }
 306         return -EINVAL;
 307 }
 308 
 309 static void *
 310 nvkm_perfdom_dtor(struct nvkm_object *object)
 311 {
 312         struct nvkm_perfdom *dom = nvkm_perfdom(object);
 313         struct nvkm_pm *pm = dom->perfmon->pm;
 314         int i;
 315 
 316         for (i = 0; i < 4; i++) {
 317                 struct nvkm_perfctr *ctr = dom->ctr[i];
 318                 if (ctr) {
 319                         nvkm_perfsrc_disable(pm, ctr);
 320                         if (ctr->head.next)
 321                                 list_del(&ctr->head);
 322                 }
 323                 kfree(ctr);
 324         }
 325 
 326         return dom;
 327 }
 328 
 329 static int
 330 nvkm_perfctr_new(struct nvkm_perfdom *dom, int slot, u8 domain,
 331                  struct nvkm_perfsig *signal[4], u64 source[4][8],
 332                  u16 logic_op, struct nvkm_perfctr **pctr)
 333 {
 334         struct nvkm_perfctr *ctr;
 335         int i, j;
 336 
 337         if (!dom)
 338                 return -EINVAL;
 339 
 340         ctr = *pctr = kzalloc(sizeof(*ctr), GFP_KERNEL);
 341         if (!ctr)
 342                 return -ENOMEM;
 343 
 344         ctr->domain   = domain;
 345         ctr->logic_op = logic_op;
 346         ctr->slot     = slot;
 347         for (i = 0; i < 4; i++) {
 348                 if (signal[i]) {
 349                         ctr->signal[i] = signal[i] - dom->signal;
 350                         for (j = 0; j < 8; j++)
 351                                 ctr->source[i][j] = source[i][j];
 352                 }
 353         }
 354         list_add_tail(&ctr->head, &dom->list);
 355 
 356         return 0;
 357 }
 358 
 359 static const struct nvkm_object_func
 360 nvkm_perfdom = {
 361         .dtor = nvkm_perfdom_dtor,
 362         .mthd = nvkm_perfdom_mthd,
 363 };
 364 
 365 static int
 366 nvkm_perfdom_new_(struct nvkm_perfmon *perfmon,
 367                   const struct nvkm_oclass *oclass, void *data, u32 size,
 368                   struct nvkm_object **pobject)
 369 {
 370         union {
 371                 struct nvif_perfdom_v0 v0;
 372         } *args = data;
 373         struct nvkm_pm *pm = perfmon->pm;
 374         struct nvkm_object *parent = oclass->parent;
 375         struct nvkm_perfdom *sdom = NULL;
 376         struct nvkm_perfctr *ctr[4] = {};
 377         struct nvkm_perfdom *dom;
 378         int c, s, m;
 379         int ret = -ENOSYS;
 380 
 381         nvif_ioctl(parent, "create perfdom size %d\n", size);
 382         if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 383                 nvif_ioctl(parent, "create perfdom vers %d dom %d mode %02x\n",
 384                            args->v0.version, args->v0.domain, args->v0.mode);
 385         } else
 386                 return ret;
 387 
 388         for (c = 0; c < ARRAY_SIZE(args->v0.ctr); c++) {
 389                 struct nvkm_perfsig *sig[4] = {};
 390                 u64 src[4][8] = {};
 391 
 392                 for (s = 0; s < ARRAY_SIZE(args->v0.ctr[c].signal); s++) {
 393                         sig[s] = nvkm_perfsig_find(pm, args->v0.domain,
 394                                                    args->v0.ctr[c].signal[s],
 395                                                    &sdom);
 396                         if (args->v0.ctr[c].signal[s] && !sig[s])
 397                                 return -EINVAL;
 398 
 399                         for (m = 0; m < 8; m++) {
 400                                 src[s][m] = args->v0.ctr[c].source[s][m];
 401                                 if (src[s][m] && !nvkm_perfsrc_find(pm, sig[s],
 402                                                                     src[s][m]))
 403                                         return -EINVAL;
 404                         }
 405                 }
 406 
 407                 ret = nvkm_perfctr_new(sdom, c, args->v0.domain, sig, src,
 408                                        args->v0.ctr[c].logic_op, &ctr[c]);
 409                 if (ret)
 410                         return ret;
 411         }
 412 
 413         if (!sdom)
 414                 return -EINVAL;
 415 
 416         if (!(dom = kzalloc(sizeof(*dom), GFP_KERNEL)))
 417                 return -ENOMEM;
 418         nvkm_object_ctor(&nvkm_perfdom, oclass, &dom->object);
 419         dom->perfmon = perfmon;
 420         *pobject = &dom->object;
 421 
 422         dom->func = sdom->func;
 423         dom->addr = sdom->addr;
 424         dom->mode = args->v0.mode;
 425         for (c = 0; c < ARRAY_SIZE(ctr); c++)
 426                 dom->ctr[c] = ctr[c];
 427         return 0;
 428 }
 429 
 430 /*******************************************************************************
 431  * Perfmon object classes
 432  ******************************************************************************/
 433 static int
 434 nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon,
 435                                void *data, u32 size)
 436 {
 437         union {
 438                 struct nvif_perfmon_query_domain_v0 v0;
 439         } *args = data;
 440         struct nvkm_object *object = &perfmon->object;
 441         struct nvkm_pm *pm = perfmon->pm;
 442         struct nvkm_perfdom *dom;
 443         u8 domain_nr;
 444         int di, ret = -ENOSYS;
 445 
 446         nvif_ioctl(object, "perfmon query domain size %d\n", size);
 447         if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 448                 nvif_ioctl(object, "perfmon domain vers %d iter %02x\n",
 449                            args->v0.version, args->v0.iter);
 450                 di = (args->v0.iter & 0xff) - 1;
 451         } else
 452                 return ret;
 453 
 454         domain_nr = nvkm_pm_count_perfdom(pm);
 455         if (di >= (int)domain_nr)
 456                 return -EINVAL;
 457 
 458         if (di >= 0) {
 459                 dom = nvkm_perfdom_find(pm, di);
 460                 if (dom == NULL)
 461                         return -EINVAL;
 462 
 463                 args->v0.id         = di;
 464                 args->v0.signal_nr  = nvkm_perfdom_count_perfsig(dom);
 465                 strncpy(args->v0.name, dom->name, sizeof(args->v0.name) - 1);
 466 
 467                 /* Currently only global counters (PCOUNTER) are implemented
 468                  * but this will be different for local counters (MP). */
 469                 args->v0.counter_nr = 4;
 470         }
 471 
 472         if (++di < domain_nr) {
 473                 args->v0.iter = ++di;
 474                 return 0;
 475         }
 476 
 477         args->v0.iter = 0xff;
 478         return 0;
 479 }
 480 
 481 static int
 482 nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon,
 483                                void *data, u32 size)
 484 {
 485         union {
 486                 struct nvif_perfmon_query_signal_v0 v0;
 487         } *args = data;
 488         struct nvkm_object *object = &perfmon->object;
 489         struct nvkm_pm *pm = perfmon->pm;
 490         struct nvkm_device *device = pm->engine.subdev.device;
 491         struct nvkm_perfdom *dom;
 492         struct nvkm_perfsig *sig;
 493         const bool all = nvkm_boolopt(device->cfgopt, "NvPmShowAll", false);
 494         const bool raw = nvkm_boolopt(device->cfgopt, "NvPmUnnamed", all);
 495         int ret = -ENOSYS, si;
 496 
 497         nvif_ioctl(object, "perfmon query signal size %d\n", size);
 498         if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 499                 nvif_ioctl(object,
 500                            "perfmon query signal vers %d dom %d iter %04x\n",
 501                            args->v0.version, args->v0.domain, args->v0.iter);
 502                 si = (args->v0.iter & 0xffff) - 1;
 503         } else
 504                 return ret;
 505 
 506         dom = nvkm_perfdom_find(pm, args->v0.domain);
 507         if (dom == NULL || si >= (int)dom->signal_nr)
 508                 return -EINVAL;
 509 
 510         if (si >= 0) {
 511                 sig = &dom->signal[si];
 512                 if (raw || !sig->name) {
 513                         snprintf(args->v0.name, sizeof(args->v0.name),
 514                                  "/%s/%02x", dom->name, si);
 515                 } else {
 516                         strncpy(args->v0.name, sig->name,
 517                                 sizeof(args->v0.name) - 1);
 518                 }
 519 
 520                 args->v0.signal = si;
 521                 args->v0.source_nr = nvkm_perfsig_count_perfsrc(sig);
 522         }
 523 
 524         while (++si < dom->signal_nr) {
 525                 if (all || dom->signal[si].name) {
 526                         args->v0.iter = ++si;
 527                         return 0;
 528                 }
 529         }
 530 
 531         args->v0.iter = 0xffff;
 532         return 0;
 533 }
 534 
 535 static int
 536 nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon,
 537                                void *data, u32 size)
 538 {
 539         union {
 540                 struct nvif_perfmon_query_source_v0 v0;
 541         } *args = data;
 542         struct nvkm_object *object = &perfmon->object;
 543         struct nvkm_pm *pm = perfmon->pm;
 544         struct nvkm_perfdom *dom = NULL;
 545         struct nvkm_perfsig *sig;
 546         struct nvkm_perfsrc *src;
 547         u8 source_nr = 0;
 548         int si, ret = -ENOSYS;
 549 
 550         nvif_ioctl(object, "perfmon query source size %d\n", size);
 551         if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 552                 nvif_ioctl(object,
 553                            "perfmon source vers %d dom %d sig %02x iter %02x\n",
 554                            args->v0.version, args->v0.domain, args->v0.signal,
 555                            args->v0.iter);
 556                 si = (args->v0.iter & 0xff) - 1;
 557         } else
 558                 return ret;
 559 
 560         sig = nvkm_perfsig_find(pm, args->v0.domain, args->v0.signal, &dom);
 561         if (!sig)
 562                 return -EINVAL;
 563 
 564         source_nr = nvkm_perfsig_count_perfsrc(sig);
 565         if (si >= (int)source_nr)
 566                 return -EINVAL;
 567 
 568         if (si >= 0) {
 569                 src = nvkm_perfsrc_find(pm, sig, sig->source[si]);
 570                 if (!src)
 571                         return -EINVAL;
 572 
 573                 args->v0.source = sig->source[si];
 574                 args->v0.mask   = src->mask;
 575                 strncpy(args->v0.name, src->name, sizeof(args->v0.name) - 1);
 576         }
 577 
 578         if (++si < source_nr) {
 579                 args->v0.iter = ++si;
 580                 return 0;
 581         }
 582 
 583         args->v0.iter = 0xff;
 584         return 0;
 585 }
 586 
 587 static int
 588 nvkm_perfmon_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
 589 {
 590         struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
 591         switch (mthd) {
 592         case NVIF_PERFMON_V0_QUERY_DOMAIN:
 593                 return nvkm_perfmon_mthd_query_domain(perfmon, data, size);
 594         case NVIF_PERFMON_V0_QUERY_SIGNAL:
 595                 return nvkm_perfmon_mthd_query_signal(perfmon, data, size);
 596         case NVIF_PERFMON_V0_QUERY_SOURCE:
 597                 return nvkm_perfmon_mthd_query_source(perfmon, data, size);
 598         default:
 599                 break;
 600         }
 601         return -EINVAL;
 602 }
 603 
 604 static int
 605 nvkm_perfmon_child_new(const struct nvkm_oclass *oclass, void *data, u32 size,
 606                        struct nvkm_object **pobject)
 607 {
 608         struct nvkm_perfmon *perfmon = nvkm_perfmon(oclass->parent);
 609         return nvkm_perfdom_new_(perfmon, oclass, data, size, pobject);
 610 }
 611 
 612 static int
 613 nvkm_perfmon_child_get(struct nvkm_object *object, int index,
 614                        struct nvkm_oclass *oclass)
 615 {
 616         if (index == 0) {
 617                 oclass->base.oclass = NVIF_CLASS_PERFDOM;
 618                 oclass->base.minver = 0;
 619                 oclass->base.maxver = 0;
 620                 oclass->ctor = nvkm_perfmon_child_new;
 621                 return 0;
 622         }
 623         return -EINVAL;
 624 }
 625 
 626 static void *
 627 nvkm_perfmon_dtor(struct nvkm_object *object)
 628 {
 629         struct nvkm_perfmon *perfmon = nvkm_perfmon(object);
 630         struct nvkm_pm *pm = perfmon->pm;
 631         mutex_lock(&pm->engine.subdev.mutex);
 632         if (pm->perfmon == &perfmon->object)
 633                 pm->perfmon = NULL;
 634         mutex_unlock(&pm->engine.subdev.mutex);
 635         return perfmon;
 636 }
 637 
 638 static const struct nvkm_object_func
 639 nvkm_perfmon = {
 640         .dtor = nvkm_perfmon_dtor,
 641         .mthd = nvkm_perfmon_mthd,
 642         .sclass = nvkm_perfmon_child_get,
 643 };
 644 
 645 static int
 646 nvkm_perfmon_new(struct nvkm_pm *pm, const struct nvkm_oclass *oclass,
 647                  void *data, u32 size, struct nvkm_object **pobject)
 648 {
 649         struct nvkm_perfmon *perfmon;
 650 
 651         if (!(perfmon = kzalloc(sizeof(*perfmon), GFP_KERNEL)))
 652                 return -ENOMEM;
 653         nvkm_object_ctor(&nvkm_perfmon, oclass, &perfmon->object);
 654         perfmon->pm = pm;
 655         *pobject = &perfmon->object;
 656         return 0;
 657 }
 658 
 659 /*******************************************************************************
 660  * PPM engine/subdev functions
 661  ******************************************************************************/
 662 
 663 static int
 664 nvkm_pm_oclass_new(struct nvkm_device *device, const struct nvkm_oclass *oclass,
 665                    void *data, u32 size, struct nvkm_object **pobject)
 666 {
 667         struct nvkm_pm *pm = nvkm_pm(oclass->engine);
 668         int ret;
 669 
 670         ret = nvkm_perfmon_new(pm, oclass, data, size, pobject);
 671         if (ret)
 672                 return ret;
 673 
 674         mutex_lock(&pm->engine.subdev.mutex);
 675         if (pm->perfmon == NULL)
 676                 pm->perfmon = *pobject;
 677         ret = (pm->perfmon == *pobject) ? 0 : -EBUSY;
 678         mutex_unlock(&pm->engine.subdev.mutex);
 679         return ret;
 680 }
 681 
 682 static const struct nvkm_device_oclass
 683 nvkm_pm_oclass = {
 684         .base.oclass = NVIF_CLASS_PERFMON,
 685         .base.minver = -1,
 686         .base.maxver = -1,
 687         .ctor = nvkm_pm_oclass_new,
 688 };
 689 
 690 static int
 691 nvkm_pm_oclass_get(struct nvkm_oclass *oclass, int index,
 692                    const struct nvkm_device_oclass **class)
 693 {
 694         if (index == 0) {
 695                 oclass->base = nvkm_pm_oclass.base;
 696                 *class = &nvkm_pm_oclass;
 697                 return index;
 698         }
 699         return 1;
 700 }
 701 
 702 static int
 703 nvkm_perfsrc_new(struct nvkm_pm *pm, struct nvkm_perfsig *sig,
 704                  const struct nvkm_specsrc *spec)
 705 {
 706         const struct nvkm_specsrc *ssrc;
 707         const struct nvkm_specmux *smux;
 708         struct nvkm_perfsrc *src;
 709         u8 source_nr = 0;
 710 
 711         if (!spec) {
 712                 /* No sources are defined for this signal. */
 713                 return 0;
 714         }
 715 
 716         ssrc = spec;
 717         while (ssrc->name) {
 718                 smux = ssrc->mux;
 719                 while (smux->name) {
 720                         bool found = false;
 721                         u8 source_id = 0;
 722                         u32 len;
 723 
 724                         list_for_each_entry(src, &pm->sources, head) {
 725                                 if (src->addr == ssrc->addr &&
 726                                     src->shift == smux->shift) {
 727                                         found = true;
 728                                         break;
 729                                 }
 730                                 source_id++;
 731                         }
 732 
 733                         if (!found) {
 734                                 src = kzalloc(sizeof(*src), GFP_KERNEL);
 735                                 if (!src)
 736                                         return -ENOMEM;
 737 
 738                                 src->addr   = ssrc->addr;
 739                                 src->mask   = smux->mask;
 740                                 src->shift  = smux->shift;
 741                                 src->enable = smux->enable;
 742 
 743                                 len = strlen(ssrc->name) +
 744                                       strlen(smux->name) + 2;
 745                                 src->name = kzalloc(len, GFP_KERNEL);
 746                                 if (!src->name) {
 747                                         kfree(src);
 748                                         return -ENOMEM;
 749                                 }
 750                                 snprintf(src->name, len, "%s_%s", ssrc->name,
 751                                          smux->name);
 752 
 753                                 list_add_tail(&src->head, &pm->sources);
 754                         }
 755 
 756                         sig->source[source_nr++] = source_id + 1;
 757                         smux++;
 758                 }
 759                 ssrc++;
 760         }
 761 
 762         return 0;
 763 }
 764 
 765 int
 766 nvkm_perfdom_new(struct nvkm_pm *pm, const char *name, u32 mask,
 767                  u32 base, u32 size_unit, u32 size_domain,
 768                  const struct nvkm_specdom *spec)
 769 {
 770         const struct nvkm_specdom *sdom;
 771         const struct nvkm_specsig *ssig;
 772         struct nvkm_perfdom *dom;
 773         int ret, i;
 774 
 775         for (i = 0; i == 0 || mask; i++) {
 776                 u32 addr = base + (i * size_unit);
 777                 if (i && !(mask & (1 << i)))
 778                         continue;
 779 
 780                 sdom = spec;
 781                 while (sdom->signal_nr) {
 782                         dom = kzalloc(struct_size(dom, signal, sdom->signal_nr),
 783                                       GFP_KERNEL);
 784                         if (!dom)
 785                                 return -ENOMEM;
 786 
 787                         if (mask) {
 788                                 snprintf(dom->name, sizeof(dom->name),
 789                                          "%s/%02x/%02x", name, i,
 790                                          (int)(sdom - spec));
 791                         } else {
 792                                 snprintf(dom->name, sizeof(dom->name),
 793                                          "%s/%02x", name, (int)(sdom - spec));
 794                         }
 795 
 796                         list_add_tail(&dom->head, &pm->domains);
 797                         INIT_LIST_HEAD(&dom->list);
 798                         dom->func = sdom->func;
 799                         dom->addr = addr;
 800                         dom->signal_nr = sdom->signal_nr;
 801 
 802                         ssig = (sdom++)->signal;
 803                         while (ssig->name) {
 804                                 struct nvkm_perfsig *sig =
 805                                         &dom->signal[ssig->signal];
 806                                 sig->name = ssig->name;
 807                                 ret = nvkm_perfsrc_new(pm, sig, ssig->source);
 808                                 if (ret)
 809                                         return ret;
 810                                 ssig++;
 811                         }
 812 
 813                         addr += size_domain;
 814                 }
 815 
 816                 mask &= ~(1 << i);
 817         }
 818 
 819         return 0;
 820 }
 821 
 822 static int
 823 nvkm_pm_fini(struct nvkm_engine *engine, bool suspend)
 824 {
 825         struct nvkm_pm *pm = nvkm_pm(engine);
 826         if (pm->func->fini)
 827                 pm->func->fini(pm);
 828         return 0;
 829 }
 830 
 831 static void *
 832 nvkm_pm_dtor(struct nvkm_engine *engine)
 833 {
 834         struct nvkm_pm *pm = nvkm_pm(engine);
 835         struct nvkm_perfdom *dom, *next_dom;
 836         struct nvkm_perfsrc *src, *next_src;
 837 
 838         list_for_each_entry_safe(dom, next_dom, &pm->domains, head) {
 839                 list_del(&dom->head);
 840                 kfree(dom);
 841         }
 842 
 843         list_for_each_entry_safe(src, next_src, &pm->sources, head) {
 844                 list_del(&src->head);
 845                 kfree(src->name);
 846                 kfree(src);
 847         }
 848 
 849         return pm;
 850 }
 851 
 852 static const struct nvkm_engine_func
 853 nvkm_pm = {
 854         .dtor = nvkm_pm_dtor,
 855         .fini = nvkm_pm_fini,
 856         .base.sclass = nvkm_pm_oclass_get,
 857 };
 858 
 859 int
 860 nvkm_pm_ctor(const struct nvkm_pm_func *func, struct nvkm_device *device,
 861              int index, struct nvkm_pm *pm)
 862 {
 863         pm->func = func;
 864         INIT_LIST_HEAD(&pm->domains);
 865         INIT_LIST_HEAD(&pm->sources);
 866         return nvkm_engine_ctor(&nvkm_pm, device, index, true, &pm->engine);
 867 }

/* [<][>][^][v][top][bottom][index][help] */