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 2007 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/file.h>
30 #include <sys/sunndi.h>
31 #include <sys/sunddi.h>
32 #include <sys/sunldi.h>
33 #include <io/px/px_regs.h>
34 #include <sys/pci_tools.h>
35 #include <fpc.h>
36 #include <fpc-impl.h>
37
38 #define CHIP_COMPATIBLE_NAME "pciex108e,80f0"
39 #define BANK_ADDR_MASK 0x7FFFFF
40
41 #define OPEN_FLAGS (FREAD | FWRITE)
42
43 #define PCIE_BANK 0
44 #define JBUS_BANK 1
45
46 typedef struct px_regs {
47 uint32_t addr_hi;
48 uint32_t addr_lo;
49 uint32_t size_hi;
50 uint32_t size_lo;
51 } px_regs_t;
52
53 /* There is one of these for every root nexus device found */
54 typedef struct fire4u_specific {
55 char *nodename;
56 uintptr_t jbus_bank_base;
57 } fire4u_specific_t;
58
59 typedef struct fire_counter_handle_impl {
60 ldi_handle_t devhandle;
61 fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
62 } fire_counter_handle_impl_t;
63
64 static uint64_t counter_select_offsets[] = {
65 JBC_PERFORMANCE_COUNTER_SELECT,
66 IMU_PERFORMANCE_COUNTER_SELECT,
67 MMU_PERFORMANCE_COUNTER_SELECT,
68 TLU_PERFORMANCE_COUNTER_SELECT,
69 LPU_LINK_PERFORMANCE_COUNTER_SELECT
70 };
71
72 /*
73 * The following event and offset arrays is organized by grouping in major
74 * order the fire_perfcnt_t register types, and in minor order the register
75 * numbers within that type.
76 */
77
78 static uint64_t counter_reg_offsets[] = {
79 JBC_PERFORMANCE_COUNTER_ZERO,
80 JBC_PERFORMANCE_COUNTER_ONE,
81 IMU_PERFORMANCE_COUNTER_ZERO,
82 IMU_PERFORMANCE_COUNTER_ONE,
83 MMU_PERFORMANCE_COUNTER_ZERO,
84 MMU_PERFORMANCE_COUNTER_ONE,
85 TLU_PERFORMANCE_COUNTER_ZERO,
86 TLU_PERFORMANCE_COUNTER_ONE,
87 TLU_PERFORMANCE_COUNTER_TWO,
88 LPU_LINK_PERFORMANCE_COUNTER1,
89 LPU_LINK_PERFORMANCE_COUNTER2
90 };
91
92 /*
93 * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
94 * write a value to that counter.
95 */
96 #define LPU_LINK_PERFCTR_WRITE_OFFSET 0x8
97
98 /*
99 * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
100 * zeros and this is the value we want. This register isn't touched by this
101 * module, and as long as it remains untouched by other modules we're OK.
102 */
103
104 static ldi_ident_t ldi_identifier;
105 static boolean_t ldi_identifier_valid = B_FALSE;
106 static cred_t *credentials = NULL;
107
108 /* Called by _init to determine if it is OK to install driver. */
109 int
fpc_platform_check()110 fpc_platform_check()
111 {
112 return (SUCCESS);
113 }
114
115 /* Called during attach to do module-wide initialization. */
116 int
fpc_platform_module_init(dev_info_t * dip)117 fpc_platform_module_init(dev_info_t *dip)
118 {
119 int status;
120
121 credentials = crget();
122 status = ldi_ident_from_dip(dip, &ldi_identifier);
123 if (status == 0)
124 ldi_identifier_valid = B_TRUE;
125 return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
126 }
127
128 int
fpc_platform_node_init(dev_info_t * dip,int * avail)129 fpc_platform_node_init(dev_info_t *dip, int *avail)
130 {
131 int index;
132 char *name;
133 int nodename_size;
134 char *nodename = NULL;
135 fire4u_specific_t *platform_specific_data = NULL;
136 char *compatible = NULL;
137 px_regs_t *regs_p = NULL;
138 int regs_length = 0;
139
140 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
141 "compatible", &compatible) != DDI_PROP_SUCCESS)
142 return (DDI_SUCCESS);
143
144 if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
145 ddi_prop_free(compatible);
146 return (DDI_SUCCESS);
147 }
148 ddi_prop_free(compatible);
149
150 fpc_common_node_setup(dip, &index);
151
152 name = fpc_get_dev_name_by_number(index);
153 nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
154 nodename = kmem_zalloc(nodename_size, KM_SLEEP);
155
156 platform_specific_data =
157 kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
158
159 (void) strcpy(nodename, name);
160 (void) strcat(nodename, ":");
161 (void) strcat(nodename, PCI_MINOR_REG);
162 platform_specific_data->nodename = nodename;
163
164 /* Get register banks. */
165 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
166 "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) {
167 goto bad_regs_p;
168 }
169
170 if ((regs_length / sizeof (px_regs_t)) < 2) {
171 goto bad_regs_length;
172 }
173
174 platform_specific_data->jbus_bank_base =
175 regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
176
177 kmem_free(regs_p, regs_length);
178
179 if (index == 0)
180 *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
181 else
182 *avail |= PCIE_B_REGS_AVAIL;
183
184 (void) fpc_set_platform_data_by_number(index, platform_specific_data);
185
186 return (DDI_SUCCESS);
187
188 bad_regs_length:
189 if (regs_p)
190 kmem_free(regs_p, regs_length);
191 bad_regs_p:
192 if (platform_specific_data)
193 kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
194 if (nodename)
195 kmem_free(nodename, nodename_size);
196
197 return (DDI_FAILURE);
198 }
199
200 void
fpc_platform_node_fini(void * arg)201 fpc_platform_node_fini(void *arg)
202 {
203 fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
204 if (plat_arg == NULL)
205 return;
206 if (plat_arg->nodename)
207 kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
208 kmem_free(plat_arg, sizeof (fire4u_specific_t));
209 }
210
211 /*ARGSUSED*/
212 void
fpc_platform_module_fini(dev_info_t * dip)213 fpc_platform_module_fini(dev_info_t *dip)
214 {
215 if (ldi_identifier_valid)
216 ldi_ident_release(ldi_identifier);
217 if (credentials)
218 crfree(credentials);
219 }
220
221 fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)222 fpc_get_perfreg_handle(int devnum)
223 {
224 int rval = EINVAL;
225
226 fire_counter_handle_impl_t *handle_impl =
227 kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
228
229 if ((handle_impl->devspec =
230 fpc_get_platform_data_by_number(devnum)) != NULL) {
231 rval = ldi_open_by_name(handle_impl->devspec->nodename,
232 OPEN_FLAGS, credentials, &handle_impl->devhandle,
233 ldi_identifier);
234 }
235
236 if (rval != SUCCESS) {
237 kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
238 return ((fire_perfreg_handle_t)-1);
239 } else {
240 return ((fire_perfreg_handle_t)handle_impl);
241 }
242 }
243
244 int
fpc_free_counter_handle(fire_perfreg_handle_t handle)245 fpc_free_counter_handle(fire_perfreg_handle_t handle)
246 {
247 fire_counter_handle_impl_t *handle_impl =
248 (fire_counter_handle_impl_t *)handle;
249 (void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials);
250 kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
251 return (SUCCESS);
252 }
253
254 int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)255 fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
256 uint64_t *reg_data, boolean_t is_write)
257 {
258 int rval;
259 int ioctl_rval;
260 pcitool_reg_t prg;
261 fire_counter_handle_impl_t *handle_impl =
262 (fire_counter_handle_impl_t *)handle;
263 int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
264
265 prg.user_version = PCITOOL_VERSION;
266
267 if (group == jbc) {
268 prg.barnum = JBUS_BANK;
269 prg.offset = counter_select_offsets[group] -
270 handle_impl->devspec->jbus_bank_base;
271 } else {
272 prg.barnum = PCIE_BANK;
273
274 /*
275 * Note that a pcie_bank_base isn't needed. Pcie register
276 * offsets are already relative to the start of their bank. No
277 * base needs to be subtracted to get the relative offset that
278 * pcitool ioctls want.
279 */
280 prg.offset = counter_select_offsets[group];
281 }
282 prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
283 prg.data = *reg_data;
284
285 /* Read original value. */
286 if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
287 FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
288 *reg_data = prg.data;
289 }
290
291 return (rval);
292 }
293
294 int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * value,boolean_t is_write)295 fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
296 int counter_index, uint64_t *value, boolean_t is_write)
297 {
298 int rval;
299 int ioctl_rval;
300 pcitool_reg_t prg;
301 fire_counter_handle_impl_t *handle_impl =
302 (fire_counter_handle_impl_t *)handle;
303 int command =
304 (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
305
306 prg.user_version = PCITOOL_VERSION;
307 /*
308 * Note that stated PCIE offsets are relative to the beginning of their
309 * register bank, while JBUS offsets are absolute.
310 */
311 if (group == jbc) {
312 prg.barnum = JBUS_BANK;
313 prg.offset = counter_reg_offsets[counter_index] -
314 handle_impl->devspec->jbus_bank_base;
315 } else {
316 prg.barnum = PCIE_BANK;
317 prg.offset = counter_reg_offsets[counter_index];
318 }
319
320 if ((group == lpu) && (is_write)) {
321 prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
322 }
323
324 prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
325 prg.data = *value;
326
327 if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
328 FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
329 *value = prg.data;
330 }
331
332 return (rval);
333 }
334