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/param.h>
30#include <sys/types.h>
31#include <sys/errno.h>
32#include <sys/kmem.h>
33#include <sys/sunddi.h>
34#include <sys/disp.h>
35#include <fpc.h>
36#include <fpc-impl.h>
37
38#define	BOUNDS_CHECK_FAILS(arg, max)	((arg < 0) && (arg >= max))
39
40static int this_node = 0;
41node_data_t node_data[NUM_LEAVES];
42
43int fpc_debug = 0;
44
45static int counters_per_type[MAX_REG_TYPES] = {
46	NUM_JBC_COUNTERS,
47	NUM_IMU_COUNTERS,
48	NUM_MMU_COUNTERS,
49	NUM_TLU_COUNTERS,
50	NUM_LPU_COUNTERS
51};
52
53static int first_reg_of_type[MAX_REG_TYPES];
54
55static uint64_t event_field_mask[NUM_TOTAL_COUNTERS] = {
56	JBC_PIC0_EVT_MASK,		/* JBC counter 0 */
57	JBC_PIC1_EVT_MASK,		/* JBC counter 1 */
58	IMU_PIC0_EVT_MASK,		/* IMU counter 0 */
59	IMU_PIC1_EVT_MASK,		/* IMU counter 1 */
60	MMU_PIC0_EVT_MASK,		/* MMU counter 0 */
61	MMU_PIC1_EVT_MASK,		/* MMU counter 1 */
62	TLU_PIC0_EVT_MASK,		/* TLU counter 0 */
63	TLU_PIC1_EVT_MASK,		/* TLU counter 1 */
64	TLU_PIC2_EVT_MASK,		/* TLU counter 2 */
65	LPU_PIC0_EVT_MASK,		/* LPU counter 1 */
66	LPU_PIC1_EVT_MASK		/* LPU counter 2 */
67};
68
69/* Offsets of the fields shown in event_field_masks. */
70static int event_field_offset[NUM_TOTAL_COUNTERS] = {
71	PIC0_EVT_SEL_SHIFT,		/* JBC counter 0 */
72	PIC1_EVT_SEL_SHIFT,		/* JBC counter 1 */
73	PIC0_EVT_SEL_SHIFT,		/* IMU counter 0 */
74	PIC1_EVT_SEL_SHIFT,		/* IMU counter 1 */
75	PIC0_EVT_SEL_SHIFT,		/* MMU counter 0 */
76	PIC1_EVT_SEL_SHIFT,		/* MMU counter 1 */
77	PIC0_EVT_SEL_SHIFT,		/* TLU counter 0 */
78	PIC1_EVT_SEL_SHIFT,		/* TLU counter 1 */
79	PIC2_EVT_SEL_SHIFT,		/* TLU counter 2 */
80	PIC0_EVT_SEL_SHIFT,		/* LPU counter 1 */
81	PIC2_EVT_SEL_SHIFT		/* LPU counter 2 */
82};
83
84/* For determining platform suitability at _init time. */
85int
86fpc_init_platform_check()
87{
88	return (fpc_platform_check());
89}
90
91/*ARGSUSED*/
92void
93fpc_common_node_setup(dev_info_t *dip, int *index_p)
94{
95	char pathname[MAXPATHLEN];
96
97	(void) ddi_pathname(dip, pathname);
98	node_data[this_node].name =
99	    kmem_zalloc(strlen(pathname)+1, KM_SLEEP);
100	(void) strcpy(node_data[this_node].name, pathname);
101	mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL);
102	*index_p = this_node++;
103}
104
105int
106fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail)
107{
108	int i;
109	dev_info_t *dip;
110
111	*avail = 0;
112
113	for (i = 1; i < MAX_REG_TYPES; i++) {
114		first_reg_of_type[i] =
115		    first_reg_of_type[i-1] + counters_per_type[i-1];
116	}
117
118	/*
119	 * Look thru first level of device tree only.
120	 * Assume there can be no more than NUM_LEAVES nodes in the system.
121	 */
122	dip = ddi_root_node();
123	for (dip = ddi_get_child(dip);
124	    ((dip != NULL) && (this_node < NUM_LEAVES));
125	    dip = ddi_get_next_sibling(dip)) {
126		if (fpc_platform_node_init(dip, avail) != SUCCESS)
127			return (DDI_FAILURE);
128	}
129
130	return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE);
131}
132
133int
134fpc_perfcnt_module_fini(dev_info_t *dip)
135{
136	int i;
137
138	for (i = 0; i < NUM_LEAVES; i++) {
139		fpc_platform_node_fini(node_data[i].plat_data_p);
140		if (node_data[i].name != NULL) {
141			kmem_free(node_data[i].name,
142			    strlen(node_data[i].name) + 1);
143			mutex_destroy(&node_data[i].mutex);
144		}
145	}
146
147	fpc_platform_module_fini(dip);
148	return (DDI_SUCCESS);
149}
150
151char
152*fpc_get_dev_name_by_number(int index)
153{
154	return (node_data[index].name);
155}
156
157void *
158fpc_get_platform_data_by_number(int index)
159{
160	return (node_data[index].plat_data_p);
161}
162
163
164int
165fpc_set_platform_data_by_number(int index, void *data_p)
166{
167	node_data[index].plat_data_p = data_p;
168	return (SUCCESS);
169}
170
171
172static int
173fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp)
174{
175	*mutex_pp = &node_data[index].mutex;
176	return (SUCCESS);
177}
178
179
180static int
181fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter)
182{
183	FPC_DBG1(
184	    "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n",
185	    regtype, counter, counters_per_type[regtype]);
186	if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype]))
187		return (-1);
188	FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter);
189	return (first_reg_of_type[regtype] + counter);
190}
191
192
193/*
194 * Program a performance counter.
195 *
196 * reggroup is which type of counter.
197 * counter is the counter number.
198 * event is the event to program for that counter.
199 */
200int
201fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup,
202    uint64_t new_events)
203{
204	int counter_index;
205	fire_perfreg_handle_t firehdl;
206	kmutex_t *mutex_p;
207	uint64_t old_events;
208	int rval = SUCCESS;
209	uint64_t zero = 0ull;
210	int num_counters, counter;
211
212	FPC_DBG1("fpc_perfcnt_program enter:\n");
213	FPC_DBG1("  devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n",
214	    devnum, reggroup, new_events);
215
216	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
217	    (fire_perfreg_handle_t)-1)
218		return (EIO);
219
220	num_counters = counters_per_type[reggroup];
221
222	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) {
223		(void) fpc_free_counter_handle(firehdl);
224		return (EIO);
225	}
226
227	mutex_enter(mutex_p);
228
229	if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) !=
230	    SUCCESS) {
231		FPC_DBG1("Read of old event data failed, group:%d\n", reggroup);
232		goto done_pgm;
233	}
234
235	for (counter = 0; counter < num_counters; counter++) {
236
237		counter_index = fpc_get_counter_reg_index(reggroup, counter);
238
239		if ((old_events & event_field_mask[counter_index]) ==
240		    (new_events & event_field_mask[counter_index]))
241			continue;
242
243		FPC_DBG1("Zeroing counter %d\n", counter_index);
244		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
245		    &zero, IS_WRITE)) != SUCCESS)
246			goto done_pgm;
247	}
248
249	if (old_events != new_events) {
250		if ((rval =
251		    fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) !=
252		    SUCCESS) {
253			FPC_DBG1("Write of new event data failed, group:%d\n",
254			    reggroup);
255			goto done_pgm;
256		}
257	}
258done_pgm:
259	mutex_exit(mutex_p);
260	(void) fpc_free_counter_handle(firehdl);
261	return (rval);
262}
263
264
265/*
266 * Read a performance counter.
267 *
268 * reggroup is which type of counter.
269 * event_p returns the event programmed for that counter.
270 * values returns the counter values.
271 */
272int
273fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup,
274    uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS])
275{
276	fire_perfreg_handle_t firehdl;
277	int counter_index;
278	kmutex_t *mutex_p;
279	int rval;
280	int num_counters, counter;
281
282	FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum);
283	num_counters = counters_per_type[reggroup];
284
285	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
286	    (fire_perfreg_handle_t)-1)
287		return (EIO);
288
289	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS)
290		return (EIO);
291
292	mutex_enter(mutex_p);
293
294	if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) !=
295	    SUCCESS)
296		goto done_read;
297
298	for (counter = 0; counter < num_counters; counter++) {
299		counter_index = fpc_get_counter_reg_index(reggroup, counter);
300
301		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
302		    &values[counter], IS_READ)) != SUCCESS)
303			goto done_read;
304
305		FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%"
306		    PRIx64 "\n", reggroup, counter, rval, values[counter]);
307	}
308
309done_read:
310	mutex_exit(mutex_p);
311	(void) fpc_free_counter_handle(firehdl);
312	return (rval);
313}
314