patch-2.3.43 linux/arch/ppc/kernel/prom.c

Next file: linux/arch/ppc/kernel/semaphore.c
Previous file: linux/arch/ppc/kernel/process.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.42/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
@@ -30,6 +30,10 @@
 #include <asm/system.h>
 #include <asm/gemini.h>
 
+#ifdef CONFIG_FB
+#include <asm/linux_logo.h>
+#endif
+
 /*
  * Properties whose value is longer than this get excluded from our
  * copy of the device tree.  This way we don't waste space storing
@@ -91,7 +95,7 @@
 char *of_stdout_device = 0;
 
 prom_entry prom = 0;
-ihandle prom_chosen = 0, prom_stdout = 0;
+ihandle prom_chosen = 0, prom_stdout = 0, prom_disp_node = 0;
 
 extern char *klimit;
 char *bootpath = 0;
@@ -102,33 +106,35 @@
 unsigned int rtas_size = 0;
 unsigned int old_rtas = 0;
 
+int use_of_interrupt_tree = 0;
 static struct device_node *allnodes = 0;
 
+#ifdef CONFIG_BOOTX_TEXT
+
 static void clearscreen(void);
 static void flushscreen(void);
 
-#ifdef CONFIG_BOOTX_TEXT
-
 void drawchar(char c);
 void drawstring(const char *c);
 static void drawhex(unsigned long v);
 static void scrollscreen(void);
 
 static void draw_byte(unsigned char c, long locX, long locY);
-static void draw_byte_32(unsigned char *bits, unsigned long *base);
-static void draw_byte_16(unsigned char *bits, unsigned long *base);
-static void draw_byte_8(unsigned char *bits, unsigned long *base);
-
-static long				g_loc_X;
-static long				g_loc_Y;
-static long				g_max_loc_X;
-static long				g_max_loc_Y;
+static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb);
+static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb);
+static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb);
+
+/* We want those in data, not BSS */
+static long				g_loc_X = 0;
+static long				g_loc_Y = 0;
+static long				g_max_loc_X = 0;
+static long				g_max_loc_Y = 0;
 
 #define cmapsz	(16*256)
 
 static unsigned char vga_font[cmapsz];
 
-#endif
+#endif /* CONFIG_BOOTX_TEXT */
 
 
 static void *call_prom(const char *service, int nargs, int nret, ...);
@@ -138,15 +144,25 @@
 				  unsigned long, struct device_node ***);
 static unsigned long finish_node(struct device_node *, unsigned long,
 				 interpret_func *);
+static unsigned long finish_node_interrupts(struct device_node *, unsigned long);
 static unsigned long check_display(unsigned long);
 static int prom_next_node(phandle *);
 static void *early_get_property(unsigned long, unsigned long, char *);
 
+#ifdef CONFIG_BOOTX_TEXT
+static void setup_disp_fake_bi(ihandle dp);
+static void prom_welcome(boot_infos_t* bi, unsigned long phys);
+#endif
+
 extern void enter_rtas(void *);
 extern unsigned long reloc_offset(void);
 
 extern char cmd_line[512];	/* XXX */
 boot_infos_t *boot_infos = 0;	/* init it so it's in data segment not bss */
+#ifdef CONFIG_BOOTX_TEXT
+boot_infos_t *disp_bi = 0;
+boot_infos_t fake_bi = {0,};
+#endif
 unsigned long dev_tree_size;
 
 /*
@@ -240,7 +256,7 @@
 	if (RELOC(prom_stdout) == 0)
 	{
 #ifdef CONFIG_BOOTX_TEXT
-		if (RELOC(boot_infos) != 0)
+		if (RELOC(disp_bi) != 0)
 			drawstring(msg);
 #endif
 		return;
@@ -261,7 +277,6 @@
 	}
 }
 
-unsigned long smp_ibm_chrp_hack __initdata = 0;
 unsigned long smp_chrp_cpu_nr __initdata = 1;
 
 /*
@@ -269,23 +284,29 @@
  * handling exceptions and the MMU hash table for us.
  */
 __init
