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/LINUXSDK-OMAPL138: GPIO wakeup

Part Number: LINUXSDK-OMAPL138
Other Parts Discussed in Thread: OMAPL138, OMAP-L138

Tool/software: Linux

Hi,

Would like to wakeup omapl138 from deepsleep mode using GP0[8].

The device is entering the deepsleep mode upon doing echo mem > /sys/power/state.

Would like to wake the device up from deep sleep mode using the interrupt on GP0[8] by pulling the line low.

Please share your inputs on how to proceed on this.

  • Which Processor SDK Linux is this?

    Best Regards,
    Yordan
  • The Linux kernel version is 3.10.12

  • Hello,

    I'm sorry, we can't support the v3.10 kernel as it is beyond our support window. You are welcome to refer to our Processor SDK v5.03 release, the current release, to see if it would help guide you on how to use this feature on a much older kernel.

    Thank you for your post.
  • Ok. Thank you RonB

    Does the SDK V5.03 have the Deepsleep Wakeup using external interrupt implementation.

  • Hello,

    While it is quite old, I believe it is still valid:

    e2e.ti.com/.../867672

    Essentially, the SoC doesn't support using a GPIO to wakeup the device from deepsleep.

    I hope this helps you.
  • Shyam,

    Here's an excerpt from the OMAP-L138 Technical Reference Manual regarding wakeup from deepsleep using a pin:

    You have a couple options:

    1. Use a higher power standby mode that allows wakeup from GPIO.
    2. Adjust your hardware to enable wakeup via deepsleep pin.

    Best regards,
    Brad

  • Hi Brad,

    Thank you for the comments.

    Currently I'm entering the deepsleep mode using the echo mem > /sys/power/state which actually calls the davinci_pm_suspend in the pm.c file under arch/arm/mach-davinci folder as mentione below.

    I am configuring the GPO8 using pinmux register 0 by setting value of 8 in the offset PINMUX0_31_28. Before i made changes to register PINMUX0_31_28 the RTC_ALARM was configured in our platform. I commented out the RTC_ALARM mux config and changed it to GP0[8]

    To wake the device from deepsleep mode I,m using a switch to provide 3.3 Volts externally using a switch. Initial position of the switch is set in manner it will generate 0V and when we want to wake the device from deepsleep mode we are changing the position of switch such that it will generate a 3.3 V.

    My concern here is to wake the device from deepsleep mode should i need to add some more code in the existing davinci_pm_suspend function such that it will wake the device from the deepsleep mode.

    IF the changes need to be added for deepsleep wake up logic. Kindly share some info on the same.

    Please correct me if there is somethng wrong in the existing method i,m following.



    static void davinci_pm_suspend(void)
    {
    unsigned val;

    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Switch CPU PLL to bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    udelay(PLL_BYPASS_TIME);

    /* Powerdown CPU PLL */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);
    }

    /* Configure sleep count in deep sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
    val |= pdata->sleepcount;
    __raw_writel(val, pdata->deepsleep_reg);

    /* System goes to sleep in this call */
    davinci_sram_suspend(pdata);

    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* put CPU PLL in reset */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* put CPU PLL in power down */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* wait for CPU PLL reset */
    udelay(PLL_RESET_TIME);

    /* bring CPU PLL out of reset */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Wait for CPU PLL to lock */
    udelay(PLL_LOCK_TIME);

    /* Remove CPU PLL from bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLENSRC;
    val |= PLLCTL_PLLEN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);
    }
    }
  • What you describe sounds similar to Section 10.10.4 Entering/Exiting Deep Sleep Mode Using Software Handshaking.  Is that what you're trying to implement? A couple other questions:

    1. While you're in deepsleep, do you confirm that the input clock has stopped completely (i.e. crystal stops oscillating)?
    2. When GP0[8] goes high to wake the device, do you see the clock turn back on?
    3. Are you able to connect with JTAG after a failed wakeup?  If so, we may want to create some scripts for dumping various registers so you can compare before/after to see what's not being properly preserved through the DeepSleep.

  • Hi Bradd,

    Thank you for the quick reply.


    Did you get a chance to look at my code snippet shared in earlier mail. Can you review and confirm if any changes are required.

    Our idea is enter deep sleep using echo mem > /sys/power/state. and exit deep sleep using the what is mentioned in 10.10.4.2

    10.10.4.2 Exiting Deep Sleep Mode
    To exit the Deep Sleep mode, follow this sequence:
    1. An external device drives the GP0[8] pin high.
    2. The device exits the Deep Sleep mode. When the SLEEPCOUNT delay is complete, the Deep Sleep
    logic releases the clock to the device and sets the SLEEPCOMPLETE bit in the deep sleep register
    (DEEPSLEEP) in the System Configuration (SYSCFG) Module chapter.
    3. Clear the SLEEPENABLE bit in DEEPSLEEP to 0.




    For your questions please find my reply below.

    While you're in deepsleep, do you confirm that the input clock has stopped completely (i.e. crystal stops oscillating)?
    [shyam] I will check with Oscilloscope across the crystal. In the mean time When I do a echo mem > /sys/power/state the behaviour observed is the device is entering into non responsive state also the USB , UART1 pheripherals which are drivern by PLL0 , PLL1 are not responsive and the only way to come out of this state is power cycle the device.

    When GP0[8] goes high to wake the device, do you see the clock turn back on?
    [shyam] Didnt observe device waking up upon driving the GP0[8] using the external circuit.

    Are you able to connect with JTAG after a failed wakeup? If so, we may want to create some scripts for dumping various registers so you can compare before/after to see what's not being properly preserved through the DeepSleep.

    [Shyam]  Ours is custom board made  from OMAPL138 . We have JTAG interface on our custom board . Can you please share instrcuions on interfacing  and use the  JTAG  to our custom board  .

    We have the XDS 200 spectrum digital hardware.  With firmware already running on the device. I tried connecting to device using XDS200 by running a sample app . I am seeing below error.

    Please share your comments on this.


    I,m refering the link below for JTAG releated  info

    http://processors.wiki.ti.com/index.php/OMAP-L1x_Debug_Gel_Files


    Regards,
    Shyam

  • Do you know if that suspend code is running from internal RAM?  That is an important requirement.  Also, in the resume portion, I didn't see any code related to DDR PLL or taking DDR out of self refresh.

    In CCS:

    1. Please make sure your target configuration isn't using any gel files (except those Debug Gel Files).  If you choose "OMAP-L138" as the device and not a board, that should be the case.
    2. Please don't load any out files when you connect.  I want to see if you can connect and look at some register locations.  Here's how I usually do it:
      1. In Edit perspective, go to View -> Target Configurations.
      2. Right click on your target configuration file, and select "Launch Selected Configuration".
      3. Once the debugger launches, right-click on the ARM core and connect.

    After you've connected, take a look to see if the MMU is enabled or disabled (bottom status bar should show this).  Open a memory window and see if you can view EMIF registers, PSC registers, etc.  Take note of where the program counter is pointing, i.e. is it stuck in an exception, etc.

    Best regards,
    Brad

  • On a related note, I recommend reviewing this thread if you haven't already:

    e2e.ti.com/.../585349

    It might give you some ideas as to what the problem is.
  • Hi Brad,

    Thank you for quick reply.   I am able to connect CCS compiler to my device.

    Do you know if that suspend code is running from internal RAM?  

    For your question i.m not sure on how to validate this.  Have attached the latest code  for moving  DDR out of self refresh mode.

    With the latest code changes also I,m not able to wake the device up from deepsleep by driving the Gp0[8] using external source.

    Have capture the screen shots of Deepsleep regiter in case default behaviour, Device put into deepsleep mode and wakingup device using the external signal on GP0[8]

    Saw the below error message during device wakeup from deepsleep mode by driving the GP0[8] using external source. Please share your valid inputs on the same

    ARM9_0: Error: (Error -1029 @ 0x2B5F) Invalid data read from ICECrusher register. Reset the device, and retry the operation. If error persists, confirm configuration, power-cycle the board, and/or try more reliable JTAG settings (e.g. lower TCLK). (Emulation package 6.0.407.3)
    ARM9_0: Unable to determine target status after 20 attempts
    ARM9_0: Failed to remove the debug state from the target before disconnecting. There may still be breakpoint op-codes embedded in program memory. It is recommended that you reset the emulator before you connect and reload your program before you continue debugging

    static void davinci_pm_suspend(void)

    {

    unsigned val;

    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Active Self-refresh mode to DDR2 */

    //Clear Self Refresh / low power (SR_PD)bit to 0

    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);

    val &= ~(0x00800000);

    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    //set the low power enable

    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);

    val |= (0x80000000);

    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    /* Switch CPU PLL to bypass mode */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    udelay(PLL_BYPASS_TIME);

    /* Powerdown CPU PLL */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val |= PLLCTL_PLLPWRDN;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    }

    /* Configure sleep count in deep sleep register */

    val = __raw_readl(pdata->deepsleep_reg);

    val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,

    val |= 0x000F;//  pdata->sleepcount;

    /* Configure Enable Deep bit in the  sleep register */

    val |= 0x80000000;

    __raw_writel(val, pdata->deepsleep_reg);

    /* System goes to sleep in this call */

    davinci_sram_suspend(pdata);

    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Configure sleep count in deep sleep register */

    val = __raw_readl(pdata->deepsleep_reg);

    val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,

    /* Configure Enable Deep bit in the  sleep register */

    val |= 0x00000000;

    __raw_writel(val, pdata->deepsleep_reg);

    /* put CPU PLL in reset */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val &= ~PLLCTL_PLLRST;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Set CPU PLL Disable  output */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val |= PLLCTL_PLLDIS;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* put CPU PLL in power down */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val &= ~PLLCTL_PLLPWRDN;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Eanble ToggleCPU PLL Disable */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val &= ~PLLCTL_PLLDIS;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* wait for CPU PLL reset */

    udelay(PLL_RESET_TIME);

    /* bring CPU PLL out of reset */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val |= PLLCTL_PLLRST;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Wait for CPU PLL to lock */

    udelay(PLL_LOCK_TIME);

    /* Remove CPU PLL from bypass mode */

    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);

    val &= ~PLLCTL_PLLENSRC;

    val |= PLLCTL_PLLEN;

    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    //Enable clock to the SDRAM register

    val = __raw_readl(pdata->ddrpsc_reg_base);

    val |= (0x00000003);

    __raw_writel(val, pdata->ddrpsc_reg_base);

    //Set Reset PHY bit in DDR PHY  reset control register

    val = __raw_readl(pdata->ddr2_ctlr_base + 0x60);

    val |= (0x00000400);

    __raw_writel(val, pdata->ddr2_ctlr_base + 0x60);

    //Clear the MCLKSTOPEN bit in SDRCR

    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);

    val &= ~(0x40000000);

    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    //Clear the low power mode bit in SDRCR

    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);

    val &= ~(0x80000000);

    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    //ser PLL

    }

    }

  • Your screenshots show code is executing from DDR (i.e. 0xCxxxxxxx address space). You definitely need that shutdown and wakeup code executing from internal RAM.
  • Hi Brad,

    Thank you for the quick comment.

    The below function is called in our case after thedeepsleep enable bit is in deepsleep register where SRAM is used.
    /* System goes to sleep in this call */

    davinci_sram_suspend(pdata);


    After putting the device into deepsleep using echo mem > /sys/power/state the device is not waking after we drive the DEEPSleep pin uisng the external device.

    After Device entered the deepsleep mode  the sleepenable and sleepcomplete bits are showing up as  1 . On driving the Deepsleep pin using external device the sleepenable and sleepcomplete bit are not changing they are showing as 1. 

    The device is never coming out of deepsleep mode as the code post the davinci_sram_Suspend () is not executing.

    Also commented the  davinci_sram_Suspend () and the behaviour is same. 



    Did you get a chance to check my code. Please share your inputs on am i missing something.

    Regards,
    Shyam G

  • Sharing my code snippet for device enter deep sleep and wakeup. With the current code the device is entering deepsleep but not able to exit the deep sleep after driving the pin high using the external device. During device wake up using the external pin observed that the  usb not recognised message is coming on my machine. the Device is cnnected to my machine using the USB port for config purpose.

    Using CCS  observed that the Sleep enable and sleep complete bits are set to 1 in the deepsleep register  post driving the deepsleep pin high using the external device.

    It looks like the deepsleep wakeup code in my case is not invoking. I am clearing the sleep enable bit in the deepsleep register in deepsleep wake logic.

    Attached the screenshots for registers DDRAMcontrolregister, Deepsleep Register and EMIFA register before entering the deepsleep mode case and post device wakeup from deepsleep mode.

    Kindly share your comments.

    The Pinumux0 is configured to use the pin as deepsleep pin. 

    Deepsleep enter and exit code in pm.c
    static void davinci_pm_suspend(void)
    {
    unsigned val;

    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Active Self-refresh mode to DDR2 */

    //Clear Self Refresh / low power (SR_PD)bit to 0
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x00800000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    //set the low power enable
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val |= (0x80000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    /* Switch CPU PLL to bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    udelay(PLL_BYPASS_TIME);

    /* Powerdown CPU PLL */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);
    }

    /* Configure sleep count in deep sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
    val |= 0x000F;// pdata->sleepcount;
    /* Configure Enable Deep bit in the sleep register */
    val |= 0x80000000;
    __raw_writel(val, pdata->deepsleep_reg);

    /*
    do{
    val = __raw_readl(pdata->deepsleep_reg);
    val &= 0x40000000;
    }
    while(0==val);
    */

    /* System goes to sleep in this call */
    davinci_sram_suspend(pdata);



    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Configure sleep count in deep sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    /* Clear Enable Deepsleep bit in the sleep register */
    val &= ~(0x80000000);
    __raw_writel(val, pdata->deepsleep_reg);

    /* put CPU PLL in reset */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);


    /* put CPU PLL in power down */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* wait for CPU PLL reset */
    udelay(PLL_RESET_TIME);

    /* bring CPU PLL out of reset */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Wait for CPU PLL to lock */
    udelay(PLL_LOCK_TIME);

    /* Remove CPU PLL from bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC|PLLCTL_PLLEN);
    val |= PLLCTL_PLLEN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    //Enable clock to the SDRAM register

    val = __raw_readl(pdata->ddrpsc_reg_base);
    val |= (0x00000003);
    __raw_writel(val, pdata->ddrpsc_reg_base);


    //Set Reset PHY bit in DDR PHY reset control register
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x60);
    val |= (0x00000400);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x60);


    //Clear the MCLKSTOPEN bit in SDRCR
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x40000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);


    //Clear the low power mode bit in SDRCR
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x80000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);


    }

     Register value  before entering the deepsleep mode.

    Register value postdevice wakeup using external device.

  • Were you able to get the code to run from internal memory?  That seems like the main thing you're missing.

  • Hi Brad,

    Do we have a mechanism to validate if our code is running from internal memory ?

    Regards,

    Shyam G

  • Check the map file.

  • Hi Brad,

    Thank you for the quick reply.

    Attached the sleep.S file for mach-davinci platformsleep.S

    Also attached the code snippet for pm_probe from Pm.c file under mach-davinci. My understanding is the code is expected to run from SRAM when device is put to deepsleep mode . In the code below mentioned  the data from davinici is moved to SRAM   in the call  davinci_sram_push.

    static int __init davinci_pm_probe(struct platform_device *pdev)
    {
    	pdata = pdev->dev.platform_data;
    	if (!pdata) {
    		dev_err(&pdev->dev, "cannot get platform data\n");
    		return -ENOENT;
    	}
    
    	davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL);
    	if (!davinci_sram_suspend) {
    		dev_err(&pdev->dev, "cannot allocate SRAM memory\n");
    		return -ENOMEM;
    	}
    
    	davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend,
    						davinci_cpu_suspend_sz);
    
    	suspend_set_ops(&davinci_pm_ops);
    
    	return 0;
    }
    

    My observation is when the device enters deepsleep mode and if i try to connect the CCS over JTAG with device i.m seeing below error. Post driving the deepsleep pin using external device i,m able to connect CCS with device and see the regisers . But the device doesnt wakeup which is what we expect. Also observed the Deepsleep enable bit is not cleared and also the deepsleep complete bit is set to 1.

    Also when the deepsleep pin is driven from the external device observed that the USB port  from the Device connected to my machine  is showing up as port not recognised.

    Is this an indication the  device is waking up incompletely ?

    Also observed the CCS snapshots in case of ccs error observed on connecting to device in deepsleep.

     below snaphot is CCS connected to device post deepsleep and device not waking up  

  • I recommend putting a spin loop in the "suspend" code, e.g. something like this:

    volatile int wait_for_jtag = 1;

    while (wait_for_jtag);

    When you initiate the suspend operation, it will stick in that loop.  When you connect with JTAG you will see it spinning there.  You can either change that variable to a value of zero through a memory window to gracefully exit, or you can just force the program counter past the loop.  At that point you can step through the code (you could try to load symbols, though you might be stuck stepping through disassembly).  I'd like to see the following:

    1. Is the davinci_pm_suspend() function in internal RAM?  You've talked about code in internal RAM, but I'm not clear if davinci_pm_suspend() is in internal RAM.  It won't matter if there is other code executing from RAM if davinci_pm_suspend() is not internal RAM.  It's the davinci_pm_suspend() function that turns on the DDR clock and takes the DDR out of self refresh.  If that code is located in DDR, it won't be able to execute...
    2. See from the disassembly which instructions precisely where things shutoff, and be able to capture the next few instructions that follow (i.e. to better understand where it resumes.

    There's another test that can be done too.  Once you see from step 2 precisely where it turns off, you can similarly insert a while(1) loop in that code.  The goal would be to see if we can at least get to a point where after you wakeup the target that we get to a known place in the code.  We need to know where we are in the code in order to make proper adjustments.

  • Hi Brad,

    Thank you very much for the prompt reply.

    Added the logic suggested by you and as expected device doesnt enter deepsleep mode because of while loop.  I,m not able to exit the while loop with JTAG connected to device over CCS. below is the attached ccs disassembly window. the code loops between the disassembly mentioned. 

    Also  i,m not able to   changing  the jtag  variable to a value of zero through a memory window I dont see the wait_for_jtag variable, Also not clear on how to  force the program counter past the loop in the disassembly window.

    c001ac8c: E59D3004 LDR R3, [R13, #0x4]
    c001ac90: E3530000 CMP R3, #0x0
    c001ac94: 1AFFFFFC BNE 0xC001AC8C


    static void davinci_pm_suspend(void)
    {
    unsigned val;

    volatile int wait_for_jtag = 1;

    while (wait_for_jtag);



    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Active Self-refresh mode to DDR2 */

    //Clear Self Refresh / low power (SR_PD)bit to 0
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x00800000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    //set the low power enable 
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val |= (0x80000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    /* Switch CPU PLL to bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    udelay(PLL_BYPASS_TIME);

    /* Powerdown CPU PLL */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);
    }

    /* Configure sleep count in deep sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
    val |= 0x000F;// pdata->sleepcount;
    /* Configure Enable Deep bit in the sleep register */
    val |= 0x80000000;
    __raw_writel(val, pdata->deepsleep_reg);

    /*
    do{
    val = __raw_readl(pdata->deepsleep_reg);
    val &= 0x40000000;
    }
    while(0==val); 
    */

    /* System goes to sleep in this call */
    //davinci_sram_suspend(pdata);



    if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) {

    /* Configure sleep count in deep sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    /* Clear Enable Deepsleep bit in the sleep register */
    val &= ~(0x80000000);
    __raw_writel(val, pdata->deepsleep_reg);

    /* put CPU PLL in reset */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);


    /* put CPU PLL in power down */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* wait for CPU PLL reset */
    udelay(PLL_RESET_TIME);

    /* bring CPU PLL out of reset */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Wait for CPU PLL to lock */
    udelay(PLL_LOCK_TIME);

    /* Remove CPU PLL from bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC|PLLCTL_PLLEN);
    val |= PLLCTL_PLLEN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    //Enable clock to the SDRAM register

    val = __raw_readl(pdata->ddrpsc_reg_base);
    val |= (0x00000003);
    __raw_writel(val, pdata->ddrpsc_reg_base);


    //Set Reset PHY bit in DDR PHY reset control register
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x60);
    val |= (0x00000400);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x60);


    //Clear the MCLKSTOPEN bit in SDRCR
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x40000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);


    //Clear the low power mode bit in SDRCR
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x80000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);


    }

    coming to next question

    Regarding the internal ram the sleep.S file clearly states the below info in the comments section. Currently in our pm.c suspend we have commented out the 

    below line in the davinci_pm_suspend and we have written code in  davinci_pm_suspend fucntion similar to the assembly code in sleep.S.

    Kindly share your comments on my understanding.

    /* System goes to sleep in this call */.

    //davinci_sram_suspend(pdata);

    /*

    * Move DaVinci into deep sleep state
    *
    * Note: This code is copied to internal SRAM by PM code. When the DaVinci
    * wakes up it continues execution at the point it went to sleep.
    * Register Usage:
    * r0: contains virtual base for DDR2 controller
    * r1: contains virtual base for DDR2 Power and Sleep controller (PSC)
    * r2: contains PSC number for DDR2
    * r3: contains virtual base DDR2 PLL controller
    * r4: contains virtual address of the DEEPSLEEP register
    */

  • Shyam G said:
    I,m not able to exit the while loop with JTAG connected to device over CCS. below is the attached ccs disassembly window. the code loops between the disassembly mentioned. 

    When you get to the CMP instruction at line 0xC001AC90 you should be able to set R3=1 and then it will exit the loop.

    Shyam G said:
    * Note: This code is copied to internal SRAM by PM code. When the DaVinci
    * wakes up it continues execution at the point it went to sleep.
    * Register Usage:

    Which code is being copied into SRAM?  Code from sleep.S?  It isn't clear to me whether the davinci_pm_suspend() code ever gets copied to SRAM.

  • Which code is being copied into SRAM?  Code from sleep.S?  It isn't clear to me whether the davinci_pm_suspend() code ever gets copied to SRAM.

    The SRAM memory allocationIn  is done in the  davinci_pm_probe()  in pm.c. Also the davinci _cpu_suspend is copied to sram in the same function using davici_SRAM_push.

    Please correct me if my understanding is wrong

    static int __init davinci_pm_probe(struct platform_device *pdev)
    {
    	pdata = pdev->dev.platform_data;
    	if (!pdata) {
    		dev_err(&pdev->dev, "cannot get platform data\n");
    		return -ENOENT;
    	}
    
    	davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL);
    	if (!davinci_sram_suspend) {
    		dev_err(&pdev->dev, "cannot allocate SRAM memory\n");
    		return -ENOMEM;
    	}
    
    	davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend,
    						davinci_cpu_suspend_sz);
    
    	suspend_set_ops(&davinci_pm_ops);
    
    	return 0;
    }
  • Shyam,

    It's not clear to me whether you're understanding is right or wrong.  Is there a reason why you cannot step through the code and verify that you do in fact execute code from SRAM?  I would like to see a screenshot of the disassembly window showing code being executed from SRAM.

    Brad

  • Brad,

    The moment the device enters the deepsleep mode  the CCS connection with Device over JTAG is lost . Only  after i driver deepsleep pin using the externel device the JTAG connection with device is restored.

    Regards,

    Shyam G

  • I'm interested in the moment immediately before the device enters deepsleep.  I want to see it executing from SRAM.   We need to verify that much is working as expected before we can start looking at what happens on the wakeup path.

  • Hi Brad,

    Attached my deepsleep code snippet with while(jtag) in place just before enabling the deepsleepbit in the deepsleep register.

    Attaching snapshots  captured  with code stuck at  while(jtag) loop and post breaking from while loop. 

    Observed that on breaking while loop the only the  deepsleep count mask is showing up in the deepsleep register .  I didnt see the deepsleep enable bit set in the deepsleep register eventhough i,m enabling it in our source code. Also on exiting the while loop observed that the address is not 0xCXXXX in the CCS 

    Is there a way to check if code is running from internal Ram?

    while loop:

    Breking while loiop:

    static void davinci_pm_suspend(void)
    {
    unsigned val;
    volatile int jtag=1;


    //Clear Self Refresh / low power (SR_PD)bit to 0
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x00800000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    //set the low power enable
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val |= (0x80000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);

    /* Switch CPU PLL0 to bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLEN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Switch CPU PLL1 to bypass mode */
    val = __raw_readl(pdata->ddrpll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLEN;
    __raw_writel(val, pdata->ddrpll_reg_base + PLLCTL);


    udelay(PLL_BYPASS_TIME);

    /* Powerdown CPU PLL0 */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Powerdown CPU PLL1 */
    val = __raw_readl(pdata->ddrpll_reg_base + PLLCTL);
    val |= PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->ddrpll_reg_base + PLLCTL);

    /* Configure sleep count in deep sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
    val |= 0x0F;//pdata->sleepcount;
    __raw_writel(val, pdata->deepsleep_reg);

    /* Configure Enable Deep bit in the sleep register */
    val = __raw_readl(pdata->deepsleep_reg);
    val |= 0x80000000;
    while(jtag);
    __raw_writel(val, pdata->deepsleep_reg);

    /* System goes to sleep in this call */
    //davinci_sram_suspend(pdata);


    /* Clear Enable Deepsleep bit in the sleep register */

    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~(0x80000000);
    __raw_writel(val, pdata->deepsleep_reg);


    /* Clear PLLRST bit in PLLCTL 0 */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Clear PLLRST bit in PLLCTL 1 */
    val = __raw_readl(pdata->ddrpll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLRST;
    __raw_writel(val, pdata->ddrpll_reg_base + PLLCTL);


    /* Clear PLLPWRDWN bit in PLLCTL 0 */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Clear PLLPWRDWN bit in PLLCTL 1 */
    val = __raw_readl(pdata->ddrpll_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLPWRDN;
    __raw_writel(val, pdata->ddrpll_reg_base + PLLCTL);

    /* wait for CPU PLL reset */
    udelay(PLL_RESET_TIME);

    /* Set PLLRST bit in PLLCTL 0 */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val |= PLLCTL_PLLRST;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Set PLLRST bit in PLLCTL 1 */
    val = __raw_readl(pdata->ddrpll_reg_base + PLLCTL);
    val |= PLLCTL_PLLRST;
    __raw_writel(val, pdata->ddrpll_reg_base + PLLCTL);


    /* Wait for CPU PLL to lock */
    udelay(PLL_LOCK_TIME);

    /* Remove CPU PLL0 from bypass mode */
    val = __raw_readl(pdata->cpupll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC|PLLCTL_PLLEN);
    val |= PLLCTL_PLLEN;
    __raw_writel(val, pdata->cpupll_reg_base + PLLCTL);

    /* Remove CPU PLL1 from bypass mode */
    val = __raw_readl(pdata->ddrpll_reg_base + PLLCTL);
    val &= ~(PLLCTL_PLLENSRC|PLLCTL_PLLEN);
    val |= PLLCTL_PLLEN;
    __raw_writel(val, pdata->ddrpll_reg_base + PLLCTL);


    //Enable clock to the SDRAM register

    val = __raw_readl(pdata->ddrpsc_reg_base);
    val |= (0x00000003);
    __raw_writel(val, pdata->ddrpsc_reg_base);


    //Set Reset PHY bit in DDR PHY reset control register
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x60);
    val |= (0x00000400);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x60);


    //Clear the MCLKSTOPEN bit in SDRCR
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x40000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);


    //Clear the low power mode bit in SDRCR
    val = __raw_readl(pdata->ddr2_ctlr_base + 0x0C);
    val &= ~(0x80000000);
    __raw_writel(val, pdata->ddr2_ctlr_base + 0x0C);


    }

  • Shyam G said:

    Also on exiting the while loop observed that the address is not 0xCXXXX in the CCS 

    Is there a way to check if code is running from internal Ram?

    Did it unexpectedly jump to that address?  The screenshot looked like a vector table, so I'm wondering if you hit some kind of exception or something.  There is 8KB of ARM RAM at 0xFFFF0000.  I was expecting to see code copied to Shared RAM at 0x80000000 since there's 128KB available.  Either would work though.

  • Hi Bradd,

    upon breaking the while loop it is jumping to address 0xFFxxxxxx

    Attached the latest snapshot exiting while loop.

  • How are you exiting the loop?  Looking at your previous screenshot (i.e. before it exits the loop), I do not see any instructions that look like they are deliberately jumping to address 0xFFFF1010.  This looks like an exception.  The bottom bar of CCS should show the mode you're in (user, supervisor, etc.).  What does it say when you get to address 0xFFFF1010?

  • Hi Brad,

    I am exiting the loop by setting the register value to 1 using the memory browser utility.  

    attaching the complete screenshot . Also the deepsleepenable bit is not showing up as enabled in Deepsleep register.

  • Hi Bradd,

    Thank you for the quick reply.

    Looking at my code it is understood that the  function davinci_pm_suspend() which does the enterdeepsleep and exitdeepsleep is not running from SRAM.

    I am not doing anything for executing the function  davinci_pm_suspend() from SRAM(internal RAM).

    On searching forums found sample code on how to execute the code from SRAM.  Added the below line before  the davinci_pm_suspend() function defination

    #pragma CODE_SECTION(davinci_pm_suspend(), ".TI.ramfunc")

    observed the warning : ignoring the #pragma CODE_SECTION.  Also the function entry of davinci_pm_suspend is missing in the system.map file generated during  the uImage  

    Kindly request your support me on how to add the code to execute my function from SRAM.

    Regards,
    Shyam G




  • Hi Shyam

    Looks like we did not get back to you on this. As far as I know Brad is out of office.

    Have you been able to make any progress on this or still waiting for further guidance from TI on this?

    Regards

    Mukul 

  • I am awaiting for TI help on how to get the suspend function to run from the internal RAM. Post Brad's leave there is not much progress on this .

    Raised another query for 

    how to link code to internal memory in Linux

    https://e2e.ti.com/support/processors/f/791/p/810504/3012462#3012462

  • Hi Shyam

    Ok thanks. I am marking this thread closed as I believe your initial issue around understanding the deep sleep sequence/ GPIO8 wake up etc was addressed. Your open issues are on the other thread. 

    I see that you are still struggling with getting the deep sleep code running from internal RAM. I have requested inputs from additional experts. 

    Do note that you are on an older kernel and our support maybe limited.

    Regards

    Mukul 

  • Hi Mukul,

    My issue is not resolved yet.

    The device wakeup from deepsleep using external signal is not working for me.

    Regards,

    Shyam G

  • Hi Shyam

    Thanks. I will still go ahead and mark this thread closed, and will follow up further on the other thread that you going with  

    Regards

    Mukul

  • Hi Mukul,

    Am ok with closure of this thread but would need your support in getting this feature working on omapl138. Would follow up the same in the other thread.

    Regards,
    Shyam G