ofw_autoconf.c revision 1.2.4.2 1 /* $NetBSD: ofw_autoconf.c,v 1.2.4.2 2007/11/13 15:59:07 bouyer Exp $ */
2 /*
3 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
4 * Copyright (C) 1995, 1996 TooLs GmbH.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by TooLs GmbH.
18 * 4. The name of TooLs GmbH may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: ofw_autoconf.c,v 1.2.4.2 2007/11/13 15:59:07 bouyer Exp $");
35
36 #include <sys/param.h>
37 #include <sys/conf.h>
38 #include <sys/device.h>
39 #include <sys/reboot.h>
40 #include <sys/systm.h>
41
42 #include <uvm/uvm_extern.h>
43
44 #include <machine/autoconf.h>
45 #include <machine/bus.h>
46 #include <machine/stdarg.h>
47
48 #include <dev/ofw/openfirm.h>
49 #include <dev/pci/pcivar.h>
50 #include <dev/scsipi/scsi_all.h>
51 #include <dev/scsipi/scsipi_all.h>
52 #include <dev/scsipi/scsiconf.h>
53 #include <dev/ata/atavar.h>
54 #include <dev/ic/wdcvar.h>
55 #include <dev/wsfb/genfbvar.h>
56
57 extern char bootpath[256];
58 char cbootpath[256];
59 int console_node = 0, console_instance = 0;
60
61 #ifdef macppc
62 volatile uint32_t *heathrow_FCR = NULL;
63 #endif
64
65 struct genfb_colormap_callback gfb_cb;
66 static void of_set_palette(void *, int, int, int, int);
67 static void add_model_specifics(prop_dictionary_t);
68 static void copyprops(int, prop_dictionary_t);
69 static void canonicalize_bootpath(void);
70
71 /*
72 * Determine device configuration for a machine.
73 */
74 void
75 cpu_configure(void)
76 {
77 init_interrupt();
78 canonicalize_bootpath();
79
80 if (config_rootfound("mainbus", NULL) == NULL)
81 panic("configure: mainbus not configured");
82
83 genppc_cpu_configure();
84 }
85
86 static void
87 canonicalize_bootpath(void)
88 {
89 int node;
90 char *p, *lastp;
91 char last[32];
92
93 /*
94 * If the bootpath doesn't start with a / then it isn't
95 * an OFW path and probably is an alias, so look up the alias
96 * and regenerate the full bootpath so device_register will work.
97 */
98 if (bootpath[0] != '/' && bootpath[0] != '\0') {
99 int aliases = OF_finddevice("/aliases");
100 char tmpbuf[100];
101 char aliasbuf[256];
102 if (aliases != 0) {
103 char *cp1, *cp2, *cp;
104 char saved_ch = '\0';
105 int len;
106 cp1 = strchr(bootpath, ':');
107 cp2 = strchr(bootpath, ',');
108 cp = cp1;
109 if (cp1 == NULL || (cp2 != NULL && cp2 < cp1))
110 cp = cp2;
111 tmpbuf[0] = '\0';
112 if (cp != NULL) {
113 strcpy(tmpbuf, cp);
114 saved_ch = *cp;
115 *cp = '\0';
116 }
117 len = OF_getprop(aliases, bootpath, aliasbuf,
118 sizeof(aliasbuf));
119 if (len > 0) {
120 if (aliasbuf[len-1] == '\0')
121 len--;
122 memcpy(bootpath, aliasbuf, len);
123 strcpy(&bootpath[len], tmpbuf);
124 } else {
125 *cp = saved_ch;
126 }
127 }
128 }
129
130 /*
131 * Strip kernel name. bootpath contains "OF-path"/"kernel".
132 *
133 * for example:
134 * /bandit@F2000000/gc@10/53c94@10000/sd@0,0/netbsd (OF-1.x)
135 * /pci/mac-io/ata-3@2000/disk@0:0/netbsd.new (OF-3.x)
136 */
137 strcpy(cbootpath, bootpath);
138 while ((node = OF_finddevice(cbootpath)) == -1) {
139 if ((p = strrchr(cbootpath, '/')) == NULL)
140 break;
141 *p = '\0';
142 }
143
144 if (node == -1) {
145 /* Cannot canonicalize... use bootpath anyway. */
146 strcpy(cbootpath, bootpath);
147
148 return;
149 }
150
151 /*
152 * cbootpath is a valid OF path. Use package-to-path to
153 * canonicalize pathname.
154 */
155
156 /* Back up the last component for later use. */
157 if ((p = strrchr(cbootpath, '/')) != NULL)
158 strcpy(last, p + 1);
159 else
160 last[0] = '\0';
161
162 memset(cbootpath, 0, sizeof(cbootpath));
163 OF_package_to_path(node, cbootpath, sizeof(cbootpath) - 1);
164
165 /*
166 * OF_1.x (at least) always returns addr == 0 for
167 * SCSI disks (i.e. "/bandit (at) .../.../sd@0,0").
168 */
169 lastp = strrchr(cbootpath, '/');
170 if (lastp != NULL) {
171 lastp++;
172 if (strncmp(lastp, "sd@", 3) == 0
173 && strncmp(last, "sd@", 3) == 0)
174 strcpy(lastp, last);
175 } else {
176 lastp = cbootpath;
177 }
178
179 /*
180 * At this point, cbootpath contains like:
181 * "/pci@80000000/mac-io@10/ata-3@20000/disk"
182 *
183 * The last component may have no address... so append it.
184 */
185 if (strchr(lastp, '@') == NULL) {
186 /* Append it. */
187 if ((p = strrchr(last, '@')) != NULL)
188 strcat(cbootpath, p);
189 }
190
191 if ((p = strrchr(lastp, ':')) != NULL) {
192 *p++ = '\0';
193 /* booted_partition = *p - '0'; XXX correct? */
194 }
195
196 /* XXX Does this belong here, or device_register()? */
197 if ((p = strrchr(lastp, ',')) != NULL)
198 *p = '\0';
199 }
200
201 /*
202 * device_register is called from config_attach as each device is
203 * attached. We use it to find the NetBSD device corresponding to the
204 * known OF boot device.
205 */
206 void
207 device_register(dev, aux)
208 struct device *dev;
209 void *aux;
210 {
211 static struct device *parent;
212 static char *bp = bootpath + 1, *cp = cbootpath;
213 unsigned long addr;
214 char *p;
215
216 if (booted_device)
217 return;
218
219 /* Skip over devices not represented in the OF tree. */
220 if (device_is_a(dev, "mainbus")) {
221 parent = dev;
222 return;
223 }
224 if (device_is_a(dev, "atapibus") || device_is_a(dev, "pci") ||
225 device_is_a(dev, "scsibus") || device_is_a(dev, "atabus"))
226 return;
227
228 if (device_is_a(device_parent(dev), "pci")) {
229 /* see if this is going to be console */
230 struct pci_attach_args *pa = aux;
231 prop_dictionary_t dict;
232 int node, sub;
233 int console = 0;
234
235 dict = device_properties(dev);
236 node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag);
237 prop_dictionary_set_uint32(dict, "device_node", node);
238
239 console = (node == console_node);
240
241 if (!console) {
242 /*
243 * see if any child matches since OF attaches nodes for
244 * each head and /chosen/stdout points to the head
245 * rather than the device itself in this case
246 */
247 sub = OF_child(node);
248 while ((sub != 0) && (sub != console_node)) {
249 sub = OF_peer(sub);
250 }
251 if (sub == console_node) {
252 console = true;
253 }
254 }
255
256 if (console) {
257 uint64_t cmap_cb;
258
259 prop_dictionary_set_uint32(dict, "instance_handle",
260 console_instance);
261 copyprops(console_node, dict);
262
263 gfb_cb.gcc_cookie = (void *)console_instance;
264 gfb_cb.gcc_set_mapreg = of_set_palette;
265 cmap_cb = (uint64_t)&gfb_cb;
266 prop_dictionary_set_uint64(dict, "cmap_callback",
267 cmap_cb);
268 }
269 }
270
271 if (device_is_a(device_parent(dev), "atapibus") ||
272 device_is_a(device_parent(dev), "atabus") ||
273 device_is_a(device_parent(dev), "pci") ||
274 device_is_a(device_parent(dev), "scsibus")) {
275 if (device_parent(device_parent(dev)) != parent)
276 return;
277 } else {
278 if (device_parent(dev) != parent)
279 return;
280 }
281
282 /* Get the address part of the current path component. The
283 * last component of the canonical bootpath may have no
284 * address (eg, "disk"), in which case we need to get the
285 * address from the original bootpath instead.
286 */
287 p = strchr(cp, '@');
288 if (!p) {
289 if (bp)
290 p = strchr(bp, '@');
291 if (!p)
292 addr = 0;
293 else {
294 addr = strtoul(p + 1, NULL, 16);
295 p = NULL;
296 }
297 } else
298 addr = strtoul(p + 1, &p, 16);
299
300 if (device_is_a(device_parent(dev), "mainbus")) {
301 struct confargs *ca = aux;
302
303 if (strcmp(ca->ca_name, "ofw") == 0) /* XXX */
304 return;
305 if (addr != ca->ca_reg[0])
306 return;
307 } else if (device_is_a(device_parent(dev), "pci")) {
308 struct pci_attach_args *pa = aux;
309
310 if (addr != pa->pa_device)
311 return;
312 } else if (device_is_a(device_parent(dev), "obio")) {
313 struct confargs *ca = aux;
314
315 if (addr != ca->ca_reg[0])
316 return;
317 } else if (device_is_a(device_parent(dev), "scsibus") ||
318 device_is_a(device_parent(dev), "atapibus")) {
319 struct scsipibus_attach_args *sa = aux;
320
321 /* periph_target is target for scsi, drive # for atapi */
322 if (addr != sa->sa_periph->periph_target)
323 return;
324 } else if (device_is_a(device_parent(device_parent(dev)), "pciide")) {
325 struct ata_device *adev = aux;
326
327 if (addr != adev->adev_drv_data->drive)
328 return;
329
330 /*
331 * OF splits channel and drive into separate path
332 * components, so check the addr part of the next
333 * component. (Ignore bp, because the canonical path
334 * will be complete in the pciide case.)
335 */
336 p = strchr(p, '@');
337 if (!p++)
338 return;
339 if (strtoul(p, &p, 16) != adev->adev_drv_data->drive)
340 return;
341 } else if (device_is_a(device_parent(device_parent(dev)), "wdc")) {
342 struct ata_device *adev = aux;
343
344 if (addr != adev->adev_drv_data->drive)
345 return;
346 } else
347 return;
348
349 /* If we reach this point, then dev is a match for the current
350 * path component.
351 */
352
353 if (p && *p) {
354 parent = dev;
355 cp = p;
356 bp = strchr(bp, '/');
357 if (bp)
358 bp++;
359 return;
360 } else {
361 booted_device = dev;
362 booted_partition = 0; /* XXX -- should be extracted from bootpath */
363 return;
364 }
365 }
366
367 /*
368 * Setup root device.
369 * Configure swap area.
370 */
371 void
372 cpu_rootconf()
373 {
374 printf("boot device: %s\n",
375 booted_device ? booted_device->dv_xname : "<unknown>");
376
377 setroot(booted_device, booted_partition);
378 }
379
380 /*
381 * Find OF-device corresponding to the PCI device.
382 */
383 int
384 pcidev_to_ofdev(pci_chipset_tag_t pc, pcitag_t tag)
385 {
386 int bus, dev, func;
387 u_int reg[5];
388 int p, q;
389 int l, b, d, f;
390
391 pci_decompose_tag(pc, tag, &bus, &dev, &func);
392
393 for (q = OF_peer(0); q; q = p) {
394 l = OF_getprop(q, "assigned-addresses", reg, sizeof(reg));
395 if (l > 4) {
396 b = (reg[0] >> 16) & 0xff;
397 d = (reg[0] >> 11) & 0x1f;
398 f = (reg[0] >> 8) & 0x07;
399
400 if (b == bus && d == dev && f == func)
401 return q;
402 }
403 if ((p = OF_child(q)))
404 continue;
405 while (q) {
406 if ((p = OF_peer(q)))
407 break;
408 q = OF_parent(q);
409 }
410 }
411 return 0;
412 }
413
414 static void
415 add_model_specifics(prop_dictionary_t dict)
416 {
417 const char *bl_rev_models[] = {
418 "PowerBook4,3", "PowerBook6,3", "PowerBook6,5", NULL};
419 int node;
420
421 node = OF_finddevice("/");
422
423 if (of_compatible(node, bl_rev_models) != -1) {
424 prop_dictionary_set_bool(dict, "backlight_level_reverted", 1);
425 }
426 }
427
428 static void
429 copyprops(int node, prop_dictionary_t dict)
430 {
431 uint32_t temp;
432
433 prop_dictionary_set_bool(dict, "is_console", 1);
434 if (!of_to_uint32_prop(dict, node, "width", "width")) {
435
436 OF_interpret("screen-width", 0, 1, &temp);
437 prop_dictionary_set_uint32(dict, "width", temp);
438 }
439 if (!of_to_uint32_prop(dict, console_node, "height", "height")) {
440
441 OF_interpret("screen-height", 0, 1, &temp);
442 prop_dictionary_set_uint32(dict, "height", temp);
443 }
444 of_to_uint32_prop(dict, console_node, "linebytes", "linebytes");
445 if (!of_to_uint32_prop(dict, console_node, "depth", "depth")) {
446 /*
447 * XXX we should check linebytes vs. width but those
448 * FBs that don't have a depth property ( /chaos/control... )
449 * won't have linebytes either
450 */
451 prop_dictionary_set_uint32(dict, "depth", 8);
452 }
453 if (!of_to_uint32_prop(dict, console_node, "address", "address")) {
454 uint32_t fbaddr = 0;
455 OF_interpret("frame-buffer-adr", 0, 1, &fbaddr);
456 if (fbaddr != 0)
457 prop_dictionary_set_uint32(dict, "address", fbaddr);
458 }
459 of_to_dataprop(dict, console_node, "EDID", "EDID");
460 add_model_specifics(dict);
461
462 temp = 0;
463 if (OF_getprop(console_node, "ATY,RefCLK", &temp, sizeof(temp)) != 4) {
464
465 OF_getprop(OF_parent(console_node), "ATY,RefCLK", &temp,
466 sizeof(temp));
467 }
468 if (temp != 0)
469 prop_dictionary_set_uint32(dict, "refclk", temp / 10);
470 }
471
472 static void
473 of_set_palette(void *cookie, int index, int r, int g, int b)
474 {
475 int ih = (int)cookie;
476
477 OF_call_method_1("color!", ih, 4, r, g, b, index);
478 }
479