-void
+unsigned long
 prom_init(int r3, int r4, prom_entry pp)
 {
 #ifdef CONFIG_SMP	
 	int i;
 	phandle node;
 	char type[16], *path;
-#endif	
+#endif
+	int chrp = 0;
 	unsigned long mem;
-	ihandle prom_rtas;
+	ihandle prom_rtas, prom_mmu, prom_op;
 	unsigned long offset = reloc_offset();
 	int l;
 	char *p, *d;
+	int prom_version = 0;
+ 	unsigned long phys;
+
+ 	/* Default */
+ 	phys = offset + KERNELBASE;
 
 	/* check if we're apus, return if we are */
 	if ( r3 == 0x61707573 )
-		return;
+		return phys;
 
 	/* If we came here from BootX, clear the screen,
 	 * set up some pointers and return. */
@@ -294,22 +315,20 @@
 		unsigned long space;
 		unsigned long ptr, x;
 		char *model;
-#ifdef CONFIG_BOOTX_TEXT
-		unsigned long flags;
-#endif		
 
 		RELOC(boot_infos) = PTRUNRELOC(bi);
 		if (!BOOT_INFO_IS_V2_COMPATIBLE(bi))
 			bi->logicalDisplayBase = 0;
 
-		clearscreen();
-
 #ifdef CONFIG_BOOTX_TEXT
 		RELOC(g_loc_X) = 0;
 		RELOC(g_loc_Y) = 0;
 		RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8;
 		RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16;
+		RELOC(disp_bi) = PTRUNRELOC(bi);
 		
+		clearscreen();
+
 		/* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since
 		   there is nothing much we can do with an incompatible version, except display
 		   a message and eventually hang the processor...
@@ -320,23 +339,9 @@
 		if (!BOOT_INFO_IS_COMPATIBLE(bi))
 			prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"));
 		
-		prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n"));
-		prom_print(RELOC("\nstarted at       : 0x"));
-		drawhex(reloc_offset() + KERNELBASE);
-		prom_print(RELOC("\nlinked at        : 0x"));
-		drawhex(KERNELBASE);
-		prom_print(RELOC("\nframe buffer at  : 0x"));
-		drawhex((unsigned long)bi->dispDeviceBase);
-		prom_print(RELOC(" (phys), 0x"));
-		drawhex((unsigned long)bi->logicalDisplayBase);
-		prom_print(RELOC(" (log)"));
-		prom_print(RELOC("\nMSR              : 0x"));
-		__asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory");
-		drawhex(flags);
-		prom_print(RELOC("\n\n"));
-#endif
-		/* Out of the #if/#endif since it flushes the clearscreen too */
+		prom_welcome(bi, phys);
 		flushscreen();
+#endif	/* CONFIG_BOOTX_TEXT */	
 		
 		/* New BootX enters kernel with MMU off, i/os are not allowed
 		   here. This hack will have been done by the boostrap anyway.
@@ -381,12 +386,12 @@
 		prom_print(RELOC("booting...\n"));
 		flushscreen();
 #endif
-		return;
+		return phys;
 	}
 
 	/* check if we're prep, return if we are */
 	if ( *(unsigned long *)(0) == 0xdeadc0de )
-		return;
+		return phys;
 
 	/* First get a handle for the stdout device */
 	RELOC(prom) = pp;
@@ -407,6 +412,30 @@
 	RELOC(of_stdout_device) = PTRUNRELOC(p);
 	mem += strlen(p) + 1;
 
+	/* Find the OF version */
+	prom_op = call_prom(RELOC("finddevice"), 1, 1, RELOC("/openprom"));
+	prom_version = 0;
+	if (prom_op != (void*)-1) {
+		char model[64];
+		int sz;
+		sz = (int)call_prom(RELOC("getprop"), 4, 1, prom_op, RELOC("model"), model, 64);
+		if (sz > 0) {
+			char *c;
+			/* hack to skip the ibm chrp firmware # */
+			if ( strncmp(model,RELOC("IBM"),3) ) {
+				for (c = model; *c; c++)
+					if (*c >= '0' && *c <= '9') {
+						prom_version = *c - '0';
+						break;
+					}
+			}
+			else
+				chrp = 1;
+		}
+	}
+	if (prom_version >= 3)
+		prom_print(RELOC("OF Version 3 detected.\n"));
+
 	/* Get the boot device and translate it to a full OF pathname. */
 	p = (char *) mem;
 	l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
@@ -478,6 +507,42 @@
 			prom_print(RELOC(" done\n"));
 	}
 
