linux/arch/blackfin/kernel/early_printk.c
Robin Getz 837ec2d56c Blackfin: catch hardware errors earlier during booting
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>
2009-09-16 21:31:44 -04:00

288 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);