diff options
author | Andy Green <andy@openmoko.com> | 2008-11-19 17:09:52 +0000 |
---|---|---|
committer | Andy Green <agreen@pads.home.warmcat.com> | 2008-11-19 17:09:52 +0000 |
commit | 6d3fb8064a9336438137d7987bb1e58433528d59 (patch) | |
tree | 122c31d6b17d6aec7881bd03cdaec59958b29da1 /drivers/serial | |
parent | 7e7270be85be64eeee2196bbc8cd877395870f40 (diff) |
introduce-resume-exception-capture.patch
This patch introduces a new resume debugging concept: if we
get an OOPS inbetween starting suspend and finishing resume, it
uses a new "emergency spew" device similar to BUT NOT REQUIRING
CONFIG_DEBUG_LL to dump the syslog buffer and then the OOPS
on the debug device defined by the existing CONFIG_DEBUG_S3C_UART
index. But neither CONFIG_DEBUG_LL nor the S3C low level configs
are needed to use this feature.
Another difference between this feature and CONFIG_DEBUG_LL is that
it does not affect resume timing, ordering or UART traffic UNLESS
there is an OOPS during resume.
The patch adds three global exports, one to say if we are inside
suspend / resume, and two callbacks for printk() to use to init
and dump the emergency data. The callbacks are set in s3c serial
device init, but the whole structure is arch independent.
Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/s3c2410.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index 36b5fcc9f46..bcf0fdb9096 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -26,6 +26,7 @@ #include <plat/regs-serial.h> #include <mach/regs-gpio.h> +#include <mach/regs-clock.h> /* structures */ @@ -936,6 +937,69 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { #endif }; +static void s3c24xx_serial_force_debug_port_up(void) +{ + struct s3c24xx_uart_port *ourport = &s3c24xx_serial_ports[ + CONFIG_DEBUG_S3C_UART]; + struct s3c24xx_uart_clksrc *clksrc = NULL; + struct clk *clk = NULL; + unsigned long tmp; + + s3c24xx_serial_getclk(&ourport->port, &clksrc, &clk, 115200); + + tmp = __raw_readl(S3C2410_CLKCON); + + /* re-start uart clocks */ + tmp |= S3C2410_CLKCON_UART0; + tmp |= S3C2410_CLKCON_UART1; + tmp |= S3C2410_CLKCON_UART2; + + __raw_writel(tmp, S3C2410_CLKCON); + udelay(10); + + s3c24xx_serial_setsource(&ourport->port, clksrc); + + if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { + clk_disable(ourport->baudclk); + ourport->baudclk = NULL; + } + + clk_enable(clk); + + ourport->clksrc = clksrc; + ourport->baudclk = clk; +} + +static void s3c2410_printascii(const char *sz) +{ + struct s3c24xx_uart_port *ourport = &s3c24xx_serial_ports[ + CONFIG_DEBUG_S3C_UART]; + struct uart_port *port = &ourport->port; + + /* 8 N 1 */ + wr_regl(port, S3C2410_ULCON, (rd_regl(port, S3C2410_ULCON)) | 3); + /* polling mode */ + wr_regl(port, S3C2410_UCON, (rd_regl(port, S3C2410_UCON) & ~0xc0f) | 5); + /* disable FIFO */ + wr_regl(port, S3C2410_UFCON, (rd_regl(port, S3C2410_UFCON) & ~0x01)); + /* fix baud rate */ + wr_regl(port, S3C2410_UBRDIV, 26); + + while (*sz) { + int timeout = 10000000; + + /* spin on it being busy */ + while ((!(rd_regl(port, S3C2410_UTRSTAT) & 2)) && timeout--) + ; + + /* transmit register */ + wr_regl(port, S3C2410_UTXH, *sz); + + sz++; + } +} + + /* s3c24xx_serial_resetport * * wrapper to call the specific reset for this port (reset the fifos @@ -1113,6 +1177,11 @@ static int s3c24xx_serial_resume(struct platform_device *dev) static int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info) { + /* set up the emergency debug UART functions */ + + printk_emergency_debug_spew_init = s3c24xx_serial_force_debug_port_up; + printk_emergency_debug_spew_send_string = s3c2410_printascii; + dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); return platform_driver_register(drv); } |