Commit 0ee5a7d5 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ppc32: boot and platform fixes

From: Tom Rini <trini@kernel.crashing.org>

From: Randy Vinson <rvinson@mvista.com>

- Fixup IBM Spruce support (GEN550, general fixes and cleanups).
- Forward-port the INTERACTIVE_CONSOLE bits from 2.4.
- Forward-port the bootinfo code.
- Add a weak get_mem_size() function.
parent 39ef4295
......@@ -609,7 +609,7 @@ config PPC_OF
config PPC_GEN550
bool
depends on SANDPOINT || MCPN765
depends on SANDPOINT || MCPN765 || SPRUCE
default y
config FORCE
......
/*
* 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);
}
......@@ -30,7 +30,7 @@ tftpboot := /tftpboot
# Normally, we use the 'misc.c' file for decompress_kernel and
# whatnot. Sometimes we need to override this however.
misc-y := misc.o
misc-y := misc.o ../common/bootinfo.o
# Normally, we have our images end in .elf, but something we want to
# change this.
......@@ -103,7 +103,7 @@ zimageinitrd-$(pcore) := zImage.initrd-STRIPELF
zimageinitrd-$(CONFIG_SPRUCE) := zImage.initrd-TREE
end-$(CONFIG_SPRUCE) := spruce
entrypoint-$(CONFIG_SPRUCE) := 0x00800000
misc-$(CONFIG_SPRUCE) := misc-spruce.o
misc-$(CONFIG_SPRUCE) += misc-spruce.o
# SMP images should have a '.smp' suffix.
end-$(CONFIG_SMP) += .smp
......
......@@ -15,55 +15,19 @@
*/
#include <linux/types.h>
#include <linux/elf.h>
#include <linux/config.h>
#include <linux/pci.h>
#include <asm/page.h>
#include <asm/mmu.h>
#include <asm/bootinfo.h>
#include "zlib.h"
extern unsigned long decompress_kernel(unsigned long load_addr, int num_words,
unsigned long cksum);
/* Define some important locations of the Spruce. */
#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000
#define SPRUCE_PCI_CONFIG_DATA 0xfec00004
#define SPRUCE_ISA_IO_BASE 0xf8000000
unsigned long com_port;
char *avail_ram;
char *end_avail;
/* The linker tells us where the image is. */
extern char __image_begin, __image_end;
extern char __ramdisk_begin, __ramdisk_end;
extern char _end[];
#ifdef CONFIG_CMDLINE
#define CMDLINE CONFIG_CMDLINE
#else
#define CMDLINE ""
#endif
char cmd_preset[] = CMDLINE;
char cmd_buf[256];
char *cmd_line = cmd_buf;
unsigned long initrd_size = 0;
char *zimage_start;
int zimage_size;
extern void udelay(long);
extern void puts(const char *);
extern void putc(const char c);
extern void puthex(unsigned long val);
extern int getc(void);
extern int tstc(void);
extern void gunzip(void *, int, unsigned char *, int *);
extern unsigned long serial_init(int chan, void *ignored);
/* PCI configuration space access routines. */
unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR;
unsigned char *pci_config_data = (unsigned char *)SPRUCE_PCI_CONFIG_DATA;
......@@ -146,45 +110,16 @@ unsigned long isa_io_base = SPRUCE_ISA_IO_BASE;
#define MEM_B2EA 0x60
unsigned long
load_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
get_mem_size(void)
{
int timer = 0;
char *cp, ch;
int loop;
int csr0;
int csr_id;
int *mem_addr = (int *)0xff500008;
int *mem_data = (int *)0xff50000c;
int mem_size = 0;
unsigned long mem_size = 0;
unsigned long mem_mben;
unsigned long mem_type;
unsigned long mem_start;
unsigned long mem_end;
int *pif_addr = (int *)0xff500000;
int *pif_data = (int *)0xff500004;
int pci_devfn;
int found_multi = 0;
unsigned short vendor;
unsigned short device;
unsigned short command;
unsigned char header_type;
unsigned int bar0;
#ifdef CONFIG_SERIAL_8250_CONSOLE
/* Initialize the serial console port */
com_port = serial_init(0, NULL);
#endif
/*
* Gah, these firmware guys need to learn that hardware
* byte swapping is evil! Disable all hardware byte
* swapping so it doesn't hurt anyone.
*/
*pif_addr = PLBMIFOPT;
asm("sync");
*pif_data = 0x00000000;
asm("sync");
volatile int *mem_addr = (int *)0xff500008;
volatile int *mem_data = (int *)0xff50000c;
/* Get the size of memory from the memory controller. */
*mem_addr = MEM_MBEN;
......@@ -235,6 +170,33 @@ load_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
mem_size += mem_end - mem_start + 0x100000;
}
return mem_size;
}
unsigned long
load_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
{
int csr0;
int csr_id;
int pci_devfn;
int found_multi = 0;
unsigned short vendor;
unsigned short device;
unsigned short command;
unsigned char header_type;
unsigned int bar0;
volatile int *pif_addr = (int *)0xff500000;
volatile int *pif_data = (int *)0xff500004;
/*
* Gah, these firmware guys need to learn that hardware
* byte swapping is evil! Disable all hardware byte
* swapping so it doesn't hurt anyone.
*/
*pif_addr = PLBMIFOPT;
asm("sync");
*pif_data = 0x00000000;
asm("sync");
/* Search out and turn off the PcNet ethernet boot device. */
for (pci_devfn = 1; pci_devfn < 0xff; pci_devfn++) {
......@@ -310,134 +272,5 @@ load_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
}
}
/* assume the chunk below 8M is free */
end_avail = (char *)0x00800000;
/*
* We link ourself to 0x00800000. When we run, we relocate
* ourselves there. So we just need __image_begin for the
* start. -- Tom
*/
zimage_start = (char *)(unsigned long)(&__image_begin);
zimage_size = (unsigned long)(&__image_end) -
(unsigned long)(&__image_begin);
initrd_size = (unsigned long)(&__ramdisk_end) -
(unsigned long)(&__ramdisk_begin);
/*
* The zImage and initrd will be between start and _end, so they've
* already been moved once. We're good to go now. -- Tom
*/
avail_ram = (char *)PAGE_ALIGN((unsigned long)_end);
puts("zimage at: "); puthex((unsigned long)zimage_start);
puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
puts("\n");
if ( initrd_size ) {
puts("initrd at: ");
puthex((unsigned long)(&__ramdisk_begin));
puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n");
}
avail_ram = (char *)0x00400000;
end_avail = (char *)0x00800000;
puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" ");
puthex((unsigned long)end_avail); puts("\n");
/* Display standard Linux/PPC boot prompt for kernel args */
puts("\nLinux/PPC load: ");
cp = cmd_line;
memcpy (cmd_line, cmd_preset, sizeof(cmd_preset));
while ( *cp ) putc(*cp++);
while (timer++ < 5*1000) {
if (tstc()) {
while ((ch = getc()) != '\n' && ch != '\r') {
if (ch == '\b') {
if (cp != cmd_line) {
cp--;
puts("\b \b");
}
} else {
*cp++ = ch;
putc(ch);
}
}
break; /* Exit 'timer' loop */
}
udelay(1000); /* 1 msec */
}
*cp = 0;
puts("\n");
puts("Uncompressing Linux...");
gunzip(0, 0x400000, zimage_start, &zimage_size);
puts("done.\n");
{
struct bi_record *rec;
unsigned long initrd_loc;
unsigned long rec_loc = _ALIGN((unsigned long)(zimage_size) +
(1 << 20) - 1, (1 << 20));
rec = (struct bi_record *)rec_loc;
/* We need to make sure that the initrd and bi_recs do not
* overlap. */
if ( initrd_size ) {
initrd_loc = (unsigned long)(&__ramdisk_begin);
/* If the bi_recs are in the middle of the current
* initrd, move the initrd to the next MB
* boundary. */
if ((rec_loc > initrd_loc) &&
((initrd_loc + initrd_size)
> rec_loc)) {
initrd_loc = _ALIGN((unsigned long)(zimage_size)
+ (2 << 20) - 1, (2 << 20));
memmove((void *)initrd_loc, &__ramdisk_begin,
initrd_size);
puts("initrd moved: "); puthex(initrd_loc);
puts(" "); puthex(initrd_loc + initrd_size);
puts("\n");
}
}
rec->tag = BI_FIRST;
rec->size = sizeof(struct bi_record);
rec = (struct bi_record *)((unsigned long)rec + rec->size);
rec->tag = BI_BOOTLOADER_ID;
memcpy( (void *)rec->data, "spruceboot", 11);
rec->size = sizeof(struct bi_record) + 10 + 1;
rec = (struct bi_record *)((unsigned long)rec + rec->size);
rec->tag = BI_MEMSIZE;
rec->data[0] = mem_size;
rec->size = sizeof(struct bi_record) + sizeof(unsigned long);
rec = (struct bi_record *)((unsigned long)rec + rec->size);
rec->tag = BI_CMD_LINE;
memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1);
rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1;
rec = (struct bi_record *)((ulong)rec + rec->size);
if ( initrd_size ) {
rec->tag = BI_INITRD;
rec->data[0] = initrd_loc;
rec->data[1] = initrd_size;
rec->size = sizeof(struct bi_record) + 2 *
sizeof(unsigned long);
rec = (struct bi_record *)((unsigned long)rec +
rec->size);
}
rec->tag = BI_LAST;
rec->size = sizeof(struct bi_record);
rec = (struct bi_record *)((unsigned long)rec + rec->size);
}
puts("Now booting the kernel\n");
return 0;
return decompress_kernel(load_addr, num_words, cksum);
}
/*
* arch/ppc/common/misc-simple.c
* arch/ppc/simple/misc.c
*
* Misc. bootloader code for many machines. This assumes you have are using
* a 6xx/7xx/74xx CPU in your machine. This assumes the chunk of memory
......@@ -46,6 +46,15 @@
#define HAS_KEYB 0
#endif
/* Will / Can the user give input?
* Val Henson has requested that Gemini doesn't wait for the
* user to edit the cmdline or not.
*/
#if (defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_VGA_CONSOLE)) \
&& !defined(CONFIG_GEMINI)
#define INTERACTIVE_CONSOLE 1
#endif
char *avail_ram;
char *end_avail;
char *zimage_start;
......@@ -66,19 +75,28 @@ extern char _end[];
extern unsigned long start;
extern int CRT_tstc(void);
extern unsigned long get_mem_size(void);
extern unsigned long serial_init(int chan, void *ignored);
extern void serial_close(unsigned long com_port);
extern void gunzip(void *, int, unsigned char *, int *);
extern void serial_fixups(void);
/* Allow get_mem_size to be hooked into. This is the default. */
unsigned long __attribute__ ((weak))
get_mem_size(void)
{
return 0;
}
struct bi_record *
decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
{
#ifdef INTERACTIVE_CONSOLE
int timer = 0;
char *cp, ch;
char ch;
#endif
char *cp;
struct bi_record *rec;
unsigned long TotalMemory = 0, rec_loc, initrd_loc;
unsigned long initrd_loc, TotalMemory = 0;
serial_fixups();
com_port = serial_init(0, NULL);
......@@ -93,13 +111,11 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
__asm__ __volatile__("eieio");
#endif
#if defined(CONFIG_LOPEC) || defined(CONFIG_PAL4)
/*
* Call get_mem_size(), which is memory controller dependent,
* and we must have the correct file linked in here.
*/
TotalMemory = get_mem_size();
#endif
/* assume the chunk below 8M is free */
end_avail = (char *)0x00800000;
......@@ -170,9 +186,11 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
memcpy (cmd_line, cmd_preset, sizeof(cmd_preset));
while ( *cp ) putc(*cp++);
#ifndef CONFIG_GEMINI
/* Val Henson has requested that Gemini doesn't wait for the
* user to edit the cmdline or not. */
#ifdef INTERACTIVE_CONSOLE
/*
* If they have a console, allow them to edit the command line.
* Otherwise, don't bother wasting the five seconds.
*/
while (timer++ < 5*1000) {
if (tstc()) {
while ((ch = getc()) != '\n' && ch != '\r') {
......@@ -205,16 +223,13 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
gunzip(0, 0x400000, zimage_start, &zimage_size);
puts("done.\n");
/*
* Create bi_recs for cmd_line and initrds
*/
rec_loc = _ALIGN((unsigned long)(zimage_size) +
(1 << 20) - 1, (1 << 20));
rec = (struct bi_record *)rec_loc;
/* get the bi_rec address */
rec = bootinfo_addr(zimage_size);
/* We need to make sure that the initrd and bi_recs do not
* overlap. */
if ( initrd_size ) {
unsigned long rec_loc = (unsigned long) rec;
initrd_loc = (unsigned long)(&__ramdisk_begin);
/* If the bi_recs are in the middle of the current
* initrd, move the initrd to the next MB
......@@ -231,39 +246,25 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
}
}
rec->tag = BI_FIRST;
rec->size = sizeof(struct bi_record);
rec = (struct bi_record *)((unsigned long)rec + rec->size);
bootinfo_init(rec);
if ( TotalMemory )
bootinfo_append(BI_MEMSIZE, sizeof(int), (void*)&TotalMemory);
if ( TotalMemory ) {
rec->tag = BI_MEMSIZE;
rec->data[0] = TotalMemory;
rec->size = sizeof(struct bi_record) + sizeof(unsigned long);
rec = (struct bi_record *)((unsigned long)rec + rec->size);
}
bootinfo_append(BI_CMD_LINE, strlen(cmd_line)+1, (void*)cmd_line);
rec->tag = BI_CMD_LINE;
memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1);
rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1;
rec = (struct bi_record *)((unsigned long)rec + rec->size);
/* add a bi_rec for the initrd if it exists */
if (initrd_size) {
unsigned long initrd[2];
if ( initrd_size ) {
rec->tag = BI_INITRD;
rec->data[0] = initrd_loc;
rec->data[1] = initrd_size;
rec->size = sizeof(struct bi_record) + 2 *
sizeof(unsigned long);
rec = (struct bi_record *)((unsigned long)rec +
rec->size);
}
initrd[0] = initrd_loc;
initrd[1] = initrd_size;
rec->tag = BI_LAST;
rec->size = sizeof(struct bi_record);
rec = (struct bi_record *)((unsigned long)rec + rec->size);
bootinfo_append(BI_INITRD, sizeof(initrd), &initrd);
}
puts("Now booting the kernel\n");
serial_close(com_port);
return (struct bi_record *)rec_loc;
return rec;
}
/* Allow decompress_kernel to be hooked into. This is the default. */
......
......@@ -35,11 +35,37 @@
#define SPRUCE_MEM_SIZE 0x04000000
#define SPRUCE_BUS_SPEED 66666667
#define SPRUCE_SERIAL_1_ADDR 0xff600300
#define SPRUCE_SERIAL_2_ADDR 0xff600400
#define SPRUCE_NVRAM_BASE_ADDR 0xff800000
#define SPRUCE_RTC_BASE_ADDR SPRUCE_NVRAM_BASE_ADDR
/*
* Serial port defines
*/
#define SPRUCE_FPGA_REG_A 0xff820000
#define SPRUCE_UARTCLK_33M 0x02
#define SPRUCE_UARTCLK_IS_33M(reg) (reg & SPRUCE_UARTCLK_33M)
#define UART0_IO_BASE 0xff600300
#define UART1_IO_BASE 0xff600400
#define RS_TABLE_SIZE 2
#define SPRUCE_BAUD_33M (33000000/64)
#define SPRUCE_BAUD_30M (30000000/64)
#define BASE_BAUD SPRUCE_BAUD_33M
#define UART0_INT 3
#define UART1_INT 4
#define STD_UART_OP(num) \
{ 0, BASE_BAUD, 0, UART##num##_INT, \
ASYNC_BOOT_AUTOCONF, \
iomem_base: UART##num##_IO_BASE, \
io_type: SERIAL_IO_MEM},
#define SERIAL_PORT_DFNS \
STD_UART_OP(0) \
STD_UART_OP(1)
#endif /* __ASM_SPRUCE_H__ */
#endif /* __KERNEL__ */
......@@ -28,6 +28,9 @@
#include <linux/seq_file.h>
#include <linux/ide.h>
#include <linux/root_dev.h>
#include <linux/serial.h>
#include <linux/tty.h>
#include <linux/serial_core.h>
#include <asm/system.h>
#include <asm/pgtable.h>
......@@ -39,6 +42,7 @@
#include <platforms/spruce.h>
#include <asm/todc.h>
#include <asm/bootinfo.h>
#include <asm/kgdb.h>
#include <syslib/cpc700.h>
......@@ -103,6 +107,46 @@ spruce_show_cpuinfo(struct seq_file *m)
return 0;
}
static void __init
spruce_early_serial_map(void)
{
u32 uart_clk;
struct uart_port serial_req;
if (SPRUCE_UARTCLK_IS_33M(readb(SPRUCE_FPGA_REG_A)))
uart_clk = SPRUCE_BAUD_33M * 16;
else
uart_clk = SPRUCE_BAUD_30M * 16;
/* Setup serial port access */
memset(&serial_req, 0, sizeof(serial_req));
serial_req.uartclk = uart_clk;
serial_req.irq = UART0_INT;
serial_req.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
serial_req.iotype = SERIAL_IO_MEM;
serial_req.membase = (u_char *)UART0_IO_BASE;
serial_req.regshift = 0;
gen550_init(0, &serial_req);
#ifdef CONFIG_SERIAL_8250
if (early_serial_setup(&serial_req) != 0)
printk("Early serial init of port 0 failed\n");
#endif
/* Assume early_serial_setup() doesn't modify serial_req */
serial_req.line = 1;
serial_req.irq = UART1_INT;
serial_req.membase = (u_char *)UART1_IO_BASE;
gen550_init(1, &serial_req);
#ifdef CONFIG_SERIAL_8250
if (early_serial_setup(&serial_req) != 0)
printk("Early serial init of port 1 failed\n");
#endif
}
TODC_ALLOC();
static void __init
......@@ -128,10 +172,11 @@ spruce_setup_arch(void)
ROOT_DEV = Root_SDA1;
#endif
#ifdef CONFIG_DUMMY_CONSOLE
#ifdef CONFIG_VT
conswitchp = &dummy_con;
#endif
/* Identify the system */
printk(KERN_INFO "System Identification: IBM Spruce\n");
printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n");
......@@ -146,12 +191,12 @@ spruce_restart(char *cmd)
/* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */
__asm__ __volatile__
("\n\
lis 3,0xfff0
ori 3,3,0x0100
mtspr 26,3
li 3,0
mtspr 27,3
rfi
lis 3,0xfff0 \n\
ori 3,3,0x0100 \n\
mtspr 26,3 \n\
li 3,0 \n\
mtspr 27,3 \n\
rfi \n\
");
for(;;);
}
......@@ -175,12 +220,27 @@ spruce_map_io(void)
0x08000000, _PAGE_IO);
}
/*
* Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1.
*/
static __inline__ void
spruce_set_bat(void)
{
mb();
mtspr(DBAT1U, 0xf8000ffe);
mtspr(DBAT1L, 0xf800002a);
mb();
}
void __init
platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
{
parse_bootinfo(find_bootinfo());
/* Map in board regs, etc. */
spruce_set_bat();
isa_io_base = SPRUCE_ISA_IO_BASE;
pci_dram_offset = SPRUCE_PCI_SYS_MEM_BASE;
......@@ -202,4 +262,13 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
ppc_md.nvram_read_val = todc_direct_read_val;
ppc_md.nvram_write_val = todc_direct_write_val;
spruce_early_serial_map();
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
#ifdef CONFIG_KGDB
ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
#endif
}
......@@ -10,6 +10,7 @@
#define _PPC_BOOTINFO_H
#include <linux/config.h>
#include <asm/page.h>
#if defined(CONFIG_APUS) && !defined(__BOOTER__)
#include <asm-m68k/bootinfo.h>
......@@ -29,11 +30,21 @@ struct bi_record {
#define BI_SYSMAP 0x1015
#define BI_MACHTYPE 0x1016
#define BI_MEMSIZE 0x1017
#define BI_BOARD_INFO 0x1018
extern struct bi_record *find_bootinfo(void);
extern void bootinfo_init(struct bi_record *rec);
extern void bootinfo_append(unsigned long tag, unsigned long size, void * data);
extern void parse_bootinfo(struct bi_record *rec);
extern unsigned long boot_mem_size;
static inline struct bi_record *
bootinfo_addr(unsigned long offset)
{
return (struct bi_record *)_ALIGN((offset) + (1 << 20) - 1,
(1 << 20));
}
#endif /* CONFIG_APUS */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment