diff options
-rw-r--r-- | drivers/serial/s3c2410.c | 69 | ||||
-rw-r--r-- | include/linux/kernel.h | 2 | ||||
-rw-r--r-- | include/linux/suspend.h | 6 | ||||
-rw-r--r-- | kernel/power/main.c | 7 | ||||
-rw-r--r-- | kernel/printk.c | 41 |
5 files changed, 125 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); } diff --git a/include/linux/kernel.h b/include/linux/kernel.h index fba141d3ca0..2e8c7fcab9c 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -225,6 +225,8 @@ extern struct ratelimit_state printk_ratelimit_state; extern int printk_ratelimit(void); 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 2ce8207686e..487699132d7 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -146,6 +146,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 19122cf6d82..3fdc1f42376 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -132,6 +132,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 #ifdef CONFIG_PM_TEST_SUSPEND @@ -322,6 +325,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) @@ -365,6 +370,8 @@ int suspend_devices_and_enter(suspend_state_t state) Close: if (suspend_ops->end) suspend_ops->end(); + global_inside_suspend = 0; + return error; Recover_platform: diff --git a/kernel/printk.c b/kernel/printk.c index f492f1583d7..c81ad8638aa 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 @@ -668,6 +678,37 @@ 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 |