+	/* If we are already running at 0xc0000000, we assume we were loaded by
+	 * an OF bootloader which did set a BAT for us. This breaks OF translate
+	 * so we force phys to be 0
+	 */
+	if (offset == 0)
+		phys = 0;
+	else {
+ 	    if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
+			    RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) {	
+		prom_print(RELOC(" no MMU found\n"));
+	    } else {
+		int nargs;
+		struct prom_args prom_args;
+		nargs = 4;
+		prom_args.service = RELOC("call-method");
+		prom_args.nargs = nargs;
+		prom_args.nret = 4;
+		prom_args.args[0] = RELOC("translate");
+		prom_args.args[1] = prom_mmu;
+		prom_args.args[2] = (void *)(offset + KERNELBASE);
+		prom_args.args[3] = (void *)1;
+		RELOC(prom)(&prom_args);
+
+		/* We assume the phys. address size is 3 cells */
+		if (prom_args.args[nargs] != 0)
+			prom_print(RELOC(" (translate failed) "));
+		else
+			phys = (unsigned long)prom_args.args[nargs+3];
+	    }
+	}
+
+#ifdef CONFIG_BOOTX_TEXT
+	if (!chrp && RELOC(prom_disp_node) != 0)
+		setup_disp_fake_bi(RELOC(prom_disp_node));
+#endif
+
 #ifdef CONFIG_SMP
 	/*
 	 * With CHRP SMP we need to use the OF to start the other
@@ -512,7 +577,7 @@
 		node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/"));
 		if ( (int)call_prom(RELOC("getprop"), 4, 1, node,
 			    RELOC("device_type"),type, sizeof(type)) <= 0)
-			return;
+			return phys;
 		
 		/* copy the holding pattern code to someplace safe (8M) */
 		memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x100 );
@@ -554,6 +619,79 @@
 			prom_print(RELOC("...failed\n"));
 	}
 #endif
