This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM4378: DMTimer wakeup from Standby

Part Number: AM4378
Other Parts Discussed in Thread: AM4372

Tool/software: Linux

Is it actually possible to use an overflow interrupt on a DMTimer to wake the processor from Standby?

I'm attempting to write a kernel module to wake my AM4378 from Standby.  I am using Linux kernel version 4.1.18.  I would use DMTimer0, which seems to be on all the time and support wakeup, but I cannot figure out how to access it using the existing drivers.  Therefore, I have decided to use DMTimer3, since it is available.

The timer correctly interrupts when I do not put the processor into Standby, but once the processor enters Standby, the interrupt simply seems to be ignored.  I set the `PRCM_CM_PER_TIMER3_CLKCTRL MODULEMODE` value to `2h` to ensure the clock is always enabled.  If I manually wake the board before the timer elapses, it interrupts at the appropriate time, so I'm pretty sure the interrupt actually occurs while the processor is in standby, but doesn't wake anything.  

The documentation is a bit thin on this subject, and there is some conflicting information.  In both the AM335x (1) and AM437x (2) TRMs, it says the DMTimers (excluding DMTimer0) have no wakeup capabilities, but in the AM335x Power Management Standby User's Guide (3), it gives sample code for waking the AM335x with DMTimer7.

So what is the truth?

