diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/1192-introduce-resume-exception-capture.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/1192-introduce-resume-exception-capture.patch.patch | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/1192-introduce-resume-exception-capture.patch.patch b/target/linux/s3c24xx/patches-2.6.26/1192-introduce-resume-exception-capture.patch.patch new file mode 100755 index 000000000..6f9b54de3 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.26/1192-introduce-resume-exception-capture.patch.patch @@ -0,0 +1,267 @@ +From 081a639e9274072fbd34ee778188332ed972734e Mon Sep 17 00:00:00 2001 +From: Andy Green <andy@openmoko.com> +Date: Fri, 25 Jul 2008 23:06:16 +0100 +Subject: [PATCH] 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> +--- + drivers/serial/s3c2410.c | 77 +++++++++++++++++++++++++++++++++++++++++++++- + include/linux/kernel.h | 2 + + include/linux/suspend.h | 6 +++ + kernel/power/main.c | 7 ++++ + kernel/printk.c | 42 +++++++++++++++++++++++++ + 5 files changed, 133 insertions(+), 1 deletions(-) + +diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c +index 32cceae..f20f63b 100644 +--- a/drivers/serial/s3c2410.c ++++ b/drivers/serial/s3c2410.c +@@ -81,6 +81,7 @@ + + #include <asm/plat-s3c/regs-serial.h> + #include <asm/arch/regs-gpio.h> ++#include <asm/arch/regs-clock.h> + + /* structures */ + +@@ -1170,7 +1171,13 @@ static int s3c24xx_serial_init(struct platform_driver *drv, + struct s3c24xx_uart_info *info) + { + dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); +- return platform_driver_register(drv); ++ /* 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); + } + + +@@ -1647,6 +1654,74 @@ static struct platform_driver s3c2412_serial_drv = { + }, + }; + ++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 ++ * and the settings) ++*/ + + static inline int s3c2412_serial_init(void) + { +diff --git a/include/linux/kernel.h b/include/linux/kernel.h +index 2e70006..fcad89e 100644 +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -198,6 +198,8 @@ extern int __ratelimit(int ratelimit_jiffies, int ratelimit_burst); + extern int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst); + extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, + unsigned int interval_msec); ++extern void (*printk_emergency_debug_spew_init)(void); ++extern void (*printk_emergency_debug_spew_send_string)(const char *); + #else + static inline int vprintk(const char *s, va_list args) + __attribute__ ((format (printf, 1, 0))); +diff --git a/include/linux/suspend.h b/include/linux/suspend.h +index a697742..8164615 100644 +--- a/include/linux/suspend.h ++++ b/include/linux/suspend.h +@@ -140,6 +140,12 @@ struct pbe { + struct pbe *next; + }; + ++/** ++ * global indication we are somewhere between start of suspend and end of ++ * resume, nonzero is true ++ */ ++extern int global_inside_suspend; ++ + /* mm/page_alloc.c */ + extern void mark_free_pages(struct zone *zone); + +diff --git a/kernel/power/main.c b/kernel/power/main.c +index 6a6d5eb..1169648 100644 +--- a/kernel/power/main.c ++++ b/kernel/power/main.c +@@ -130,6 +130,9 @@ static inline int suspend_test(int level) { return 0; } + + #endif /* CONFIG_PM_SLEEP */ + ++int global_inside_suspend; ++EXPORT_SYMBOL(global_inside_suspend); ++ + #ifdef CONFIG_SUSPEND + + /* This is just an arbitrary number */ +@@ -258,6 +261,8 @@ int suspend_devices_and_enter(suspend_state_t state) + if (!suspend_ops) + return -ENOSYS; + ++ global_inside_suspend = 1; ++ + if (suspend_ops->begin) { + error = suspend_ops->begin(state); + if (error) +@@ -297,6 +302,8 @@ int suspend_devices_and_enter(suspend_state_t state) + Close: + if (suspend_ops->end) + suspend_ops->end(); ++ global_inside_suspend = 0; ++ + return error; + } + +diff --git a/kernel/printk.c b/kernel/printk.c +index e2129e8..00df30e 100644 +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -32,8 +32,12 @@ + #include <linux/security.h> + #include <linux/bootmem.h> + #include <linux/syscalls.h> ++#include <linux/jiffies.h> ++#include <linux/suspend.h> + + #include <asm/uaccess.h> ++#include <asm/plat-s3c24xx/neo1973.h> ++#include <asm/arch/gta02.h> + + /* + * Architectures can override it: +@@ -67,6 +71,12 @@ int console_printk[4] = { + int oops_in_progress; + EXPORT_SYMBOL(oops_in_progress); + ++void (*printk_emergency_debug_spew_init)(void) = NULL; ++EXPORT_SYMBOL(printk_emergency_debug_spew_init); ++ ++void (*printk_emergency_debug_spew_send_string)(const char *) = NULL; ++EXPORT_SYMBOL(printk_emergency_debug_spew_send_string); ++ + /* + * console_sem protects the console_drivers list, and also + * provides serialisation for access to the entire console +@@ -718,6 +728,38 @@ asmlinkage int vprintk(const char *fmt, va_list args) + printed_len += vscnprintf(printk_buf + printed_len, + sizeof(printk_buf) - printed_len, fmt, args); + ++ /* if you're debugging resume, the normal methods can change resume ++ * ordering behaviours because their debugging output is synchronous ++ * (ie, CONFIG_DEBUG_LL). If your problem is an OOPS, this code ++ * will not affect the speed and duration and ordering of resume ++ * actions, but will give you a chance to read the full undumped ++ * syslog AND the OOPS data when it happens ++ * ++ * if you support it, your debug device init can override the exported ++ * emergency_debug_spew_init and emergency_debug_spew_send_string to ++ * usually force polling or bitbanging on your debug console device ++ */ ++ if (oops_in_progress && global_inside_suspend && ++ printk_emergency_debug_spew_init && ++ printk_emergency_debug_spew_send_string) { ++ unsigned long cur_index; ++ char ch[2]; ++ ++ if (global_inside_suspend == 1) { ++ (printk_emergency_debug_spew_init)(); ++ ++ ch[1] = '\0'; ++ cur_index = con_start; ++ while (cur_index != log_end) { ++ ch[0] = LOG_BUF(cur_index); ++ (printk_emergency_debug_spew_send_string)(ch); ++ cur_index++; ++ } ++ global_inside_suspend++; /* only once */ ++ } ++ (printk_emergency_debug_spew_send_string)(printk_buf); ++ } ++ + /* + * Copy the output into log_buf. If the caller didn't provide + * appropriate log level tags, we insert them here +-- +1.5.6.3 + |