* Tero Kristo <[email protected]> [100217 06:01]:
> From: Tero Kristo <[email protected]>
>
> 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 <[email protected]>
Kevin, do you have any comments on this?
Regards,
Tony
> ---
> 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html