837ec2d56c
Allow hardware errors to be caught during early portions of booting, and leave something in the shadow console that people can use to debug their system with (to be printed out by the bootloader on next reset). This enables the hardare error interrupts in head.S, allowing us to find hardware errors when they happen (well, as much as you can with a hardware error) and prints out the trace if it is enabled. This will catch errors (like booting the wrong image on a 533) which previously resulted in a infinite loop/hang, as well as random hardware errors before before setup_arch(). To disable this debug only feature - turn off EARLY_PRINTK. Signed-off-by: Robin Getz <robin.getz@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
287 lines
6.7 KiB
C
287 lines
6.7 KiB
C
/*
|
|
* File: arch/blackfin/kernel/early_printk.c
|
|
* Based on: arch/x86_64/kernel/early_printk.c
|
|
* Author: Robin Getz <rgetz@blackfin.uclinux.org
|
|
*
|
|
* Created: 14Aug2007
|
|
* Description: allow a console to be used for early printk
|
|
*
|
|
* Modified:
|
|
* Copyright 2004-2007 Analog Devices Inc.
|
|
*
|
|
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/serial_core.h>
|
|
#include <linux/console.h>
|
|
#include <linux/string.h>
|
|
#include <linux/reboot.h>
|
|
#include <asm/blackfin.h>
|
|
#include <asm/irq_handler.h>
|
|
#include <asm/early_printk.h>
|
|
|
|
#ifdef CONFIG_SERIAL_BFIN
|
|
extern struct console *bfin_earlyserial_init(unsigned int port,
|
|
unsigned int cflag);
|
|
#endif
|
|
#ifdef CONFIG_BFIN_JTAG_COMM
|
|
extern struct console *bfin_jc_early_init(void);
|
|
#endif
|
|
|
|
static struct console *early_console;
|
|
|
|
/* Default console */
|
|
#define DEFAULT_PORT 0
|
|
#define DEFAULT_CFLAG CS8|B57600
|
|
|
|
/* Default console for early crashes */
|
|
#define DEFAULT_EARLY_PORT "serial,uart0,57600"
|
|
|
|
#ifdef CONFIG_SERIAL_CORE
|
|
/* What should get here is "0,57600" */
|
|
static struct console * __init earlyserial_init(char *buf)
|
|
{
|
|
int baud, bit;
|
|
char parity;
|
|
unsigned int serial_port = DEFAULT_PORT;
|
|
unsigned int cflag = DEFAULT_CFLAG;
|
|
|
|
serial_port = simple_strtoul(buf, &buf, 10);
|
|
buf++;
|
|
|
|
cflag = 0;
|
|
baud = simple_strtoul(buf, &buf, 10);
|
|
switch (baud) {
|
|
case 1200:
|
|
cflag |= B1200;
|
|
break;
|
|
case 2400:
|
|
cflag |= B2400;
|
|
break;
|
|
case 4800:
|
|
cflag |= B4800;
|
|
break;
|
|
case 9600:
|
|
cflag |= B9600;
|
|
break;
|
|
case 19200:
|
|
cflag |= B19200;
|
|
break;
|
|
case 38400:
|
|
cflag |= B38400;
|
|
break;
|
|
case 115200:
|
|
cflag |= B115200;
|
|
break;
|
|
default:
|
|
cflag |= B57600;
|
|
}
|
|
|
|
parity = buf[0];
|
|
buf++;
|
|
switch (parity) {
|
|
case 'e':
|
|
cflag |= PARENB;
|
|
break;
|
|
case 'o':
|
|
cflag |= PARODD;
|
|
break;
|
|
}
|
|
|
|
bit = simple_strtoul(buf, &buf, 10);
|
|
switch (bit) {
|
|
case 5:
|
|
cflag |= CS5;
|
|
break;
|
|
case 6:
|
|
cflag |= CS6;
|
|
break;
|
|
case 7:
|
|
cflag |= CS7;
|
|
break;
|
|
default:
|
|
cflag |= CS8;
|
|
}
|
|
|
|
#ifdef CONFIG_SERIAL_BFIN
|
|
return bfin_earlyserial_init(serial_port, cflag);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
|
|
}
|
|
#endif
|
|
|
|
int __init setup_early_printk(char *buf)
|
|
{
|
|
|
|
/* Crashing in here would be really bad, so check both the var
|
|
and the pointer before we start using it
|
|
*/
|
|
if (!buf)
|
|
return 0;
|
|
|
|
if (!*buf)
|
|
return 0;
|
|
|
|
if (early_console != NULL)
|
|
return 0;
|
|
|
|
#ifdef CONFIG_SERIAL_BFIN
|
|
/* Check for Blackfin Serial */
|
|
if (!strncmp(buf, "serial,uart", 11)) {
|
|
buf += 11;
|
|
early_console = earlyserial_init(buf);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_BFIN_JTAG_COMM
|
|
/* Check for Blackfin JTAG */
|
|
if (!strncmp(buf, "jtag", 4)) {
|
|
buf += 4;
|
|
early_console = bfin_jc_early_init();
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_FB
|
|
/* TODO: add framebuffer console support */
|
|
#endif
|
|
|
|
if (likely(early_console)) {
|
|
early_console->flags |= CON_BOOT;
|
|
|
|
register_console(early_console);
|
|
printk(KERN_INFO "early printk enabled on %s%d\n",
|
|
early_console->name,
|
|
early_console->index);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set up a temporary Event Vector Table, so if something bad happens before
|
|
* the kernel is fully started, it doesn't vector off into somewhere we don't
|
|
* know
|
|
*/
|
|
|
|
asmlinkage void __init init_early_exception_vectors(void)
|
|
{
|
|
u32 evt;
|
|
SSYNC();
|
|
|
|
/*
|
|
* This starts up the shadow buffer, incase anything crashes before
|
|
* setup arch
|
|
*/
|
|
mark_shadow_error();
|
|
early_shadow_puts(linux_banner);
|
|
early_shadow_stamp();
|
|
|
|
if (CPUID != bfin_cpuid()) {
|
|
early_shadow_puts("Running on wrong machine type, expected");
|
|
early_shadow_reg(CPUID, 16);
|
|
early_shadow_puts(", but running on");
|
|
early_shadow_reg(bfin_cpuid(), 16);
|
|
early_shadow_puts("\n");
|
|
}
|
|
|
|
/* cannot program in software:
|
|
* evt0 - emulation (jtag)
|
|
* evt1 - reset
|
|
*/
|
|
for (evt = EVT2; evt <= EVT15; evt += 4)
|
|
bfin_write32(evt, early_trap);
|
|
CSYNC();
|
|
|
|
/* Set all the return from interrupt, exception, NMI to a known place
|
|
* so if we do a RETI, RETX or RETN by mistake - we go somewhere known
|
|
* Note - don't change RETS - we are in a subroutine, or
|
|
* RETE - since it might screw up if emulator is attached
|
|
*/
|
|
asm("\tRETI = %0; RETX = %0; RETN = %0;\n"
|
|
: : "p"(early_trap));
|
|
|
|
}
|
|
|
|
__attribute__((__noreturn__))
|
|
asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr)
|
|
{
|
|
/* This can happen before the uart is initialized, so initialize
|
|
* the UART now (but only if we are running on the processor we think
|
|
* we are compiled for - otherwise we write to MMRs that don't exist,
|
|
* and cause other problems. Nothing comes out the UART, but it does
|
|
* end up in the __buf_log.
|
|
*/
|
|
if (likely(early_console == NULL) && CPUID == bfin_cpuid())
|
|
setup_early_printk(DEFAULT_EARLY_PORT);
|
|
|
|
if (!shadow_console_enabled()) {
|
|
/* crap - we crashed before setup_arch() */
|
|
early_shadow_puts("panic before setup_arch\n");
|
|
early_shadow_puts("IPEND:");
|
|
early_shadow_reg(fp->ipend, 16);
|
|
if (fp->seqstat & SEQSTAT_EXCAUSE) {
|
|
early_shadow_puts("\nEXCAUSE:");
|
|
early_shadow_reg(fp->seqstat & SEQSTAT_EXCAUSE, 8);
|
|
}
|
|
if (fp->seqstat & SEQSTAT_HWERRCAUSE) {
|
|
early_shadow_puts("\nHWERRCAUSE:");
|
|
early_shadow_reg(
|
|
(fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14, 8);
|
|
}
|
|
early_shadow_puts("\nErr @");
|
|
if (fp->ipend & EVT_EVX)
|
|
early_shadow_reg(fp->retx, 32);
|
|
else
|
|
early_shadow_reg(fp->pc, 32);
|
|
#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
|
|
early_shadow_puts("\nTrace:");
|
|
if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) {
|
|
while (bfin_read_TBUFSTAT() & TBUFCNT) {
|
|
early_shadow_puts("\nT :");
|
|
early_shadow_reg(bfin_read_TBUF(), 32);
|
|
early_shadow_puts("\n S :");
|
|
early_shadow_reg(bfin_read_TBUF(), 32);
|
|
}
|
|
}
|
|
#endif
|
|
early_shadow_puts("\nUse bfin-elf-addr2line to determine "
|
|
"function names\n");
|
|
/*
|
|
* We should panic(), but we can't - since panic calls printk,
|
|
* and printk uses memcpy.
|
|
* we want to reboot, but if the machine type is different,
|
|
* can't due to machine specific reboot sequences
|
|
*/
|
|
if (CPUID == bfin_cpuid()) {
|
|
early_shadow_puts("Trying to restart\n");
|
|
machine_restart("");
|
|
}
|
|
|
|
early_shadow_puts("Halting, since it is not safe to restart\n");
|
|
while (1)
|
|
asm volatile ("EMUEXCPT; IDLE;\n");
|
|
|
|
} else {
|
|
printk(KERN_EMERG "Early panic\n");
|
|
show_regs(fp);
|
|
dump_bfin_trace_buffer();
|
|
}
|
|
|
|
panic("Died early");
|
|
}
|
|
|
|
early_param("earlyprintk", setup_early_printk);
|