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 struct nvkm_perfctx *ctx = (void *)object; 336 337 mutex_lock(&nv_subdev(ppm)->mutex); 338 nvkm_engctx_destroy(&ctx->base); 339 if (ppm->context == ctx) 340 ppm->context = NULL; 341 mutex_unlock(&nv_subdev(ppm)->mutex); 342 } 343 344 static int 345 nvkm_perfctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine, 346 struct nvkm_oclass *oclass, void *data, u32 size, 347 struct nvkm_object **pobject) 348 { 349 struct nvkm_pm *ppm = (void *)engine; 350 struct nvkm_perfctx *ctx; 351 int ret; 352 353 ret = nvkm_engctx_create(parent, engine, oclass, NULL, 0, 0, 0, &ctx); 354 *pobject = nv_object(ctx); 355 if (ret) 356 return ret; 357 358 mutex_lock(&nv_subdev(ppm)->mutex); 359 if (ppm->context == NULL) 360 ppm->context = ctx; 361 if (ctx != ppm->context) 362 ret = -EBUSY; 363 mutex_unlock(&nv_subdev(ppm)->mutex); 364 365 return ret; 366 } 367 368 struct nvkm_oclass 369 nvkm_pm_cclass = { 370 .handle = NV_ENGCTX(PM, 0x00), 371 .ofuncs = &(struct nvkm_ofuncs) { 372 .ctor = nvkm_perfctx_ctor, 373 .dtor = nvkm_perfctx_dtor, 374 .init = _nvkm_engctx_init, 375 .fini = _nvkm_engctx_fini, 376 }, 377 }; 378 379 /******************************************************************************* 380 * PPM engine/subdev functions 381 ******************************************************************************/ 382 int 383 nvkm_perfdom_new(struct nvkm_pm *ppm, const char *name, u32 mask, 384 u32 base, u32 size_unit, u32 size_domain, 385 const struct nvkm_specdom *spec) 386 { 387 const struct nvkm_specdom *sdom; 388 const struct nvkm_specsig *ssig; 389 struct nvkm_perfdom *dom; 390 int i; 391 392 for (i = 0; i == 0 || mask; i++) { 393 u32 addr = base + (i * size_unit); 394 if (i && !(mask & (1 << i))) 395 continue; 396 397 sdom = spec; 398 while (sdom->signal_nr) { 399 dom = kzalloc(sizeof(*dom) + sdom->signal_nr * 400 sizeof(*dom->signal), GFP_KERNEL); 401 if (!dom) 402 return -ENOMEM; 403 404 if (mask) { 405 snprintf(dom->name, sizeof(dom->name), 406 "%s/%02x/%02x", name, i, 407 (int)(sdom - spec)); 408 } else { 409 snprintf(dom->name, sizeof(dom->name), 410 "%s/%02x", name, (int)(sdom - spec)); 411 } 412 413 list_add_tail(&dom->head, &ppm->domains); 414 INIT_LIST_HEAD(&dom->list); 415 dom->func = sdom->func; 416 dom->addr = addr; 417 dom->quad = QUAD_MASK; 418 dom->signal_nr = sdom->signal_nr; 419 420 ssig = (sdom++)->signal; 421 while (ssig->name) { 422 dom->signal[ssig->signal].name = ssig->name; 423 ssig++; 424 } 425 426 addr += size_domain; 427 } 428 429 mask &= ~(1 << i); 430 } 431 432 return 0; 433 } 434 435 int 436 _nvkm_pm_fini(struct nvkm_object *object, bool suspend) 437 { 438 struct nvkm_pm *ppm = (void *)object; 439 return nvkm_engine_fini(&ppm->base, suspend); 440 } 441 442 int 443 _nvkm_pm_init(struct nvkm_object *object) 444 { 445 struct nvkm_pm *ppm = (void *)object; 446 return nvkm_engine_init(&ppm->base); 447 } 448 449 void 450 _nvkm_pm_dtor(struct nvkm_object *object) 451 { 452 struct nvkm_pm *ppm = (void *)object; 453 struct nvkm_perfdom *dom, *tmp; 454 455 list_for_each_entry_safe(dom, tmp, &ppm->domains, head) { 456 list_del(&dom->head); 457 kfree(dom); 458 } 459 460 nvkm_engine_destroy(&ppm->base); 461 } 462 463 int 464 nvkm_pm_create_(struct nvkm_object *parent, struct nvkm_object *engine, 465 struct nvkm_oclass *oclass, int length, void **pobject) 466 { 467 struct nvkm_pm *ppm; 468 int ret; 469 470 ret = nvkm_engine_create_(parent, engine, oclass, true, "PPM", 471 "pm", length, pobject); 472 ppm = *pobject; 473 if (ret) 474 return ret; 475 476 INIT_LIST_HEAD(&ppm->domains); 477 return 0; 478 } 479