summaryrefslogtreecommitdiff
path: root/target/linux/s3c24xx/patches/0192-introduce-resume-exception-capture.patch.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/s3c24xx/patches/0192-introduce-resume-exception-capture.patch.patch')
-rwxr-xr-xtarget/linux/s3c24xx/patches/0192-introduce-resume-exception-capture.patch.patch267
1 files changed, 267 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0192-introduce-resume-exception-capture.patch.patch b/target/linux/s3c24xx/patches/0192-introduce-resume-exception-capture.patch.patch
new file mode 100755
index 0000000000..6f9b54de3d
--- /dev/null
+++ b/target/linux/s3c24xx/patches/0192-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
+