+	/* If OpenFirmware version >= 3, then use quiesce call */
+	if (prom_version >= 3) {
+		prom_print(RELOC("Calling quiesce ...\n"));
+		call_prom(RELOC("quiesce"), 0, 0);
+		offset = reloc_offset();
+		phys = offset + KERNELBASE;
+	}
+
+#ifdef CONFIG_BOOTX_TEXT
+	if (!chrp && RELOC(disp_bi)) {
+		RELOC(prom_stdout) = 0;
+		clearscreen();
+		prom_welcome(PTRRELOC(RELOC(disp_bi)), phys);
+		prom_print(RELOC("booting...\n"));
+	}
+#endif
+
+	return phys;
+}
+
+#ifdef CONFIG_BOOTX_TEXT
+__init static void
+prom_welcome(boot_infos_t* bi, unsigned long phys)
+{
+	unsigned long offset = reloc_offset();
+	unsigned long flags;
+	unsigned long pvr;
+	
+	prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n"));
+	prom_print(RELOC("\nstarted at       : 0x"));
+	drawhex(phys);
+	prom_print(RELOC("\nlinked at        : 0x"));
+	drawhex(KERNELBASE);
+	prom_print(RELOC("\nframe buffer at  : 0x"));
+	drawhex((unsigned long)bi->dispDeviceBase);
+	prom_print(RELOC(" (phys), 0x"));
+	drawhex((unsigned long)bi->logicalDisplayBase);
+	prom_print(RELOC(" (log)"));
+	prom_print(RELOC("\nMSR              : 0x"));
+	__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
+	drawhex(flags);
+	__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
+	pvr >>= 16;
+	if (pvr > 1) {
+	    prom_print(RELOC("\nHID0             : 0x"));
+	    __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
+	    drawhex(flags);
+	}
+	if (pvr == 8 || pvr == 12) {
+	    prom_print(RELOC("\nICTC             : 0x"));
+	    __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
+	    drawhex(flags);
+	}
+	prom_print(RELOC("\n\n"));
+}
+#endif
+
+static int prom_set_color(ihandle ih, int i, int r, int g, int b)
+{
+	struct prom_args prom_args;
+	unsigned long offset = reloc_offset();
+
+	prom_args.service = RELOC("call-method");
+	prom_args.nargs = 6;
+	prom_args.nret = 1;
+	prom_args.args[0] = RELOC("color!");
+	prom_args.args[1] = ih;
+	prom_args.args[2] = (void *) i;
+	prom_args.args[3] = (void *) b;
+	prom_args.args[4] = (void *) g;
+	prom_args.args[5] = (void *) r;
+	RELOC(prom)(&prom_args);
+	return (int) prom_args.args[6];
 }
 
 /*
@@ -573,6 +711,26 @@
 	int i;
 	unsigned long offset = reloc_offset();
 	char type[16], *path;
+	static unsigned char default_colors[] = {
+		0x00, 0x00, 0x00,
+		0x00, 0x00, 0xaa,
+		0x00, 0xaa, 0x00,
+		0x00, 0xaa, 0xaa,
+		0xaa, 0x00, 0x00,
+		0xaa, 0x00, 0xaa,
+		0xaa, 0xaa, 0x00,
+		0xaa, 0xaa, 0xaa,
+		0x55, 0x55, 0x55,
+		0x55, 0x55, 0xff,
+		0x55, 0xff, 0x55,
+		0x55, 0xff, 0xff,
+		0xff, 0x55, 0x55,
+		0xff, 0x55, 0xff,
+		0xff, 0xff, 0x55,
+		0xff, 0xff, 0xff
+	};
+
+	RELOC(prom_disp_node) = 0;
 
 	for (node = 0; prom_next_node(&node); ) {
 		type[0] = 0;
@@ -595,6 +753,26 @@
 		}
 		prom_print(RELOC("... ok\n"));
 
+		if (RELOC(prom_disp_node) == 0)
+			RELOC(prom_disp_node) = node;
+			
+		/* Setup a useable color table when the appropriate
+		 * method is available. Should update this to set-colors */
+		for (i = 0; i < 32; i++)
+			if (prom_set_color(ih, i, RELOC(default_colors)[i*3],
+					   RELOC(default_colors)[i*3+1],
+					   RELOC(default_colors)[i*3+2]) != 0)
+				break;
+
+#ifdef CONFIG_FB
+		for (i = 0; i < LINUX_LOGO_COLORS; i++)
+			if (prom_set_color(ih, i + 32,
+					   RELOC(linux_logo_red)[i],
+					   RELOC(linux_logo_green)[i],
+					   RELOC(linux_logo_blue)[i]) != 0)
+				break;
+#endif /* CONFIG_FB */
+
 		/*
 		 * If this display is the device that OF is using for stdout,
 		 * move it to the front of the list.
@@ -614,6 +792,79 @@
 	return ALIGN(mem);
 }
 
+/* This function will enable the early boot text when doing OF booting. This
+ * way, xmon output should work too
+ */
+#ifdef CONFIG_BOOTX_TEXT
+__init
+static void
+setup_disp_fake_bi(ihandle dp)
+{
+	unsigned int len;
+	int width = 640, height = 480, depth = 8, pitch;
+	unsigned  address;
+	boot_infos_t* bi;
+	unsigned long offset = reloc_offset();
+	
+	prom_print(RELOC("Initing fake screen\n"));
+
+	len = 0;
+	call_prom(RELOC("getprop"), 4, 1, dp, RELOC("depth"), &len, sizeof(len));
+	if (len == 0)
+		prom_print(RELOC("Warning: assuming display depth = 8\n"));
+	else
+		depth = len;
+	width = len = 0;
+	call_prom(RELOC("getprop"), 4, 1, dp, RELOC("width"), &len, sizeof(len));
+	width = len;
+	if (width == 0) {
+		prom_print(RELOC("Failed to get width\n"));
+		return;
+	}
+	height = len = 0;
+	call_prom(RELOC("getprop"), 4, 1, dp, RELOC("height"), &len, sizeof(len));
+	height = len;
+	if (height == 0) {
+		prom_print(RELOC("Failed to get height\n"));
+		return;
+	}
+	pitch = len = 0;
+	call_prom(RELOC("getprop"), 4, 1, dp, RELOC("linebytes"), &len, sizeof(len));
+	pitch = len;
+	if (pitch == 0) {
+		prom_print(RELOC("Failed to get pitch\n"));
+		return;
+	}
+	address = len = 0;
+	call_prom(RELOC("getprop"), 4, 1, dp, RELOC("address"), &len, sizeof(len));
+	address = len;
+	if (address == 0) {
+		prom_print(RELOC("Failed to get address\n"));
+		return;
+	}
+#if 0
+	/* kludge for valkyrie */
+	if (strcmp(dp->name, "valkyrie") == 0) 
+	    address += 0x1000;
+    }
+#endif
+ 
+    RELOC(disp_bi) = &fake_bi;
+    bi = PTRRELOC((&fake_bi));
+    RELOC(g_loc_X) = 0;
+    RELOC(g_loc_Y) = 0;
+    RELOC(g_max_loc_X) = width / 8;
+    RELOC(g_max_loc_Y) = height / 16;
+    bi->logicalDisplayBase = (unsigned char *)address;
+    bi->dispDeviceBase = (unsigned char *)address;
+    bi->dispDeviceRowBytes = pitch;
+    bi->dispDeviceDepth = depth;
+    bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
+    bi->dispDeviceRect[2] = width;
+    bi->dispDeviceRect[3] = height;
+}
+#endif
+
 __init
 static int
 prom_next_node(phandle *nodep)
