>-----Original Message-----
>From: ext Kevin Hilman [mailto:[email protected]] 
>Sent: 24 February, 2010 18:05
>To: Kristo Tero (Nokia-D/Tampere)
>Cc: [email protected]
>Subject: Re: [PATCHv5] OMAP3: Serial: Improved sleep logic
>
>Tero Kristo <[email protected]> writes:
>
>> 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)
>
>Could also probably use hrtimers for this.

Maybe, I didn't try this out though. It is possible that there are some issues 
as hrtimers use ktime() as far as I understand and it is not accessible during 
suspend. There is a separate issue with this patch and suspend though, I am 
currently working on a fix for that. Suspend skewes the timebase even more, and 
we get expire timers that point far into the future after suspend.

>
>That being said, I'm not sure what is the problem you're trying to
>solve with this change.  I don't see any timings that are timing events
>inside the idle loop.

Here is the call-chain that currently accesses jiffies count incorrectly:

cpu_idle:
   tick_nohz_stop_sched_tick(1); /* jiffy timer stops here */
   omap_sram_idle();
     omap_uart_prepare_idle();
     asm("wfi"); /* sleep here for N seconds */
     omap_uart_resume_idle(); /* This call uses incorrect jiffy timer now */
   tick_nohz_restart_sched_tick(); /* jiffy timer restarted here, and jiffy 
timer is refreshed also to correct value */

>
>> - 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]>
>
>Something is still not quite right here.
>
>This doesn't work on my n900 here when testing this patch on top of
>the PM branch.  The default is now a default timeout of zero.  When I
>enable a 5 sec. timeout for UART2 on RX-51, when the timer expires, I
>no longer have response on the console.
>
>To test, I booted my n900 with init=/bin/sh to avoid all the setup
>done by /sbin/preinit.  Then I enabled a timeout for UART2 only, and
>then the console hangs.
>
>Here's my hunch as to what's happening:
>
>I think the problem is a deadlock in getrawmonotonic().  Nested calls
>here will deadlock due to the xtime_lock being held.

Looking at the seqlock code, I think a seqlock reader can "hang" only in a case 
where someone is constantly writing the seqlock. And, as we are inside 
interrupt, this should not be possible.

>When updading the timeout, sleep_timeout_store() does a
>getrawmonotonic() to update the expiry time.  While this happening,
>the UART interrupt could fire, causing an omap_uart_block_sleep()
>which would also getrawmonotonic() and deadlock in interrupt mode.

It does not really explain why it hangs after the 5 second period though, as 
the device has called getrawmonotonic several times by this already. I have not 
seen this kind of behavior in my testing, even while fiddling with the 
sleep_timeout_store().

Anyway, I'll attempt to re-run my test on the latest PM / master branches and 
see what happens.

>
>Kevin
>
>> ---
>>  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

Reply via email to