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 25 #include <core/client.h> 26 #include <core/option.h> 27 #include <nvif/unpack.h> 28 #include <nvif/class.h> 29 #include <nvif/ioctl.h> 30 31 #include <subdev/clk.h> 32 33 #include "priv.h" 34 35 #define QUAD_MASK 0x0f 36 #define QUAD_FREE 0x01 37 38 static struct nouveau_perfsig * 39 nouveau_perfsig_find_(struct nouveau_perfdom *dom, const char *name, u32 size) 40 { 41 char path[64]; 42 int i; 43 44 if (name[0] != '/') { 45 for (i = 0; i < dom->signal_nr; i++) { 46 if ( dom->signal[i].name && 47 !strncmp(name, dom->signal[i].name, size)) 48 return &dom->signal[i]; 49 } 50 } else { 51 for (i = 0; i < dom->signal_nr; i++) { 52 snprintf(path, sizeof(path), "/%s/%02x", dom->name, i); 53 if (!strncmp(name, path, size)) 54 return &dom->signal[i]; 55 } 56 } 57 58 return NULL; 59 } 60 61 struct nouveau_perfsig * 62 nouveau_perfsig_find(struct nouveau_pm *ppm, const char *name, u32 size, 63 struct nouveau_perfdom **pdom) 64 { 65 struct nouveau_perfdom *dom = *pdom; 66 struct nouveau_perfsig *sig; 67 68 if (dom == NULL) { 69 list_for_each_entry(dom, &ppm->domains, head) { 70 sig = nouveau_perfsig_find_(dom, name, size); 71 if (sig) { 72 *pdom = dom; 73 return sig; 74 } 75 } 76 77 return NULL; 78 } 79 80 return nouveau_perfsig_find_(dom, name, size); 81 } 82 83 struct nouveau_perfctr * 84 nouveau_perfsig_wrap(struct nouveau_pm *ppm, const char *name, 85 struct nouveau_perfdom **pdom) 86 { 87 struct nouveau_perfsig *sig; 88 struct nouveau_perfctr *ctr; 89 90 sig = nouveau_perfsig_find(ppm, name, strlen(name), pdom); 91 if (!sig) 92 return NULL; 93 94 ctr = kzalloc(sizeof(*ctr), GFP_KERNEL); 95 if (ctr) { 96 ctr->signal[0] = sig; 97 ctr->logic_op = 0xaaaa; 98 } 99 100 return ctr; 101 } 102 103 /******************************************************************************* 104 * Perfmon object classes 105 ******************************************************************************/ 106 static int 107 nouveau_perfctr_query(struct nouveau_object *object, void *data, u32 size) 108 { 109 union { 110 struct nvif_perfctr_query_v0 v0; 111 } *args = data; 112 struct nouveau_device *device = nv_device(object); 113 struct nouveau_pm *ppm = (void *)object->engine; 114 struct nouveau_perfdom *dom = NULL, *chk; 115 const bool all = nouveau_boolopt(device->cfgopt, "NvPmShowAll", false); 116 const bool raw = nouveau_boolopt(device->cfgopt, "NvPmUnnamed", all); 117 const char *name; 118 int tmp = 0, di, si; 119 int ret; 120 121 nv_ioctl(object, "perfctr query size %d\n", size); 122 if (nvif_unpack(args->v0, 0, 0, false)) { 123 nv_ioctl(object, "perfctr query vers %d iter %08x\n", 124 args->v0.version, args->v0.iter); 125 di = (args->v0.iter & 0xff000000) >> 24; 126 si = (args->v0.iter & 0x00ffffff) - 1; 127 } else 128 return ret; 129 130 list_for_each_entry(chk, &ppm->domains, head) { 131 if (tmp++ == di) { 132 dom = chk; 133 break; 134 } 135 } 136 137 if (dom == NULL || si >= (int)dom->signal_nr) 138 return -EINVAL; 139 140 if (si >= 0) { 141 if (raw || !(name = dom->signal[si].name)) { 142 snprintf(args->v0.name, sizeof(args->v0.name), 143 "/%s/%02x", dom->name, si); 144 } else { 145 strncpy(args->v0.name, name, sizeof(args->v0.name)); 146 } 147 } 148 149 do { 150 while (++si < dom->signal_nr) { 151 if (all || dom->signal[si].name) { 152 args->v0.iter = (di << 24) | ++si; 153 return 0; 154 } 155 } 156 si = -1; 157 di = di + 1; 158 dom = list_entry(dom->head.next, typeof(*dom), head); 159 } while (&dom->head != &ppm->domains); 160 161 args->v0.iter = 0xffffffff; 162 return 0; 163 } 164 165 static int 166 nouveau_perfctr_sample(struct nouveau_object *object, void *data, u32 size) 167 { 168 union { 169 struct nvif_perfctr_sample none; 170 } *args = data; 171 struct nouveau_pm *ppm = (void *)object->engine; 172 struct nouveau_perfctr *ctr, *tmp; 173 struct nouveau_perfdom *dom; 174 int ret; 175 176 nv_ioctl(object, "perfctr sample size %d\n", size); 177 if (nvif_unvers(args->none)) { 178 nv_ioctl(object, "perfctr sample\n"); 179 } else 180 return ret; 181 ppm->sequence++; 182 183 list_for_each_entry(dom, &ppm->domains, head) { 184 /* sample previous batch of counters */ 185 if (dom->quad != QUAD_MASK) { 186 dom->func->next(ppm, dom); 187 tmp = NULL; 188 while (!list_empty(&dom->list)) { 189 ctr = list_first_entry(&dom->list, 190 typeof(*ctr), head); 191 if (ctr->slot < 0) break; 192 if ( tmp && tmp == ctr) break; 193 if (!tmp) tmp = ctr; 194 dom->func->read(ppm, dom, ctr); 195 ctr->slot = -1; 196 list_move_tail(&ctr->head, &dom->list); 197 } 198 } 199 200 dom->quad = QUAD_MASK; 201 202 /* setup next batch of counters for sampling */ 203 list_for_each_entry(ctr, &dom->list, head) { 204 ctr->slot = ffs(dom->quad) - 1; 205 if (ctr->slot < 0) 206 break; 207 dom->quad &= ~(QUAD_FREE << ctr->slot); 208 dom->func->init(ppm, dom, ctr); 209 } 210 211 if (dom->quad != QUAD_MASK) 212 dom->func->next(ppm, dom); 213 } 214 215 return 0; 216 } 217 218 static int 219 nouveau_perfctr_read(struct nouveau_object *object, void *data, u32 size) 220 { 221 union { 222 struct nvif_perfctr_read_v0 v0; 223 } *args = data; 224 struct nouveau_perfctr *ctr = (void *)object; 225 int ret; 226 227 nv_ioctl(object, "perfctr read size %d\n", size); 228 if (nvif_unpack(args->v0, 0, 0, false)) { 229 nv_ioctl(object, "perfctr read vers %d\n", args->v0.version); 230 } else 231 return ret; 232 233 if (!ctr->clk) 234 return -EAGAIN; 235 236 args->v0.clk = ctr->clk; 237 args->v0.ctr = ctr->ctr; 238 return 0; 239 } 240 241 static int 242 nouveau_perfctr_mthd(struct nouveau_object *object, u32 mthd, 243 void *data, u32 size) 244 { 245 switch (mthd) { 246 case NVIF_PERFCTR_V0_QUERY: 247 return nouveau_perfctr_query(object, data, size); 248 case NVIF_PERFCTR_V0_SAMPLE: 249 return nouveau_perfctr_sample(object, data, size); 250 case NVIF_PERFCTR_V0_READ: 251 return nouveau_perfctr_read(object, data, size); 252 default: 253 break; 254 } 255 return -EINVAL; 256 } 257 258 static void 259 nouveau_perfctr_dtor(struct nouveau_object *object) 260 { 261 struct nouveau_perfctr *ctr = (void *)object; 262 if (ctr->head.next) 263 list_del(&ctr->head); 264 nouveau_object_destroy(&ctr->base); 265 } 266 267 static int 268 nouveau_perfctr_ctor(struct nouveau_object *parent, 269 struct nouveau_object *engine, 270 struct nouveau_oclass *oclass, void *data, u32 size, 271 struct nouveau_object **pobject) 272 { 273 union { 274 struct nvif_perfctr_v0 v0; 275 } *args = data; 276 struct nouveau_pm *ppm = (void *)engine; 277 struct nouveau_perfdom *dom = NULL; 278 struct nouveau_perfsig *sig[4] = {}; 279 struct nouveau_perfctr *ctr; 280 int ret, i; 281 282 nv_ioctl(parent, "create perfctr size %d\n", size); 283 if (nvif_unpack(args->v0, 0, 0, false)) { 284 nv_ioctl(parent, "create perfctr vers %d logic_op %04x\n", 285 args->v0.version, args->v0.logic_op); 286 } else 287 return ret; 288 289 for (i = 0; i < ARRAY_SIZE(args->v0.name) && args->v0.name[i][0]; i++) { 290 sig[i] = nouveau_perfsig_find(ppm, args->v0.name[i], 291 strnlen(args->v0.name[i], 292 sizeof(args->v0.name[i])), 293 &dom); 294 if (!sig[i]) 295 return -EINVAL; 296 } 297 298 ret = nouveau_object_create(parent, engine, oclass, 0, &ctr); 299 *pobject = nv_object(ctr); 300 if (ret) 301 return ret; 302 303 ctr->slot = -1; 304 ctr->logic_op = args->v0.logic_op; 305 ctr->signal[0] = sig[0]; 306 ctr->signal[1] = sig[1]; 307 ctr->signal[2] = sig[2]; 308 ctr->signal[3] = sig[3]; 309 if (dom) 310 list_add_tail(&ctr->head, &dom->list); 311 return 0; 312 } 313 314 static struct nouveau_ofuncs 315 nouveau_perfctr_ofuncs = { 316 .ctor = nouveau_perfctr_ctor, 317 .dtor = nouveau_perfctr_dtor, 318 .init = nouveau_object_init, 319 .fini = nouveau_object_fini, 320 .mthd = nouveau_perfctr_mthd, 321 }; 322 323 struct nouveau_oclass 324 nouveau_pm_sclass[] = { 325 { .handle = NVIF_IOCTL_NEW_V0_PERFCTR, 326 .ofuncs = &nouveau_perfctr_ofuncs, 327 }, 328 {}, 329 }; 330 331 /******************************************************************************* 332 * PPM context 333 ******************************************************************************/ 334 static void 335 nouveau_perfctx_dtor(struct nouveau_object *object) 336 { 337 struct nouveau_pm *ppm = (void *)object->engine; 338 mutex_lock(&nv_subdev(ppm)->mutex); 339 nouveau_engctx_destroy(&ppm->context->base); 340 ppm->context = NULL; 341 mutex_unlock(&nv_subdev(ppm)->mutex); 342 } 343 344 static int 345 nouveau_perfctx_ctor(struct nouveau_object *parent, 346 struct nouveau_object *engine, 347 struct nouveau_oclass *oclass, void *data, u32 size, 348 struct nouveau_object **pobject) 349 { 350 struct nouveau_pm *ppm = (void *)engine; 351 struct nouveau_perfctx *ctx; 352 int ret; 353 354 ret = nouveau_engctx_create(parent, engine, oclass, NULL, 355 0, 0, 0, &ctx); 356 *pobject = nv_object(ctx); 357 if (ret) 358 return ret; 359 360 mutex_lock(&nv_subdev(ppm)->mutex); 361 if (ppm->context == NULL) 362 ppm->context = ctx; 363 mutex_unlock(&nv_subdev(ppm)->mutex); 364 365 if (ctx != ppm->context) 366 return -EBUSY; 367 368 return 0; 369 } 370 371 struct nouveau_oclass 372 nouveau_pm_cclass = { 373 .handle = NV_ENGCTX(PM, 0x00), 374 .ofuncs = &(struct nouveau_ofuncs) { 375 .ctor = nouveau_perfctx_ctor, 376 .dtor = nouveau_perfctx_dtor, 377 .init = _nouveau_engctx_init, 378 .fini = _nouveau_engctx_fini, 379 }, 380 }; 381 382 /******************************************************************************* 383 * PPM engine/subdev functions 384 ******************************************************************************/ 385 int 386 nouveau_perfdom_new(struct nouveau_pm *ppm, const char *name, u32 mask, 387 u32 base, u32 size_unit, u32 size_domain, 388 const struct nouveau_specdom *spec) 389 { 390 const struct nouveau_specdom *sdom; 391 const struct nouveau_specsig *ssig; 392 struct nouveau_perfdom *dom; 393 int i; 394 395 for (i = 0; i == 0 || mask; i++) { 396 u32 addr = base + (i * size_unit); 397 if (i && !(mask & (1 << i))) 398 continue; 399 400 sdom = spec; 401 while (sdom->signal_nr) { 402 dom = kzalloc(sizeof(*dom) + sdom->signal_nr * 403 sizeof(*dom->signal), GFP_KERNEL); 404 if (!dom) 405 return -ENOMEM; 406 407 if (mask) { 408 snprintf(dom->name, sizeof(dom->name), 409 "%s/%02x/%02x", name, i, 410 (int)(sdom - spec)); 411 } else { 412 snprintf(dom->name, sizeof(dom->name), 413 "%s/%02x", name, (int)(sdom - spec)); 414 } 415 416 list_add_tail(&dom->head, &ppm->domains); 417 INIT_LIST_HEAD(&dom->list); 418 dom->func = sdom->func; 419 dom->addr = addr; 420 dom->quad = QUAD_MASK; 421 dom->signal_nr = sdom->signal_nr; 422 423 ssig = (sdom++)->signal; 424 while (ssig->name) { 425 dom->signal[ssig->signal].name = ssig->name; 426 ssig++; 427 } 428 429 addr += size_domain; 430 } 431 432 mask &= ~(1 << i); 433 } 434 435 return 0; 436 } 437 438 int 439 _nouveau_pm_fini(struct nouveau_object *object, bool suspend) 440 { 441 struct nouveau_pm *ppm = (void *)object; 442 return nouveau_engine_fini(&ppm->base, suspend); 443 } 444 445 int 446 _nouveau_pm_init(struct nouveau_object *object) 447 { 448 struct nouveau_pm *ppm = (void *)object; 449 return nouveau_engine_init(&ppm->base); 450 } 451 452 void 453 _nouveau_pm_dtor(struct nouveau_object *object) 454 { 455 struct nouveau_pm *ppm = (void *)object; 456 struct nouveau_perfdom *dom, *tmp; 457 458 list_for_each_entry_safe(dom, tmp, &ppm->domains, head) { 459 list_del(&dom->head); 460 kfree(dom); 461 } 462 463 nouveau_engine_destroy(&ppm->base); 464 } 465 466 int 467 nouveau_pm_create_(struct nouveau_object *parent, 468 struct nouveau_object *engine, 469 struct nouveau_oclass *oclass, 470 int length, void **pobject) 471 { 472 struct nouveau_pm *ppm; 473 int ret; 474 475 ret = nouveau_engine_create_(parent, engine, oclass, true, "PPM", 476 "pm", length, pobject); 477 ppm = *pobject; 478 if (ret) 479 return ret; 480 481 INIT_LIST_HEAD(&ppm->domains); 482 return 0; 483 } 484