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 37static struct nvkm_perfsig * 38nvkm_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 60struct nvkm_perfsig * 61nvkm_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 82struct nvkm_perfctr * 83nvkm_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 ******************************************************************************/ 105static int 106nvkm_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 164static int 165nvkm_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 217static int 218nvkm_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 240static int 241nvkm_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 256static void 257nvkm_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 265static int 266nvkm_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 311static struct nvkm_ofuncs 312nvkm_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 320struct nvkm_oclass 321nvkm_pm_sclass[] = { 322 { .handle = NVIF_IOCTL_NEW_V0_PERFCTR, 323 .ofuncs = &nvkm_perfctr_ofuncs, 324 }, 325 {}, 326}; 327 328/******************************************************************************* 329 * PPM context 330 ******************************************************************************/ 331static void 332nvkm_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 341static int 342nvkm_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 366struct nvkm_oclass 367nvkm_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 ******************************************************************************/ 380int 381nvkm_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 433int 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 440int 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 447void 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 461int 462nvkm_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} 477