(1) : AM335x TRM, page 4437, table 20-3 "Idle/Wakeup Signals | Smart Idle (No wakeup capabilities)" [http://www.ti.com/lit/ug/spruh73p/spruh73p.pdf]

(2) : AM437x TRM, page 2752, table 19-3 "Idle/Wakeup Signals | Smart Idle (No wakeup capabilities)" [http://www.ti.com/lit/ug/spruhl7g/spruhl7g.pdf]

(3) : AM335x Power Management Standby User's Guide [http://processors.wiki.ti.com/index.php/AM335x_Power_Management_Standby_User%27s_Guide#DM_Timer_wakeup]

  • For AM437x devices DMTimer0 is a valid wakeup source. As for Linux support, the actual wiki page is processors.wiki.ti.com/.../Linux_Core_Power_Management_User's_Guide_(v4.1)
  • Thank you for your reply!

    I'm not seeing any information about DMTimer0 as a wake device in the link, but it does have some useful information. I'm also guessing that this means the other DMTimers are *not* valid wakeup sources?

    I will attempt to configure DMTimer0 as a wakeup source using direct register memory manipulation, since I don't see any Linux support for this specific task...
  • As a follow-up, I've configured DMTimer0 to overflow like DMTimer3 using only `readl_relaxed()` direct register access, but now I can't figure out how to correctly handle the DMTimer0 interrupt. With DMTimer3, I was able to use `omap_dm_timer_request()` to get the timer, which allowed me to call ` omap_dm_timer_get_irq()` which gave me the correct IRQ number, which I could use to `request_irq()` through the kernel.

    Without the actual DMTimer struct, I can't get the DMTimer0 IRQ number, so I can't link a function to its interrupt. Is there a way I can do this using lower-level register access?
  • Hi Kevin,

    Kevin Sylvestre93 said:
    Is it actually possible to use an overflow interrupt on a DMTimer to wake the processor from Standby?

    Yes, I think it is possible.

    Kevin Sylvestre93 said:
    I set the `PRCM_CM_PER_TIMER3_CLKCTRL MODULEMODE` value to `2h` to ensure the clock is always enabled.

    You might also check PRCM_CM_PER_L4LS_CLKSTCTRL[13] CLKACTIVITY_TIMER3_GCLK and [8] CLKACTIVITY_L4LS_GCLK.

    Note that with this PRCM_CM_PER_TIMER3_CLKCTRL register you ensure that functional clock is not gated, but you should also verify interface/interconnect clock (L4_PER_CLK, L4LS_GCLK) is not gated, see AM437x TRM:

    6.4.3.2 Standby

    If additional or other wakeup sources are required, the associated peripheral module clock and interconnect clock domain should remain enabled (this may require the associated PLL to remain locked) and the module must be configured appropriately for wakeup by configuring it to generate an interrupt to the MPUSS.

    19.1.2.3 Timer Clock Signals

    Timer[2–7] Clock Signals

    Kevin Sylvestre93 said:
    The documentation is a bit thin on this subject, and there is some conflicting information.  In both the AM335x (1) and AM437x (2) TRMs, it says the DMTimers (excluding DMTimer0) have no wakeup capabilities, but in the AM335x Power Management Standby User's Guide (3), it gives sample code for waking the AM335x with DMTimer7.

    Per mine understanding, you can use both DMTimer0 and DMTimer7 to wake up from Standby mode, but you can use only DMTimer0 to wake up from DeepSleep mode.

    Make sure also you are aligned with AM437x Silicon Errata, Advisory 20 GPTimer: Delay Needed to Read Some GPTimer Registers After Wakeup

    Regards,
    Pavel

  • Kevin Sylvestre93 said:
    As a follow-up, I've configured DMTimer0 to overflow like DMTimer3 using only `readl_relaxed()` direct register access, but now I can't figure out how to correctly handle the DMTimer0 interrupt. With DMTimer3, I was able to use `omap_dm_timer_request()` to get the timer, which allowed me to call ` omap_dm_timer_get_irq()` which gave me the correct IRQ number, which I could use to `request_irq()` through the kernel.

    Without the actual DMTimer struct, I can't get the DMTimer0 IRQ number, so I can't link a function to its interrupt. Is there a way I can do this using lower-level register access?

    Check if the below e2e threads will be in help:

    e2e.ti.com/.../599114

  • Hi Pavel,

    Thanks for you help so far!  I'm back to fiddling with DMTimer 3, and it looks like all the clocks are appropriately enabled (their register values are in the desired state) both before and after entering standby...  

    BEFORE STANDBY:

    • PRCM_CM_PER_L4LS_CLKCTRL   [1-0]   MODULEMODE              = 2h (interface clock, explicitly enabled)
    • PRCM_CM_PER_L4LS_CLKCTRL   [17-16] IDLST                   = 2h (interface clock, module fully functional)
    • PRCM_CM_PER_TIMER3_CLKCTRL [1-0]   MODULEMODE              = 2h (functional clock, explicitly enabled)
    • PRCM_CM_PER_TIMER3_CLKCTRL [17-16] IDLEST                  = 2h (functional clock, module fully functional)
    • PRCM_CM_PER_L4LS_CLKSTCTRL [8]     CLKACTIVITY_L4LS_GCLK   = 1h (interface clock active)
    • PRCM_CM_PER_L4LS_CLKSTCTRL [13]    CLKACTIVITY_TIMER3_GCLK = 1h (functional clock active)
    • PRCM_CM_PER_L4LS_CLKSTCTRL [1-0]   CLKTRCTRL               = 0h (NO_SLEEP: Sleep transition cannot be initiated...)

    Note that re-reading PRCM_CM_PER_L4LS_CLKSTCTRL [1-0] CLKTRCTRL after its initial setting yields 2h; not sure if this is an issue or not, but it doesn't seem to stay set on "NO_SLEEP".  

    With this configuration, the timer correctly interrupts after an appropriate period, as long as the processor is not set to Standby.  If the processor is set to standby, the timer interrupts after the system wakes.  For example:

    Normally...

        # echo 10000 > /sys/kernel/standy-timer/standby_time_ms

        [  907.118484] starting standby timer

        # [  917.117724] standby timer expired

    As you can see, roughly 10000ms has passed (907.118s --> 917.118s = 10000ms).

    Alternatively, running the timer with Standby...

        # echo 10000 > /sys/kernel/standy-timer/standby_time_ms

        [ 1157.227378] starting standby timer

        # echo "standby" > /sys/power/state

        [ 1161.360338] PM: Syncing filesystems ... done.

        [ 1161.401763] Freezing user space processes ... (elapsed 0.001 seconds) done.

        [ 1161.410082] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.

        [ 1161.427833] Suspending console(s) (use no_console_suspend to debug)

        < arbitrary delay, then manually wake with UART keystroke >

        [ 1161.458759] omap_hwmod: adc_tsc: _wait_target_disable failed

        [ 1161.460424] PM: suspend of devices complete after 25.558 msecs

        [ 1161.461716] PM: late suspend of devices complete after 1.269 msecs

        [ 1161.463291] PM: noirq suspend of devices complete after 1.553 msecs

        [ 1161.463298] PM: Successfully put all powerdomains to target state

        [ 1161.463298] PM: Wakeup source UART

        [ 1161.479192] PM: noirq resume of devices complete after 15.856 msecs

        [ 1161.480508] PM: early resume of devices complete after 1.045 msecs

        [ 1161.481089] net eth0: initializing cpsw version 1.15 (0)

        [ 1161.482274] libphy: PHY 4a101000.mdio:00 not found

        [ 1161.482283] net eth0: phy 4a101000.mdio:00 not found on slave 0

        [ 1161.557224] net eth0: phy found : id is : 0x2000a240

        [ 1161.762268] PM: resume of devices complete after 281.734 msecs

        [ 1161.844410] Restarting tasks ... done.

        # [ 1167.227358] standby timer expired

    Again, the timer expired in 10000ms, according to the system time (1157.227s --> 1167.227s = 10000ms).  This leads me to believe that most likely the clocks aren't staying enabled when the processor goes to standby.  

    I'm wondering if the highlighted section may be related:

    6.4.3.2 Standby

    If additional or other wakeup sources are required, the associated peripheral module clock and interconnect clock domain should remain enabled (this may require the associated PLL to remain locked) and the module must be configured appropriately for wakeup by configuring it to generate an interrupt to the MPUSS.

    How do I keep the PLL locked and/or keep it from turning off when I enter standby?  In section 6.4.3 of the TRM, Table 6-13 indicates that the Main Oscillator should remain on in standby, but that all DPLLs are in Bypass.  I'm using `CLK_M_OSC` as the clock source for DMTimer 3, so I guess this means that TIMER3_GCLK (my PICLKTIMER Functional clock) is probably okay, but maybe I'm still losing L4LS_GCLK (my PICLKOCP Interface clock).  Ideally, I'd prefer a method which allows me to leverage existing drivers, but at this point, that isn't very important.

  • Kevin Sylvestre93 said:
    PRCM_CM_PER_L4LS_CLKCTRL   [17-16] IDLST                   = 2h (interface clock, module fully functional)

    Kevin Sylvestre93 said:
    PRCM_CM_PER_TIMER3_CLKCTRL [17-16] IDLEST                  = 2h (functional clock, module fully functional)

    Can you check again that value? IDLEST = 0x2 is Idle, not functional. The correct value should be 0x0

    Kevin Sylvestre93 said:
    How do I keep the PLL locked and/or keep it from turning off when I enter standby?  In section 6.4.3 of the TRM, Table 6-13 indicates that the Main Oscillator should remain on in standby, but that all DPLLs are in Bypass.  I'm using `CLK_M_OSC` as the clock source for DMTimer 3, so I guess this means that TIMER3_GCLK (my PICLKTIMER Functional clock) is probably okay, but maybe I'm still losing L4LS_GCLK (my PICLKOCP Interface clock).  Ideally, I'd prefer a method which allows me to leverage existing drivers, but at this point, that isn't very important.

    L4LS_GCLK is generated by Core PLL, make sure Core PLL is active during Standby. You should check Core PLL registers before Standby and during standby (with CCS/JTAG). See AM437x TRM, section 6.6.7 Core PLL Description

    CORE_CLKOUTM4 -> /2 -> L4_PER_CLK -> L4LS_GCLK -> DMTimer3

    You should also check TIMER3_GCLK and L4LS_GCLK PRMC related registers during Standby, using CCS/JTAG. See belowwiki page for more info:

    Regards,
    Pavel

  • Ah, yes, sorry that was a 'copy and paste' error...  They are correctly '0h'.  I'll follow up once I can confirm the register values with JTAG during standby.

  • Kevin,

    Regarding DMTIMER3 functional clock, you can also check it status (beside dumping PRCM registers during standby through CCS/JTAG) through tracing the linux kernel timer driver. This clock is referred as timer3_fck:

    linux-kernel/drivers/clk/ti/clk-43xx.c
    linux-kernel/arch/arm/boot/dts/am43xx-clocks.dtsi
    linux-kernel/arch/arm/boot/dts/am4372.dtsi

    The timer driver is:

    linux-kernel/arch/arm/mach-omap2/timer.c
    linux-kernel/arch/arm/plat-omap/dmtimer.c

    You can track (with printk and/or breakpoints) how suspend/resume flow goes through these two files and how fck is impacted by suspend/resume.

    Regarding DMTIMER3 interconnect clock and DPLL_CORE, you can trace the below code:

    (dpll_core_ck -> dpll_core_x2_ck -> dpll_core_m4_ck -> sysclk_div -> dpll_core_m4_div2_ck -> l4ls_gclk)

    linux-kernel/drivers/clk/ti/dpll.c

    Regards,
    Pavel
  • Hi Pavel,

    I was able to confirm the register values during standby using some of the methods detailed here (http://processors.wiki.ti.com/index.php/Debugging_AM335x_Suspend-Resume_Issues#Inspecting_the_SoC_state_immediately_prior_to_entering_DeepSleep0).

    As I thought, the register values are being forced to a different state as a result of entering standby.  The even though before standby I set PRCM_CM_PER_L4LS_CLKCTRL and PRCM_CM_PER_TIMER3_CLKCTRL such that these modules are explicitly enabled (MODULEMODE = 2h), PRCM_CM_PER_TIMER3_CLKCTRL shows IDLEST= 3h during standby, indicating the module is disabled.

    I'm using the standard linux echo "standby" > /sys/kernel/power command to enter standby, which I believe calls into the firmware on the on-chip Cortex M3, which I am not very familiar with.  Will I need to modify the firmware here to essentially change standby mode, or is it possible to just keep these clocks alive from the A9 core?

    Thanks,

    Kevin

  • I have attached 4 register dumps I have performed using modified versions of the scripts from the link above.  The files are as follows:

    • "...163117.rd1.txt" = captured immediately after logging into linux as root
    • "...163138.rd1.txt" = captured immediately after loading my custom module (the __init function contains some register manipulation)
    • "...163155.rd1.txt" = captured immediately after setting a timeout value on DMTimer 3 (note that for some reason the register values for DMTImer3 all show 0... other methods of reading the registers show the correct information
    • "...163209.rd1.txt" = captured immediately after entering standby (with the M3 held in the loop)

    /cfs-file/__key/communityserver-discussions-components-files/791/8255.am335x_2D00_ctt_5F00_2017_2D00_10_2D00_16_5F00_163117.txt

    /cfs-file/__key/communityserver-discussions-components-files/791/3060.am335x_2D00_ctt_5F00_2017_2D00_10_2D00_16_5F00_163138.txt

    /cfs-file/__key/communityserver-discussions-components-files/791/am335x_2D00_ctt_5F00_2017_2D00_10_2D00_16_5F00_163155.txt

    /cfs-file/__key/communityserver-discussions-components-files/791/am335x_2D00_ctt_5F00_2017_2D00_10_2D00_16_5F00_163209.txt

  • Kevin,

    Kevin Sylvestre93 said:

    As I thought, the register values are being forced to a different state as a result of entering standby.  The even though before standby I set PRCM_CM_PER_L4LS_CLKCTRL and PRCM_CM_PER_TIMER3_CLKCTRL such that these modules are explicitly enabled (MODULEMODE = 2h), PRCM_CM_PER_TIMER3_CLKCTRL shows IDLEST= 3h during standby, indicating the module is disabled.

    I'm using the standard linux echo "standby" > /sys/kernel/power command to enter standby, which I believe calls into the firmware on the on-chip Cortex M3, which I am not very familiar with.  Will I need to modify the firmware here to essentially change standby mode, or is it possible to just keep these clocks alive from the A9 core?

    Before debugging Cortex-M3 firmware, I would suggest you to debug Timer driver. Check my hints in the below post:

    Regards,
    Pavel

  • Kevin Sylvestre93 said:

    I have attached 4 register dumps I have performed using modified versions of the scripts from the link above.  The files are as follows:

    • "...163117.rd1.txt" = captured immediately after logging into linux as root
    • "...163138.rd1.txt" = captured immediately after loading my custom module (the __init function contains some register manipulation)
    • "...163155.rd1.txt" = captured immediately after setting a timeout value on DMTimer 3 (note that for some reason the register values for DMTImer3 all show 0... other methods of reading the registers show the correct information
    • "...163209.rd1.txt" = captured immediately after entering standby (with the M3 held in the loop)

    From the last log (after enter standby), I see that timer3_fck goes to disable state, while l4ls_gclk stays enabled. You can focus on debugging timer3_fck impacted by the suspend/resume flow in the timer driver. See my hints in below e2e post:

    Regards,
    Pavel