mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-20 11:36:51 +02:00
268 lines
8.1 KiB
Diff
268 lines
8.1 KiB
Diff
|
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
|
||
|
|