@@ -748,6 +999,16 @@
 finish_device_tree(void)
 {
 	unsigned long mem = (unsigned long) klimit;
+	char* model;
+	
+	/* Here, we decide if we'll use the interrupt-tree (new Core99 code) or not.
+	 * This code was only tested with Core99 machines so far, but should be easily
+	 * adapted to older newworld machines (iMac, B&W G3, Lombard).
+	 */
+	model = get_property(allnodes, "model", 0);
+	if ((boot_infos == 0) && model && (strcmp(model, "PowerBook2,1") == 0
+	    || strcmp(model, "PowerMac2,1") == 0 || strcmp(model, "PowerMac3,1") == 0))
+		use_of_interrupt_tree = 1;
 
 	mem = finish_node(allnodes, mem, NULL);
 	dev_tree_size = mem - (unsigned long) allnodes;
@@ -788,6 +1049,9 @@
 	if (ifunc != NULL) {
 		mem_start = ifunc(np, mem_start);
 	}
+	if (use_of_interrupt_tree) {
+		mem_start = finish_node_interrupts(np, mem_start);
+	}
 
 	/* the f50 sets the name to 'display' and 'compatible' to what we
 	 * expect for the name -- Cort
@@ -834,6 +1098,133 @@
 	return mem_start;
 }
 
+/* This routine walks the interrupt tree for a given device node and gather 
+ * all necessary informations according to the draft interrupt mapping
+ * for CHRP. The current version was only tested on Apple "Core99" machines
+ * and may not handle cascaded controllers correctly.
+ */
+__init
+static unsigned long
+finish_node_interrupts(struct device_node *np, unsigned long mem_start)
+{
+	/* Finish this node */
+	unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg;
+	phandle *parent;
+	struct device_node *node, *parent_node;
+	int l, isize, ipsize, asize, map_size, regpsize;
+
+	/* Currently, we don't look at all nodes with no "interrupts" property */
+	interrupts = (unsigned int *)get_property(np, "interrupts", &l);
+	if (interrupts == NULL)
+		return mem_start;
+	ipsize = l>>2;
+
+	reg = (unsigned int *)get_property(np, "reg", &l);
+	regpsize = l>>2;
+
+	/* We assume default interrupt cell size is 1 (bugus ?) */
+	isize = 1;
+	node = np;
+	
+	do {
+	    /* We adjust the cell size if the current parent contains an #interrupt-cells
+	     * property */
+	    isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l);
+	    if (isizep)
+	    	isize = *isizep;
+
+	    /* We don't do interrupt cascade (ISA) for now, we stop on the first 
+	     * controller found
+	     */
+	    if (get_property(node, "interrupt-controller", &l)) {
+	    	int i,j;
+	    	np->intrs = (struct interrupt_info *) mem_start;
+		np->n_intrs = ipsize / isize;
+		mem_start += np->n_intrs * sizeof(struct interrupt_info);
+		for (i = 0; i < np->n_intrs; ++i) {
+		    np->intrs[i].line = *interrupts++;
+		    np->intrs[i].sense = 0;
+		    if (isize > 1)
+		        np->intrs[i].sense = *interrupts++;
+		    for (j=2; j<isize; j++)
+		    	interrupts++;
+		}
+		return mem_start;
+	    }
+	    /* We lookup for an interrupt-map. This code can only handle one interrupt
+	     * per device in the map. We also don't handle #address-cells in the parent
+	     * I skip the pci node itself here, may not be necessary but I don't like it's
+	     * reg property.
+	     */
+	    if (np != node)
+	        map = (unsigned int *)get_property(node, "interrupt-map", &l);
+	     else
+	     	map = NULL;
+	    if (map && l) {
+	    	int i, found, temp_isize;
+	        map_size = l>>2;
+	        map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l);
+	        asizep = (unsigned int *)get_property(node, "#address-cells", &l);
+	        if (asizep && l == sizeof(unsigned int))
+	            asize = *asizep;
+	        else
+	            asize = 0;
+	        found = 0;
+	        while(map_size>0 && !found) {
+	            found = 1;
+	            for (i=0; i<asize; i++) {
+	            	unsigned int mask = map_mask ? map_mask[i] : 0xffffffff;
+	            	if (!reg || (i>=regpsize) || ((mask & *map) != (mask & reg[i])))
+	           	    found = 0;
+	           	map++;
+	           	map_size--;
+	            }
+	            for (i=0; i<isize; i++) {
+	            	unsigned int mask = map_mask ? map_mask[i+asize] : 0xffffffff;
+	            	if ((mask & *map) != (mask & interrupts[i]))
+	            	    found = 0;
+	            	map++;
+	            	map_size--;
+	            }
+	            parent = *((phandle *)(map));
+	            map+=1; map_size-=1;
+	            parent_node = find_phandle(parent);
+	            temp_isize = isize;
+	            if (parent_node) {
+			isizep = (unsigned int *)get_property(parent_node, "#interrupt-cells", &l);
+	    		if (isizep)
+	    		    temp_isize = *isizep;
+	            }
+	            if (!found) {
+	            	map += temp_isize;
+	            	map_size-=temp_isize;
+	            }
+	        }
+	        if (found) {
+	            node = parent_node;
+	            reg = NULL;
+	            regpsize = 0;
+	            interrupts = (unsigned int *)map;
+	            ipsize = temp_isize*1;
+		    continue;
+	        }
+	    }
+	    /* We look for an explicit interrupt-parent.
+	     */
+	    parent = (phandle *)get_property(node, "interrupt-parent", &l);
+	    if (parent && (l == sizeof(phandle)) &&
+	    	(parent_node = find_phandle(*parent))) {
+	    	node = parent_node;
+	    	continue;
+	    }
+	    /* Default, get real parent */
+	    node = node->parent;
+	} while(node);
+
+	return mem_start;
+}
+
+
 /*
  * When BootX makes a copy of the device tree from the MacOS
  * Name Registry, it is in the format we use but all of the pointers
@@ -892,6 +1283,9 @@
 		mem_start += i * sizeof(struct address_range);
 	}
 
+	if (use_of_interrupt_tree)
+		return mem_start;
+
 	/*
 	 * If the pci host bridge has an interrupt-map property,
 	 * look for our node in it.
@@ -901,14 +1295,28 @@
 		get_property(np->parent, "interrupt-map", &ml)) != 0
 	    && (ip = (int *) get_property(np, "interrupts", &l)) != 0) {
 		unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00;
+		unsigned int cell_size;
+		struct device_node* np2;
+		/* This is hackish, but is only used for BootX booting */
+		cell_size = sizeof(struct pci_intr_map);
+		np2 = np->parent;
+		while(np2) {
+			if (device_is_compatible(np2, "uni-north")) {
+				cell_size += 4;
+				break;
+			}
+			np2 = np2->parent;
+		}
 		np->n_intrs = 0;
 		np->intrs = (struct interrupt_info *) mem_start;
