1 /* $NetBSD: ofw_machdep.c,v 1.46 2016/07/07 06:55:38 msaitoh Exp $ */
2
3 /*
4 * Copyright (C) 1996 Wolfgang Solfrank.
5 * Copyright (C) 1996 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "opt_multiprocessor.h"
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.46 2016/07/07 06:55:38 msaitoh Exp $");
38
39 #include <sys/param.h>
40 #include <sys/buf.h>
41 #include <sys/conf.h>
42 #include <sys/device.h>
43 #include <sys/disk.h>
44 #include <sys/disklabel.h>
45 #include <sys/fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/kprintf.h>
48 #include <sys/malloc.h>
49 #include <sys/stat.h>
50 #include <sys/systm.h>
51
52 #include <machine/openfirm.h>
53 #include <machine/promlib.h>
54
55 #include <dev/ofw/ofw_pci.h>
56
57 #include <machine/sparc64.h>
58
59 static u_int mmuh = -1, memh = -1;
60
61 static u_int get_mmu_handle(void);
62 static u_int get_memory_handle(void);
63
64 static u_int
get_mmu_handle(void)65 get_mmu_handle(void)
66 {
67 u_int chosen;
68
69 if ((chosen = OF_finddevice("/chosen")) == -1) {
70 prom_printf("get_mmu_handle: cannot get /chosen\n");
71 return -1;
72 }
73 if (OF_getprop(chosen, "mmu", &mmuh, sizeof(mmuh)) == -1) {
74 prom_printf("get_mmu_handle: cannot get mmuh\n");
75 return -1;
76 }
77 return mmuh;
78 }
79
80 static u_int
get_memory_handle(void)81 get_memory_handle(void)
82 {
83 u_int chosen;
84
85 if ((chosen = OF_finddevice("/chosen")) == -1) {
86 prom_printf("get_memory_handle: cannot get /chosen\n");
87 return -1;
88 }
89 if (OF_getprop(chosen, "memory", &memh, sizeof(memh)) == -1) {
90 prom_printf("get_memory_handle: cannot get memh\n");
91 return -1;
92 }
93 return memh;
94 }
95
96
97 /*
98 * Point prom to our sun4u trap table. This stops the prom from mapping us.
99 */
100 int
prom_set_trap_table_sun4u(vaddr_t tba)101 prom_set_trap_table_sun4u(vaddr_t tba)
102 {
103 struct {
104 cell_t name;
105 cell_t nargs;
106 cell_t nreturns;
107 cell_t tba;
108 } args;
109
110 args.name = ADR2CELL(&"SUNW,set-trap-table");
111 args.nargs = 1;
112 args.nreturns = 0;
113 args.tba = ADR2CELL(tba);
114 return openfirmware(&args);
115 }
116
117 #ifdef SUN4V
118 /*
119 * Point prom to our sun4v trap table. This stops the prom from mapping us.
120 */
121 int
prom_set_trap_table_sun4v(vaddr_t tba,paddr_t mmfsa)122 prom_set_trap_table_sun4v(vaddr_t tba, paddr_t mmfsa)
123 {
124 struct {
125 cell_t name;
126 cell_t nargs;
127 cell_t nreturns;
128 cell_t tba;
129 cell_t mmfsa;
130 } args;
131
132 args.name = ADR2CELL("SUNW,set-trap-table");
133 args.nargs = 2;
134 args.nreturns = 0;
135 args.tba = ADR2CELL(tba);
136 args.mmfsa = ADR2CELL(mmfsa);
137 return openfirmware(&args);
138 }
139 #endif
140
141 /*
142 * Have the prom convert from virtual to physical addresses.
143 *
144 * Only works while the prom is actively mapping us.
145 */
146 paddr_t
prom_vtop(vaddr_t vaddr)147 prom_vtop(vaddr_t vaddr)
148 {
149 struct {
150 cell_t name;
151 cell_t nargs;
152 cell_t nreturns;
153 cell_t method;
154 cell_t ihandle;
155 cell_t vaddr;
156 cell_t status;
157 cell_t retaddr;
158 cell_t mode;
159 cell_t phys_hi;
160 cell_t phys_lo;
161 } args;
162
163 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
164 prom_printf("prom_vtop: cannot get mmuh\n");
165 return 0;
166 }
167 args.name = ADR2CELL(&"call-method");
168 args.nargs = 3;
169 args.nreturns = 5;
170 args.method = ADR2CELL(&"translate");
171 args.ihandle = HDL2CELL(mmuh);
172 args.vaddr = ADR2CELL(vaddr);
173 if (openfirmware(&args) == -1)
174 return -1;
175 #if 0
176 prom_printf("Called \"translate\", mmuh=%x, vaddr=%x, status=%x %x,\n retaddr=%x %x, mode=%x %x, phys_hi=%x %x, phys_lo=%x %x\n",
177 mmuh, vaddr, (int)(args.status>>32), (int)args.status, (int)(args.retaddr>>32), (int)args.retaddr,
178 (int)(args.mode>>32), (int)args.mode, (int)(args.phys_hi>>32), (int)args.phys_hi,
179 (int)(args.phys_lo>>32), (int)args.phys_lo);
180 #endif
181 return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
182 }
183
184 /*
185 * Grab some address space from the prom
186 *
187 * Only works while the prom is actively mapping us.
188 */
189 vaddr_t
prom_claim_virt(vaddr_t vaddr,int len)190 prom_claim_virt(vaddr_t vaddr, int len)
191 {
192 struct {
193 cell_t name;
194 cell_t nargs;
195 cell_t nreturns;
196 cell_t method;
197 cell_t ihandle;
198 cell_t align;
199 cell_t len;
200 cell_t vaddr;
201 cell_t status;
202 cell_t retaddr;
203 } args;
204
205 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
206 prom_printf("prom_claim_virt: cannot get mmuh\n");
207 return 0;
208 }
209 args.name = ADR2CELL(&"call-method");
210 args.nargs = 5;
211 args.nreturns = 2;
212 args.method = ADR2CELL(&"claim");
213 args.ihandle = HDL2CELL(mmuh);
214 args.align = 0;
215 args.len = len;
216 args.vaddr = ADR2CELL(vaddr);
217 if (openfirmware(&args) == -1)
218 return -1;
219 return (vaddr_t)args.retaddr;
220 }
221
222 /*
223 * Request some address space from the prom
224 *
225 * Only works while the prom is actively mapping us.
226 */
227 vaddr_t
prom_alloc_virt(int len,int align)228 prom_alloc_virt(int len, int align)
229 {
230 struct {
231 cell_t name;
232 cell_t nargs;
233 cell_t nreturns;
234 cell_t method;
235 cell_t ihandle;
236 cell_t align;
237 cell_t len;
238 cell_t status;
239 cell_t retaddr;
240 } args;
241
242 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
243 prom_printf("prom_alloc_virt: cannot get mmuh\n");
244 return -1LL;
245 }
246 args.name = ADR2CELL(&"call-method");
247 args.nargs = 4;
248 args.nreturns = 2;
249 args.method = ADR2CELL(&"claim");
250 args.ihandle = HDL2CELL(mmuh);
251 args.align = align;
252 args.len = len;
253 if (openfirmware(&args) != 0)
254 return -1;
255 return (vaddr_t)args.retaddr;
256 }
257
258 /*
259 * Release some address space to the prom
260 *
261 * Only works while the prom is actively mapping us.
262 */
263 int
prom_free_virt(vaddr_t vaddr,int len)264 prom_free_virt(vaddr_t vaddr, int len)
265 {
266 struct {
267 cell_t name;
268 cell_t nargs;
269 cell_t nreturns;
270 cell_t method;
271 cell_t ihandle;
272 cell_t len;
273 cell_t vaddr;
274 } args;
275
276 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
277 prom_printf("prom_free_virt: cannot get mmuh\n");
278 return -1;
279 }
280 args.name = ADR2CELL(&"call-method");
281 args.nargs = 4;
282 args.nreturns = 0;
283 args.method = ADR2CELL(&"release");
284 args.ihandle = HDL2CELL(mmuh);
285 args.vaddr = ADR2CELL(vaddr);
286 args.len = len;
287 return openfirmware(&args);
288 }
289
290
291 /*
292 * Unmap some address space
293 *
294 * Only works while the prom is actively mapping us.
295 */
296 int
prom_unmap_virt(vaddr_t vaddr,int len)297 prom_unmap_virt(vaddr_t vaddr, int len)
298 {
299 struct {
300 cell_t name;
301 cell_t nargs;
302 cell_t nreturns;
303 cell_t method;
304 cell_t ihandle;
305 cell_t len;
306 cell_t vaddr;
307 } args;
308
309 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
310 prom_printf("prom_unmap_virt: cannot get mmuh\n");
311 return -1;
312 }
313 args.name = ADR2CELL(&"call-method");
314 args.nargs = 4;
315 args.nreturns = 0;
316 args.method = ADR2CELL(&"unmap");
317 args.ihandle = HDL2CELL(mmuh);
318 args.vaddr = ADR2CELL(vaddr);
319 args.len = len;
320 return openfirmware(&args);
321 }
322
323 /*
324 * Have prom map in some memory
325 *
326 * Only works while the prom is actively mapping us.
327 */
328 int
prom_map_phys(paddr_t paddr,off_t size,vaddr_t vaddr,int mode)329 prom_map_phys(paddr_t paddr, off_t size, vaddr_t vaddr, int mode)
330 {
331 struct {
332 cell_t name;
333 cell_t nargs;
334 cell_t nreturns;
335 cell_t method;
336 cell_t ihandle;
337 cell_t mode;
338 cell_t size;
339 cell_t vaddr;
340 cell_t phys_hi;
341 cell_t phys_lo;
342 cell_t status;
343 cell_t retaddr;
344 } args;
345
346 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
347 prom_printf("prom_map_phys: cannot get mmuh\n");
348 return 0;
349 }
350 args.name = ADR2CELL(&"call-method");
351 args.nargs = 7;
352 args.nreturns = 1;
353 args.method = ADR2CELL(&"map");
354 args.ihandle = HDL2CELL(mmuh);
355 args.mode = mode;
356 args.size = size;
357 args.vaddr = ADR2CELL(vaddr);
358 args.phys_hi = HDQ2CELL_HI(paddr);
359 args.phys_lo = HDQ2CELL_LO(paddr);
360
361 if (openfirmware(&args) == -1)
362 return -1;
363 if (args.status)
364 return -1;
365 return (int)args.retaddr;
366 }
367
368
369 /*
370 * Request some RAM from the prom
371 *
372 * Only works while the prom is actively mapping us.
373 */
374 paddr_t
prom_alloc_phys(int len,int align)375 prom_alloc_phys(int len, int align)
376 {
377 struct {
378 cell_t name;
379 cell_t nargs;
380 cell_t nreturns;
381 cell_t method;
382 cell_t ihandle;
383 cell_t align;
384 cell_t len;
385 cell_t status;
386 cell_t phys_hi;
387 cell_t phys_lo;
388 } args;
389
390 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
391 prom_printf("prom_alloc_phys: cannot get memh\n");
392 return -1;
393 }
394 args.name = ADR2CELL(&"call-method");
395 args.nargs = 4;
396 args.nreturns = 3;
397 args.method = ADR2CELL(&"claim");
398 args.ihandle = HDL2CELL(memh);
399 args.align = align;
400 args.len = len;
401 if (openfirmware(&args) != 0)
402 return -1;
403 return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
404 }
405
406 /*
407 * Request some specific RAM from the prom
408 *
409 * Only works while the prom is actively mapping us.
410 */
411 paddr_t
prom_claim_phys(paddr_t phys,int len)412 prom_claim_phys(paddr_t phys, int len)
413 {
414 struct {
415 cell_t name;
416 cell_t nargs;
417 cell_t nreturns;
418 cell_t method;
419 cell_t ihandle;
420 cell_t align;
421 cell_t len;
422 cell_t phys_hi;
423 cell_t phys_lo;
424 cell_t status;
425 cell_t rphys_hi;
426 cell_t rphys_lo;
427 } args;
428
429 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
430 prom_printf("prom_claim_phys: cannot get memh\n");
431 return -1;
432 }
433 args.name = ADR2CELL(&"call-method");
434 args.nargs = 6;
435 args.nreturns = 3;
436 args.method = ADR2CELL(&"claim");
437 args.ihandle = HDL2CELL(memh);
438 args.align = 0;
439 args.len = len;
440 args.phys_hi = HDQ2CELL_HI(phys);
441 args.phys_lo = HDQ2CELL_LO(phys);
442 if (openfirmware(&args) != 0)
443 return -1;
444 return (paddr_t)CELL2HDQ(args.rphys_hi, args.rphys_lo);
445 }
446
447 /*
448 * Free some RAM to prom
449 *
450 * Only works while the prom is actively mapping us.
451 */
452 int
prom_free_phys(paddr_t phys,int len)453 prom_free_phys(paddr_t phys, int len)
454 {
455 struct {
456 cell_t name;
457 cell_t nargs;
458 cell_t nreturns;
459 cell_t method;
460 cell_t ihandle;
461 cell_t len;
462 cell_t phys_hi;
463 cell_t phys_lo;
464 } args;
465
466 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
467 prom_printf("prom_free_phys: cannot get memh\n");
468 return -1;
469 }
470 args.name = ADR2CELL(&"call-method");
471 args.nargs = 5;
472 args.nreturns = 0;
473 args.method = ADR2CELL(&"release");
474 args.ihandle = HDL2CELL(memh);
475 args.len = len;
476 args.phys_hi = HDQ2CELL_HI(phys);
477 args.phys_lo = HDQ2CELL_LO(phys);
478 return openfirmware(&args);
479 }
480
481 /*
482 * Get the msgbuf from the prom. Only works once.
483 *
484 * Only works while the prom is actively mapping us.
485 */
486 paddr_t
prom_get_msgbuf(int len,int align)487 prom_get_msgbuf(int len, int align)
488 {
489 struct {
490 cell_t name;
491 cell_t nargs;
492 cell_t nreturns;
493 cell_t method;
494 cell_t ihandle;
495 cell_t align;
496 cell_t len;
497 cell_t id;
498 cell_t status;
499 cell_t phys_hi;
500 cell_t phys_lo;
501 } args;
502 paddr_t addr;
503
504 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
505 prom_printf("prom_get_msgbuf: cannot get memh\n");
506 return -1;
507 }
508 if (OF_test("test-method") == 0) {
509 if (OF_test_method(OF_instance_to_package(memh),
510 "SUNW,retain") == 0) {
511 args.name = ADR2CELL(&"call-method");
512 args.nargs = 5;
513 args.nreturns = 3;
514 args.method = ADR2CELL(&"SUNW,retain");
515 args.id = ADR2CELL(&"msgbuf");
516 args.ihandle = HDL2CELL(memh);
517 args.len = len;
518 args.align = align;
519 args.status = -1;
520 if (openfirmware(&args) == 0 && args.status == 0) {
521 return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
522 } else prom_printf("prom_get_msgbuf: SUNW,retain failed\n");
523 } else prom_printf("prom_get_msgbuf: test-method failed\n");
524 } else prom_printf("prom_get_msgbuf: test failed\n");
525 /* Allocate random memory -- page zero avail?*/
526 addr = prom_claim_phys(0x000, len);
527 prom_printf("prom_get_msgbuf: allocated new buf at %08x\n", (int)addr);
528 if (addr == -1) {
529 prom_printf("prom_get_msgbuf: cannot get allocate physmem\n");
530 return -1;
531 }
532 prom_printf("prom_get_msgbuf: claiming new buf at %08x\n", (int)addr);
533 { int i; for (i=0; i<200000000; i++); }
534 return addr; /* Kluge till we go 64-bit */
535 }
536
537 #ifdef MULTIPROCESSOR
538 /*
539 * Start secondary cpu identified by node, arrange 'func' as the entry.
540 */
541 void
prom_startcpu(u_int cpu,void * func,u_long arg)542 prom_startcpu(u_int cpu, void *func, u_long arg)
543 {
544 static struct {
545 cell_t name;
546 cell_t nargs;
547 cell_t nreturns;
548 cell_t cpu;
549 cell_t func;
550 cell_t arg;
551 } args;
552
553 args.name = ADR2CELL(&"SUNW,start-cpu");
554 args.nargs = 3;
555 args.nreturns = 0;
556 args.cpu = cpu;
557 args.func = (cell_t)(u_long)func;
558 args.arg = (cell_t)arg;
559
560 openfirmware(&args);
561 }
562
563 /*
564 * Start secondary cpu identified by cpuid, arrange 'func' as the entry.
565 * Returns -1 in case the openfirmware method is not available.
566 * Otherwise the result value from the openfirmware call is returned.
567 */
568 int
prom_startcpu_by_cpuid(u_int cpu,void * func,u_long arg)569 prom_startcpu_by_cpuid(u_int cpu, void *func, u_long arg)
570 {
571 static struct {
572 cell_t name;
573 cell_t nargs;
574 cell_t nreturns;
575 cell_t cpu;
576 cell_t func;
577 cell_t arg;
578 cell_t status;
579 } args;
580
581 if (OF_test("SUNW,start-cpu-by-cpuid") != 0)
582 return -1;
583
584 args.name = ADR2CELL("SUNW,start-cpu-by-cpuid");
585 args.nargs = 3;
586 args.nreturns = 1;
587 args.cpu = cpu;
588 args.func = ADR2CELL(func);
589 args.arg = arg;
590
591 return openfirmware(&args);
592 }
593
594 /*
595 * Stop the calling cpu.
596 */
597 void
prom_stopself(void)598 prom_stopself(void)
599 {
600 extern void openfirmware_exit(void*);
601 static struct {
602 cell_t name;
603 cell_t nargs;
604 cell_t nreturns;
605 } args;
606
607 args.name = ADR2CELL(&"SUNW,stop-self");
608 args.nargs = 0;
609 args.nreturns = 0;
610
611 openfirmware_exit(&args);
612 panic("prom_stopself: failed.");
613 }
614
615 bool
prom_has_stopself(void)616 prom_has_stopself(void)
617 {
618 return OF_test("SUNW,stop-self") == 0;
619 }
620
621 int
prom_stop_other(u_int id)622 prom_stop_other(u_int id)
623 {
624 static struct {
625 cell_t name;
626 cell_t nargs;
627 cell_t nreturns;
628 cell_t cpuid;
629 cell_t result;
630 } args;
631
632 args.name = ADR2CELL(&"SUNW,stop-cpu-by-cpuid");
633 args.nargs = 1;
634 args.nreturns = 1;
635 args.cpuid = id;
636 args.result = 0;
637
638 if (openfirmware(&args) == -1)
639 return -1;
640 return args.result;
641 }
642
643 bool
prom_has_stop_other(void)644 prom_has_stop_other(void)
645 {
646 return OF_test("SUNW,stop-cpu-by-cpuid") == 0;
647 }
648 #endif
649
650 #ifdef DEBUG
651 int ofmapintrdebug = 0;
652 #define DPRINTF(x) if (ofmapintrdebug) printf x
653 #else
654 #define DPRINTF(x)
655 #endif
656
657
658 /*
659 * Recursively hunt for a property.
660 */
661 int
OF_searchprop(int node,const char * prop,void * sbuf,int buflen)662 OF_searchprop(int node, const char *prop, void *sbuf, int buflen)
663 {
664 int len;
665
666 for( ; node; node = OF_parent(node)) {
667 len = OF_getprop(node, prop, sbuf, buflen);
668 if (len >= 0)
669 return (len);
670 }
671 /* Error -- not found */
672 return (-1);
673 }
674
675
676 /*
677 * Compare a sequence of cells with a mask,
678 * return 1 if they match and 0 if they don't.
679 */
680 static int compare_cells (int *cell1, int *cell2, int *mask, int ncells);
681 static int
compare_cells(int * cell1,int * cell2,int * mask,int ncells)682 compare_cells(int *cell1, int *cell2, int *mask, int ncells)
683 {
684 int i;
685
686 for (i=0; i<ncells; i++) {
687 DPRINTF(("src %x ^ dest %x -> %x & mask %x -> %x\n",
688 cell1[i], cell2[i], (cell1[i] ^ cell2[i]),
689 mask[i], ((cell1[i] ^ cell2[i]) & mask[i])));
690 if (((cell1[i] ^ cell2[i]) & mask[i]) != 0)
691 return (0);
692 }
693 return (1);
694 }
695
696 /*
697 * Find top pci bus host controller for a node.
698 */
699 static int
find_pci_host_node(int node)700 find_pci_host_node(int node)
701 {
702 char dev_type[16];
703 int pch = 0;
704 int len;
705
706 for (; node; node = OF_parent(node)) {
707 len = OF_getprop(node, "device_type",
708 &dev_type, sizeof(dev_type));
709 if (len <= 0)
710 continue;
711 if (!strcmp(dev_type, "pci") ||
712 !strcmp(dev_type, "pciex"))
713 pch = node;
714 }
715 return pch;
716 }
717
718 /*
719 * Follow the OFW algorithm and return an interrupt specifier.
720 *
721 * Pass in the interrupt specifier you want mapped and the node
722 * you want it mapped from. validlen is the number of cells in
723 * the interrupt specifier, and buflen is the number of cells in
724 * the buffer.
725 */
726 int
OF_mapintr(int node,int * interrupt,int validlen,int buflen)727 OF_mapintr(int node, int *interrupt, int validlen, int buflen)
728 {
729 int i, len;
730 int address_cells, size_cells, interrupt_cells, interrupt_map_len;
731 int static_interrupt_map[256];
732 int interrupt_map_mask[10];
733 int *interrupt_map = &static_interrupt_map[0];
734 int maplen = sizeof static_interrupt_map;
735 int *free_map = NULL;
736 int reg[10];
737 char dev_type[32];
738 int phc_node;
739 int rc = -1;
740
741 phc_node = find_pci_host_node(node);
742
743 /*
744 * On machines with psycho PCI controllers, we don't need to map
745 * interrupts if they are already fully specified (0x20 to 0x3f
746 * for onboard devices and IGN 0x7c0 for psycho0/psycho1).
747 */
748 if (*interrupt & 0x20 || *interrupt & 0x7c0) {
749 char model[40];
750
751 if (OF_getprop(phc_node, "model", &model, sizeof(model)) > 10
752 && !strcmp(model, "SUNW,psycho")) {
753 DPRINTF(("OF_mapintr: interrupt %x already mapped\n",
754 *interrupt));
755 return validlen;
756 }
757 }
758
759 /*
760 * If there is no interrupt map in the bus node, we
761 * need to convert the slot address to its parent
762 * bus format, and hunt up the parent bus to see if
763 * we need to remap.
764 *
765 * The specification for interrupt mapping is borken.
766 * You are supposed to query the interrupt parent in
767 * the interrupt-map specification to determine the
768 * number of address and interrupt cells, but we need
769 * to know how many address and interrupt cells to skip
770 * to find the phandle...
771 *
772 */
773 if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) {
774 printf("OF_mapintr: no reg property?\n");
775 return (-1);
776 }
777
778 while (node) {
779 #ifdef DEBUG
780 char name[40];
781
782 if (ofmapintrdebug) {
783 OF_getprop(node, "name", &name, sizeof(name));
784 printf("Node %s (%x), host %x\n", name,
785 node, phc_node);
786 }
787 #endif
788
789 retry_map:
790 if ((interrupt_map_len = OF_getprop(node,
791 "interrupt-map", interrupt_map, maplen)) <= 0) {
792
793 /* Swizzle interrupt if this is a PCI bridge. */
794 if (((len = OF_getprop(node, "device_type", &dev_type,
795 sizeof(dev_type))) > 0) &&
796 (!strcmp(dev_type, "pci") ||
797 !strcmp(dev_type, "pciex")) &&
798 (node != phc_node)) {
799 #ifdef DEBUG
800 int ointerrupt = *interrupt;
801 #endif
802
803 *interrupt = ((*interrupt +
804 OFW_PCI_PHYS_HI_DEVICE(reg[0]) - 1) & 3) + 1;
805 DPRINTF(("OF_mapintr: interrupt %x -> %x, reg[0] %x\n",
806 ointerrupt, *interrupt, reg[0]));
807 }
808
809 /* Get reg for next level compare. */
810 reg[0] = 0;
811 OF_getprop(node, "reg", ®, sizeof(reg));
812
813 node = OF_parent(node);
814 continue;
815 }
816 if (interrupt_map_len > maplen) {
817 DPRINTF(("interrupt_map_len %d > maplen %d, "
818 "allocating\n", interrupt_map_len, maplen));
819 KASSERT(!free_map);
820 free_map = malloc(interrupt_map_len, M_DEVBUF,
821 M_NOWAIT);
822 if (!free_map) {
823 interrupt_map_len = sizeof static_interrupt_map;
824 } else {
825 interrupt_map = free_map;
826 maplen = interrupt_map_len;
827 goto retry_map;
828 }
829 }
830 /* Convert from bytes to cells. */
831 interrupt_map_len = interrupt_map_len/sizeof(int);
832 if ((len = (OF_searchprop(node, "#address-cells",
833 &address_cells, sizeof(address_cells)))) <= 0) {
834 /* How should I know. */
835 address_cells = 2;
836 }
837 DPRINTF(("#address-cells = %d len %d ", address_cells, len));
838 if ((len = OF_searchprop(node, "#size-cells", &size_cells,
839 sizeof(size_cells))) <= 0) {
840 /* How should I know. */
841 size_cells = 2;
842 }
843 DPRINTF(("#size-cells = %d len %d ", size_cells, len));
844 if ((len = OF_getprop(node, "#interrupt-cells", &interrupt_cells,
845 sizeof(interrupt_cells))) <= 0) {
846 /* How should I know. */
847 interrupt_cells = 1;
848 }
849 DPRINTF(("#interrupt-cells = %d, len %d\n", interrupt_cells,
850 len));
851 if ((len = OF_getprop(node, "interrupt-map-mask", &interrupt_map_mask,
852 sizeof(interrupt_map_mask))) <= 0) {
853 /* Create a mask that masks nothing. */
854 for (i = 0; i<(address_cells + interrupt_cells); i++)
855 interrupt_map_mask[i] = -1;
856 }
857 #ifdef DEBUG
858 DPRINTF(("interrupt-map-mask len %d = ", len));
859 for (i=0; i<(address_cells + interrupt_cells); i++)
860 DPRINTF(("%x.", interrupt_map_mask[i]));
861 DPRINTF(("reg = "));
862 for (i=0; i<(address_cells); i++)
863 DPRINTF(("%x.", reg[i]));
864 DPRINTF(("interrupts = "));
865 for (i=0; i<(interrupt_cells); i++)
866 DPRINTF(("%x.", interrupt[i]));
867
868 #endif
869
870 /* finally we can attempt the compare */
871 i = 0;
872 while (i < interrupt_map_len + address_cells + interrupt_cells) {
873 int pintr_cells;
874 int *imap = &interrupt_map[i];
875 int *parent = &imap[address_cells + interrupt_cells];
876
877 #ifdef DEBUG
878 DPRINTF(("\ninterrupt-map addr "));
879 for (len = 0; len < address_cells; len++)
880 DPRINTF(("%x.", imap[len]));
881 DPRINTF((" intr "));
882 for (; len < (address_cells+interrupt_cells); len++)
883 DPRINTF(("%x.", imap[len]));
884 DPRINTF(("\nnode %x vs parent %x\n",
885 imap[len], *parent));
886 #endif
887
888 /* Find out how many cells we'll need to skip. */
889 if ((len = OF_searchprop(*parent, "#interrupt-cells",
890 &pintr_cells, sizeof(pintr_cells))) < 0) {
891 pintr_cells = interrupt_cells;
892 }
893 DPRINTF(("pintr_cells = %d len %d\n", pintr_cells, len));
894
895 if (compare_cells(imap, reg,
896 interrupt_map_mask, address_cells) &&
897 compare_cells(&imap[address_cells],
898 interrupt,
899 &interrupt_map_mask[address_cells],
900 interrupt_cells))
901 {
902 /* Bingo! */
903 if (buflen < pintr_cells) {
904 /* Error -- ran out of storage. */
905 if (free_map)
906 free(free_map, M_DEVBUF);
907 return (-1);
908 }
909 node = *parent;
910 parent++;
911 #ifdef DEBUG
912 DPRINTF(("Match! using "));
913 for (len = 0; len < pintr_cells; len++)
914 DPRINTF(("%x.", parent[len]));
915 DPRINTF(("\n"));
916 #endif
917 for (i = 0; i < pintr_cells; i++)
918 interrupt[i] = parent[i];
919 rc = validlen = pintr_cells;
920 if (node == phc_node)
921 return(rc);
922 break;
923 }
924 /* Move on to the next interrupt_map entry. */
925 #ifdef DEBUG
926 DPRINTF(("skip %d cells:",
927 address_cells + interrupt_cells +
928 pintr_cells + 1));
929 for (len = 0; len < (address_cells +
930 interrupt_cells + pintr_cells + 1); len++)
931 DPRINTF(("%x.", imap[len]));
932 #endif
933 i += address_cells + interrupt_cells + pintr_cells + 1;
934 }
935
936 /* Get reg for the next level search. */
937 if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) {
938 DPRINTF(("OF_mapintr: no reg property?\n"));
939 } else {
940 DPRINTF(("reg len %d\n", len));
941 }
942
943 if (free_map) {
944 free(free_map, M_DEVBUF);
945 free_map = NULL;
946 }
947 node = OF_parent(node);
948 }
949 return (rc);
950 }
951