1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/kstat.h> 31 #include "n2piupc_acc.h" 32 #include "n2piupc_tables.h" 33 #include "n2piupc.h" 34 #include "n2piupc_biterr.h" 35 36 #define PIC_STR_LEN 5 /* Size of a PICx name string. */ 37 38 static int n2piupc_create_name_kstat(n2piu_grp_t *grp); 39 static void n2piupc_delete_name_kstats(kstat_t **name_kstats_pp, 40 int num_kstats); 41 static kstat_t *n2piupc_create_cntr_kstat(char *name, int dev_inst, 42 int (*update)(kstat_t *, int), n2piu_ksinfo_t *ksinfop, int num_pics); 43 static int n2piupc_kstat_update(kstat_t *ksp, int rw); 44 static kstat_t *n2piupc_create_picN_kstat(char *mod_name, int pic, 45 uint64_t mask, int num_ev, n2piu_event_t *ev_array); 46 47 /* 48 * One-time initialization for this module. 49 */ 50 int 51 n2piupc_kstat_init() 52 { 53 n2piu_grp_t **grp_pp; 54 n2piu_grp_t *grp_p; 55 56 N2PIUPC_DBG2("n2piupc: kstat_init: enter\n"); 57 58 /* 59 * Initialize the name kstats for each group, drawing upon the table 60 * for values. 61 */ 62 for (grp_pp = leaf_grps; *grp_pp != NULL; grp_pp++) { 63 64 grp_p = *grp_pp; 65 66 N2PIUPC_DBG2("Setting up group for %s\n", grp_p->grp_name); 67 68 /* Create basic pic event-type pair. */ 69 grp_p->name_kstats_pp = kmem_zalloc((grp_p->num_counters * 70 sizeof (kstat_t)), KM_SLEEP); 71 if (n2piupc_create_name_kstat(grp_p) != DDI_SUCCESS) { 72 n2piupc_kstat_fini(); 73 N2PIUPC_DBG1("n2piupc: init: failure exit\n"); 74 return (DDI_FAILURE); 75 } 76 } 77 78 N2PIUPC_DBG2("n2piupc: kstat_init: success exit\n"); 79 80 return (DDI_SUCCESS); 81 } 82 83 /* 84 * Per-instance initialization for this module. 85 */ 86 int 87 n2piupc_kstat_attach(n2piupc_t *n2piupc_p) 88 { 89 n2piu_grp_t **grp_pp; 90 n2piu_grp_t *grp_p; 91 n2piu_ksinfo_t *ksinfo_p; 92 93 int i; 94 95 N2PIUPC_DBG2("n2piupc: kstat_attach %d: enter\n", 96 ddi_get_instance(n2piupc_p->n2piupc_dip)); 97 98 /* Initialize biterr module. Save opaque result. */ 99 if (n2piupc_biterr_attach(&n2piupc_p->n2piupc_biterr_p) != DDI_SUCCESS) 100 goto err; 101 102 /* Set up kstats for each group. */ 103 for (i = 0, grp_pp = leaf_grps; *grp_pp != NULL; i++, grp_pp++) { 104 105 grp_p = *grp_pp; 106 107 /* 108 * ksinfo_p keeps all info needed by n2piupc_kstat_update, 109 * which is fired off asynchronously on demand by the kstat 110 * framework. 111 */ 112 ksinfo_p = (n2piu_ksinfo_t *)kmem_zalloc( 113 sizeof (n2piu_ksinfo_t), KM_SLEEP); 114 115 ksinfo_p->n2piupc_p = n2piupc_p; 116 ksinfo_p->grp_p = grp_p; 117 118 /* Also save in state structure, for later cleanup. */ 119 n2piupc_p->n2piupc_ksinfo_p[i] = ksinfo_p; 120 121 /* Create counter kstats */ 122 ksinfo_p->cntr_ksp = n2piupc_create_cntr_kstat(grp_p->grp_name, 123 ddi_get_instance(n2piupc_p->n2piupc_dip), 124 n2piupc_kstat_update, ksinfo_p, grp_p->num_counters); 125 if (ksinfo_p->cntr_ksp == NULL) 126 goto err; 127 } 128 129 N2PIUPC_DBG2("n2piupc: kstat_attach: success exit\n"); 130 return (DDI_SUCCESS); 131 err: 132 n2piupc_kstat_detach(n2piupc_p); 133 N2PIUPC_DBG2("n2piupc: kstat_attach: failure exit\n"); 134 return (DDI_FAILURE); 135 } 136 137 /* 138 * Create the name kstats for each group. 139 */ 140 static int 141 n2piupc_create_name_kstat(n2piu_grp_t *grp_p) 142 { 143 int i; 144 145 for (i = 0; i < grp_p->num_counters; i++) { 146 grp_p->name_kstats_pp[i] = n2piupc_create_picN_kstat( 147 grp_p->grp_name, i, 148 grp_p->regsel_p->fields_p[i].event_offset, 149 grp_p->regsel_p->fields_p[i].num_events, 150 grp_p->regsel_p->fields_p[i].events_p); 151 152 if (grp_p->name_kstats_pp[i] == NULL) 153 return (DDI_FAILURE); 154 } 155 return (DDI_SUCCESS); 156 } 157 158 /* 159 * Create the picN kstat. Returns a pointer to the 160 * kstat which the driver must store to allow it 161 * to be deleted when necessary. 162 */ 163 static kstat_t * 164 n2piupc_create_picN_kstat(char *mod_name, int pic, uint64_t ev_offset, 165 int num_ev, n2piu_event_t *ev_array) 166 { 167 int event; 168 char pic_name[PIC_STR_LEN]; 169 kstat_t *picN_ksp = NULL; 170 struct kstat_named *pic_named_data; 171 172 173 (void) snprintf(pic_name, PIC_STR_LEN, "pic%1d", pic); 174 175 if ((picN_ksp = kstat_create(mod_name, 0, pic_name, 176 "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) { 177 cmn_err(CE_WARN, "%s %s : kstat create failed", 178 mod_name, pic_name); 179 return (NULL); 180 } 181 182 /* NOTE: Number of events is assumed to always be non-zero. */ 183 184 pic_named_data = (struct kstat_named *)picN_ksp->ks_data; 185 186 /* 187 * Fill up data section of the kstat 188 * Write event names and their associated pcr masks. 189 * num_ev - 1 is because CLEAR_PIC is added separately. 190 */ 191 for (event = 0; event < num_ev - 1; event++) { 192 pic_named_data[event].value.ui64 = 193 ev_array[event].value << ev_offset; 194 195 kstat_named_init(&pic_named_data[event], 196 ev_array[event].name, KSTAT_DATA_UINT64); 197 } 198 199 /* 200 * add the clear_pic entry 201 */ 202 pic_named_data[event].value.ui64 = 203 (uint64_t)~(ev_array[event].value << ev_offset); 204 205 kstat_named_init(&pic_named_data[event], ev_array[event].name, 206 KSTAT_DATA_UINT64); 207 208 kstat_install(picN_ksp); 209 210 return (picN_ksp); 211 } 212 213 /* 214 * Create the "counters" kstat. 215 */ 216 static kstat_t * 217 n2piupc_create_cntr_kstat(char *name, int dev_inst, 218 int (*update)(kstat_t *, int), n2piu_ksinfo_t *ksinfop, int num_pics) 219 { 220 int i; 221 char pic_str[PIC_STR_LEN]; 222 struct kstat *counters_ksp; 223 struct kstat_named *counters_named_data; 224 225 N2PIUPC_DBG2("n2piupc_create_cntr_kstat: name: %s instance: %d\n", 226 name, dev_inst); 227 228 /* 229 * Size of kstat is num_pics + 1. extra one for pcr. 230 */ 231 232 if ((counters_ksp = kstat_create(name, dev_inst, "counters", "bus", 233 KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) { 234 cmn_err(CE_WARN, "%s%d: kstat_create for %s counters failed", 235 NAMEINST(ksinfop->n2piupc_p->n2piupc_dip), name); 236 return (NULL); 237 } 238 239 counters_named_data = (struct kstat_named *)(counters_ksp->ks_data); 240 kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64); 241 242 for (i = 0; i < num_pics; i++) { 243 (void) snprintf(pic_str, PIC_STR_LEN, "pic%1d", i); 244 245 kstat_named_init(&counters_named_data[i+1], pic_str, 246 KSTAT_DATA_UINT64); 247 } 248 249 /* 250 * Store the reg type and other info. in the kstat's private field 251 * so that they are available to the update function. 252 */ 253 counters_ksp->ks_private = (void *)ksinfop; 254 counters_ksp->ks_update = update; 255 256 kstat_install(counters_ksp); 257 258 return (counters_ksp); 259 } 260 261 /* Higher-level register write, hides SW abstractions. */ 262 static int 263 n2piupc_write(n2piupc_t *n2piupc_p, int regid, uint64_t data) 264 { 265 int rval = SUCCESS; 266 267 switch (regid) { 268 case SW_N2PIU_BITERR_SEL: 269 case SW_N2PIU_BITERR_CLR: 270 rval = n2piupc_biterr_write(n2piupc_p, regid, data); 271 break; 272 273 default: 274 if (n2piupc_set_perfreg(n2piupc_p->n2piupc_handle, 275 regid, data) != H_EOK) 276 rval = EIO; 277 break; 278 } 279 280 N2PIUPC_DBG1("n2piupc_write: status:%d\n", rval); 281 return (rval); 282 } 283 284 285 /* Higher-level register read, hides SW abstractions. */ 286 static int 287 n2piupc_read(n2piupc_t *n2piupc_p, int regid, uint64_t *data) 288 { 289 int rval = SUCCESS; 290 291 N2PIUPC_DBG2("n2piupc_read enter: regid:%d\n", regid); 292 293 /* This "register" is a layered SW-implemented reg. */ 294 switch (regid) { 295 case SW_N2PIU_BITERR_CNT1_DATA: 296 case SW_N2PIU_BITERR_CNT2_DATA: 297 case SW_N2PIU_BITERR_SEL: 298 rval = n2piupc_biterr_read(n2piupc_p, regid, data); 299 break; 300 301 default: 302 if (n2piupc_get_perfreg(n2piupc_p->n2piupc_handle, 303 regid, data) != H_EOK) 304 rval = EIO; 305 break; 306 } 307 308 N2PIUPC_DBG1("n2piupc_read exit: data:0x%lx, status:%d\n", *data, 309 rval); 310 311 return (rval); 312 } 313 314 315 /* 316 * Program a performance counter. 317 * 318 * reggroup is which type of counter. 319 * counter is the counter number. 320 * event is the event to program for that counter. 321 */ 322 static int 323 n2piupc_perfcnt_program(n2piupc_t *n2piupc_p, n2piu_grp_t *grp_p, 324 uint64_t new_events) 325 { 326 uint64_t old_events; 327 int rval = SUCCESS; 328 uint64_t event_mask; 329 int counter; 330 331 N2PIUPC_DBG1( 332 "n2piupc_perfcnt_program enter: new_events:0x%" PRIx64 "\n", 333 new_events); 334 335 if ((rval = n2piupc_read(n2piupc_p, grp_p->regsel_p->regoff, 336 &old_events)) != SUCCESS) { 337 N2PIUPC_DBG1( 338 "Read of old event data failed, select reg offset:%ld\n", 339 grp_p->regsel_p->regoff); 340 goto done_pgm; 341 } 342 343 N2PIUPC_DBG1(" old_events:0x%" PRIx64 "\n", old_events); 344 345 for (counter = 0; counter < grp_p->num_counters; counter++) { 346 347 if (grp_p->counters_p[counter].zero_regoff == NO_REGISTER) 348 continue; 349 350 event_mask = grp_p->regsel_p->fields_p[counter].event_mask << 351 grp_p->regsel_p->fields_p[counter].event_offset; 352 353 N2PIUPC_DBG1( 354 "grp:%s, counter:%d, zero_regoff:0x%lx, " 355 "event_mask:0x%" PRIx64 ", old&mask:0x%lx, " 356 "new&mask:0x%lx\n", 357 grp_p->grp_name, counter, 358 grp_p->counters_p[counter].zero_regoff, 359 event_mask, old_events & event_mask, 360 new_events & event_mask); 361 362 if ((old_events & event_mask) == 363 (new_events & event_mask)) 364 continue; 365 366 N2PIUPC_DBG1("Zeroing counter %d\n", counter); 367 if ((rval = n2piupc_write(n2piupc_p, 368 grp_p->counters_p[counter].zero_regoff, 369 grp_p->counters_p[counter].zero_value)) != SUCCESS) 370 goto done_pgm; 371 } 372 373 if (old_events != new_events) { 374 N2PIUPC_DBG1("old != new, setting event reg %ld to 0x%lx\n", 375 grp_p->regsel_p->regoff, new_events); 376 if ((rval = n2piupc_write(n2piupc_p, grp_p->regsel_p->regoff, 377 new_events)) != SUCCESS) { 378 N2PIUPC_DBG1( 379 "Write of new event data failed, " 380 "select reg offset: %ld\n", 381 grp_p->regsel_p->regoff); 382 goto done_pgm; 383 } 384 } 385 done_pgm: 386 N2PIUPC_DBG1("n2piupc_perfcnt_program: returning status %d.\n", rval); 387 return (rval); 388 } 389 390 /* 391 * kstat update function. Handles reads/writes 392 * from/to kstat. 393 */ 394 static int 395 n2piupc_kstat_update(kstat_t *ksp, int rw) 396 { 397 struct kstat_named *data_p; 398 int counter; 399 n2piu_ksinfo_t *ksinfop = ksp->ks_private; 400 n2piu_grp_t *grp_p = ksinfop->grp_p; 401 n2piupc_t *n2piupc_p = ksinfop->n2piupc_p; 402 403 data_p = (struct kstat_named *)ksp->ks_data; 404 405 if (rw == KSTAT_WRITE) { 406 407 N2PIUPC_DBG2("n2piupc_kstat_update: wr %ld\n", 408 data_p[0].value.ui64); 409 410 /* 411 * Fields without programmable events won't be zeroed as 412 * n2piupc_perfcnt_program is what zeros them. 413 */ 414 415 /* This group has programmable events. */ 416 if (grp_p->regsel_p->regoff != NO_REGISTER) { 417 418 N2PIUPC_DBG2("write: regoff has valid register\n"); 419 if (n2piupc_perfcnt_program(n2piupc_p, grp_p, 420 data_p[0].value.ui64) != SUCCESS) 421 return (EIO); 422 } 423 424 } else { /* Read the event register and all of the counters. */ 425 426 /* This group has programmable events. */ 427 if (grp_p->regsel_p->regoff != NO_REGISTER) { 428 429 N2PIUPC_DBG2("read: regoff has valid register\n"); 430 if (n2piupc_read(n2piupc_p, grp_p->regsel_p->regoff, 431 &data_p[0].value.ui64) != SUCCESS) 432 return (EIO); 433 } else 434 data_p[0].value.ui64 = 0ull; 435 436 N2PIUPC_DBG2("n2piupc_kstat_update: rd event %ld", 437 data_p[0].value.ui64); 438 439 for (counter = 0; counter < grp_p->num_counters; counter++) { 440 if (n2piupc_read(n2piupc_p, 441 grp_p->counters_p[counter].regoff, 442 &data_p[counter + 1].value.ui64) != SUCCESS) 443 return (EIO); 444 445 N2PIUPC_DBG2("cntr%d, off:0x%lx, val:0x%ld", counter, 446 grp_p->counters_p[counter].regoff, 447 data_p[counter + 1].value.ui64); 448 } 449 } 450 return (SUCCESS); 451 } 452 453 void 454 n2piupc_kstat_fini() 455 { 456 n2piu_grp_t **grp_pp; 457 n2piu_grp_t *grp_p; 458 int j; 459 460 N2PIUPC_DBG2("n2piupc_kstat_fini called\n"); 461 462 for (j = 0, grp_pp = leaf_grps; *grp_pp != NULL; j++, grp_pp++) { 463 grp_p = *grp_pp; 464 if (grp_p->name_kstats_pp != NULL) { 465 n2piupc_delete_name_kstats(grp_p->name_kstats_pp, 466 grp_p->num_counters); 467 kmem_free(grp_p->name_kstats_pp, 468 grp_p->num_counters * sizeof (kstat_t)); 469 grp_p->name_kstats_pp = NULL; 470 } 471 } 472 } 473 474 static void 475 n2piupc_delete_name_kstats(kstat_t **name_kstats_pp, int num_kstats) 476 { 477 int i; 478 479 if (name_kstats_pp != NULL) { 480 for (i = 0; i < num_kstats; i++) { 481 if (name_kstats_pp[i] != NULL) 482 kstat_delete(name_kstats_pp[i]); 483 } 484 } 485 } 486 487 void 488 n2piupc_kstat_detach(n2piupc_t *n2piupc_p) 489 { 490 n2piu_grp_t **grp_pp; 491 int i; 492 493 N2PIUPC_DBG2("n2piupc_kstat_detach called\n"); 494 495 for (i = 0, grp_pp = leaf_grps; *grp_pp != NULL; i++, grp_pp++) { 496 if (n2piupc_p->n2piupc_ksinfo_p[i] != NULL) { 497 if (n2piupc_p->n2piupc_ksinfo_p[i]->cntr_ksp != NULL) 498 kstat_delete( 499 n2piupc_p->n2piupc_ksinfo_p[i]->cntr_ksp); 500 kmem_free(n2piupc_p->n2piupc_ksinfo_p[i], 501 sizeof (n2piu_ksinfo_t)); 502 } 503 504 } 505 506 n2piupc_biterr_detach(n2piupc_p->n2piupc_biterr_p); 507 } 508