-		for (i = 0; (ml -= sizeof(struct pci_intr_map)) >= 0; ++i) {
-			if (imp[i].addr.a_hi == devfn) {
-				np->intrs[np->n_intrs].line = imp[i].intr;
-				np->intrs[np->n_intrs].sense = 0;
+		for (i = 0; (ml -= cell_size) >= 0; ++i) {
+			if (imp->addr.a_hi == devfn) {
+				np->intrs[np->n_intrs].line = imp->intr;
+				np->intrs[np->n_intrs].sense = 0; /* FIXME */
 				++np->n_intrs;
 			}
+			imp = (struct pci_intr_map *)(((unsigned int)imp)
+				+ cell_size);
 		}
 		if (np->n_intrs == 0)
 			np->intrs = 0;
@@ -965,6 +1373,9 @@
 		mem_start += i * sizeof(struct address_range);
 	}
 
+	if (use_of_interrupt_tree)
+		return mem_start;
+
 	ip = (int *) get_property(np, "AAPL,interrupts", &l);
 	if (ip == 0)
 		ip = (int *) get_property(np, "interrupts", &l);
@@ -988,13 +1399,14 @@
 	struct reg_property *rp;
 	struct address_range *adr;
 	unsigned long base_address;
-	int i, l, *ip;
+	int i, l, keylargo, *ip;
 	struct device_node *db;
 
 	base_address = 0;
 	for (db = np->parent; db != NULL; db = db->parent) {
 		if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
 			base_address = db->addrs[0].address;
+			keylargo = device_is_compatible(db, "Keylargo");
 			break;
 		}
 	}
@@ -1014,6 +1426,9 @@
 		mem_start += i * sizeof(struct address_range);
 	}
 
+	if (use_of_interrupt_tree)
+		return mem_start;
+
 	ip = (int *) get_property(np, "interrupts", &l);
 	if (ip == 0)
 		ip = (int *) get_property(np, "AAPL,interrupts", &l);
