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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/types.h>
29#include <sys/async.h>
30#include <sys/sunddi.h>
31#include <sys/sunndi.h>
32#include <sys/ddi_impldefs.h>
33#include <sys/pcicmu/pcicmu.h>
34#include <sys/machsystm.h>
35#include <sys/kstat.h>
36
37static kstat_t *pcmu_create_picN_kstat(char *, int, int, int,
38	pcmu_kev_mask_t *);
39
40void
41pcmu_kstat_create(pcmu_t *pcmu_p)
42{
43	pcmu_add_upstream_kstat(pcmu_p);
44}
45
46void
47pcmu_kstat_destroy(pcmu_t *pcmu_p)
48{
49	pcmu_rem_upstream_kstat(pcmu_p);
50}
51
52void
53pcmu_create_name_kstat(char *name, pcmu_ksinfo_t *pp, pcmu_kev_mask_t *ev)
54{
55	int	i;
56
57	for (i = 0; i < NUM_OF_PICS; i++) {
58		pp->pic_name_ksp[i] = pcmu_create_picN_kstat(name,
59			i, pp->pic_shift[i], pp->pic_no_evs, ev);
60
61		if (pp->pic_name_ksp[i] == NULL) {
62			cmn_err(CE_WARN, "pci: unable to create name kstat");
63		}
64	}
65}
66
67void
68pcmu_delete_name_kstat(pcmu_ksinfo_t *pp)
69{
70	int	i;
71
72	if (pp == NULL) {
73		return;
74	}
75	for (i = 0; i < NUM_OF_PICS; i++) {
76		if (pp->pic_name_ksp[i] != NULL)
77			kstat_delete(pp->pic_name_ksp[i]);
78	}
79}
80
81/*
82 * Create the picN kstat. Returns a pointer to the
83 * kstat which the driver must store to allow it
84 * to be deleted when necessary.
85 */
86static kstat_t *
87pcmu_create_picN_kstat(char *mod_name, int pic, int pic_shift,
88	int num_ev, pcmu_kev_mask_t *ev_array)
89{
90	struct kstat_named *pic_named_data;
91	int	inst = 0;
92	int	event;
93	char	pic_name[30];
94	kstat_t	*picN_ksp = NULL;
95
96	(void) sprintf(pic_name, "pic%d", pic);
97	if ((picN_ksp = kstat_create(mod_name, inst, pic_name,
98		"bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
99		cmn_err(CE_WARN, "%s %s : kstat create failed",
100			mod_name, pic_name);
101
102		/*
103		 * It is up to the calling function to delete any kstats
104		 * that may have been created already. We just
105		 * return NULL to indicate an error has occured.
106		 */
107		return (NULL);
108	}
109
110	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
111
112	/*
113	 * Write event names and their associated pcr masks. The
114	 * last entry in the array (clear_pic) is added seperately
115	 * below as the pic value must be inverted.
116	 */
117	for (event = 0; event < num_ev - 1; event++) {
118		pic_named_data[event].value.ui64 =
119		    (ev_array[event].pcr_mask << pic_shift);
120
121		kstat_named_init(&pic_named_data[event],
122		    ev_array[event].event_name, KSTAT_DATA_UINT64);
123	}
124
125	/*
126	 * add the clear_pic entry.
127	 */
128	pic_named_data[event].value.ui64 =
129	    (uint64_t)~(ev_array[event].pcr_mask << pic_shift);
130
131	kstat_named_init(&pic_named_data[event],
132	    ev_array[event].event_name, KSTAT_DATA_UINT64);
133
134	kstat_install(picN_ksp);
135	return (picN_ksp);
136}
137
138/*
139 * Create the "counters" kstat.
140 */
141kstat_t *pcmu_create_cntr_kstat(pcmu_t *pcmu_p, char *name,
142	int num_pics, int (*update)(kstat_t *, int),
143	void *cntr_addr_p)
144{
145	struct kstat_named *counters_named_data;
146	struct kstat	*counters_ksp;
147	dev_info_t	*dip = pcmu_p->pcmu_dip;
148	char		*drv_name = (char *)ddi_driver_name(dip);
149	int		drv_instance = ddi_get_instance(dip);
150	char		pic_str[10];
151	int		i;
152
153	/*
154	 * Size of kstat is num_pics + 1 as it
155	 * also contains the %pcr
156	 */
157	if ((counters_ksp = kstat_create(name, drv_instance,
158	    "counters", "bus", KSTAT_TYPE_NAMED, num_pics + 1,
159	    KSTAT_FLAG_WRITABLE)) == NULL) {
160		cmn_err(CE_WARN, "%s%d counters kstat_create failed",
161			drv_name, drv_instance);
162		return (NULL);
163	}
164
165	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
166
167	/* initialize the named kstats */
168	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
169
170	for (i = 0; i < num_pics; i++) {
171		(void) sprintf(pic_str, "pic%d", i);
172		kstat_named_init(&counters_named_data[i+1],
173		    pic_str, KSTAT_DATA_UINT64);
174	}
175
176	/*
177	 * Store the register offset's in the kstat's
178	 * private field so that they are available
179	 * to the update function.
180	 */
181	counters_ksp->ks_private = (void *)cntr_addr_p;
182	counters_ksp->ks_update = update;
183	kstat_install(counters_ksp);
184	return (counters_ksp);
185}
186
187/*
188 * kstat update function. Handles reads/writes
189 * from/to kstat.
190 */
191int
192pcmu_cntr_kstat_update(kstat_t *ksp, int rw)
193{
194	struct kstat_named	*data_p;
195	pcmu_cntr_addr_t	*cntr_addr_p = ksp->ks_private;
196	uint64_t	pic;
197
198	data_p = (struct kstat_named *)ksp->ks_data;
199	if (rw == KSTAT_WRITE) {
200		*cntr_addr_p->pcr_addr = data_p[0].value.ui64;
201		return (0);
202	} else {
203		pic = *cntr_addr_p->pic_addr;
204		data_p[0].value.ui64 = *cntr_addr_p->pcr_addr;
205
206		/* pic0 : lo 32 bits */
207		data_p[1].value.ui64 = (pic <<32) >> 32;
208		/* pic1 : hi 32 bits */
209		data_p[2].value.ui64 = pic >> 32;
210	}
211	return (0);
212}
213
214/*
215 * kstat update function using physical addresses.
216 */
217int
218pcmu_cntr_kstat_pa_update(kstat_t *ksp, int rw)
219{
220	struct kstat_named	*data_p;
221	pcmu_cntr_pa_t *cntr_pa_p = (pcmu_cntr_pa_t *)ksp->ks_private;
222	uint64_t	pic;
223
224	data_p = (struct kstat_named *)ksp->ks_data;
225
226	if (rw == KSTAT_WRITE) {
227		stdphysio(cntr_pa_p->pcr_pa, data_p[0].value.ui64);
228		return (0);
229	} else {
230		pic = lddphysio(cntr_pa_p->pic_pa);
231		data_p[0].value.ui64 = lddphysio(cntr_pa_p->pcr_pa);
232
233		/* pic0 : lo 32 bits */
234		data_p[1].value.ui64 = (pic << 32) >> 32;
235		/* pic1 : hi 32 bits */
236		data_p[2].value.ui64 = pic >> 32;
237	}
238	return (0);
239}
240
241
242/*
243 * Matched with pcmu_add_upstream_kstat()
244 */
245void
246pcmu_rem_upstream_kstat(pcmu_t *pcmu_p)
247{
248	if (pcmu_p->pcmu_uksp != NULL)
249		kstat_delete(pcmu_p->pcmu_uksp);
250	pcmu_p->pcmu_uksp = NULL;
251}
252