aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc/boot/common
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/boot/common')
-rw-r--r--arch/ppc/boot/common/Makefile13
-rw-r--r--arch/ppc/boot/common/bootinfo.c70
-rw-r--r--arch/ppc/boot/common/crt0.S81
-rw-r--r--arch/ppc/boot/common/misc-common.c553
-rw-r--r--arch/ppc/boot/common/ns16550.c99
-rw-r--r--arch/ppc/boot/common/serial_stub.c23
-rw-r--r--arch/ppc/boot/common/string.S150
-rw-r--r--arch/ppc/boot/common/util.S293
8 files changed, 1282 insertions, 0 deletions
diff --git a/arch/ppc/boot/common/Makefile b/arch/ppc/boot/common/Makefile
new file mode 100644
index 000000000000..f88d647d5dd4
--- /dev/null
+++ b/arch/ppc/boot/common/Makefile
@@ -0,0 +1,13 @@
+#
+# arch/ppc/boot/common/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Tom Rini January 2001
+#
+
+lib-y := string.o util.o misc-common.o \
+ serial_stub.o bootinfo.o
+lib-$(CONFIG_SERIAL_8250_CONSOLE) += ns16550.o
diff --git a/arch/ppc/boot/common/bootinfo.c b/arch/ppc/boot/common/bootinfo.c
new file mode 100644
index 000000000000..9c6e528940e9
--- /dev/null
+++ b/arch/ppc/boot/common/bootinfo.c
@@ -0,0 +1,70 @@
+/*
+ * arch/ppc/common/bootinfo.c
+ *
+ * General bootinfo record utilities
+ * Author: Randy Vinson <rvinson@mvista.com>
+ *
+ * 2002 (c) MontaVista Software, Inc. This file is licensed under the terms
+ * of the GNU General Public License version 2. This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <asm/bootinfo.h>
+
+#include "nonstdio.h"
+
+static struct bi_record * birec = NULL;
+
+static struct bi_record *
+__bootinfo_build(struct bi_record *rec, unsigned long tag, unsigned long size,
+ void *data)
+{
+ /* set the tag */
+ rec->tag = tag;
+
+ /* if the caller has any data, copy it */
+ if (size)
+ memcpy(rec->data, (char *)data, size);
+
+ /* set the record size */
+ rec->size = sizeof(struct bi_record) + size;
+
+ /* advance to the next available space */
+ rec = (struct bi_record *)((unsigned long)rec + rec->size);
+
+ return rec;
+}
+
+void
+bootinfo_init(struct bi_record *rec)
+{
+
+ /* save start of birec area */
+ birec = rec;
+
+ /* create an empty list */
+ rec = __bootinfo_build(rec, BI_FIRST, 0, NULL);
+ (void) __bootinfo_build(rec, BI_LAST, 0, NULL);
+
+}
+
+void
+bootinfo_append(unsigned long tag, unsigned long size, void * data)
+{
+
+ struct bi_record *rec = birec;
+
+ /* paranoia */
+ if ((rec == NULL) || (rec->tag != BI_FIRST))
+ return;
+
+ /* find the last entry in the list */
+ while (rec->tag != BI_LAST)
+ rec = (struct bi_record *)((ulong)rec + rec->size);
+
+ /* overlay BI_LAST record with new one and tag on a new BI_LAST */
+ rec = __bootinfo_build(rec, tag, size, data);
+ (void) __bootinfo_build(rec, BI_LAST, 0, NULL);
+}
diff --git a/arch/ppc/boot/common/crt0.S b/arch/ppc/boot/common/crt0.S
new file mode 100644
index 000000000000..4d31b824bbd1
--- /dev/null
+++ b/arch/ppc/boot/common/crt0.S
@@ -0,0 +1,81 @@
+/* Copyright (c) 1997 Paul Mackerras <paulus@cs.anu.edu.au>
+ * Initial Power Macintosh COFF version.
+ * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ * Modifications for IBM PowerPC 400-class processor evaluation
+ * boards.
+ *
+ * Module name: crt0.S
+ *
+ * Description:
+ * Boot loader execution entry point. Clears out .bss section as per
+ * ANSI C requirements. Invalidates and flushes the caches over the
+ * range covered by the boot loader's .text section. Sets up a stack
+ * below the .text section entry point.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/ppc_asm.h>
+
+ .text
+
+ .globl _start
+_start:
+#ifdef XCOFF
+ .long __start,0,0
+
+ .globl __start
+__start:
+#endif
+
+ ## Flush and invalidate the caches for the range in memory covering
+ ## the .text section of the boot loader
+
+ lis r9,_start@h # r9 = &_start
+ lis r8,_etext@ha #
+ addi r8,r8,_etext@l # r8 = &_etext
+3: dcbf r0,r9 # Flush the data cache
+ icbi r0,r9 # Invalidate the instruction cache
+ addi r9,r9,0x10 # Increment by one cache line
+ cmplw cr0,r9,r8 # Are we at the end yet?
+ blt 3b # No, keep flushing and invalidating
+ sync # sync ; isync after flushing the icache
+ isync
+
+ ## Clear out the BSS as per ANSI C requirements
+
+ lis r7,_end@ha
+ addi r7,r7,_end@l # r7 = &_end
+ lis r8,__bss_start@ha #
+ addi r8,r8,__bss_start@l # r8 = &_bss_start
+
+ ## Determine how large an area, in number of words, to clear
+
+ subf r7,r8,r7 # r7 = &_end - &_bss_start + 1
+ addi r7,r7,3 # r7 += 3
+ srwi. r7,r7,2 # r7 = size in words.
+ beq 2f # If the size is zero, do not bother
+ addi r8,r8,-4 # r8 -= 4
+ mtctr r7 # SPRN_CTR = number of words to clear
+ li r0,0 # r0 = 0
+1: stwu r0,4(r8) # Clear out a word
+ bdnz 1b # If we are not done yet, keep clearing
+2:
+
+#ifdef CONFIG_40x
+ ## Set up the stack
+
+ lis r9,_start@h # r9 = &_start (text section entry)
+ ori r9,r9,_start@l
+ subi r1,r9,64 # Start the stack 64 bytes below _start
+ clrrwi r1,r1,4 # Make sure it is aligned on 16 bytes.
+ li r0,0
+ stwu r0,-16(r1)
+ mtlr r9
+#endif
+
+ b start # All done, start the real work.
diff --git a/arch/ppc/boot/common/misc-common.c b/arch/ppc/boot/common/misc-common.c
new file mode 100644
index 000000000000..e79e6b3f276e
--- /dev/null
+++ b/arch/ppc/boot/common/misc-common.c
@@ -0,0 +1,553 @@
+/*
+ * arch/ppc/boot/common/misc-common.c
+ *
+ * Misc. bootloader code (almost) all platforms can use
+ *
+ * Author: Johnnie Peters <jpeters@mvista.com>
+ * Editor: Tom Rini <trini@mvista.com>
+ *
+ * Derived from arch/ppc/boot/prep/misc.c
+ *
+ * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <stdarg.h> /* for va_ bits */
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/zlib.h>
+#include "nonstdio.h"
+
+/* If we're on a PReP, assume we have a keyboard controller
+ * Also note, if we're not PReP, we assume you are a serial
+ * console - Tom */
+#if defined(CONFIG_PPC_PREP) && defined(CONFIG_VGA_CONSOLE)
+extern void cursor(int x, int y);
+extern void scroll(void);
+extern char *vidmem;
+extern int lines, cols;
+extern int orig_x, orig_y;
+extern int keyb_present;
+extern int CRT_tstc(void);
+extern int CRT_getc(void);
+#else
+int cursor(int x, int y) {return 0;}
+void scroll(void) {}
+char vidmem[1];
+#define lines 0
+#define cols 0
+int orig_x = 0;
+int orig_y = 0;
+#define keyb_present 0
+int CRT_tstc(void) {return 0;}
+int CRT_getc(void) {return 0;}
+#endif
+
+extern char *avail_ram;
+extern char *end_avail;
+extern char _end[];
+
+void puts(const char *);
+void putc(const char c);
+void puthex(unsigned long val);
+void gunzip(void *, int, unsigned char *, int *);
+static int _cvt(unsigned long val, char *buf, long radix, char *digits);
+
+void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap);
+unsigned char *ISA_io = NULL;
+
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPSC_CONSOLE)
+extern unsigned long com_port;
+
+extern int serial_tstc(unsigned long com_port);
+extern unsigned char serial_getc(unsigned long com_port);
+extern void serial_putc(unsigned long com_port, unsigned char c);
+#endif
+
+void pause(void)
+{
+ puts("pause\n");
+}
+
+void exit(void)
+{
+ puts("exit\n");
+ while(1);
+}
+
+int tstc(void)
+{
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPSC_CONSOLE)
+ if(keyb_present)
+ return (CRT_tstc() || serial_tstc(com_port));
+ else
+ return (serial_tstc(com_port));
+#else
+ return CRT_tstc();
+#endif
+}
+
+int getc(void)
+{
+ while (1) {
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPSC_CONSOLE)
+ if (serial_tstc(com_port))
+ return (serial_getc(com_port));
+#endif /* serial console */
+ if (keyb_present)
+ if(CRT_tstc())
+ return (CRT_getc());
+ }
+}
+
+void
+putc(const char c)
+{
+ int x,y;
+
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPSC_CONSOLE)
+ serial_putc(com_port, c);
+ if ( c == '\n' )
+ serial_putc(com_port, '\r');
+#endif /* serial console */
+
+ x = orig_x;
+ y = orig_y;
+
+ if ( c == '\n' ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ } else if (c == '\r') {
+ x = 0;
+ } else if (c == '\b') {
+ if (x > 0) {
+ x--;
+ }
+ } else {
+ vidmem [ ( x + cols * y ) * 2 ] = c;
+ if ( ++x >= cols ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ }
+ }
+
+ cursor(x, y);
+
+ orig_x = x;
+ orig_y = y;
+}
+
+void puts(const char *s)
+{
+ int x,y;
+ char c;
+
+ x = orig_x;
+ y = orig_y;
+
+ while ( ( c = *s++ ) != '\0' ) {
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \
+ || defined(CONFIG_SERIAL_MPSC_CONSOLE)
+ serial_putc(com_port, c);
+ if ( c == '\n' ) serial_putc(com_port, '\r');
+#endif /* serial console */
+
+ if ( c == '\n' ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ } else if (c == '\b') {
+ if (x > 0) {
+ x--;
+ }
+ } else {
+ vidmem [ ( x + cols * y ) * 2 ] = c;
+ if ( ++x >= cols ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ }
+ }
+ }
+
+ cursor(x, y);
+
+ orig_x = x;
+ orig_y = y;
+}
+
+void error(char *x)
+{
+ puts("\n\n");
+ puts(x);
+ puts("\n\n -- System halted");
+
+ while(1); /* Halt */
+}
+
+static void *zalloc(unsigned size)
+{
+ void *p = avail_ram;
+
+ size = (size + 7) & -8;
+ avail_ram += size;
+ if (avail_ram > end_avail) {
+ puts("oops... out of memory\n");
+ pause();
+ }
+ return p;
+}
+
+#define HEAD_CRC 2
+#define EXTRA_FIELD 4
+#define ORIG_NAME 8
+#define COMMENT 0x10
+#define RESERVED 0xe0
+
+void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
+{
+ z_stream s;
+ int r, i, flags;
+
+ /* skip header */
+ i = 10;
+ flags = src[3];
+ if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
+ puts("bad gzipped data\n");
+ exit();
+ }
+ if ((flags & EXTRA_FIELD) != 0)
+ i = 12 + src[10] + (src[11] << 8);
+ if ((flags & ORIG_NAME) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & COMMENT) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & HEAD_CRC) != 0)
+ i += 2;
+ if (i >= *lenp) {
+ puts("gunzip: ran out of data in header\n");
+ exit();
+ }
+
+ /* Initialize ourself. */
+ s.workspace = zalloc(zlib_inflate_workspacesize());
+ r = zlib_inflateInit2(&s, -MAX_WBITS);
+ if (r != Z_OK) {
+ puts("zlib_inflateInit2 returned "); puthex(r); puts("\n");
+ exit();
+ }
+ s.next_in = src + i;
+ s.avail_in = *lenp - i;
+ s.next_out = dst;
+ s.avail_out = dstlen;
+ r = zlib_inflate(&s, Z_FINISH);
+ if (r != Z_OK && r != Z_STREAM_END) {
+ puts("inflate returned "); puthex(r); puts("\n");
+ exit();
+ }
+ *lenp = s.next_out - (unsigned char *) dst;
+ zlib_inflateEnd(&s);
+}
+
+void
+puthex(unsigned long val)
+{
+
+ unsigned char buf[10];
+ int i;
+ for (i = 7; i >= 0; i--)
+ {
+ buf[i] = "0123456789ABCDEF"[val & 0x0F];
+ val >>= 4;
+ }
+ buf[8] = '\0';
+ puts(buf);
+}
+
+#define FALSE 0
+#define TRUE 1
+
+void
+_printk(char const *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _vprintk(putc, fmt, ap);
+ va_end(ap);
+ return;
+}
+
+#define is_digit(c) ((c >= '0') && (c <= '9'))
+
+void
+_vprintk(void(*putc)(const char), const char *fmt0, va_list ap)
+{
+ char c, sign, *cp = 0;
+ int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right;
+ char buf[32];
+ long val;
+ while ((c = *fmt0++))
+ {
+ if (c == '%')
+ {
+ c = *fmt0++;
+ left_prec = right_prec = pad_on_right = 0;
+ if (c == '-')
+ {
+ c = *fmt0++;
+ pad_on_right++;
+ }
+ if (c == '0')
+ {
+ zero_fill = TRUE;
+ c = *fmt0++;
+ } else
+ {
+ zero_fill = FALSE;
+ }
+ while (is_digit(c))
+ {
+ left_prec = (left_prec * 10) + (c - '0');
+ c = *fmt0++;
+ }
+ if (c == '.')
+ {
+ c = *fmt0++;
+ zero_fill++;
+ while (is_digit(c))
+ {
+ right_prec = (right_prec * 10) + (c - '0');
+ c = *fmt0++;
+ }
+ } else
+ {
+ right_prec = left_prec;
+ }
+ sign = '\0';
+ switch (c)
+ {
+ case 'd':
+ case 'x':
+ case 'X':
+ val = va_arg(ap, long);
+ switch (c)
+ {
+ case 'd':
+ if (val < 0)
+ {
+ sign = '-';
+ val = -val;
+ }
+ length = _cvt(val, buf, 10, "0123456789");
+ break;
+ case 'x':
+ length = _cvt(val, buf, 16, "0123456789abcdef");
+ break;
+ case 'X':
+ length = _cvt(val, buf, 16, "0123456789ABCDEF");
+ break;
+ }
+ cp = buf;
+ break;
+ case 's':
+ cp = va_arg(ap, char *);
+ length = strlen(cp);
+ break;
+ case 'c':
+ c = va_arg(ap, long /*char*/);
+ (*putc)(c);
+ continue;
+ default:
+ (*putc)('?');
+ }
+ pad = left_prec - length;
+ if (sign != '\0')
+ {
+ pad--;
+ }
+ if (zero_fill)
+ {
+ c = '0';
+ if (sign != '\0')
+ {
+ (*putc)(sign);
+ sign = '\0';
+ }
+ } else
+ {
+ c = ' ';
+ }
+ if (!pad_on_right)
+ {
+ while (pad-- > 0)
+ {
+ (*putc)(c);
+ }
+ }
+ if (sign != '\0')
+ {
+ (*putc)(sign);
+ }
+ while (length-- > 0)
+ {
+ (*putc)(c = *cp++);
+ if (c == '\n')
+ {
+ (*putc)('\r');
+ }
+ }
+ if (pad_on_right)
+ {
+ while (pad-- > 0)
+ {
+ (*putc)(c);
+ }
+ }
+ } else
+ {
+ (*putc)(c);
+ if (c == '\n')
+ {
+ (*putc)('\r');
+ }
+ }
+ }
+}
+
+int
+_cvt(unsigned long val, char *buf, long radix, char *digits)
+{
+ char temp[80];
+ char *cp = temp;
+ int length = 0;
+ if (val == 0)
+ { /* Special case */
+ *cp++ = '0';
+ } else
+ while (val)
+ {
+ *cp++ = digits[val % radix];
+ val /= radix;
+ }
+ while (cp != temp)
+ {
+ *buf++ = *--cp;
+ length++;
+ }
+ *buf = '\0';
+ return (length);
+}
+
+void
+_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base)
+{
+ int i, c;
+ if ((unsigned int)s > (unsigned int)p)
+ {
+ s = (unsigned int)s - (unsigned int)p;
+ }
+ while (s > 0)
+ {
+ if (base)
+ {
+ _printk("%06X: ", (int)p - (int)base);
+ } else
+ {
+ _printk("%06X: ", p);
+ }
+ for (i = 0; i < 16; i++)
+ {
+ if (i < s)
+ {
+ _printk("%02X", p[i] & 0xFF);
+ } else
+ {
+ _printk(" ");
+ }
+ if ((i % 2) == 1) _printk(" ");
+ if ((i % 8) == 7) _printk(" ");
+ }
+ _printk(" |");
+ for (i = 0; i < 16; i++)
+ {
+ if (i < s)
+ {
+ c = p[i] & 0xFF;
+ if ((c < 0x20) || (c >= 0x7F)) c = '.';
+ } else
+ {
+ c = ' ';
+ }
+ _printk("%c", c);
+ }
+ _printk("|\n");
+ s -= 16;
+ p += 16;
+ }
+}
+
+void
+_dump_buf(unsigned char *p, int s)
+{
+ _printk("\n");
+ _dump_buf_with_offset(p, s, 0);
+}
+
+/* Very simple inb/outb routines. We declare ISA_io to be 0 above, and
+ * then modify it on platforms which need to. We do it like this
+ * because on some platforms we give inb/outb an exact location, and
+ * on others it's an offset from a given location. -- Tom
+ */
+
+void ISA_init(unsigned long base)
+{
+ ISA_io = (unsigned char *)base;
+}
+
+void
+outb(int port, unsigned char val)
+{
+ /* Ensure I/O operations complete */
+ __asm__ volatile("eieio");
+ ISA_io[port] = val;
+}
+
+unsigned char
+inb(int port)
+{
+ /* Ensure I/O operations complete */
+ __asm__ volatile("eieio");
+ return (ISA_io[port]);
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/arch/ppc/boot/common/ns16550.c b/arch/ppc/boot/common/ns16550.c
new file mode 100644
index 000000000000..9017c547a6f6
--- /dev/null
+++ b/arch/ppc/boot/common/ns16550.c
@@ -0,0 +1,99 @@
+/*
+ * COM1 NS16550 support
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <asm/serial.h>
+
+#include "nonstdio.h"
+#include "serial.h"
+
+#define SERIAL_BAUD 9600
+
+extern unsigned long ISA_io;
+
+static struct serial_state rs_table[RS_TABLE_SIZE] = {
+ SERIAL_PORT_DFNS /* Defined in <asm/serial.h> */
+};
+
+static int shift;
+
+unsigned long serial_init(int chan, void *ignored)
+{
+ unsigned long com_port;
+ unsigned char lcr, dlm;
+
+ /* We need to find out which type io we're expecting. If it's
+ * 'SERIAL_IO_PORT', we get an offset from the isa_io_base.
+ * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */
+ switch (rs_table[chan].io_type) {
+ case SERIAL_IO_PORT:
+ com_port = rs_table[chan].port;
+ break;
+ case SERIAL_IO_MEM:
+ com_port = (unsigned long)rs_table[chan].iomem_base;
+ break;
+ default:
+ /* We can't deal with it. */
+ return -1;
+ }
+
+ /* How far apart the registers are. */
+ shift = rs_table[chan].iomem_reg_shift;
+
+ /* save the LCR */
+ lcr = inb(com_port + (UART_LCR << shift));
+ /* Access baud rate */
+ outb(com_port + (UART_LCR << shift), 0x80);
+ dlm = inb(com_port + (UART_DLM << shift));
+ /*
+ * Test if serial port is unconfigured.
+ * We assume that no-one uses less than 110 baud or
+ * less than 7 bits per character these days.
+ * -- paulus.
+ */
+
+ if ((dlm <= 4) && (lcr & 2))
+ /* port is configured, put the old LCR back */
+ outb(com_port + (UART_LCR << shift), lcr);
+ else {
+ /* Input clock. */
+ outb(com_port + (UART_DLL << shift),
+ (BASE_BAUD / SERIAL_BAUD) & 0xFF);
+ outb(com_port + (UART_DLM << shift),
+ (BASE_BAUD / SERIAL_BAUD) >> 8);
+ /* 8 data, 1 stop, no parity */
+ outb(com_port + (UART_LCR << shift), 0x03);
+ /* RTS/DTR */
+ outb(com_port + (UART_MCR << shift), 0x03);
+ }
+ /* Clear & enable FIFOs */
+ outb(com_port + (UART_FCR << shift), 0x07);
+
+ return (com_port);
+}
+
+void
+serial_putc(unsigned long com_port, unsigned char c)
+{
+ while ((inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0)
+ ;
+ outb(com_port, c);
+}
+
+unsigned char
+serial_getc(unsigned long com_port)
+{
+ while ((inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0)
+ ;
+ return inb(com_port);
+}
+
+int
+serial_tstc(unsigned long com_port)
+{
+ return ((inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0);
+}
diff --git a/arch/ppc/boot/common/serial_stub.c b/arch/ppc/boot/common/serial_stub.c
new file mode 100644
index 000000000000..03dfaa01fa63
--- /dev/null
+++ b/arch/ppc/boot/common/serial_stub.c
@@ -0,0 +1,23 @@
+/*
+ * arch/ppc/boot/common/serial_stub.c
+ *
+ * This is a few stub routines to make the boot code cleaner looking when
+ * there is no serial port support doesn't need to be closed, for example.
+ *
+ * Author: Tom Rini <trini@mvista.com>
+ *
+ * 2003 (c) MontaVista, Software, Inc. This file is licensed under the terms
+ * of the GNU General Public License version 2. This program is licensed "as
+ * is" without any warranty of any kind, whether express or implied.
+ */
+
+unsigned long __attribute__ ((weak))
+serial_init(int chan, void *ignored)
+{
+ return 0;
+}
+
+void __attribute__ ((weak))
+serial_close(unsigned long com_port)
+{
+}
diff --git a/arch/ppc/boot/common/string.S b/arch/ppc/boot/common/string.S
new file mode 100644
index 000000000000..8016e43c4771
--- /dev/null
+++ b/arch/ppc/boot/common/string.S
@@ -0,0 +1,150 @@
+/*
+ * String handling functions for PowerPC.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#define r0 0
+#define r3 3
+#define r4 4
+#define r5 5
+#define r6 6
+#define r7 7
+#define r8 8
+
+ .globl strlen
+strlen:
+ addi r4,r3,-1
+1: lbzu r0,1(r4)
+ cmpwi 0,r0,0
+ bne 1b
+ subf r3,r3,r4
+ blr
+
+ .globl memset
+memset:
+ rlwimi r4,r4,8,16,23
+ rlwimi r4,r4,16,0,15
+ addi r6,r3,-4
+ cmplwi 0,r5,4
+ blt 7f
+ stwu r4,4(r6)
+ beqlr
+ andi. r0,r6,3
+ add r5,r0,r5
+ subf r6,r0,r6
+ rlwinm r0,r5,32-2,2,31
+ mtctr r0
+ bdz 6f
+1: stwu r4,4(r6)
+ bdnz 1b
+6: andi. r5,r5,3
+7: cmpwi 0,r5,0
+ beqlr
+ mtctr r5
+ addi r6,r6,3
+8: stbu r4,1(r6)
+ bdnz 8b
+ blr
+
+ .globl memmove
+memmove:
+ cmplw 0,r3,r4
+ bgt backwards_memcpy
+ /* fall through */
+
+ .globl memcpy
+memcpy:
+ rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */
+ addi r6,r3,-4
+ addi r4,r4,-4
+ beq 2f /* if less than 8 bytes to do */
+ andi. r0,r6,3 /* get dest word aligned */
+ mtctr r7
+ bne 5f
+1: lwz r7,4(r4)
+ lwzu r8,8(r4)
+ stw r7,4(r6)
+ stwu r8,8(r6)
+ bdnz 1b
+ andi. r5,r5,7
+2: cmplwi 0,r5,4
+ blt 3f
+ lwzu r0,4(r4)
+ addi r5,r5,-4
+ stwu r0,4(r6)
+3: cmpwi 0,r5,0
+ beqlr
+ mtctr r5
+ addi r4,r4,3
+ addi r6,r6,3
+4: lbzu r0,1(r4)
+ stbu r0,1(r6)
+ bdnz 4b
+ blr
+5: subfic r0,r0,4
+ mtctr r0
+6: lbz r7,4(r4)
+ addi r4,r4,1
+ stb r7,4(r6)
+ addi r6,r6,1
+ bdnz 6b
+ subf r5,r0,r5
+ rlwinm. r7,r5,32-3,3,31
+ beq 2b
+ mtctr r7
+ b 1b
+
+ .globl backwards_memcpy
+backwards_memcpy:
+ rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */
+ add r6,r3,r5
+ add r4,r4,r5
+ beq 2f
+ andi. r0,r6,3
+ mtctr r7
+ bne 5f
+1: lwz r7,-4(r4)
+ lwzu r8,-8(r4)
+ stw r7,-4(r6)
+ stwu r8,-8(r6)
+ bdnz 1b
+ andi. r5,r5,7
+2: cmplwi 0,r5,4
+ blt 3f
+ lwzu r0,-4(r4)
+ subi r5,r5,4
+ stwu r0,-4(r6)
+3: cmpwi 0,r5,0
+ beqlr
+ mtctr r5
+4: lbzu r0,-1(r4)
+ stbu r0,-1(r6)
+ bdnz 4b
+ blr
+5: mtctr r0
+6: lbzu r7,-1(r4)
+ stbu r7,-1(r6)
+ bdnz 6b
+ subf r5,r0,r5
+ rlwinm. r7,r5,32-3,3,31
+ beq 2b
+ mtctr r7
+ b 1b
+
+ .globl memcmp
+memcmp:
+ cmpwi 0,r5,0
+ blelr
+ mtctr r5
+ addi r6,r3,-1
+ addi r4,r4,-1
+1: lbzu r3,1(r6)
+ lbzu r0,1(r4)
+ subf. r3,r0,r3
+ bdnzt 2,1b
+ blr
diff --git a/arch/ppc/boot/common/util.S b/arch/ppc/boot/common/util.S
new file mode 100644
index 000000000000..47e641455bc5
--- /dev/null
+++ b/arch/ppc/boot/common/util.S
@@ -0,0 +1,293 @@
+/*
+ * arch/ppc/boot/common/util.S
+ *
+ * Useful bootup functions, which are more easily done in asm than C.
+ *
+ * NOTE: Be very very careful about the registers you use here.
+ * We don't follow any ABI calling convention among the
+ * assembler functions that call each other, especially early
+ * in the initialization. Please preserve at least r3 and r4
+ * for these early functions, as they often contain information
+ * passed from boot roms into the C decompress function.
+ *
+ * Author: Tom Rini
+ * trini@mvista.com
+ * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others).
+ *
+ * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/ppc_asm.h>
+
+
+ .text
+
+#ifdef CONFIG_6xx
+ .globl disable_6xx_mmu
+disable_6xx_mmu:
+ /* Establish default MSR value, exception prefix 0xFFF.
+ * If necessary, this function must fix up the LR if we
+ * return to a different address space once the MMU is
+ * disabled.
+ */
+ li r8,MSR_IP|MSR_FP
+ mtmsr r8
+ isync
+
+ /* Test for a 601 */
+ mfpvr r10
+ srwi r10,r10,16
+ cmpwi 0,r10,1 /* 601 ? */
+ beq .clearbats_601
+
+ /* Clear BATs */
+ li r8,0
+ mtspr SPRN_DBAT0U,r8
+ mtspr SPRN_DBAT0L,r8
+ mtspr SPRN_DBAT1U,r8
+ mtspr SPRN_DBAT1L,r8
+ mtspr SPRN_DBAT2U,r8
+ mtspr SPRN_DBAT2L,r8
+ mtspr SPRN_DBAT3U,r8
+ mtspr SPRN_DBAT3L,r8
+.clearbats_601:
+ mtspr SPRN_IBAT0U,r8
+ mtspr SPRN_IBAT0L,r8
+ mtspr SPRN_IBAT1U,r8
+ mtspr SPRN_IBAT1L,r8
+ mtspr SPRN_IBAT2U,r8
+ mtspr SPRN_IBAT2L,r8
+ mtspr SPRN_IBAT3U,r8
+ mtspr SPRN_IBAT3L,r8
+ isync
+ sync
+ sync
+
+ /* Set segment registers */
+ li r8,16 /* load up segment register values */
+ mtctr r8 /* for context 0 */
+ lis r8,0x2000 /* Ku = 1, VSID = 0 */
+ li r10,0
+3: mtsrin r8,r10
+ addi r8,r8,0x111 /* increment VSID */
+ addis r10,r10,0x1000 /* address of next segment */
+ bdnz 3b
+ blr
+
+ .globl disable_6xx_l1cache
+disable_6xx_l1cache:
+ /* Enable, invalidate and then disable the L1 icache/dcache. */
+ li r8,0
+ ori r8,r8,(HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI)
+ mfspr r11,SPRN_HID0
+ or r11,r11,r8
+ andc r10,r11,r8
+ isync
+ mtspr SPRN_HID0,r8
+ sync
+ isync
+ mtspr SPRN_HID0,r10
+ sync
+ isync
+ blr
+#endif
+
+ .globl _setup_L2CR
+_setup_L2CR:
+/*
+ * We should be skipping this section on CPUs where this results in an
+ * illegal instruction. If not, please send trini@kernel.crashing.org
+ * the PVR of your CPU.
+ */
+ /* Invalidate/disable L2 cache */
+ sync
+ isync
+ mfspr r8,SPRN_L2CR
+ rlwinm r8,r8,0,1,31
+ oris r8,r8,L2CR_L2I@h
+ sync
+ isync
+ mtspr SPRN_L2CR,r8
+ sync
+ isync
+
+ /* Wait for the invalidation to complete */
+ mfspr r8,SPRN_PVR
+ srwi r8,r8,16
+ cmplwi cr0,r8,0x8000 /* 7450 */
+ cmplwi cr1,r8,0x8001 /* 7455 */
+ cmplwi cr2,r8,0x8002 /* 7457 */
+ cror 4*cr0+eq,4*cr0+eq,4*cr1+eq /* Now test if any are true. */
+ cror 4*cr0+eq,4*cr0+eq,4*cr2+eq
+ bne 2f
+
+1: mfspr r8,SPRN_L2CR /* On 745x, poll L2I bit (bit 10) */
+ rlwinm. r9,r8,0,10,10
+ bne 1b
+ b 3f
+
+2: mfspr r8,SPRN_L2CR /* On 75x & 74[01]0, poll L2IP bit (bit 31) */
+ rlwinm. r9,r8,0,31,31
+ bne 2b
+
+3: rlwinm r8,r8,0,11,9 /* Turn off L2I bit */
+ sync
+ isync
+ mtspr SPRN_L2CR,r8
+ sync
+ isync
+ blr
+
+ .globl _setup_L3CR
+_setup_L3CR:
+ /* Invalidate/disable L3 cache */
+ sync
+ isync
+ mfspr r8,SPRN_L3CR
+ rlwinm r8,r8,0,1,31
+ ori r8,r8,L3CR_L3I@l
+ sync
+ isync
+ mtspr SPRN_L3CR,r8
+ sync
+ isync
+
+ /* Wait for the invalidation to complete */
+1: mfspr r8,SPRN_L3CR
+ rlwinm. r9,r8,0,21,21
+ bne 1b
+
+ rlwinm r8,r8,0,22,20 /* Turn off L3I bit */
+ sync
+ isync
+ mtspr SPRN_L3CR,r8
+ sync
+ isync
+ blr
+
+
+/* udelay (on non-601 processors) needs to know the period of the
+ * timebase in nanoseconds. This used to be hardcoded to be 60ns
+ * (period of 66MHz/4). Now a variable is used that is initialized to
+ * 60 for backward compatibility, but it can be overridden as necessary
+ * with code something like this:
+ * extern unsigned long timebase_period_ns;
+ * timebase_period_ns = 1000000000 / bd->bi_tbfreq;
+ */
+ .data
+ .globl timebase_period_ns
+timebase_period_ns:
+ .long 60
+
+ .text
+/*
+ * Delay for a number of microseconds
+ */
+ .globl udelay
+udelay:
+ mfspr r4,SPRN_PVR
+ srwi r4,r4,16
+ cmpwi 0,r4,1 /* 601 ? */
+ bne .udelay_not_601
+00: li r0,86 /* Instructions / microsecond? */
+ mtctr r0
+10: addi r0,r0,0 /* NOP */
+ bdnz 10b
+ subic. r3,r3,1
+ bne 00b
+ blr
+
+.udelay_not_601:
+ mulli r4,r3,1000 /* nanoseconds */
+ /* Change r4 to be the number of ticks using:
+ * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns
+ * timebase_period_ns defaults to 60 (16.6MHz) */
+ lis r5,timebase_period_ns@ha
+ lwz r5,timebase_period_ns@l(r5)
+ add r4,r4,r5
+ addi r4,r4,-1
+ divw r4,r4,r5 /* BUS ticks */
+1: mftbu r5
+ mftb r6
+ mftbu r7
+ cmpw 0,r5,r7
+ bne 1b /* Get [synced] base time */
+ addc r9,r6,r4 /* Compute end time */
+ addze r8,r5
+2: mftbu r5
+ cmpw 0,r5,r8
+ blt 2b
+ bgt 3f
+ mftb r6
+ cmpw 0,r6,r9
+ blt 2b
+3: blr
+
+ .section ".relocate_code","xa"
+/*
+ * Flush and enable instruction cache
+ * First, flush the data cache in case it was enabled and may be
+ * holding instructions for copy back.
+ */
+_GLOBAL(flush_instruction_cache)
+ mflr r6
+ bl flush_data_cache
+
+#ifdef CONFIG_8xx
+ lis r3, IDC_INVALL@h
+ mtspr SPRN_IC_CST, r3
+ lis r3, IDC_ENABLE@h
+ mtspr SPRN_IC_CST, r3
+ lis r3, IDC_DISABLE@h
+ mtspr SPRN_DC_CST, r3
+#elif CONFIG_4xx
+ lis r3,start@h # r9 = &_start
+ lis r4,_etext@ha
+ addi r4,r4,_etext@l # r8 = &_etext
+1: dcbf r0,r3 # Flush the data cache
+ icbi r0,r3 # Invalidate the instruction cache
+ addi r3,r3,0x10 # Increment by one cache line
+ cmplwi cr0,r3,r4 # Are we at the end yet?
+ blt 1b # No, keep flushing and invalidating
+#else
+ /* Enable, invalidate and then disable the L1 icache/dcache. */
+ li r3,0
+ ori r3,r3,(HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI)
+ mfspr r4,SPRN_HID0
+ or r5,r4,r3
+ isync
+ mtspr SPRN_HID0,r5
+ sync
+ isync
+ ori r5,r4,HID0_ICE /* Enable cache */
+ mtspr SPRN_HID0,r5
+ sync
+ isync
+#endif
+ mtlr r6
+ blr
+
+#define NUM_CACHE_LINES 128*8
+#define cache_flush_buffer 0x1000
+
+/*
+ * Flush data cache
+ * Do this by just reading lots of stuff into the cache.
+ */
+_GLOBAL(flush_data_cache)
+ lis r3,cache_flush_buffer@h
+ ori r3,r3,cache_flush_buffer@l
+ li r4,NUM_CACHE_LINES
+ mtctr r4
+00: lwz r4,0(r3)
+ addi r3,r3,L1_CACHE_BYTES /* Next line, please */
+ bdnz 00b
+10: blr
+
+ .previous
+