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