xref: /illumos-gate/usr/src/uts/sun4u/pcbe/us234_pcbe.c (revision 66e150d7d3c0cb2de3c45c74612784ffd3e73de6)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * UltraSPARC Performance Counter Backend
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/cpuvar.h>
33 #include <sys/systm.h>
34 #include <sys/cmn_err.h>
35 #include <sys/spitregs.h>
36 #include <sys/cheetahregs.h>
37 #include <sys/cpc_impl.h>
38 #include <sys/cpc_pcbe.h>
39 #include <sys/modctl.h>
40 #include <sys/machsystm.h>
41 #include <sys/sdt.h>
42 
43 static int us_pcbe_init(void);
44 static uint_t us_pcbe_ncounters(void);
45 static const char *us_pcbe_impl_name(void);
46 static const char *us_pcbe_cpuref(void);
47 static char *us_pcbe_list_events(uint_t picnum);
48 static char *us_pcbe_list_attrs(void);
49 static uint64_t us_pcbe_event_coverage(char *event);
50 static uint64_t us_pcbe_overflow_bitmap(void);
51 static int us_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
52     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
53     void *token);
54 static void us_pcbe_program(void *token);
55 static void us_pcbe_allstop(void);
56 static void us_pcbe_sample(void *token);
57 static void us_pcbe_free(void *config);
58 
59 extern void ultra_setpcr(uint64_t);
60 extern uint64_t ultra_getpcr(void);
61 extern void ultra_setpic(uint64_t);
62 extern uint64_t ultra_getpic(void);
63 extern uint64_t ultra_gettick(void);
64 
65 pcbe_ops_t us_pcbe_ops = {
66 	PCBE_VER_1,
67 	CPC_CAP_OVERFLOW_INTERRUPT,
68 	us_pcbe_ncounters,
69 	us_pcbe_impl_name,
70 	us_pcbe_cpuref,
71 	us_pcbe_list_events,
72 	us_pcbe_list_attrs,
73 	us_pcbe_event_coverage,
74 	us_pcbe_overflow_bitmap,
75 	us_pcbe_configure,
76 	us_pcbe_program,
77 	us_pcbe_allstop,
78 	us_pcbe_sample,
79 	us_pcbe_free
80 };
81 
82 typedef struct _us_pcbe_config {
83 	uint8_t		us_picno;	/* 0 for pic0 or 1 for pic1 */
84 	uint32_t	us_bits;	/* %pcr event code unshifted */
85 	uint32_t	us_flags;	/* user/system/priv */
86 	uint32_t	us_pic;		/* unshifted raw %pic value */
87 } us_pcbe_config_t;
88 
89 struct nametable {
90 	const uint8_t	bits;
91 	const char	*name;
92 };
93 
94 #define	PIC0_MASK (((uint64_t)1 << 32) - 1)
95 
96 #define	ULTRA_PCR_SYS		(UINT64_C(1) << CPC_ULTRA_PCR_SYS)
97 #define	ULTRA_PCR_PRIVPIC	(UINT64_C(1) << CPC_ULTRA_PCR_PRIVPIC)
98 
99 #define	CPC_ULTRA_PCR_USR		2
100 #define	CPC_ULTRA_PCR_SYS		1
101 #define	CPC_ULTRA_PCR_PRIVPIC		0
102 
103 #define	CPC_ULTRA_PCR_PIC0_SHIFT	4
104 #define	CPC_ULTRA2_PCR_PIC_MASK		UINT64_C(0xf)
105 #define	CPC_ULTRA3_PCR_PIC_MASK		UINT64_C(0x3f)
106 #define	CPC_ULTRA_PCR_PIC1_SHIFT	11
107 
108 #define	NT_END 0xFF
109 
110 static const uint64_t   allstopped = ULTRA_PCR_PRIVPIC;
111 
112 #define	USall_EVENTS_0						\
113 	{0x0,	"Cycle_cnt"},					\
114 	{0x1,	"Instr_cnt"},					\
115 	{0x2,	"Dispatch0_IC_miss"},				\
116 	{0x8,	"IC_ref"},					\
117 	{0x9,	"DC_rd"},					\
118 	{0xa,	"DC_wr"},					\
119 	{0xc,	"EC_ref"},					\
120 	{0xe,	"EC_snoop_inv"}
121 
122 static const struct nametable US12_names0[] = {
123 	USall_EVENTS_0,
124 	{0x3,	"Dispatch0_storeBuf"},
125 	{0xb,	"Load_use"},
126 	{0xd,	"EC_write_hit_RDO"},
127 	{0xf,	"EC_rd_hit"},
128 	{NT_END, ""}
129 };
130 
131 #define	US3all_EVENTS_0						\
132 	{0x3,	"Dispatch0_br_target"},				\
133 	{0x4,	"Dispatch0_2nd_br"},				\
134 	{0x5,	"Rstall_storeQ"},				\
135 	{0x6,	"Rstall_IU_use"},				\
136 	{0xd,	"EC_write_hit_RTO"},				\
137 	{0xf,	"EC_rd_miss"},					\
138 	{0x10,	"PC_port0_rd"},					\
139 	{0x11,	"SI_snoop"},					\
140 	{0x12,	"SI_ciq_flow"},					\
141 	{0x13,	"SI_owned"},					\
142 	{0x14,	"SW_count_0"},					\
143 	{0x15,	"IU_Stat_Br_miss_taken"},			\
144 	{0x16,	"IU_Stat_Br_count_taken"},			\
145 	{0x17,	"Dispatch_rs_mispred"},				\
146 	{0x18,	"FA_pipe_completion"}
147 
148 #define	US3_MC_EVENTS_0						\
149 	{0x20,	"MC_reads_0"},					\
150 	{0x21,	"MC_reads_1"},					\
151 	{0x22,	"MC_reads_2"},					\
152 	{0x23,	"MC_reads_3"},					\
153 	{0x24,	"MC_stalls_0"},					\
154 	{0x25,	"MC_stalls_2"}
155 
156 #define	US3_I_MC_EVENTS_0					\
157 	{0x20,	"MC_read_dispatched"},				\
158 	{0x21,	"MC_write_dispatched"},				\
159 	{0x22,	"MC_read_returned_to_JBU"},			\
160 	{0x23,	"MC_msl_busy_stall"},				\
161 	{0x24,	"MC_mdb_overflow_stall"},			\
162 	{0x25,	"MC_miu_spec_request"}
163 
164 #define	USall_EVENTS_1						\
165 	{0x0,	"Cycle_cnt"},					\
166 	{0x1,	"Instr_cnt"},					\
167 	{0x2,	"Dispatch0_mispred"},				\
168 	{0xd,	"EC_wb"},					\
169 	{0xe,	"EC_snoop_cb"}
170 
171 static const struct nametable US3_names0[] = {
172 	USall_EVENTS_0,
173 	US3all_EVENTS_0,
174 	US3_MC_EVENTS_0,
175 	{NT_END, ""}
176 };
177 
178 static const struct nametable US3_PLUS_names0[] = {
179 	USall_EVENTS_0,
180 	US3all_EVENTS_0,
181 	US3_MC_EVENTS_0,
182 	{0x19,	"EC_wb_remote"},
183 	{0x1a,	"EC_miss_local"},
184 	{0x1b,	"EC_miss_mtag_remote"},
185 	{NT_END, ""}
186 };
187 
188 static const struct nametable US3_I_names0[] = {
189 	USall_EVENTS_0,
190 	US3all_EVENTS_0,
191 	US3_I_MC_EVENTS_0,
192 	{NT_END, ""}
193 };
194 
195 static const struct nametable US4_PLUS_names0[] = {
196 	{0x0,   "Cycle_cnt"},
197 	{0x1,   "Instr_cnt"},
198 	{0x2,   "Dispatch0_IC_miss"},
199 	{0x3,   "IU_stat_jmp_correct_pred"},
200 	{0x4,   "Dispatch0_2nd_br"},
201 	{0x5,   "Rstall_storeQ"},
202 	{0x6,   "Rstall_IU_use"},
203 	{0x7,   "IU_stat_ret_correct_pred"},
204 	{0x8,   "IC_ref"},
205 	{0x9,   "DC_rd"},
206 	{0xa,   "Rstall_FP_use"},
207 	{0xb,   "SW_pf_instr"},
208 	{0xc,   "L2_ref"},
209 	{0xd,   "L2_write_hit_RTO"},
210 	{0xe,   "L2_snoop_inv_sh"},
211 	{0xf,   "L2_rd_miss"},
212 	{0x10,  "PC_rd"},
213 	{0x11,  "SI_snoop_sh"},
214 	{0x12,  "SI_ciq_flow_sh"},
215 	{0x13,  "Re_DC_miss"},
216 	{0x14,  "SW_count_NOP"},
217 	{0x15,  "IU_stat_br_miss_taken"},
218 	{0x16,  "IU_stat_br_count_untaken"},
219 	{0x17,  "HW_pf_exec"},
220 	{0x18,  "FA_pipe_completion"},
221 	{0x19,  "SSM_L3_wb_remote"},
222 	{0x1a,  "SSM_L3_miss_local"},
223 	{0x1b,  "SSM_L3_miss_mtag_remote"},
224 	{0x1c,  "SW_pf_str_trapped"},
225 	{0x1d,  "SW_pf_PC_installed"},
226 	{0x1e,  "IPB_to_IC_fill"},
227 	{0x1f,  "L2_write_miss"},
228 	{0x20,  "MC_reads_0_sh"},
229 	{0x21,  "MC_reads_1_sh"},
230 	{0x22,  "MC_reads_2_sh"},
231 	{0x23,  "MC_reads_3_sh"},
232 	{0x24,  "MC_stalls_0_sh"},
233 	{0x25,  "MC_stalls_2_sh"},
234 	{0x26,  "L2_hit_other_half"},
235 	{0x28,  "L3_rd_miss"},
236 	{0x29,  "Re_L2_miss"},
237 	{0x2a,  "IC_miss_cancelled"},
238 	{0x2b,  "DC_wr_miss"},
239 	{0x2c,  "L3_hit_I_state_sh"},
240 	{0x2d,  "SI_RTS_src_data"},
241 	{0x2e,  "L2_IC_miss"},
242 	{0x2f,  "SSM_new_transaction_sh"},
243 	{0x30,  "L2_SW_pf_miss"},
244 	{0x31,  "L2_wb"},
245 	{0x32,  "L2_wb_sh"},
246 	{0x33,  "L2_snoop_cb_sh"},
247 	{NT_END, ""}
248 };
249 
250 
251 #define	US3all_EVENTS_1				\
252 	{0x3,	"IC_miss_cancelled"},		\
253 	{0x5,	"Re_FPU_bypass"},		\
254 	{0x6,	"Re_DC_miss"},			\
255 	{0x7,	"Re_EC_miss"},			\
256 	{0x8,	"IC_miss"},			\
257 	{0x9,	"DC_rd_miss"},			\
258 	{0xa,	"DC_wr_miss"},			\
259 	{0xb,	"Rstall_FP_use"},		\
260 	{0xc,	"EC_misses"},			\
261 	{0xf,	"EC_ic_miss"},			\
262 	{0x10,	"Re_PC_miss"},			\
263 	{0x11,	"ITLB_miss"},			\
264 	{0x12,	"DTLB_miss"},			\
265 	{0x13,	"WC_miss"},			\
266 	{0x14,	"WC_snoop_cb"},			\
267 	{0x15,	"WC_scrubbed"},			\
268 	{0x16,	"WC_wb_wo_read"},		\
269 	{0x18,	"PC_soft_hit"},			\
270 	{0x19,	"PC_snoop_inv"},		\
271 	{0x1a,	"PC_hard_hit"},			\
272 	{0x1b,	"PC_port1_rd"},			\
273 	{0x1c,	"SW_count_1"},			\
274 	{0x1d,	"IU_Stat_Br_miss_untaken"},	\
275 	{0x1e,	"IU_Stat_Br_count_untaken"},	\
276 	{0x1f,	"PC_MS_misses"},		\
277 	{0x26,	"Re_RAW_miss"},			\
278 	{0x27,	"FM_pipe_completion"}
279 
280 #define	US3_MC_EVENTS_1				\
281 	{0x20,	"MC_writes_0"},			\
282 	{0x21,	"MC_writes_1"},			\
283 	{0x22,	"MC_writes_2"},			\
284 	{0x23,	"MC_writes_3"},			\
285 	{0x24,	"MC_stalls_1"},			\
286 	{0x25,	"MC_stalls_3"}
287 
288 #define	US3_I_MC_EVENTS_1			\
289 	{0x20,	"MC_open_bank_cmds"},		\
290 	{0x21,	"MC_reads"},			\
291 	{0x22,	"MC_writes"},			\
292 	{0x23,	"MC_page_close_stall"}
293 
294 static const struct nametable US3_names1[] = {
295 	USall_EVENTS_1,
296 	US3all_EVENTS_1,
297 	US3_MC_EVENTS_1,
298 	{0x4,	"Re_endian_miss"},
299 	{NT_END, ""}
300 };
301 
302 static const struct nametable US3_PLUS_names1[] = {
303 	USall_EVENTS_1,
304 	US3all_EVENTS_1,
305 	US3_MC_EVENTS_1,
306 	{0x4,	"Re_DC_missovhd"},
307 	{0x28,	"EC_miss_mtag_remote"},
308 	{0x29,	"EC_miss_remote"},
309 	{NT_END, ""}
310 };
311 
312 static const struct nametable US3_I_names1[] = {
313 	USall_EVENTS_1,
314 	US3all_EVENTS_1,
315 	US3_I_MC_EVENTS_1,
316 	{0x4,	"Re_DC_missovhd"},
317 	{NT_END, ""}
318 };
319 
320 static const struct nametable US4_PLUS_names1[] = {
321 	{0x0,   "Cycle_cnt"},
322 	{0x1,   "Instr_cnt"},
323 	{0x2,   "Dispatch0_other"},
324 	{0x3,   "DC_wr"},
325 	{0x4,   "Re_DC_missovhd"},
326 	{0x5,   "Re_FPU_bypass"},
327 	{0x6,   "L3_write_hit_RTO"},
328 	{0x7,   "L2L3_snoop_inv_sh"},
329 	{0x8,   "IC_L2_req"},
330 	{0x9,   "DC_rd_miss"},
331 	{0xa,   "L2_hit_I_state_sh"},
332 	{0xb,   "L3_write_miss_RTO"},
333 	{0xc,   "L2_miss"},
334 	{0xd,   "SI_owned_sh"},
335 	{0xe,   "SI_RTO_src_data"},
336 	{0xf,   "SW_pf_duplicate"},
337 	{0x10,  "IU_stat_jmp_mispred"},
338 	{0x11,  "ITLB_miss"},
339 	{0x12,  "DTLB_miss"},
340 	{0x13,  "WC_miss"},
341 	{0x14,  "IC_fill"},
342 	{0x15,  "IU_stat_ret_mispred"},
343 	{0x16,  "Re_L3_miss"},
344 	{0x17,  "Re_PFQ_full"},
345 	{0x18,  "PC_soft_hit"},
346 	{0x19,  "PC_inv"},
347 	{0x1a,  "PC_hard_hit"},
348 	{0x1b,  "IC_pf"},
349 	{0x1c,  "SW_count_NOP"},
350 	{0x1d,  "IU_stat_br_miss_untaken"},
351 	{0x1e,  "IU_stat_br_count_taken"},
352 	{0x1f,  "PC_miss"},
353 	{0x20,  "MC_writes_0_sh"},
354 	{0x21,  "MC_writes_1_sh"},
355 	{0x22,  "MC_writes_2_sh"},
356 	{0x23,  "MC_writes_3_sh"},
357 	{0x24,  "MC_stalls_1_sh"},
358 	{0x25,  "MC_stalls_3_sh"},
359 	{0x26,  "Re_RAW_miss"},
360 	{0x27,  "FM_pipe_completion"},
361 	{0x28,  "SSM_L3_miss_mtag_remote"},
362 	{0x29,  "SSM_L3_miss_remote"},
363 	{0x2a,  "SW_pf_exec"},
364 	{0x2b,  "SW_pf_str_exec"},
365 	{0x2c,  "SW_pf_dropped"},
366 	{0x2d,  "SW_pf_L2_installed"},
367 	{0x2f,  "L2_HW_pf_miss"},
368 	{0x31,  "L3_miss"},
369 	{0x32,  "L3_IC_miss"},
370 	{0x33,  "L3_SW_pf_miss"},
371 	{0x34,  "L3_hit_other_half"},
372 	{0x35,  "L3_wb"},
373 	{0x36,  "L3_wb_sh"},
374 	{0x37,  "L2L3_snoop_cb_sh"},
375 	{NT_END, ""}
376 };
377 
378 static const struct nametable US12_names1[] = {
379 	USall_EVENTS_1,
380 	{0x3,	"Dispatch0_FP_use"},
381 	{0x8,	"IC_hit"},
382 	{0x9,	"DC_rd_hit"},
383 	{0xa,	"DC_wr_hit"},
384 	{0xb,	"Load_use_RAW"},
385 	{0xc,	"EC_hit"},
386 	{0xf,	"EC_ic_hit"},
387 	{NT_END, ""}
388 };
389 
390 static const struct nametable *US12_names[2] = {
391 	US12_names0,
392 	US12_names1
393 };
394 
395 static const struct nametable *US3_names[2] = {
396 	US3_names0,
397 	US3_names1
398 };
399 
400 static const struct nametable *US3_PLUS_names[2] = {
401 	US3_PLUS_names0,
402 	US3_PLUS_names1
403 };
404 
405 static const struct nametable *US4_PLUS_names[2] = {
406 	US4_PLUS_names0,
407 	US4_PLUS_names1
408 };
409 
410 static const struct nametable *US3_I_names[2] = {
411 	US3_I_names0,
412 	US3_I_names1
413 };
414 
415 static const struct nametable **events;
416 static const char *us_impl_name;
417 static const char *us_cpuref;
418 static char *pic_events[2];
419 static uint16_t pcr_pic_mask;
420 
421 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
422 			"http://www.sun.com/processors/manuals"
423 
424 static const char *us_2_ref = "See the \"UltraSPARC I/II User\'s Manual\" "
425 			"(Part No. 802-7220-02) "
426 			"for descriptions of these events." CPU_REF_URL;
427 
428 static const char *us_3cu_ref = "See the \"UltraSPARC III Cu User's Manual\" "
429 			"for descriptions of these events." CPU_REF_URL;
430 
431 static const char *us4_plus_ref = "See the \"UltraSPARC IV+ User's Manual\" "
432 			"for descriptions of these events." CPU_REF_URL;
433 
434 static const char *us_3i_ref = "See the \"UltraSPARC IIIi User's Manual\"  "
435 			"for descriptions of these events." CPU_REF_URL;
436 
437 static int
438 us_pcbe_init(void)
439 {
440 	const struct nametable	*n;
441 	int			i;
442 	size_t			size;
443 
444 	/*
445 	 * Discover type of CPU
446 	 *
447 	 * Point nametable to that CPU's table
448 	 */
449 	switch (ULTRA_VER_IMPL(ultra_getver())) {
450 	case SPITFIRE_IMPL:
451 	case BLACKBIRD_IMPL:
452 	case SABRE_IMPL:
453 	case HUMMBRD_IMPL:
454 		events = US12_names;
455 		us_impl_name = "UltraSPARC I&II";
456 		us_cpuref = us_2_ref;
457 		pcr_pic_mask = CPC_ULTRA2_PCR_PIC_MASK;
458 		us_pcbe_ops.pcbe_caps &= ~CPC_CAP_OVERFLOW_INTERRUPT;
459 		break;
460 	case CHEETAH_IMPL:
461 		events = US3_names;
462 		us_impl_name = "UltraSPARC III";
463 		us_cpuref = us_3cu_ref;
464 		pcr_pic_mask = CPC_ULTRA3_PCR_PIC_MASK;
465 		break;
466 	case CHEETAH_PLUS_IMPL:
467 	case JAGUAR_IMPL:
468 		events = US3_PLUS_names;
469 		us_impl_name = "UltraSPARC III+ & IV";
470 		us_cpuref = us_3cu_ref;
471 		pcr_pic_mask = CPC_ULTRA3_PCR_PIC_MASK;
472 		break;
473 	case PANTHER_IMPL:
474 		events = US4_PLUS_names;
475 		us_impl_name = "UltraSPARC IV+";
476 		us_cpuref = us4_plus_ref;
477 		pcr_pic_mask = CPC_ULTRA3_PCR_PIC_MASK;
478 		break;
479 	case JALAPENO_IMPL:
480 	case SERRANO_IMPL:
481 		events = US3_I_names;
482 		us_impl_name = "UltraSPARC IIIi & IIIi+";
483 		us_cpuref = us_3i_ref;
484 		pcr_pic_mask = CPC_ULTRA3_PCR_PIC_MASK;
485 		break;
486 	default:
487 		return (-1);
488 	}
489 
490 	/*
491 	 * Initialize the list of events for each PIC.
492 	 * Do two passes: one to compute the size necessary and another
493 	 * to copy the strings. Need room for event, comma, and NULL terminator.
494 	 */
495 	for (i = 0; i < 2; i++) {
496 		size = 0;
497 		for (n = events[i]; n->bits != NT_END; n++)
498 			size += strlen(n->name) + 1;
499 		pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
500 		*pic_events[i] = '\0';
501 		for (n = events[i]; n->bits != NT_END; n++) {
502 			(void) strcat(pic_events[i], n->name);
503 			(void) strcat(pic_events[i], ",");
504 		}
505 		/*
506 		 * Remove trailing comma.
507 		 */
508 		pic_events[i][size - 1] = '\0';
509 	}
510 
511 	return (0);
512 }
513 
514 static uint_t
515 us_pcbe_ncounters(void)
516 {
517 	return (2);
518 }
519 
520 static const char *
521 us_pcbe_impl_name(void)
522 {
523 	return (us_impl_name);
524 }
525 
526 static const char *
527 us_pcbe_cpuref(void)
528 {
529 	return (us_cpuref);
530 }
531 
532 static char *
533 us_pcbe_list_events(uint_t picnum)
534 {
535 	ASSERT(picnum >= 0 && picnum < cpc_ncounters);
536 
537 	return (pic_events[picnum]);
538 }
539 
540 static char *
541 us_pcbe_list_attrs(void)
542 {
543 	return ("");
544 }
545 
546 static const struct nametable *
547 find_event(int regno, char *name)
548 {
549 	const struct nametable *n;
550 
551 	n = events[regno];
552 
553 	for (; n->bits != NT_END; n++)
554 		if (strcmp(name, n->name) == 0)
555 			return (n);
556 
557 	return (NULL);
558 }
559 
560 static uint64_t
561 us_pcbe_event_coverage(char *event)
562 {
563 	uint64_t bitmap = 0;
564 
565 	if (find_event(0, event) != NULL)
566 		bitmap = 0x1;
567 	if (find_event(1, event) != NULL)
568 		bitmap |= 0x2;
569 
570 	return (bitmap);
571 }
572 
573 /*
574  * These processors cannot tell which counter overflowed. The PCBE interface
575  * requires such processors to act as if _all_ counters had overflowed.
576  */
577 static uint64_t
578 us_pcbe_overflow_bitmap(void)
579 {
580 	return (0x3);
581 }
582 
583 /*ARGSUSED*/
584 static int
585 us_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
586     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
587 {
588 	us_pcbe_config_t *conf;
589 	const struct nametable *n;
590 	us_pcbe_config_t *other_config;
591 
592 	/*
593 	 * If we've been handed an existing configuration, we need only preset
594 	 * the counter value.
595 	 */
596 	if (*data != NULL) {
597 		conf = *data;
598 		conf->us_pic = (uint32_t)preset;
599 		return (0);
600 	}
601 
602 	if (picnum < 0 || picnum > 1)
603 		return (CPC_INVALID_PICNUM);
604 
605 	if (nattrs != 0)
606 		return (CPC_INVALID_ATTRIBUTE);
607 
608 	/*
609 	 * Find other requests that will be programmed with this one, and ensure
610 	 * the flags don't conflict.
611 	 */
612 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
613 	    (other_config->us_flags != flags))
614 		return (CPC_CONFLICTING_REQS);
615 
616 	if ((n = find_event(picnum, event)) == NULL)
617 		return (CPC_INVALID_EVENT);
618 
619 	conf = kmem_alloc(sizeof (us_pcbe_config_t), KM_SLEEP);
620 
621 	conf->us_picno = picnum;
622 	conf->us_bits = (uint32_t)n->bits;
623 	conf->us_flags = flags;
624 	conf->us_pic = (uint32_t)preset;
625 
626 	*data = conf;
627 	return (0);
628 }
629 
630 static void
631 us_pcbe_program(void *token)
632 {
633 	us_pcbe_config_t	*pic0;
634 	us_pcbe_config_t	*pic1;
635 	us_pcbe_config_t	*tmp;
636 	us_pcbe_config_t	empty = { 1, 0x1c, 0, 0 }; /* SW_count_1 */
637 	uint64_t		pcr;
638 	uint64_t		curpic;
639 
640 	if ((pic0 = (us_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
641 	    NULL)
642 		panic("us_pcbe: token %p has no configs", token);
643 
644 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
645 		pic1 = &empty;
646 		empty.us_flags = pic0->us_flags;
647 	}
648 
649 	if (pic0->us_picno != 0) {
650 		/*
651 		 * pic0 is counter 1, so if we need the empty config it should
652 		 * be counter 0.
653 		 */
654 		empty.us_picno = 0;
655 		empty.us_bits = 0x14; /* SW_count_0 - won't overflow */
656 		tmp = pic0;
657 		pic0 = pic1;
658 		pic1 = tmp;
659 	}
660 
661 	if (pic0->us_picno != 0 || pic1->us_picno != 1)
662 		panic("us_pcbe: bad config on token %p\n", token);
663 
664 	/*
665 	 * UltraSPARC does not allow pic0 to be configured differently
666 	 * from pic1. If the flags on these two configurations are
667 	 * different, they are incompatible. This condition should be
668 	 * caught at configure time.
669 	 */
670 	ASSERT(pic0->us_flags == pic1->us_flags);
671 
672 	ultra_setpcr(allstopped);
673 	ultra_setpic(((uint64_t)pic1->us_pic << 32) | (uint64_t)pic0->us_pic);
674 
675 	pcr = (pic0->us_bits & pcr_pic_mask) <<
676 	    CPC_ULTRA_PCR_PIC0_SHIFT;
677 	pcr |= (pic1->us_bits & pcr_pic_mask) <<
678 	    CPC_ULTRA_PCR_PIC1_SHIFT;
679 
680 	if (pic0->us_flags & CPC_COUNT_USER)
681 		pcr |= (1ull << CPC_ULTRA_PCR_USR);
682 	if (pic0->us_flags & CPC_COUNT_SYSTEM)
683 		pcr |= (1ull << CPC_ULTRA_PCR_SYS);
684 
685 	DTRACE_PROBE1(ultra__pcr, uint64_t, pcr);
686 
687 	ultra_setpcr(pcr);
688 
689 	/*
690 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
691 	 * expect the value we wrote into the PIC, above, to be there after
692 	 * starting the counter. We must sample the counter value now and use
693 	 * that as the baseline for future samples.
694 	 */
695 	curpic = ultra_getpic();
696 	pic0->us_pic = (uint32_t)(curpic & PIC0_MASK);
697 	pic1->us_pic = (uint32_t)(curpic >> 32);
698 }
699 
700 static void
701 us_pcbe_allstop(void)
702 {
703 	ultra_setpcr(allstopped);
704 }
705 
706 
707 static void
708 us_pcbe_sample(void *token)
709 {
710 	uint64_t		curpic;
711 	int64_t			diff;
712 	uint64_t		*pic0_data;
713 	uint64_t		*pic1_data;
714 	uint64_t		*dtmp;
715 	uint64_t		tmp;
716 	us_pcbe_config_t	*pic0;
717 	us_pcbe_config_t	*pic1;
718 	us_pcbe_config_t	empty = { 1, 0, 0, 0 };
719 	us_pcbe_config_t	*ctmp;
720 
721 	curpic = ultra_getpic();
722 
723 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
724 		panic("us_pcbe: token %p has no configs", token);
725 
726 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
727 		pic1 = &empty;
728 		pic1_data = &tmp;
729 	}
730 
731 	if (pic0->us_picno != 0) {
732 		empty.us_picno = 0;
733 		ctmp = pic0;
734 		pic0 = pic1;
735 		pic1 = ctmp;
736 		dtmp = pic0_data;
737 		pic0_data = pic1_data;
738 		pic1_data = dtmp;
739 	}
740 
741 	if (pic0->us_picno != 0 || pic1->us_picno != 1)
742 		panic("us_pcbe: bad config on token %p\n", token);
743 
744 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->us_pic;
745 	if (diff < 0)
746 		diff += (1ll << 32);
747 	*pic0_data += diff;
748 
749 	diff = (curpic >> 32) - (uint64_t)pic1->us_pic;
750 	if (diff < 0)
751 		diff += (1ll << 32);
752 	*pic1_data += diff;
753 
754 	pic0->us_pic = (uint32_t)(curpic & PIC0_MASK);
755 	pic1->us_pic = (uint32_t)(curpic >> 32);
756 }
757 
758 static void
759 us_pcbe_free(void *config)
760 {
761 	kmem_free(config, sizeof (us_pcbe_config_t));
762 }
763 
764 
765 static struct modlpcbe modlpcbe = {
766 	&mod_pcbeops,
767 	"UltraSPARC Performance Counters",
768 	&us_pcbe_ops
769 };
770 
771 static struct modlinkage modl = {
772 	MODREV_1,
773 	&modlpcbe,
774 };
775 
776 int
777 _init(void)
778 {
779 	if (us_pcbe_init() != 0)
780 		return (ENOTSUP);
781 	return (mod_install(&modl));
782 }
783 
784 int
785 _fini(void)
786 {
787 	return (mod_remove(&modl));
788 }
789 
790 int
791 _info(struct modinfo *mi)
792 {
793 	return (mod_info(&modl, mi));
794 }
795