diff options
Diffstat (limited to 'arch/ppc/boot/common')
-rw-r--r-- | arch/ppc/boot/common/Makefile | 13 | ||||
-rw-r--r-- | arch/ppc/boot/common/bootinfo.c | 70 | ||||
-rw-r--r-- | arch/ppc/boot/common/crt0.S | 81 | ||||
-rw-r--r-- | arch/ppc/boot/common/misc-common.c | 553 | ||||
-rw-r--r-- | arch/ppc/boot/common/ns16550.c | 99 | ||||
-rw-r--r-- | arch/ppc/boot/common/serial_stub.c | 23 | ||||
-rw-r--r-- | arch/ppc/boot/common/string.S | 150 | ||||
-rw-r--r-- | arch/ppc/boot/common/util.S | 293 |
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 + |