Skip to content

Commit

Permalink
Blackfin arch: Initial patch to add earlyprintk support
Browse files Browse the repository at this point in the history
This allows debugging of problems which happen eary in the kernel
boot process (after bootargs are parsed, but before serial subsystem
is fully initialized)

Signed-off-by: Robin Getz <robin.getz@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
  • Loading branch information
Robin Getz authored and Bryan Wu committed Oct 9, 2007
1 parent 1d487f4 commit 0ae5364
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 29 deletions.
3 changes: 2 additions & 1 deletion Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]]

Expand Down
14 changes: 14 additions & 0 deletions arch/blackfin/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions arch/blackfin/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
161 changes: 161 additions & 0 deletions arch/blackfin/kernel/early_printk.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* 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 <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

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);
129 changes: 101 additions & 28 deletions drivers/serial/bfin_5xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 0ae5364

Please sign in to comment.