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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Schizo/PCI Functions to the Safari Configurator
29 *
30 */
31
32 #include <sys/types.h>
33 #include <sys/cred.h>
34 #include <sys/mman.h>
35 #include <sys/kmem.h>
36 #include <sys/conf.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunndi.h>
40 #include <sys/modctl.h>
41 #include <sys/stat.h>
42 #include <sys/param.h>
43 #include <sys/autoconf.h>
44 #include <sys/ksynch.h>
45 #include <sys/promif.h>
46 #include <sys/ndi_impldefs.h>
47 #include <sys/ddi_impldefs.h>
48 #include <sys/machsystm.h>
49 #include <sys/gp2cfg.h>
50 #include <sys/gptwo_pci.h>
51
52 #ifdef DEBUG
53 int gptwo_pci_debug = 0;
54
55 static void debug(char *, uintptr_t, uintptr_t,
56 uintptr_t, uintptr_t, uintptr_t);
57
58 #define GPTWO_DEBUG0(level, flag, s) if (gptwo_pci_debug >= level) \
59 cmn_err(flag, s)
60 #define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwo_pci_debug >= level) \
61 debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
62 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwo_pci_debug >= level) \
63 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
64 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \
65 if (gptwo_pci_debug >= level) \
66 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0);
67 #else
68 #define GPTWO_DEBUG0(level, flag, s)
69 #define GPTWO_DEBUG1(level, flag, fmt, a1)
70 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2)
71 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3)
72 #endif
73
74 void gptwocfg_devi_attach_to_parent(dev_info_t *);
75 static char *gptwo_get_probe_string(spcd_t *, int);
76 static void gptwo_find_nodes(dev_info_t *, int, gptwo_new_nodes_t *);
77
78 extern caddr_t efcode_vaddr;
79 extern int efcode_size;
80
81 /*
82 * Module linkage information for the kernel.
83 */
84
85 extern struct mod_ops mod_miscops;
86
87 static struct modlmisc modlmisc = {
88 &mod_miscops, /* Type of module */
89 "gptwo->pci configurator",
90 };
91
92 static struct modlinkage modlinkage = {
93 MODREV_1, (void *)&modlmisc, NULL
94 };
95
96 int
_init(void)97 _init(void)
98 {
99 int err = 0;
100
101 /*
102 * Create a resource map for the contigous memory allocated
103 * at start-of-day in startup.c
104 */
105 if (ndi_ra_map_setup(ddi_root_node(), "gptwo-contigousmem")
106 == NDI_FAILURE) {
107 GPTWO_DEBUG0(1, CE_WARN,
108 "Can not setup resource map - gptwo-contigousmem\n");
109 return (1);
110 }
111
112 /*
113 * Put the allocated memory into the pool.
114 */
115 (void) ndi_ra_free(ddi_root_node(), (uint64_t)efcode_vaddr,
116 (uint64_t)efcode_size, "gptwo-contigousmem", 0);
117
118 /* register devices with the configurator */
119 gptwocfg_register_ops(SAFPTYPE_sPCI, gptwo_configure_pci,
120 gptwo_unconfigure_pci);
121 gptwocfg_register_ops(SAFPTYPE_cPCI, gptwo_configure_pci,
122 gptwo_unconfigure_pci);
123 gptwocfg_register_ops(SAFPTYPE_PCIX, gptwo_configure_pci,
124 gptwo_unconfigure_pci);
125
126 if ((err = mod_install(&modlinkage)) != 0) {
127 GPTWO_DEBUG1(1, CE_WARN, "gptwo_pci (PCI Functions) "
128 "failed to load, error=%d\n", err);
129 gptwocfg_unregister_ops(SAFPTYPE_sPCI);
130 gptwocfg_unregister_ops(SAFPTYPE_cPCI);
131 gptwocfg_unregister_ops(SAFPTYPE_PCIX);
132 } else {
133 GPTWO_DEBUG0(1, CE_WARN, "gptwo_pci (PCI Functions) "
134 "has been loaded.\n");
135 }
136 return (err);
137 }
138
139 int
_fini(void)140 _fini(void)
141 {
142 gptwocfg_unregister_ops(SAFPTYPE_sPCI);
143 gptwocfg_unregister_ops(SAFPTYPE_cPCI);
144 gptwocfg_unregister_ops(SAFPTYPE_PCIX);
145 return (mod_remove(&modlinkage));
146 }
147
148 int
_info(modinfop)149 _info(modinfop)
150 struct modinfo *modinfop;
151 {
152 return (mod_info(&modlinkage, modinfop));
153 }
154
155 /*ARGSUSED*/
156 static int
set_name_prop(dev_info_t * dip,void * arg,uint_t flags)157 set_name_prop(dev_info_t *dip, void *arg, uint_t flags)
158 {
159 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip,
160 "name", "pci") != DDI_SUCCESS) {
161 return (DDI_WALK_ERROR);
162 }
163
164 return (DDI_WALK_TERMINATE);
165 }
166
167 /*ARGSUSED*/
168 static void
get_new_child(dev_info_t * rdip,void * arg,uint_t flags)169 get_new_child(dev_info_t *rdip, void *arg, uint_t flags)
170 {
171 dev_info_t **dipp = (dev_info_t **)arg;
172
173 ASSERT(dipp && (*dipp == NULL));
174
175 *dipp = rdip;
176 }
177
178 gptwo_new_nodes_t *
gptwo_configure_pci(dev_info_t * ap,spcd_t * pcd,uint_t id)179 gptwo_configure_pci(dev_info_t *ap, spcd_t *pcd, uint_t id)
180 {
181 fco_handle_t fco_handle;
182 int error, i, circ, freq;
183 dev_info_t *new_child;
184 char unit_address[64];
185 gptwo_new_nodes_t *new_nodes;
186 char *probe_string;
187 devi_branch_t b = {0};
188
189 GPTWO_DEBUG2(1, CE_CONT, "gptwo_configure_pci: id=%x pcd=%lx\n",
190 id, pcd);
191
192 new_nodes = gptwocfg_allocate_node_list(IOBUS_PER_PORT);
193
194 i = IOBUS_PER_PORT;
195
196 while (i) {
197 i--;
198
199 if (pcd->spcd_iobus_rsv[i] != SPCD_RSV_PASS) {
200
201 cmn_err(CE_WARN, "gptwo_configure_pci: saf id=0x%x "
202 "leaf %d - Can not be probed\n", id, i);
203
204 continue;
205 }
206
207 /*
208 * Ideally, fcode would be run from the "sid_branch_create"
209 * callback (that is the primary purpose of that callback).
210 * However, the fcode interpreter was written with the
211 * assumption that the "new_child" was linked into the
212 * device tree. The callback is invoked with the devinfo node
213 * in the DS_PROTO state. More investigation is needed before
214 * we can invoke the interpreter from the callback. For now,
215 * we create the "new_child" in the BOUND state, invoke the
216 * fcode interpreter and then rebind the dip to use any
217 * compatible properties created by fcode.
218 */
219
220 new_child = NULL;
221
222 b.arg = &new_child;
223 b.type = DEVI_BRANCH_SID;
224 b.create.sid_branch_create = set_name_prop;
225 b.devi_branch_callback = get_new_child;
226
227 /*
228 * Prevent any changes to new_child
229 * until we have bound it to the correct driver.
230 */
231 ndi_devi_enter(ap, &circ);
232 if (e_ddi_branch_create(ap, &b, NULL, 0)) {
233 ASSERT(new_child == NULL);
234
235 if (new_nodes->gptwo_nodes[0] == NULL) {
236 GPTWO_DEBUG0(1, CE_CONT, "gptwo_configure_pci: "
237 "No nodes configured - "
238 "removing new_nodes\n");
239 gptwocfg_free_node_list(new_nodes);
240 new_nodes = NULL;
241 }
242
243 ndi_devi_exit(ap, circ);
244
245 return (new_nodes);
246 }
247
248 /*
249 * The platform DR interfaces created the dip in
250 * bound state. Bring devinfo node down to linked
251 * state and hold it there until compatible
252 * properties are created.
253 */
254 e_ddi_branch_rele(new_child);
255 (void) i_ndi_unconfig_node(new_child, DS_LINKED, 0);
256 ASSERT(i_ddi_node_state(new_child) == DS_LINKED);
257 e_ddi_branch_hold(new_child);
258
259 mutex_enter(&DEVI(new_child)->devi_lock);
260 DEVI(new_child)->devi_flags |= DEVI_NO_BIND;
261 mutex_exit(&DEVI(new_child)->devi_lock);
262
263 /*
264 * Drop the busy-hold on parent before calling
265 * fcode_interpreter to prevent potential deadlocks
266 */
267 ndi_devi_exit(ap, circ);
268
269 (void) sprintf(unit_address, "%x", id);
270
271 /*
272 * Build the probe string from the PCD that will be passed
273 * in to the interpreter as my-args. This will tell the
274 * fcode what pci devices to probe after the pci node has
275 * been probed.
276 */
277 probe_string = gptwo_get_probe_string(pcd, i);
278
279 GPTWO_DEBUG3(1, CE_CONT, "gptwo_configure_pci: args to "
280 "interpreter ap=%lx new_child=%lx unit_address=%s\n",
281 ap, new_child, unit_address);
282
283 if (probe_string)
284 GPTWO_DEBUG1(1, CE_CONT, "gptwo_configure_pci: "
285 "probe string=%s\n", probe_string);
286
287 fco_handle = gp2_fc_ops_alloc_handle(ap, new_child, NULL, NULL,
288 unit_address, probe_string);
289
290 GPTWO_DEBUG0(1, CE_CONT,
291 "gptwocfg: Calling Fcode Interpeter...\n");
292
293 error = fcode_interpreter(ap, &gp2_fc_ops, fco_handle);
294
295 GPTWO_DEBUG1(1, CE_CONT,
296 "gptwo_configure_pci: fcode_interpreter "
297 " returned %x\n", error);
298
299 if (error) {
300 cmn_err(CE_WARN, "gptwo_pci: Unable to probe pci leaf "
301 "%s\n", unit_address);
302
303 gp2_fc_ops_free_handle(fco_handle);
304
305 (void) e_ddi_branch_destroy(new_child, NULL, 0);
306 } else {
307 gptwocfg_save_handle(new_child, fco_handle);
308
309 /*
310 * Compatible properties (if any) have been created,
311 * so bind driver.
312 */
313 ndi_devi_enter(ap, &circ);
314 ASSERT(i_ddi_node_state(new_child) <= DS_LINKED);
315
316 mutex_enter(&DEVI(new_child)->devi_lock);
317 DEVI(new_child)->devi_flags &= ~DEVI_NO_BIND;
318 mutex_exit(&DEVI(new_child)->devi_lock);
319
320 ndi_devi_exit(ap, circ);
321
322 if (ndi_devi_bind_driver(new_child, 0) !=
323 DDI_SUCCESS) {
324 cmn_err(CE_WARN, "gptwo_pci: Unable to bind"
325 " new pci child at dip=0x%p\n",
326 (void *)new_child);
327 }
328
329 /*
330 * If POST provided a frequency, the clock-frequency
331 * property needs to be updated.
332 */
333 if (pcd->spcd_afreq) {
334
335 /*
336 * The upper byte is for leaf B and the lower
337 * byte is for leaf A.
338 */
339 if (i)
340 freq = pcd->spcd_afreq >> 8;
341 else
342 freq = pcd->spcd_afreq & 0x00ff;
343
344 (void) ndi_prop_update_int(DDI_DEV_T_NONE,
345 new_child, "clock-frequency",
346 (freq * 1000 * 1000));
347 }
348 }
349 }
350
351 gptwo_find_nodes(ap, id, new_nodes);
352
353 if (new_nodes->gptwo_nodes[0] == NULL) {
354 GPTWO_DEBUG0(1, CE_CONT, "gptwo_configure_pci: "
355 "No nodes configured - removing new_nodes\n");
356 gptwocfg_free_node_list(new_nodes);
357 new_nodes = NULL;
358 }
359
360 GPTWO_DEBUG1(1, CE_CONT, "gptwo_configure_pci: "
361 "Returning new_nodes=%p\n", new_nodes);
362
363 return (new_nodes);
364 }
365
366 dev_info_t *
gptwo_unconfigure_pci(dev_info_t * dip)367 gptwo_unconfigure_pci(dev_info_t *dip)
368 {
369 fco_handle_t fco_handle;
370
371 fco_handle = gptwocfg_get_handle(dip);
372
373 if (fco_handle != NULL) {
374 /*
375 * If there is a handle, there may be resources
376 * that need to be freed from when the
377 * devices's fcode ran.
378 */
379 GPTWO_DEBUG1(1, CE_CONT, "fco_handle=%lx\n", fco_handle);
380 gp2_fc_ops_free_handle(fco_handle);
381 }
382 return (NULL);
383 }
384
385 static void
gptwo_find_nodes(dev_info_t * ap,int id,gptwo_new_nodes_t * new_nodes)386 gptwo_find_nodes(dev_info_t *ap, int id, gptwo_new_nodes_t *new_nodes)
387 {
388 dev_info_t *saf_dev;
389 int found, j, circ;
390 int i = 0;
391
392 GPTWO_DEBUG1(1, CE_CONT, "gptwo_find_nodes - id=%x\n", id);
393
394 /*
395 * We are walking child list of ap, so hold it busy
396 */
397 ndi_devi_enter(ap, &circ);
398
399 saf_dev = ddi_get_child(ap);
400 while (saf_dev != NULL) {
401 if (ddi_getprop(DDI_DEV_T_ANY, saf_dev,
402 DDI_PROP_DONTPASS, "portid", -1) == id) {
403 if (i < IOBUS_PER_PORT) {
404
405 GPTWO_DEBUG2(1, CE_CONT,
406 "gptwo_find_nodes - "
407 "Found %d %p\n", i, saf_dev);
408
409 found = 0;
410 for (j = 0; j < IOBUS_PER_PORT; j++) {
411 if (new_nodes->gptwo_nodes[j] ==
412 saf_dev) {
413 found = 1;
414 }
415 }
416 if (!found) {
417 /*
418 * Branch rooted at saf-dev was
419 * held earlier.
420 */
421 ASSERT(e_ddi_branch_held(saf_dev));
422 new_nodes->gptwo_nodes[i] = saf_dev;
423 i++;
424 }
425 } else {
426 GPTWO_DEBUG0(1, CE_CONT,
427 "gptwo_find_nodes - "
428 "No room in new_nodes\n");
429 }
430 }
431 saf_dev = ddi_get_next_sibling(saf_dev);
432 }
433
434 ndi_devi_exit(ap, circ);
435 }
436
437 static char *
gptwo_get_probe_string(spcd_t * pcd,int bus_number)438 gptwo_get_probe_string(spcd_t *pcd, int bus_number)
439 {
440 int i, str_size;
441 char temp[64];
442 char num[8];
443 char *probe;
444
445 GPTWO_DEBUG2(1, CE_CONT, "gptwo_get_probe_string - %p %x\n", pcd,
446 bus_number);
447
448 temp[0] = NULL;
449
450 for (i = 0; i < IOCARD_PER_BUS; i++) {
451
452 GPTWO_DEBUG2(1, CE_CONT, "gptwo_get_probe_string - "
453 "card status %x %x\n",
454 i, pcd->spcd_iocard_rsv[bus_number][i]);
455
456 if (pcd->spcd_iocard_rsv[bus_number][i] == SPCD_RSV_PASS) {
457 numtos(i, num);
458 if (temp[0] == NULL)
459 (void) sprintf(temp, "%s", num);
460 else
461 (void) sprintf(temp, "%s,%s", temp, num);
462 }
463 }
464
465 if (bus_number == 0)
466 (void) sprintf(temp, "%sa", temp); /* Append a 'a' for leaf A */
467 else
468 (void) sprintf(temp, "%sb", temp); /* Append a 'b' for leaf B */
469
470 str_size = strlen(temp);
471
472 if (str_size == 0)
473 return (NULL);
474
475 probe = kmem_zalloc(str_size + 1, KM_SLEEP);
476
477 (void) strcpy(probe, temp);
478
479 GPTWO_DEBUG1(1, CE_CONT, "gptwo_get_probe_string - Returning %s\n",
480 probe);
481
482 return (probe);
483 }
484
485 #ifdef DEBUG
486 static void
debug(char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)487 debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
488 uintptr_t a4, uintptr_t a5)
489 {
490 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
491 }
492 #endif
493