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