@@ -1022,9 +1437,15 @@
 		if (_machine == _MACH_Pmac) {
 			/* for the iMac */
 			np->n_intrs = l / sizeof(int);
+			/* Hack for BootX on Core99 */
+			if (keylargo)
+				np->n_intrs = np->n_intrs/2;
 			for (i = 0; i < np->n_intrs; ++i) {
 				np->intrs[i].line = *ip++;
-				np->intrs[i].sense = 0;
+				if (keylargo)
+					np->intrs[i].sense = *ip++;
+				else
+					np->intrs[i].sense = 0;
 			}
 		} else {
 			/* CHRP machines */
@@ -1064,6 +1485,9 @@
 		mem_start += i * sizeof(struct address_range);
 	}
 
+	if (use_of_interrupt_tree)
+		return mem_start;
+ 
 	ip = (int *) get_property(np, "interrupts", &l);
 	if (ip != 0) {
 		np->intrs = (struct interrupt_info *) mem_start;
@@ -1101,6 +1525,9 @@
 		mem_start += i * sizeof(struct address_range);
 	}
 
+	if (use_of_interrupt_tree)
+		return mem_start;
+
 	ip = (int *) get_property(np, "AAPL,interrupts", &l);
 	if (ip == 0)
 		ip = (int *) get_property(np, "interrupts", &l);
@@ -1157,6 +1584,49 @@
 	return head;
 }
 
+/* Finds a device node given its PCI bus number, device number
+ * and function number
+ */
+__openfirmware
+struct device_node *
+find_pci_device_OFnode(unsigned char bus, unsigned char dev_fn)
+{
+	struct device_node* np;
+	unsigned int *reg;
+	int l;
+	
+	for (np = allnodes; np != 0; np = np->allnext) {
+		char *pname = np->parent ?
+			(char *)get_property(np->parent, "name", &l) : 0;
+		if (pname && strcmp(pname, "mac-io") == 0)
+			continue;
+		reg = (unsigned int *) get_property(np, "reg", &l);
+		if (reg == 0 || l < sizeof(struct reg_property))
+			continue;
+		if (((reg[0] >> 8) & 0xff) == dev_fn && ((reg[0] >> 16) & 0xff) == bus)
+			break;
+	}
+	return np;
+}
+
+/*
+ * Returns all nodes linked together
+ */
+__openfirmware
+struct device_node *
+find_all_nodes(void)
+{
+	struct device_node *head, **prevp, *np;
+
+	prevp = &head;
+	for (np = allnodes; np != 0; np = np->allnext) {
+		*prevp = np;
+		prevp = &np->next;
+	}
+	*prevp = 0;
+	return head;
+}
+
 /* Checks if the given "compat" string matches one of the strings in
  * the device's "compatible" property
  */
@@ -1377,18 +1847,28 @@
 	prom_exit();
 }
 
-#ifdef CONFIG_XMON
+#ifdef CONFIG_BOOTX_TEXT
+
+/* Here's a small text engine to use during early boot or for debugging purposes
+ * 
+ * todo:
+ * 
+ *  - build some kind of vgacon with it to enable early printk
+ *  - move to a separate file
+ *  - add a few video driver hooks to keep in sync with display
+ *    changes.
+ */
+
 __init
 void
 map_bootx_text(void)
 {
-	if (boot_infos == 0)
+	if (disp_bi == 0)
 		return;
-	boot_infos->logicalDisplayBase =
-		ioremap((unsigned long) boot_infos->dispDeviceBase,
-			boot_infos->dispDeviceRowBytes * boot_infos->dispDeviceRect[3]);
+	disp_bi->logicalDisplayBase =
+		ioremap((unsigned long) disp_bi->dispDeviceBase,
+			disp_bi->dispDeviceRowBytes * disp_bi->dispDeviceRect[3]);
 }
-#endif /* CONFIG_XMON */
 
 /* Calc the base address of a given point (x,y) */
 __pmac
@@ -1410,7 +1890,7 @@
 clearscreen(void)
 {
 	unsigned long offset	= reloc_offset();
-	boot_infos_t* bi	= PTRRELOC(RELOC(boot_infos));
+	boot_infos_t* bi	= PTRRELOC(RELOC(disp_bi));
 	unsigned long *base	= (unsigned long *)calc_base(bi, 0, 0);
 	unsigned long width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
 					(bi->dispDeviceDepth >> 3)) >> 2;
@@ -1435,7 +1915,7 @@
 flushscreen(void)
 {
 	unsigned long offset	= reloc_offset();
-	boot_infos_t* bi	= PTRRELOC(RELOC(boot_infos));
+	boot_infos_t* bi	= PTRRELOC(RELOC(disp_bi));
 	unsigned long *base	= (unsigned long *)calc_base(bi, 0, 0);
 	unsigned long width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
 					(bi->dispDeviceDepth >> 3)) >> 2;
