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