diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4d175c751246..a57c1f216b21 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -35,6 +35,7 @@ parameter is applicable: APIC APIC support is enabled. APM Advanced Power Management support is enabled. AX25 Appropriate AX.25 support is enabled. + BLACKFIN Blackfin architecture is enabled. DRM Direct Rendering Management support is enabled. EDD BIOS Enhanced Disk Drive Services (EDD) is enabled EFI EFI Partitioning (GPT) is enabled @@ -550,7 +551,7 @@ and is between 256 and 4096 characters. It is defined in the file dtc3181e= [HW,SCSI] - earlyprintk= [X86-32,X86-64,SH] + earlyprintk= [X86-32,X86-64,SH,BLACKFIN] earlyprintk=vga earlyprintk=serial[,ttySn[,baudrate]] diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 26ebb0e8c431..cc789b988f3a 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -1164,6 +1164,20 @@ config DEBUG_BFIN_NO_KERN_HWTRACE Say Y here to disable hardware tracing in some known "jumpy" pieces of code so that the trace buffer will extend further back. +config EARLY_PRINTK + bool "Early printk" + default n + help + This option enables special console drivers which allow the kernel + to print messages very early in the bootup process. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. After enabling this + feature, you must add "earlyprintk=serial,uart0,57600" to the + command line (bootargs). It is safe to say Y here in all cases, as + all of this lives in the init section and is thrown away after the + kernel boots completely. + config DUAL_CORE_TEST_MODULE tristate "Dual Core Test Module" depends on (BF561) diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index 243883ec6de1..8aeb6066b19b 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o obj-$(CONFIG_DUAL_CORE_TEST_MODULE) += dualcore_test.o obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c new file mode 100644 index 000000000000..9bf61706694f --- /dev/null +++ b/arch/blackfin/kernel/early_printk.c @@ -0,0 +1,161 @@ +/* + * File: arch/blackfin/kernel/early_printk.c + * Based on: arch/x86_64/kernel/early_printk.c + * Author: Robin Getz +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SERIAL_BFIN +extern struct console *bfin_earlyserial_init(unsigned int port, + unsigned int cflag); +#endif + +static struct console *early_console; + +/* Default console + * Port n == ttyBFn + * cflags == UART output modes + */ +#define DEFAULT_PORT 0 +#define DEFAULT_CFLAG CS8|B57600 + +#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 |= CS5; + break; + case 7: + cflag |= CS5; + 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_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; +} + +early_param("earlyprintk", setup_early_printk); diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index 1e79ee605d93..5039e2675abc 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -962,30 +962,6 @@ static void __init bfin_serial_init_ports(void) } #ifdef CONFIG_SERIAL_BFIN_CONSOLE -static void bfin_serial_console_putchar(struct uart_port *port, int ch) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - while (!(UART_GET_LSR(uart) & THRE)) - barrier(); - UART_PUT_CHAR(uart, ch); - SSYNC(); -} - -/* - * Interrupts are disabled on entering - */ -static void -bfin_serial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct bfin_serial_port *uart = &bfin_serial_ports[co->index]; - int flags = 0; - - spin_lock_irqsave(&uart->port.lock, flags); - uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); - spin_unlock_irqrestore(&uart->port.lock, flags); - -} - /* * If the port was already initialised (eg, by a boot loader), * try to determine the current setup. @@ -1038,19 +1014,25 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, } pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__, *baud, *parity, *bits); } +#endif + +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) +static struct uart_driver bfin_serial_reg; static int __init bfin_serial_console_setup(struct console *co, char *options) { struct bfin_serial_port *uart; +# ifdef CONFIG_SERIAL_BFIN_CONSOLE int baud = 57600; int bits = 8; int parity = 'n'; -#ifdef CONFIG_SERIAL_BFIN_CTSRTS +# ifdef CONFIG_SERIAL_BFIN_CTSRTS int flow = 'r'; -#else +# else int flow = 'n'; -#endif +# endif +# endif /* * Check whether an invalid uart number has been specified, and @@ -1061,15 +1043,45 @@ bfin_serial_console_setup(struct console *co, char *options) co->index = 0; uart = &bfin_serial_ports[co->index]; +# ifdef CONFIG_SERIAL_BFIN_CONSOLE if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else bfin_serial_console_get_options(uart, &baud, &parity, &bits); return uart_set_options(&uart->port, co, baud, parity, bits, flow); +# else + return 0; +# endif +} +#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || + defined (CONFIG_EARLY_PRINTK) */ + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE +static void bfin_serial_console_putchar(struct uart_port *port, int ch) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + while (!(UART_GET_LSR(uart) & THRE)) + barrier(); + UART_PUT_CHAR(uart, ch); + SSYNC(); +} + +/* + * Interrupts are disabled on entering + */ +static void +bfin_serial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct bfin_serial_port *uart = &bfin_serial_ports[co->index]; + int flags = 0; + + spin_lock_irqsave(&uart->port.lock, flags); + uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); + spin_unlock_irqrestore(&uart->port.lock, flags); + } -static struct uart_driver bfin_serial_reg; static struct console bfin_serial_console = { .name = BFIN_SERIAL_NAME, .write = bfin_serial_console_write, @@ -1095,7 +1107,68 @@ console_initcall(bfin_serial_rs_console_init); #define BFIN_SERIAL_CONSOLE &bfin_serial_console #else #define BFIN_SERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_BFIN_CONSOLE */ + + +#ifdef CONFIG_EARLY_PRINTK +static __init void early_serial_putc(struct uart_port *port, int ch) +{ + unsigned timeout = 0xffff; + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + while ((!(UART_GET_LSR(uart) & THRE)) && --timeout) + cpu_relax(); + UART_PUT_CHAR(uart, ch); +} + +static __init void early_serial_write(struct console *con, const char *s, + unsigned int n) +{ + struct bfin_serial_port *uart = &bfin_serial_ports[con->index]; + unsigned int i; + + for (i = 0; i < n; i++, s++) { + if (*s == '\n') + early_serial_putc(&uart->port, '\r'); + early_serial_putc(&uart->port, *s); + } +} + +static struct __init console bfin_early_serial_console = { + .name = "early_BFuart", + .write = early_serial_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .setup = bfin_serial_console_setup, + .index = -1, + .data = &bfin_serial_reg, +}; + +struct console __init *bfin_earlyserial_init(unsigned int port, + unsigned int cflag) +{ + struct bfin_serial_port *uart; + struct ktermios t; + + if (port == -1 || port >= nr_ports) + port = 0; + bfin_serial_init_ports(); + bfin_early_serial_console.index = port; +#ifdef CONFIG_KGDB_UART + kgdb_entry_state = 0; + init_kgdb_uart(); #endif + uart = &bfin_serial_ports[port]; + t.c_cflag = cflag; + t.c_iflag = 0; + t.c_oflag = 0; + t.c_lflag = ICANON; + t.c_line = port; + bfin_serial_set_termios(&uart->port, &t, &t); + return &bfin_early_serial_console; +} + +#endif /* CONFIG_SERIAL_BFIN_CONSOLE */ static struct uart_driver bfin_serial_reg = { .owner = THIS_MODULE, diff --git a/include/asm-blackfin/early_printk.h b/include/asm-blackfin/early_printk.h new file mode 100644 index 000000000000..110f1c1f845c --- /dev/null +++ b/include/asm-blackfin/early_printk.h @@ -0,0 +1,28 @@ +/* + * File: include/asm-blackfin/early_printk.h + * Author: Robin Getz