@@ -1452,14 +1932,12 @@
 	}
 }
 
-#ifdef CONFIG_BOOTX_TEXT
-
 __pmac
 static void
 scrollscreen(void)
 {
 	unsigned long offset		= reloc_offset();
-	boot_infos_t* bi		= PTRRELOC(RELOC(boot_infos));
+	boot_infos_t* bi		= PTRRELOC(RELOC(disp_bi));
 	unsigned long *src		= (unsigned long *)calc_base(bi,0,16);
 	unsigned long *dst		= (unsigned long *)calc_base(bi,0,0);
 	unsigned long width		= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
@@ -1563,19 +2041,20 @@
 draw_byte(unsigned char c, long locX, long locY)
 {
 	unsigned long offset	= reloc_offset();
-	boot_infos_t* bi	= PTRRELOC(RELOC(boot_infos));
+	boot_infos_t* bi	= PTRRELOC(RELOC(disp_bi));
 	unsigned char *base	= calc_base(bi, locX << 3, locY << 4);
 	unsigned char *font	= &RELOC(vga_font)[((unsigned long)c) * 16];
+	int rb			= bi->dispDeviceRowBytes;
 	
 	switch(bi->dispDeviceDepth) {
 		case 32:
-			draw_byte_32(font, (unsigned long *)base);
+			draw_byte_32(font, (unsigned long *)base, rb);
 			break;
 		case 16:
-			draw_byte_16(font, (unsigned long *)base);
+			draw_byte_16(font, (unsigned long *)base, rb);
 			break;
 		case 8:
-			draw_byte_8(font, (unsigned long *)base);
+			draw_byte_8(font, (unsigned long *)base, rb);
 			break;
 		default:
 			break;
@@ -1613,15 +2092,12 @@
 
 __pmac
 static void
-draw_byte_32(unsigned char *font, unsigned long *base)
+draw_byte_32(unsigned char *font, unsigned long *base, int rb)
 {
-	unsigned long offset = reloc_offset();
-	boot_infos_t* bi		= PTRRELOC(RELOC(boot_infos));
 	int l, bits;	
 	int fg = 0xFFFFFFFFUL;
 	int bg = 0x00000000UL;
 	
-
 	for (l = 0; l < 16; ++l)
 	{
 		bits = *font++;
@@ -1633,19 +2109,18 @@
 		base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
 		base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
 		base[7] = (-(bits & 1) & fg) ^ bg;
-		base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes);
+		base = (unsigned long *) ((char *)base + rb);
 	}
 }
 
 __pmac
 static void
-draw_byte_16(unsigned char *font, unsigned long *base)
+draw_byte_16(unsigned char *font, unsigned long *base, int rb)
 {
-	unsigned long offset = reloc_offset();
-	boot_infos_t* bi		= PTRRELOC(RELOC(boot_infos));
 	int l, bits;	
 	int fg = 0xFFFFFFFFUL;
 	int bg = 0x00000000UL;
+	unsigned long offset = reloc_offset();
 	unsigned long *eb = RELOC(expand_bits_16);
 
 	for (l = 0; l < 16; ++l)
@@ -1655,19 +2130,18 @@
 		base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
 		base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
 		base[3] = (eb[bits & 3] & fg) ^ bg;
-		base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes);
+		base = (unsigned long *) ((char *)base + rb);
 	}
 }
 
 __pmac
 static void
-draw_byte_8(unsigned char *font, unsigned long *base)
+draw_byte_8(unsigned char *font, unsigned long *base, int rb)
 {
-	unsigned long offset = reloc_offset();
-	boot_infos_t* bi		= PTRRELOC(RELOC(boot_infos));
 	int l, bits;	
 	int fg = 0x0F0F0F0FUL;
 	int bg = 0x00000000UL;
+	unsigned long offset = reloc_offset();
 	unsigned long *eb = RELOC(expand_bits_8);
 
 	for (l = 0; l < 16; ++l)
@@ -1675,7 +2149,7 @@
 		bits = *font++;
 		base[0] = (eb[bits >> 4] & fg) ^ bg;
 		base[1] = (eb[bits & 0xf] & fg) ^ bg;
-		base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes);
+		base = (unsigned long *) ((char *)base + rb);
 	}
 }
 
@@ -2026,3 +2500,4 @@
 };
 
 #endif /* CONFIG_BOOTX_TEXT */
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)