summaryrefslogtreecommitdiffstats
path: root/arch/ppc/kernel/prom.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/kernel/prom.c')
-rw-r--r--arch/ppc/kernel/prom.c263
1 files changed, 223 insertions, 40 deletions
diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c
index da9d7a04c..b2221481a 100644
--- a/arch/ppc/kernel/prom.c
+++ b/arch/ppc/kernel/prom.c
@@ -1,5 +1,5 @@
/*
- * $Id: prom.c,v 1.46 1998/11/11 03:55:09 paulus Exp $
+ * $Id: prom.c,v 1.54 1999/05/10 04:43:46 cort Exp $
*
* Procedures for interfacing to the Open Firmware PROM on
* Power Macintosh computers.
@@ -16,6 +16,7 @@
#include <linux/string.h>
#include <linux/init.h>
#include <linux/version.h>
+#include <asm/spinlock.h>
#include <asm/prom.h>
#include <asm/page.h>
#include <asm/processor.h>
@@ -99,10 +100,12 @@ unsigned int old_rtas = 0;
static struct device_node *allnodes = 0;
static void clearscreen(void);
+static void flushscreen(void);
#ifdef CONFIG_BOOTX_TEXT
static void drawchar(char c);
+static void drawhex(unsigned long v);
static void drawstring(const char *c);
static void scrollscreen(void);
@@ -167,6 +170,11 @@ boot_infos_t *boot_infos = 0; /* init it so it's in data segment not bss */
#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
+/* Is boot-info compatible ? */
+#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION)
+#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2)
+#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4)
+
__init
static void
prom_exit()
@@ -256,6 +264,11 @@ __init
void
prom_init(int r3, int r4, prom_entry pp)
{
+#ifdef CONFIG_SMP
+ int cpu = 0, i;
+ phandle node;
+ char type[16], *path;
+#endif
unsigned long mem;
ihandle prom_rtas;
unsigned long offset = reloc_offset();
@@ -273,6 +286,9 @@ prom_init(int r3, int r4, prom_entry pp)
unsigned long space;
unsigned long ptr, x;
char *model;
+#ifdef CONFIG_BOOTX_TEXT
+ unsigned long flags;
+#endif
RELOC(boot_infos) = PTRUNRELOC(bi);
@@ -283,32 +299,72 @@ prom_init(int r3, int r4, prom_entry pp)
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;
- prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE " booting...\n"));
+
+ /* 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...
+
+ I'll try to keep enough of boot-info compatible in the future to always allow
+ display of this message;
+ */
+ 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
-
- /*
- * XXX If this is an iMac, turn off the USB controller.
+ /* Out of the #if/#endif since it flushes the clearscreen too */
+ flushscreen();
+
+ /* New BootX enters kernel with MMU off, i/os are not allowed
+ here. This hack will have been done by the boostrap anyway.
*/
- model = (char *) early_get_property
- (r4 + bi->deviceTreeOffset, 4, RELOC("model"));
- if (model && strcmp(model, RELOC("iMac,1")) == 0) {
- out_le32((unsigned *)0x80880008, 1); /* XXX */
+ if (bi->version < 4) {
+ /*
+ * XXX If this is an iMac, turn off the USB controller.
+ */
+ model = (char *) early_get_property
+ (r4 + bi->deviceTreeOffset, 4, RELOC("model"));
+ if (model && strcmp(model, RELOC("iMac,1")) == 0) {
+ out_le32((unsigned *)0x80880008, 1); /* XXX */
+ }
}
-
+
space = bi->deviceTreeOffset + bi->deviceTreeSize;
if (bi->ramDisk)
space = bi->ramDisk + bi->ramDiskSize;
RELOC(klimit) = PTRUNRELOC((char *) bi + space);
- /*
- * Touch each page to make sure the PTEs for them
- * are in the hash table - the aim is to try to avoid
- * getting DSI exceptions while copying the kernel image.
+ /* New BootX will have flushed all TLBs and enters kernel with
+ MMU switched OFF, so this should not be useful anymore.
*/
- for (ptr = (KERNELBASE + offset) & PAGE_MASK;
- ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
- x = *(volatile unsigned long *)ptr;
-
+ if (bi->version < 4) {
+ /*
+ * Touch each page to make sure the PTEs for them
+ * are in the hash table - the aim is to try to avoid
+ * getting DSI exceptions while copying the kernel image.
+ */
+ for (ptr = (KERNELBASE + offset) & PAGE_MASK;
+ ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
+ x = *(volatile unsigned long *)ptr;
+ }
+
+#ifdef CONFIG_BOOTX_TEXT
+ prom_print(RELOC("booting...\n"));
+ flushscreen();
+#endif
return;
}
@@ -379,7 +435,7 @@ prom_init(int r3, int r4, prom_entry pp)
prom_args.nret = 2;
prom_args.args[0] = RELOC("instantiate-rtas");
prom_args.args[1] = prom_rtas;
- prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE-offset);
+ prom_args.args[2] = ((void *)(RELOC(rtas_data)-KERNELBASE));
RELOC(prom)(&prom_args);
if (prom_args.args[nargs] != 0)
i = 0;
@@ -393,6 +449,81 @@ prom_init(int r3, int r4, prom_entry pp)
prom_print(RELOC(" done\n"));
}
RELOC(klimit) = (char *) (mem - offset);
+#ifdef CONFIG_SMP
+ /*
+ * With CHRP SMP we need to use the OF to start the other
+ * processors so we can't wait until smp_boot_cpus (the OF is
+ * trashed by then) so we have to put the processors into
+ * a holding pattern controlled by the kernel (not OF) before
+ * we destroy the OF.
+ *
+ * This uses a chunk of high memory, puts some holding pattern
+ * code there and sends the other processors off to there until
+ * smp_boot_cpus tells them to do something. We do that by using
+ * physical address 0x0. The holding pattern checks that address
+ * until its cpu # is there, when it is that cpu jumps to
+ * __secondary_start(). smp_boot_cpus() takes care of setting those
+ * values.
+ *
+ * We also use physical address 0x4 here to tell when a cpu
+ * is in its holding pattern code.
+ *
+ * -- Cort
+ */
+ {
+ extern void __secondary_hold(void);
+ unsigned long i;
+ char type[16];
+
+
+ /*
+ * XXX: hack to make sure we're chrp, assume that if we're
+ * chrp we have a device_type property -- Cort
+ */
+ 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;
+
+ /* copy the holding pattern code to someplace safe (8M) */
+ memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x10000 );
+ for (i = 8<<20; i < ((8<<20)+0x10000); i += 32)
+ {
+ asm volatile("dcbf 0,%0" : : "r" (i) : "memory");
+ asm volatile("icbi 0,%0" : : "r" (i) : "memory");
+ }
+ }
+
+ /* look for cpus */
+ for (node = 0; prom_next_node(&node);)
+ {
+ type[0] = 0;
+ call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),
+ type, sizeof(type));
+ if (strcmp(type, RELOC("cpu")) != 0)
+ continue;
+ path = (char *) mem;
+ memset(path, 0, 256);
+ if ((int) call_prom(RELOC("package-to-path"), 3, 1,
+ node, path, 255) < 0)
+ continue;
+ /* XXX: hack - don't start cpu 0, this cpu -- Cort */
+ if ( cpu++ == 0 )
+ continue;
+ prom_print(RELOC("starting cpu "));
+ prom_print(path);
+ *(unsigned long *)(0x4) = 0;
+ asm volatile("dcbf 0,%0": : "r" (0x4) : "memory");
+ call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, cpu-1);
+ for ( i = 0 ; (i < 10000) &&
+ (*(ulong *)(0x4) == (ulong)0); i++ )
+ ;
+ if (*(ulong *)(0x4) == (ulong)cpu-1 )
+ prom_print(RELOC("...ok\n"));
+ else
+ prom_print(RELOC("...failed\n"));
+ }
+#endif
}
/*
@@ -631,6 +762,12 @@ finish_node(struct device_node *np, unsigned long mem_start,
mem_start = ifunc(np, mem_start);
}
+ /* the f50 sets the name to 'display' and 'compatible' to what we
+ * expect for the name -- Cort
+ */
+ if (!strcmp(np->name, "display"))
+ np->name = get_property(np, "compatible", 0);
+
if (!strcmp(np->name, "device-tree"))
ifunc = interpret_root_props;
else if (np->type == 0)
@@ -1007,7 +1144,7 @@ device_is_compatible(struct device_node *device, const char *compat)
if (cp == NULL)
return 0;
while (cplen > 0) {
- if (strcasecmp(cp, compat) == 0)
+ if (strncasecmp(cp, compat, strlen(compat)) == 0)
return 1;
l = strlen(cp) + 1;
cp += l;
@@ -1143,6 +1280,8 @@ print_properties(struct device_node *np)
}
#endif
+spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED;
+
/* this can be called after setup -- Cort */
__openfirmware
int
@@ -1173,7 +1312,9 @@ call_rtas(const char *service, int nargs, int nret,
for (i = 0; i < nargs; ++i)
u.words[i+3] = va_arg(list, unsigned long);
va_end(list);
+ spin_lock(&rtas_lock);
enter_rtas((void *)__pa(&u));
+ spin_unlock(&rtas_lock);
if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i)
outputs[i] = u.words[i+nargs+4];
@@ -1191,8 +1332,11 @@ abort()
prom_exit();
}
-#define CALC_BASE(y) (bi->dispDeviceBase + bi->dispDeviceRect[0] * \
- (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * (y))
+/* Calc the base address of a given point (x,y) */
+#define CALC_BASE(x,y) ((BOOT_INFO_IS_V2_COMPATIBLE(bi) ? bi->logicalDisplayBase : \
+ bi->dispDeviceBase) + (bi->dispDeviceRect[0] + (x)) * \
+ (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * \
+ ((y) + bi->dispDeviceRect[1]))
__init
static void
@@ -1200,7 +1344,7 @@ clearscreen(void)
{
unsigned long offset = reloc_offset();
boot_infos_t* bi = PTRRELOC(RELOC(boot_infos));
- unsigned long *base = (unsigned long *)CALC_BASE(0);
+ unsigned long *base = (unsigned long *)CALC_BASE(0,0);
unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
(bi->dispDeviceDepth >> 3)) >> 2;
int i,j;
@@ -1214,6 +1358,33 @@ clearscreen(void)
}
}
+__inline__ void dcbst(const void* addr)
+{
+ __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
+}
+
+__init
+static void
+flushscreen(void)
+{
+ unsigned long offset = reloc_offset();
+ boot_infos_t* bi = PTRRELOC(RELOC(boot_infos));
+ unsigned long *base = (unsigned long *)CALC_BASE(0,0);
+ unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ int i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ unsigned long *ptr = base;
+ for(j=width; j>0; j-=8) {
+ dcbst(ptr);
+ ptr += 8;
+ }
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+
#ifdef CONFIG_BOOTX_TEXT
__init
@@ -1222,8 +1393,8 @@ scrollscreen(void)
{
unsigned long offset = reloc_offset();
boot_infos_t* bi = PTRRELOC(RELOC(boot_infos));
- unsigned long *src = (unsigned long *)CALC_BASE(16);
- unsigned long *dst = (unsigned long *)CALC_BASE(0);
+ unsigned long *src = (unsigned long *)CALC_BASE(0,16);
+ unsigned long *dst = (unsigned long *)CALC_BASE(0,0);
unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
(bi->dispDeviceDepth >> 3)) >> 2;
int i,j;
@@ -1252,20 +1423,17 @@ drawchar(char c)
{
unsigned long offset = reloc_offset();
- switch(c)
- {
- case '\r': RELOC(g_loc_X) = 0; break;
- case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break;
+ switch(c) {
+ case '\r': RELOC(g_loc_X) = 0; break;
+ case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break;
default:
draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y));
- if (RELOC(g_loc_X) >= RELOC(g_max_loc_X))
- {
+ if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) {
RELOC(g_loc_X) = 0;
RELOC(g_loc_Y)++;
}
}
- while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y))
- {
+ while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) {
scrollscreen();
RELOC(g_loc_Y)--;
}
@@ -1281,17 +1449,32 @@ drawstring(const char *c)
__init
static void
+drawhex(unsigned long v)
+{
+ static char hex_table[] = "0123456789abcdef";
+ unsigned long offset = reloc_offset();
+
+ drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]);
+ drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]);
+}
+
+
+__init
+static void
draw_byte(unsigned char c, long locX, long locY)
{
unsigned long offset = reloc_offset();
- boot_infos_t* bi = PTRRELOC(RELOC(boot_infos));
- unsigned char *base = bi->dispDeviceBase
- + (bi->dispDeviceRowBytes * ((locY * 16) + bi->dispDeviceRect[1]))
- + (bi->dispDeviceDepth >> 3) * ((locX * 8) + bi->dispDeviceRect[0]);
- unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16];
+ boot_infos_t* bi = PTRRELOC(RELOC(boot_infos));
+ unsigned char *base = CALC_BASE(locX << 3, locY << 4);
+ unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16];
- switch(bi->dispDeviceDepth)
- {
+ switch(bi->dispDeviceDepth) {
case 32:
draw_byte_32(font, (unsigned long *)base);
break;