From: Tero Kristo <tero.kristo@xxxxxxxxx> This patch contains following improvements: - Only RX interrupt will now kick the sleep prevent timer - TX fifo status is checked before disabling clocks, this will prevent on-going transmission to be cut - Smartidle is now enabled/disabled only while switching clocks, as having smartidle enabled while RX/TX prevents any interrupts from being received from UART module - Sleep prevent timer is changed to use timespec instead of a jiffy timer as jiffy timers are not valid within idle loop (tick scheduler is stopped) - Added RX ignore timer for ignoring the first character received during first millisecond of wakeup, this prevents garbage character to be receive in low sleep states Signed-off-by: Tero Kristo <tero.kristo@xxxxxxxxx> --- arch/arm/mach-omap2/serial.c | 98 +++++++++++++++++++++++++++++------------ 1 files changed, 69 insertions(+), 29 deletions(-) diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 5f3035e..f49c465 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -29,6 +29,8 @@ #include <plat/clock.h> #include <plat/control.h> +#include <asm/div64.h> + #include "prm.h" #include "pm.h" #include "prm-regbits-34xx.h" @@ -42,13 +44,14 @@ * disabled via sysfs. This also causes that any deeper omap sleep states are * blocked. */ -#define DEFAULT_TIMEOUT 0 +#define DEFAULT_TIMEOUT (0LL * NSEC_PER_SEC) struct omap_uart_state { int num; int can_sleep; - struct timer_list timer; - u32 timeout; + struct timespec expire_time; + struct timespec garbage_time; + u64 timeout; void __iomem *wk_st; void __iomem *wk_en; @@ -243,6 +246,9 @@ static inline void omap_uart_save_context(struct omap_uart_state *uart) {} static inline void omap_uart_restore_context(struct omap_uart_state *uart) {} #endif /* CONFIG_PM && CONFIG_ARCH_OMAP3 */ +static void omap_uart_smart_idle_enable(struct omap_uart_state *uart, + int enable); + static inline void omap_uart_enable_clocks(struct omap_uart_state *uart) { if (uart->clocked) @@ -250,8 +256,13 @@ static inline void omap_uart_enable_clocks(struct omap_uart_state *uart) clk_enable(uart->ick); clk_enable(uart->fck); + omap_uart_smart_idle_enable(uart, 0); uart->clocked = 1; omap_uart_restore_context(uart); + + /* Set up garbage timer to ignore RX during first 1ms */ + getrawmonotonic(&uart->garbage_time); + timespec_add_ns(&uart->garbage_time, NSEC_PER_MSEC); } #ifdef CONFIG_PM @@ -263,6 +274,7 @@ static inline void omap_uart_disable_clocks(struct omap_uart_state *uart) omap_uart_save_context(uart); uart->clocked = 0; + omap_uart_smart_idle_enable(uart, 1); clk_disable(uart->ick); clk_disable(uart->fck); } @@ -320,12 +332,11 @@ static void omap_uart_block_sleep(struct omap_uart_state *uart) { omap_uart_enable_clocks(uart); - omap_uart_smart_idle_enable(uart, 0); uart->can_sleep = 0; - if (uart->timeout) - mod_timer(&uart->timer, jiffies + uart->timeout); - else - del_timer(&uart->timer); + if (uart->timeout) { + getrawmonotonic(&uart->expire_time); + timespec_add_ns(&uart->expire_time, uart->timeout); + } } static void omap_uart_allow_sleep(struct omap_uart_state *uart) @@ -338,25 +349,24 @@ static void omap_uart_allow_sleep(struct omap_uart_state *uart) if (!uart->clocked) return; - omap_uart_smart_idle_enable(uart, 1); uart->can_sleep = 1; - del_timer(&uart->timer); -} - -static void omap_uart_idle_timer(unsigned long data) -{ - struct omap_uart_state *uart = (struct omap_uart_state *)data; - - omap_uart_allow_sleep(uart); } void omap_uart_prepare_idle(int num) { struct omap_uart_state *uart; + struct timespec t; list_for_each_entry(uart, &uart_list, node) { + if (num == uart->num && !uart->can_sleep && uart->timeout) { + getrawmonotonic(&t); + if (timespec_compare(&t, &uart->expire_time) > 0) + uart->can_sleep = 1; + } if (num == uart->num && uart->can_sleep) { - omap_uart_disable_clocks(uart); + if (serial_read_reg(uart->p, UART_LSR) & + UART_LSR_TEMT) + omap_uart_disable_clocks(uart); return; } } @@ -381,6 +391,7 @@ void omap_uart_resume_idle(int num) /* Check for normal UART wakeup */ if (__raw_readl(uart->wk_st) & uart->wk_mask) omap_uart_block_sleep(uart); + return; } } @@ -399,11 +410,18 @@ int omap_uart_can_sleep(void) { struct omap_uart_state *uart; int can_sleep = 1; + struct timespec t; list_for_each_entry(uart, &uart_list, node) { if (!uart->clocked) continue; + if (!uart->can_sleep && uart->timeout) { + getrawmonotonic(&t); + if (timespec_compare(&t, &uart->expire_time) > 0) + uart->can_sleep = 1; + } + if (!uart->can_sleep) { can_sleep = 0; continue; @@ -428,10 +446,25 @@ int omap_uart_can_sleep(void) static irqreturn_t omap_uart_interrupt(int irq, void *dev_id) { struct omap_uart_state *uart = dev_id; + u8 lsr; + int ret = IRQ_NONE; + struct timespec t; - omap_uart_block_sleep(uart); + lsr = serial_read_reg(uart->p, UART_LSR); + /* Check for receive interrupt */ + if (lsr & UART_LSR_DR) { + omap_uart_block_sleep(uart); + if (uart->garbage_time.tv_sec) { + getrawmonotonic(&t); + if (timespec_compare(&t, &uart->garbage_time) < 0) { + serial_read_reg(uart->p, UART_RX); + uart->garbage_time.tv_sec = 0; + ret = IRQ_HANDLED; + } + } + } - return IRQ_NONE; + return ret; } static void omap_uart_idle_init(struct omap_uart_state *uart) @@ -441,10 +474,12 @@ static void omap_uart_idle_init(struct omap_uart_state *uart) uart->can_sleep = 0; uart->timeout = DEFAULT_TIMEOUT; - setup_timer(&uart->timer, omap_uart_idle_timer, - (unsigned long) uart); - if (uart->timeout) - mod_timer(&uart->timer, jiffies + uart->timeout); + + if (uart->timeout) { + getrawmonotonic(&uart->expire_time); + timespec_add_ns(&uart->expire_time, uart->timeout); + } + omap_uart_smart_idle_enable(uart, 0); if (cpu_is_omap34xx()) { @@ -527,8 +562,12 @@ static ssize_t sleep_timeout_show(struct device *dev, struct platform_device, dev); struct omap_uart_state *uart = container_of(pdev, struct omap_uart_state, pdev); + u64 val; + + val = uart->timeout; - return sprintf(buf, "%u\n", uart->timeout / HZ); + do_div(val, NSEC_PER_SEC); + return sprintf(buf, "%llu\n", val); } static ssize_t sleep_timeout_store(struct device *dev, @@ -546,10 +585,11 @@ static ssize_t sleep_timeout_store(struct device *dev, return -EINVAL; } - uart->timeout = value * HZ; - if (uart->timeout) - mod_timer(&uart->timer, jiffies + uart->timeout); - else + uart->timeout = (u64)value * NSEC_PER_SEC; + if (uart->timeout) { + getrawmonotonic(&uart->expire_time); + timespec_add_ns(&uart->expire_time, uart->timeout); + } else /* A zero value means disable timeout feature */ omap_uart_block_sleep(uart); -- 1.5.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html