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