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.

DM365 PLL settings

Other Parts Discussed in Thread: OMAP-L138, DA8XX

Hello,

I have already asked this question in linux forum, but there was no response...

I want to implement frequency scaling, but I'm lacking the information about allowed clock settings. 

 Are there any constraints to the frequency of the arm core and the peripherals on dm365? I have not found it in documentations but with trial error method I found that PLL1->SYSCLK4 (EDMA, SPI, GPIO, ...) clock has to be significantly slower than arm core. Is it documented somewhere?


best regards
Jan

  • Jan,

    What exactly are you trying to do? What is the issue that you are facing? Have you checked the document SPRUFG5A?

  • Hello,

    I definitely checked the SPRUFG5A several times. DM365 has two PLLs and several SYSCLKs on them.  I do not know if I'm allowed eg. to set arm core clock to 10MHz and let peripherals (GPIO, SPI, ...) run on higher frequency 135MHz, etc. According to my test this is not possible, but I haven't found any documentation on this.

     regards
    Jan 

  • Jan,

    I'm not really sure about 10 MHz, but you can definitely run ARM at the bypass clock which will be same as the oscillator that you've connected (19.2, 24, 27, 36 MHz). Which all peripherals would you like test during the lower frequency operation? The problem here will be to reconfigure all the input function clocks to get these peripherals running. 

    What exactly are you trying to achieve?

  • Hi,

    thank you for the answer. I would like to implement frequency scaling with cpufreq linux driver to decrease power consumption of our device. That means that if there is no work to do the cpu will lower its frequency as much as it can and vice versa. So far I tried to set different frequencies in UBL and I noticed that not all combinations of clocks are possible. 

     regards
    Jan

  • Is bypass clock sufficient for you? Have you tried bypass clocks for ARM? I believe that should work. Bypass clock should anyways work as BOOTROM is running in bypass clock. The higher clock will get set only after UBL starts running. 

  • Hello,

    pll bypass mode really works. However I have problem when I need to get back with PLL enable because usb is not reinitialized properly and stops to detect any connected devices. Are there some steps that need to be performed to reinitialize USB after comming back from pll bypass mode?

    Also I discovered, that I'm unable to change PREDIV, PLLM and POSTDIV. After specifying new settings and enabling PLL again, whole system hangs even if I follow the steps from arm subsystem guide ...

     

    ...
    progam PLLM, PREDIV, and POSTDIV
    ...
    __raw_writel(0x00470000, pll1_reg_base + PLLSECCTL);
    __raw_writel(0x00460000, pll1_reg_base + PLLSECCTL);
    __raw_writel(0x00400000, pll1_reg_base + PLLSECCTL);
    __raw_writel(0x00410000, pll1_reg_base + PLLSECCTL);
    udelay(300);
    __raw_writel(0x1, pll1_reg_base + PLLCMD);
    udelay(300); 
    val = __raw_readl(pll1_reg_base + PLLCTL);
    val &= ~PLLCTL_PLLENSRC;
    __raw_writel(val, pll1_reg_base + PLLCTL);
     val |= PLLCTL_PLLEN;
    __raw_writel(val, pll1_reg_base + PLLCTL);

    Here the program hangs...

    best regards

    Jan

  • Jan,

    USB is something that is very sensitive to the clock changes. Have you checked the register USB_PHY_CTRL and the fields? Make sure that you try different clock source options here. 

    Also can you share the code where you program PLLM, PREDIV, and POSTDIV?

  • Hi,

    at system initialization I program PLLM, PREDIV and POSTDIV in standard ubl and this works fine. Lately in linux I use following code

     

    static void set_pll_reg(unsigned val, void *address)
    {
    unsigned reg, tmp;
    tmp = reg = __raw_readl(address);
    reg |= val;
    __raw_writel(reg, address);
    printk("reg %06x: 0x%06x -> 0x%06x\n", address, tmp,
    (unsigned int)__raw_readl(address));
    }

    static void clear_pll_reg(unsigned val, void *address)
    {
    unsigned reg, tmp;
    tmp = reg = __raw_readl(address);
    reg &= ~val;
    __raw_writel(reg, address);
    printk("reg %06x: 0x%06x -> 0x%06x\n", address, tmp,
    (unsigned int)__raw_readl(address));
    }

    static void reg_dump(void)
    {
    printk("PLLCTL: 0x%08x\n", __raw_readl(pll1_reg_base + PLLCTL));
    printk("PLLSTAT: 0x%08x\n", __raw_readl(pll1_reg_base + PLLSTAT));
    printk("PREDIV: 0x%08x\n", __raw_readl(pll1_reg_base + PREDIV));
    printk("POSTDIV: 0x%08x\n", __raw_readl(pll1_reg_base + POSTDIV));
    printk("PLLM: 0x%08x\n", __raw_readl(pll1_reg_base + PLLM));
    printk("BPDIV: 0x%08x\n", __raw_readl(pll1_reg_base + BPDIV));
    printk("PLLDIV1: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV1));
    printk("PLLDIV2: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV2));
    printk("PLLDIV3: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV3));
    printk("PLLDIV4: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV4));
    printk("PLLDIV5: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV5));
    printk("PLLDIV6: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV6));
    printk("PLLDIV7: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV7));
    printk("PLLDIV8: 0x%08x\n", __raw_readl(pll1_reg_base + PLLDIV8));
    printk("PLLC1: 0x%08x\n", __raw_readl(system_reg_base + 0x84));
    printk("PLLC2: 0x%08x\n", __raw_readl(system_reg_base + 0x88));
    printk("PERI_CLK_CTL: 0x%08x\n", __raw_readl(system_reg_base + 0x48));
    }

    static void davinci_cpu_suspend2(struct davinci_pm_config *pdata)
    {
    unsigned val;
    unsigned long flags;

    spin_lock_irqsave(&pm_lock, flags);

    /* bypass cpu pll */
    reg_dump();
    #if 1
    printk("cpu pll bypass and powerdown\n");
    clear_pll_reg(PLLCTL_PLLENSRC, pll1_reg_base + PLLCTL);
    clear_pll_reg(PLLCTL_PLLEN, pll1_reg_base + PLLCTL);
    udelay(4);
    set_pll_reg(PLLCTL_PLLPWRDN, pll1_reg_base + PLLCTL);
    #endif

    /* ddr to self refresh */
    #if 1
    val = __raw_readl(ddr2_reg_base + DDR2_SDRCR_OFFSET);
    val |= DDR2_SRPD_BIT;
    val |= DDR2_LPMODEN_BIT;
    __raw_writel(val, ddr2_reg_base + DDR2_SDRCR_OFFSET);
    #endif

    /* bypass ddr pll*/


    /* allow deepsleep */
    val = __raw_readl(pdata->deepsleep_reg);
    val |= (1 << 31);
    __raw_writel(val, pdata->deepsleep_reg);

    /* wait for return from deepsleep */
    printk("GOING TO SLEEP!!!!!!!!!!!\n");
    while (!(__raw_readl(pdata->deepsleep_reg) & (1 << 30)));
    printk("WAKE UP!!!!!!!!!!!\n");

    /* deny deepsleep again */
    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~(1 << 31);
    __raw_writel(val, pdata->deepsleep_reg);

    /* ddr out self refresh */
    #if 1
    val = __raw_readl(ddr2_reg_base + DDR2_SDRCR_OFFSET);
    val &= ~(DDR2_SRPD_BIT | DDR2_LPMODEN_BIT);
    __raw_writel(val, ddr2_reg_base + DDR2_SDRCR_OFFSET);
    #endif

    /* set cpu pll again*/
    #if 1
    printk("cpu pll set\n");
    clear_pll_reg(PLLCTL_PLLPWRDN, pll1_reg_base + PLLCTL);
    clear_pll_reg(PLLCTL_PLLENSRC, pll1_reg_base + PLLCTL);
    clear_pll_reg(PLLCTL_PLLEN, pll1_reg_base + PLLCTL);
    udelay(4);
    set_pll_reg(PLLCTL_PLLRST, pll1_reg_base + PLLCTL);
    udelay(10);
    clear_pll_reg(PLLCTL_PLLRST, pll1_reg_base + PLLCTL);

    __raw_writel(__raw_readl(pll1_reg_base + PLLM) | (1 << 15),
    pll1_reg_base + PLLM);
    __raw_writel(__raw_readl(pll1_reg_base + PREDIV) | (1 << 15),
    pll1_reg_base + PREDIV);
    __raw_writel(__raw_readl(pll1_reg_base + POSTDIV) | (1 << 15),
    pll1_reg_base + POSTDIV);

    __raw_writel(0x00470000, pll1_reg_base + PLLSECCTL);
    __raw_writel(0x00460000, pll1_reg_base + PLLSECCTL);
    __raw_writel(0x00400000, pll1_reg_base + PLLSECCTL);
    __raw_writel(0x00410000, pll1_reg_base + PLLSECCTL);

    __raw_writel(0x1, pll1_reg_base + PLLCMD);
    udelay(300);
     
    /* poll LOCKx bits in PLLC1 */
    while ((__raw_readl(system_reg_base + 0x84) & (7 << 25)) != (7 << 25))
    printk(".");
    printk("\n");

    set_pll_reg(PLLCTL_PLLEN, pll1_reg_base + PLLCTL);
    #endif
    reg_dump();

    spin_unlock_irqrestore(&pm_lock, flags);
    }

     Unfortunately this does not work as expected. Even if I omit going to deepsleep mode and just bypass PLL1 and set it again it does not work well and PLL1 is in some strange state. 

    • If the code above contains the sequence with PLLSECCTL it hangs on enabling pll again.
    • If the code above does not contain sequence with PLLSECCTL lock bits in PLLC1 registers are never set and polling loop loops forever
    • If there is no PLLSECCTL sequence and PLLC1 polling, PLL is enabled again but bits in PLLSTAT says that Resetclk counter not finished counting and lock bits in PLLC1 are not set as well

    Registry dump before bypassing PLL1:

    PLLCTL: 0x00000051
    PLLSTAT: 0x00000006
    PREDIV: 0x00008003
    POSTDIV: 0x00008000
    PLLM: 0x0000002d
    BPDIV: 0x00008000
    PLLDIV1: 0x00008000
    PLLDIV2: 0x00008001
    PLLDIV3: 0x00008001
    PLLDIV4: 0x00008003
    PLLDIV5: 0x00008001
    PLLDIV6: 0x00008013
    PLLDIV7: 0x00008000
    PLLDIV8: 0x00008003
    PLLC1: 0x1f800000
    PLLC2: 0x1f800000
    PERI_CLK_CTL: 0x243f04fc 

     Registry after re-enabling PLL1:

    PLLCTL: 0x00000051
    PLLSTAT: 0x00000004
    PREDIV: 0x00008003
    POSTDIV: 0x00008000
    PLLM: 0x0000002d
    BPDIV: 0x00008000
    PLLDIV1: 0x00008000
    PLLDIV2: 0x00008001
    PLLDIV3: 0x00008001
    PLLDIV4: 0x00008003
    PLLDIV5: 0x00008001
    PLLDIV6: 0x00008013
    PLLDIV7: 0x00008000
    PLLDIV8: 0x00008003
    PLLC1: 0x08200000
    PLLC2: 0x1f800000
    PERI_CLK_CTL: 0x243f04fc

     

    best regards
    Jan 

  • Jan,

    Give me some time to analyze. I'll get back to you during the weekend.

  • Hi,

    thank you for the help, I'm looking forward to your comments.

    Let me just clarify my objective. I need to implement these two functions:

    • frequency scaling (cpufreq driver)
    • suspend to ram (deep sleep mode or wait for interrupt)

    They are already implemented for davinci omap-l138 (da8xx) in available kernel sources so the work can be based on this, but for the dm365  I'm stuck in both cases because I'm not able to successfully change PLL settings in running linux kernel. Primary settings in UBL works without any problems and I'm able to run system on whatever frequency that I want.

    DM365 has two PLLs. One can be used for the DDR and another for CPU clocking. I think that in the cpupll settings can be changed in any time when the system is running (it will be just slowed down for the time when the pll is bypassed). With ddrpll the situation should be same but ddr should be put to self-refresh and the code should be run from internal SRAM as long as the ddr is not used. Are these assumptions correct?

    As I described in previous post I can bypass the plls, it works. But reenabling them causes problems. PLL is either wrongly configured (according to PLLSTAT) or whole systems hangs when setting PLLEN bit again (jtag debugging shows that the cpu goes to some undefined addresses). 

    best regards
    Jan 

  • Jan,

    Couple of things here.

    1. What exactly are you trying? Are you trying just deep sleep mode? If so, there will not be any code running from IRAM. What exactl are you trying to achieve? Is it deep sleep mode or you want some code to be running in IRAM, when you are in low power mode?

    2. If you are in deep sleep mode, you can put ARM in standby mode and the clocks will be stopped automatically. You don't have to worry about putting in bypass clock also. I hope you've gone through the sections 12.4 and 12.5 in SPRUFG5A.pdf. Also you might want to check GIO0 wakeup is supported on your board.

    3. If you want some code to be running to poll for an interrupt, its a different game altogether. You have to load/copy the code that you want to run to IRAM and then do the following.

    • Power down/susped all drivers which is same as regular suspend.
    • Flush, invalidate cache memory, write buffers etc.
    • Disable MMU
    • Put DDR to self-refresh mode
    • Jump to IRAM (Physical address)
    • Save the context in IRAM
    • Put ARM in bypass clock.
    • Poll for the interrupt
    • Enable required clocks for ARM 
    • Pull DDR out of self-refresh.
    • Restore the context
    • Enable MMU caches
    • Jump to the save return address (Virtual Address)
    • Restore all the drivers
    Can you give me more clarity on the requirement.
  • Hi Renjith,

    I have a custom board with DM365 (it will be a baseboard of a security camera). As the whole system can work as battery powered I need to get the power consumption as low as it is possible. In Linux cpufreq driver is commonly used for this task and it changes the cpu frequency with respect to system load. As I said before unfortunately I'm not able to change PLL settings in running kernel - when I bypass DDR PLL it works, but reenabling does not work at all. When I bypass CPU PLL, whole system hangs. PLL2 is used for CPU clocking and only CPU and voice codec are there.

     On the board we have also a small microcontroller, which can enable the deepsleep mode of DM365 with GIO0 pin. Steps for entering deepsleep mode from sprufg5a are following:

    The ARM prepares for power down, typically after an external microcontroller notifies the ARM to
    prepare for power down via an interrupt or serial communication.
    • The ARM puts DDR2 in its to self-refresh state. The program in DDR2 is preserved while DDR2 is in
    its self-refresh state. In the case of mDDR, you may utilize partial array self refresh (PASR) for
    additional power savings.
    • To reduce the chip stand-by power, power-down all the analog blocks (PLL cores, DDR PHY DLL,
    DDR PHY, USB PHY and Video DAC).
    • The ARM sets SLEEPENABLE in register DEEPSLEEP in the system module.
    • The ARM informs the microcontroller that it has initiated deep sleep and begins polling
    SLEEPCOMPLETE in DEEPSLEEP. During the recovery process, the ARM will wake up and detect
    that SLEEPCOMPLETE has changed.
    • The microcontroller transitions GIO0 from high to low and then continues to hold GIO0 low (for a
    minimum of 500 ns) until it desires to exit Deep Sleep mode. The transition of GIO0 from high to low
    creates a clock pulse advancing the deep sleep state machine. After this transition, all clocks are
    stopped and then the internal oscillators are powered down.
    • At this point, the device is in deep sleep mode; power is reduced to a minimum.
    The deep sleep wakeup process is as follows:
    • To initiate the wakeup process, the microcontroller transitions GIO0 from low to high. This transition
    creates a clock pulse advancing the Deep Sleep state machine. After this transition, the oscillators are
    powered up and allowed to stabilize and then all clocks are restarted.
    • The ARM detects that SLEEPCOMPLETE has changed
    • The ARM clears SLEEPENABLE in register DEEPSLEEP
    • The ARM brings DDR2 out of self refresh.
    • At this point the ARM resumes normal operation. The ARM may branch to program code preserved in
    DDR2. Register states are preserved. 

    If I understand it right it is good to powerdown the PLLs here and the code polling the SLEEPCOMPLETE has to be running from internal RAM.

    So here I have the same issue that I'm unable to set up PLLs again.

    I tried also only to allow deepsleep and enabling it by GIO0 without touching PLLs - after next change on GIO0 system is resumed but PLLs are in wrong state:

    • PLLSTAT value changes from  0x06 to 0x04
    • PLL1C_CONFIG and PLL2C_CONFIG values both chage from 0x1f800000 to 0x00820000

    System is then running significantly slower so I expect that PLLs remain bypassed or something like that. USB driver is not correctly reinitialized, but I think it is not an USB issue but issue of wrong clock (PLLs) settings.

    best regards
    Jan 

  • Jan,

    Jan Pohanka said:
     

    printk("GOING TO SLEEP!!!!!!!!!!!\n");
    while (!(__raw_readl(pdata->deepsleep_reg) & (1 << 30)))

    The code will wait for it to go for sleep, if GIO0 goes from HIGH to LOW. Is this triggered in your case? It will ARM will go to 

    Jan Pohanka said:
      

    The transition of GIO0 from high to low creates a clock pulse advancing the deep sleep state machine. After this transition, all clocks are stopped and then the internal oscillators are powered down.

    I don't think any code is running from IRAM to implement this feature. 

    How is your micro-controller handling GIO0?

  • Renjith, 

    Renjith Thomas said:

     

    printk("GOING TO SLEEP!!!!!!!!!!!\n");
    while (!(__raw_readl(pdata->deepsleep_reg) & (1 << 30)))

    The code will wait for it to go for sleep, if GIO0 goes from HIGH to LOW. Is this triggered in your case? It will ARM will go to 

    [/quote]

    For simplicity I can use just following function to enter to deepsleep.

    static void davinci_cpu_suspend2(struct davinci_pm_config *pdata)
    {
    unsigned val;
    unsigned long flags;

    spin_lock_irqsave(&pm_lock, flags);

    reg_dump();

    /* allow deepsleep */
    val = __raw_readl(pdata->deepsleep_reg);
    val |= (1 << 31);
    __raw_writel(val, pdata->deepsleep_reg);

    /* wait for return from deepsleep */
    printk("GOING TO SLEEP!!!!!!!!!!!\n");
    while (!(__raw_readl(pdata->deepsleep_reg) & (1 << 30)));
    printk("WAKE UP!!!!!!!!!!!\n");

    /* deny deepsleep again */
    val = __raw_readl(pdata->deepsleep_reg);
    val &= ~(1 << 31);
    __raw_writel(val, pdata->deepsleep_reg);

    reg_dump();

    spin_unlock_irqrestore(&pm_lock, flags);
    }

    When the cpu reaches the while loop, my microcontroller do the falling edge on GIO0 - watching amper meter I see that current consumption goes low. After rising edge on GIO0 consumption goes up again and the function returns.

    From registry dumps I can see that PLLSTAT and PLLxC_CONFIG changed and PLLs remain in strange state as I described before.

    I don't think any code is running from IRAM to implement this feature.  

     

     In this simple case it is not required, but when I want to power down the PLLs and DDR is just in selfrefresh it would be... 

     

    How is your micro-controller handling GIO0?

     

    For testing I do this just manually with a switch, but this works. PLLs are the problem.

    best regards
    Jan 

  • Jan,

    Basically the issue is, PLL1 is not getting locked after this. But can you try putting a delay and try dumping the registers? I'm not sure about it, but is there a stabilization period required for this? I couldn't find much explanation regarding the PLL locking. I'm just guessing. 

  • Hi,

    there is some locking period and the datasheet describes that the polling of status bits should be used. I have tried both, increasing the period and the polling but unfortunately nothing helped...

    regards
    Jan 

  • Hello Renjith,

    I do have a question along the same line as Jan:

    It's about CLKOUT2 on the TM320DMS365.

    There is an input clock of 24MHz (at MXI/O) on the SoC.

    I'd like to use CLKOUT2 to drive a device that needs a 32kHz clock, but I can't quite figure out if this is possible.

    Does TI have a PLL divider tool to help? Or, do you know offhand if this will work?

    I've combed SPRUFG5A but cannot figure out if I'm running my ARM core at full speed  (300MHz) if I can divide the Main PLL output clock down to 32KHz using PLLCL1SYSCLK9 and PLLDIV9.

    Please let me know if you can help.

    Cheers
    Seb 

  • Seb,

    I don't think you'll get to use 32KHz from CLKOUT2. Its better you go for an external PLL or crystal to get exact 32.768KHz.

  • Jan,

    Best sources of information on clocking topic is the Data Sheet (SPRS457E, sec 3.3) and ARM Subsystem User's Guide (SPRUFG5A, chapter 5).

    Regards.

  • Hi Renjith,

    I thought this was for an RTC but it is not. A 32KHz pluse is needed to drive a WiFi module in sleep mode. It this still a no go?

    Thanks

    Seb

  • Seb,

    Generally in Wi-Fi modules I've seen external crystals being used. Also it will not be accurate 32,768Hz even if you use generated clock. Also CLKOUT2 can be used for general purpose applications, like providing master clock for camera etc..

  • Jan,

      Please try to change your initializing orders of 2 PLLs when you want to get back from LP mode, sometime one PLL has to be initialized before or after the others, especially PLLs have some kinds of daise chain connection. 

    Thanks!

     Phil

  • Hi Jan,

    On page 148 of SPRUFG5A (Section 9.12.19 Peripheral Clock Control) the description for the ARMCLKS field states that:

    When changing the source clock (either 0 to 1 or 1 to 0), ARM926 clock frequency must be Greater-than-or equal to CFG/DMA bus clock frequency (PLLC1SYSCLK4).

    This doesn't quite match your test results, but at least it documents a requirement regarding the clocks.

    best regards,

    Soren

  • Hi Jan

    Did you ever manage to get deepsleep working on the dm365.

    I read through your code and I found that you treat "pll1_reg_base" as the cpu pll, however looking at "PERI_CLK_CTL: 0x243f04fc", bit 29 is set and from page 148 of SPRUFG5A, if this bit is set then the ARM926 clock source is PLLC2SYSCLK2.

    I am also trying (for quite some time now) to get deep sleep working however, I am unable to wake up from deep sleep. The steps I followed are:

    • Put cpu pll into bypass (clear pllensrc and pllen in pllctl register)
    • Power down cpu pll (set pllpwrdwn in pllctl register)
    • Set deepsleep wakeup delay to 128
    • In SRAM:
      • Put ddr in self refresh (clear srpd and set lpmoden of sdrcr register)
      • Stop MCLK (set mclkstopen of sdrcr)
      • Disable DDR2 LPSC (Module 13 of PSC modules (table 39 of SPRUFG5A))
      • Disable clock to DDR PHY (clear plldiven of plldiv7 register)
      • Go into deep sleep (set deep sleep enable bit)
      • Poll sleep complete bit of deep sleep register (while in this loop the  a micro controller causes gpio0 to transition from high to low)
      • I am having an issue at this point as I am unable to wake up from sleep. I am using the micro controller to transition the gpio0 from low to high, which according to SPRUFG5A should wake it up from deep sleep.
      • If woken up, then remove deep sleep enable (clear deep sleep enable)
      • Enable clock to DDR PHY
      • Enable DDR LPSC
      • Start MCLK
      • Take ddr out of self refresh
    • Power up cpu pll (clear pllpwrdwn in pllctl)
    • Put cpu pll in bypass mode
    • Reset cpu pll
    • Do pllsecctl sequence 
    • Poll the lock bits of pllc2_config register 
    • Continue as normal...

    If I remove the step to poll the deep sleep complete bit in the deep sleep register, the arm does not receive the transition of gpio0 from high to low and hence does not go into deep sleep. In this case, the program continues as normal and restarts everything as expected.

    Any help/tips will be much appreciated.

    Arshad

  • Hi Arshad,

    unfortunatelly I was not able successfuly solve the problem with deep sleep on DM365 and I do not work on that project anymore. Sorry, I cannot help. I had results very similar to yours.

    I think that this issue should solve some of TI FAEs as TI states that they have the best support among MCU manufacturers.

    best regards
    Jan

  • Hi Arshad,

    I managed to get it working: The issue in my case was, that the CPU pll was PLL2, and when this was bypassed the ARM clock was lower than PLL1SYSCLK4. The result of this was, that any reading or writing to a peripheral clocked from PLL1SYSCLK4 (see figure 5 in SPRUFG5A) would become unreliable. Especially note that the DDR2 EMIF is clocked by PLL1SYSCLK4. My solution was to switch the CPU to PLL1 also, such that when bypassed the CPU is still clocked sufficiently higher than PLL1SYSCLK4. So I do as follows:

    1. Let the CPU and DDR run from the same PLL

    2. Bypass the original CPU PLL

    3. Put the DDR into refresh-mode

    4. Bypass the DDR PLL (which also currently clocks the CPU)

    5. Poll enable DEEPSLEEP and poll

    Then reverse the steps when waking up. That works for me...

    Best regards,

    Soren

  • Hi Soren

    I tried the steps you followed, however I am getting stuck at bypassing the DDR PLL. I am following the same procedure as I do to bypass the CPU PLL, but it does not seem to work. The steps I follow to come out of bypass mode are:

    1. Put PLL in bypass mode

    2. Assert PLL reset

    3. Wait for 100 (5*25 assuming 25Mhz speed) cycles

    4. De-assert PLL reset

    5. Carry out PLLSECCTL sequence

    6. Poll PLLC1_CONFIG for pll to lock

    7. Remove PLL bypass.

    Could you please guide me as to how you went about taking the PLL out of bypass mode.

    Thanks

    Arshad

  • Hi Arshad,

    Before disabling the PLL for the DDR, I get the code running from internal RAM, hence the routine is implemented in assembler (sleep.S). 

    The steps are:

    1) Put DDR in self-refresh mode

      1a) Set SRPD and LPMODEN bits in SDRCR register

      2a) Set MCLKSTOPEN bit in SDRCR register

      3a) delay

    2) Disable the DDR2 LPSC (use the davinci_ddr_psc_config routine in sleep.S)

    3) Disable clock to the DDR PHY, by clearing the enable bit in the appropriate PLLxDIVy register

    4) Bypass and power-down the PLL

    Then we enter DEEPSLEEP and start polling the SLEEPCOMPLETE bit. Re-enabling the DDR goes as follows:

    1) Bring PLL out of reset

    2) Program "sequence" to get PLL to lock

    3) Enable the clock to DDR PHY (set the PLLxDIVy enable bit again)

    4) Set the PLL "GOSET" bit and wait for lock (Using system register PLLC1_CONFIG or PLLC2_CONFIG)

    5) Remove the PLL from bypass mode

    6) Re-enable the DDR2 LPSC

    7) Bring the DDR out of self-refresh

      7a) Clear the MCLKSTOPEN bit

      7b) clear the LPMODEN bit

    At this point execution returns from internal RAM to DDR ram.

    Hope that helps

    Søren

  • Hi Soren

    Thank you for you assistance. I am also running this code from internal RAM. I followed the steps you suggested to re-enable the DDR (i.e. everything except "4) Bypass and power-down the PLL" ), to ensure that the DDR can be re-enabled witthout bypassing the PLL and the code hangs at the point where I need to "1) Bring PLL out of reset".

    Does this mean set or clear PLLRST? Also according to the datasheet, I am supposed to set PLLRST, wait 5us (not sure how to do that), then clear PLLRST.

    And, when you say "2) Program "sequence" to get PLL to lock", do I need to set the PLLM, PREDIV and POSTDIV?

    If you don't mind, could you please share the piece of code for re-enabling the DDR.

    Thanks

  • Hi Arshad,

    I've added my code below. I dont set PLLM, PREDIV or POSTDIV again, but in order to get their values to "take effect" you need to apply the "sequence" following the PLL reset (see code below).

    Best regards,

    Søren

    wake_up:

    /* Clear sleep enable */
    bic ip, ip, #DEEPSLEEP_SLEEPENABLE_BIT
    str ip, [r4, #DEEPSLEEP]

    /* Clear PLL power down (power up the PLL)*/
    ldr ip, [r3, #PLLCTL]
    bic ip, ip, #PLLCTL_PLLPWRDN
    str ip, [r3, #PLLCTL]

    /* Wait at least 4 reference cycles */
    ldr ip, DELAYS+(PLL_BYPASS_CYCLES*4)
    3: subs ip, ip, #0x1
    bne 3b

    /* Put PLL in reset */
    ldr ip, [r3, #PLLCTL]
    orr ip, ip, #PLLCTL_PLLRST
    str ip, [r3, #PLLCTL]

    /* Wait at least 5 us */
    ldr ip, DELAYS+(PLL_RESET_CYCLES*4)
    4: subs ip, ip, #0x1
    bne 4b

    /* Bring PLL out of reset */
    ldr ip, [r3, #PLLCTL]
    bic ip, ip, #PLLCTL_PLLRST
    str ip, [r3, #PLLCTL]

    /* Program "sequence" to allow PLL to lock at desired frequency */
    mov ip, #0x00470000 /* Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 1 */
    str ip, [r3, #PLLSECCTL]
    mov ip, #0x00460000 /* Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 0 */
    str ip, [r3, #PLLSECCTL]
    mov ip, #0x00400000 /* Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 0 */
    str ip, [r3, #PLLSECCTL]
    mov ip, #0x00410000 /* Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 1 */
    str ip, [r3, #PLLSECCTL]

    /* Enable 2x clock to DDR2 */
    cmp r5, #0 /* r5 contains the PLL of the DDR, 0=>PLL1, 1=>PLL2 */
    ldreq ip, [r3, #PLLDIV7]
    ldrne ip, [r3, #PLLDIV3]
    orr ip, ip, #PLLDIV_EN
    streq ip, [r3, #PLLDIV7]
    strne ip, [r3, #PLLDIV3]

    /* Set the GOSET bit */
    mov ip, #1
    str ip, [r3, #PLLCMD]

    /* Wait for PLL to lock */
    cmp r5, #0
    bne poll_pllc2_config
    poll_pllc1_config:
    ldr ip, [r4, #PLLC1_CONFIG]
    and ip, ip, #PLLCx_LOCK_MASK
    teq ip, #PLLCx_LOCK_MASK
    bne poll_pllc1_config
    b pll_locked

    poll_pllc2_config:
    ldr ip, [r4, #PLLC2_CONFIG]
    and ip, ip, #PLLCx_LOCK_MASK
    teq ip, #PLLCx_LOCK_MASK
    bne poll_pllc2_config

    pll_locked:

    /* Remove PLL from bypass mode */
    ldr ip, [r3, #PLLCTL]
    bic ip, ip, #PLLCTL_PLLENSRC
    orr ip, ip, #PLLCTL_PLLEN
    str ip, [r3, #PLLCTL]

    /* Enable DDR2 LPSC */
    mov r7, r0
    mov r0, #0x3
    bl davinci_ddr_psc_config
    mov r0, r7

    /* clear MCLKSTOPEN */
    ldr ip, [r0, #DDR2_SDRCR_OFFSET]
    bic ip, ip, #DDR2_MCLKSTOPEN_BIT
    str ip, [r0, #DDR2_SDRCR_OFFSET]

    /* clear LPMODEN to stop self-refresh */
    ldr ip, [r0, #DDR2_SDRCR_OFFSET]
    bic ip, ip, #DDR2_LPMODEN_BIT
    str ip, [r0, #DDR2_SDRCR_OFFSET]

    /* Restore registers and return */
    ldmfd sp!, {r0-r12, pc}

  • Hi Soren

    The code I have is very similar to yours, except that I do not check if I am using PLL1 or PLL2 for the DDR, since I know I am using PLL1. Also, your delays are different (DELAYS is undefined for me).

    You mention that /* r5 contains the PLL of the DDR, 0=>PLL1, 1=>PLL2 */, but from the rest of the code it seems that r3 contains the base address for the DDR PLL.

    As I mentioned in the post before this, that I am not putting the DDR into bypass mode, so I can figure out if the wake up procedure works. If you where to do the same, does your code still work.

    Is it possible for you to attach your sleep.S and pm.c files.

    I appreciate your assistance.

    Thanks

    Arshad

  • Hi Soren

    I managed to get it to work. It turns out, I needed a delay after returning from internal RAM. Once again thanks alot for your assistance, I really appreciate it :).

    Regards

    Arshad

  • Hello Arshad,

    I have been experiencing similar problems with Deepsleep. It seems that you have found a solution. Would you be able to potentially post this solution, or describe in detail each of the steps involved in the solution. At the moment, I have the following steps:

    • Switch the CPU PLL to bypass - writes 0s to PLLCTL_PLLENSRC and PLLCTL_PLLEN (PLL2)
    • Wait 4 cycles for Bypass
    • Power down the CPU PLL - writes 1 to PLLCTL_PLLPWRDN
    • Configure sleep count in the deep sleep register - writes 128 into the deepsleep register
    • call sleep.S file - goes into SRAM
      • store all registers
      • call arm926_flush_kern_cache_all
      • load r0 - r4 with the information passed in from pdata
      • Put DDR into self-refresh - write 0 to SRPD and 1 to LPMODEN in SDRCR
      • Stop MCLK - write 1 to MCLKSTOPEN in SDRCR
      • wait 4096 cycles
      • Disable DDR2 LPSC
      • Put DDR into bypass mode - write 0 pllctl_pllensrc and pllctl_pllen (PLL1)
      • wait 4 cycles for bypass
      • Put PLL into reset - write 1 to PLLCTL_PLLRST (PLL1)
      • Disable clock to DDR PHY - write 0 to PLLDIV_EN
      • Power down DDR PLL - write 1 to PLLCTL_PLLPWRDN (PLL1)
      • Go to deep sleep - write 1 to DEEPSLEEP_SPEELENABLE_BIT
      • Stay in loop polling DEEPSLEEP_SLEEPCOMPLETE_BIT
        • Transition GIO0 from high to low, wait more than 500ns, then low to high
      • Clear sleep enable - write 0 to DEEPSLEEP_SLEEPENABLE_BIT
      • Clear PLL power down - write 0 to PLLCTL_PLLPWRDN
      • Put PLL into bypass mode - write 0 to PLLCTL_PLLENSRC and PLLCTL_PLLEN (PLL1)
      • Put PLL into reset - write 1 to PLLCTL_PLLRST (PLL1)
      • wait 125 cycles for reset to take place
      • Take PLL out of reset - write 0 to PLLCTL_PLLRST (PLL1)
      • Start DDR PHY - write 1 to PLLDIV_EN
      • Wait 600 cycles for PLL to lock
      • Remove PLL from bypass - write 0 to PLLCTL_PLLENSRC and 1 to PLLCTL_PLLEN (PLL1)
      • Enable DDR2 LPSC
      • Enable MCLK - write 0 to MCLKSTOPEN
      • Bring DDR2 out of self-refresh - write 0 to LPMODEEN and 1 to SRPD
      • Restore registers and return
    • Remove CPU PLL from power down - write 0 to PLLCTL_PLLPWRDN (PLL2)
    • Put CPU PLL into bypass - write 0 to PLLCTL_PLLENSRC (PLL2)
    • wait 4 clock cycles
    • Put CPU PLL in reset - write 1 to PLLCTL_PLLRST (PLL2)
    • wait 25 clock cycles
    • Bring CPU PLL out of reset - write 0 to PLLCTL_PLLRST (PLL2)
    • PLLSECCTL pattern (TENABLE, TENABLEDIV, TINITZ): (1,1,1); (1,1,0); (0,0,0); (0,0,1)
    • POLL LOCK1, LOCK2, LOCK3 of the system module PLLC2_CONFIG registers until all 1s
    • Remove PLL from bypass - write 0 to PLLCTL_PLLENSRC and PLLCTL_PLLEN (PLL2)
    • wait 4 clock cycles
    • Return to normal functionality

    This seems to either hang up after trying to return to normal functionality or after I try to put the CPU PLL into bypass mode after powering the CPU PLL back up.

    Any information that you could supply would be greatly appreciated.

    Thanks

  • Hi James,

    You will notice in one of the posts before this, where Soren mentioned:

    "I managed to get it working: The issue in my case was, that the CPU pll was PLL2, and when this was bypassed the ARM clock was lower than PLL1SYSCLK4. The result of this was, that any reading or writing to a peripheral clocked from PLL1SYSCLK4 (see figure 5 in SPRUFG5A) would become unreliable. Especially note that the DDR2 EMIF is clocked by PLL1SYSCLK4. My solution was to switch the CPU to PLL1 also, such that when bypassed the CPU is still clocked sufficiently higher than PLL1SYSCLK4."

    So before you bypass the CPU PLL, let the CPU and DDR run from the same PLL, by clearing the ARMCLKS bit in the PERI_CLKCTL register of the system control module.

    • Switch the CPU PLL to bypass - writes 0s to PLLCTL_PLLENSRC and PLLCTL_PLLEN (PLL2)
    • Wait 4 cycles for Bypass
    • Power down the CPU PLL - writes 1 to PLLCTL_PLLPWRDN
    • Configure sleep count in the deep sleep register - writes 128 into the deepsleep register

    I used a sleepcount of 4096, to ensure that the osillator is stable before enabling clocks.

    • call sleep.S file - goes into SRAM
    • store all registers
    • call arm926_flush_kern_cache_all

    You make no mention of branching to ip (blx   ip) between the following steps. I noticed that if that line (blx   ip) is included, the processor hangs, so I commented it out.

      • load r0 - r4 with the information passed in from pdata
      • Put DDR into self-refresh - write 0 to SRPD and 1 to LPMODEN in SDRCR
      • Stop MCLK - write 1 to MCLKSTOPEN in SDRCR
      • wait 4096 cycles

    I actually wait 1000 cycles. I don't see any harm in waiting longer, but I do know that if you want to load a value greater than 1000 to ip, you cannot use the mv command.

      • Disable DDR2 LPSC

    I noticed that you disable the clock to DDR PHY (PLLDIV7) after putting the DDR into bypass mode. I'm not sure if that is an issue, but I do it before.

      • Put DDR into bypass mode - write 0 pllctl_pllensrc and pllctl_pllen (PLL1)
      • wait 4 cycles for bypass
      • Put PLL into reset - write 1 to PLLCTL_PLLRST (PLL1)

    Also, I don't see the need to put the PLL into reset.

      • Disable clock to DDR PHY - write 0 to PLLDIV_EN
      • Power down DDR PLL - write 1 to PLLCTL_PLLPWRDN (PLL1)
      • Go to deep sleep - write 1 to DEEPSLEEP_SPEELENABLE_BIT
      • Stay in loop polling DEEPSLEEP_SLEEPCOMPLETE_BIT
        • Transition GIO0 from high to low, wait more than 500ns, then low to high
      • Clear sleep enable - write 0 to DEEPSLEEP_SLEEPENABLE_BIT
      • Clear PLL power down - write 0 to PLLCTL_PLLPWRDN

    After waking up and clearing PLL power down, I wait 4 clock cycles.

      • Put PLL into bypass mode - write 0 to PLLCTL_PLLENSRC and PLLCTL_PLLEN (PLL1)

    There is no need to put the PLL back into bypass mode.

      • Put PLL into reset - write 1 to PLLCTL_PLLRST (PLL1)
      • wait 125 cycles for reset to take place
      • Take PLL out of reset - write 0 to PLLCTL_PLLRST (PLL1)

    /* Program "sequence" to allow PLL to lock at desired frequency */
    mov ip, #0x00470000 /* Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 1 */
    str ip, [r3, #PLLSECCTL]
    mov ip, #0x00460000 /* Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 0 */
    str ip, [r3, #PLLSECCTL]
    mov ip, #0x00400000 /* Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 0 */
    str ip, [r3, #PLLSECCTL]
    mov ip, #0x00410000 /* Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 1 */
    str ip, [r3, #PLLSECCTL]

    /* Set the GOSET bit */
    mov ip, #1
    str ip, [r3, #PLLCMD]

    Wait for PLL lock

    Remove PLL from bypass

      • Start DDR PHY - write 1 to PLLDIV_EN
      • Wait 600 cycles for PLL to lock
      • Remove PLL from bypass - write 0 to PLLCTL_PLLENSRC and 1 to PLLCTL_PLLEN (PLL1)
      • Enable DDR2 LPSC
      • Enable MCLK - write 0 to MCLKSTOPEN

    Poll SDRSTAT to check that MCLK is started

      • Bring DDR2 out of self-refresh - write 0 to LPMODEEN and 1 to SRPD

    At this point I actually wait 2000 cycles.

      • Restore registers and return

    I also wait here for 100 microseconds.

    • Remove CPU PLL from power down - write 0 to PLLCTL_PLLPWRDN (PLL2)
    • Put CPU PLL into bypass - write 0 to PLLCTL_PLLENSRC (PLL2)

    No need to put PLL into bypass

    • wait 4 clock cycles
    • Put CPU PLL in reset - write 1 to PLLCTL_PLLRST (PLL2)
    • wait 25 clock cycles
    • Bring CPU PLL out of reset - write 0 to PLLCTL_PLLRST (PLL2)
    • PLLSECCTL pattern (TENABLE, TENABLEDIV, TINITZ): (1,1,1); (1,1,0); (0,0,0); (0,0,1)

    Set the GOSET bit

    • POLL LOCK1, LOCK2, LOCK3 of the system module PLLC2_CONFIG registers until all 1s
    • Remove PLL from bypass - write 0 to PLLCTL_PLLENSRC and PLLCTL_PLLEN (PLL2)
    • wait 4 clock cycles

    Switch clock source of CPU back to PLL2

    • Return to normal functionality

    This seems to either hang up after trying to return to normal functionality or after I try to put the CPU PLL into bypass mode after powering the CPU PLL back up.

    I suggest that you work from the outside in, to figure out where exactly your problem lies. So, as an initial test, comment out the code that puts the unit into SRAM (davinci_sram_suspend(pdata);) and check that you can successfully put the CPU PLL into bypass and successfully take it out of bypass.

    Hope this helps

    Regards

    Arshad

  • Hey Guys,

     

    In connection with deep sleep for the dm365, I am having an issue with vpfe_suspend(). Originally I had no support for vpfe_suspend, so I used part of the following patch http://osdir.com/ml/linux-media/2009-11/msg00807.html which is actually for the dm644x. I did not use the ccdc_save_context() and ccdc_restore_context() functions, as they seemed to not do anything (i.e. after ccdc_save_context, I printed the registers and they were all zero). So regardless of whether I used the ccdc_save/restore_context functions, I still experienced a different issue.

     The issue is, when vpfe_suspend is called, it calls vpfe_disable_clock(vpfe_dev), which calls clk_disable(vpfe_dev->clks[i]). At this point, where the clock is being disabled, the processor sometimes hangs. Because the issue is not consistent, it makes it more difficult to debug and hence fix.

     Any help will be much appreciated.

    Regards

    Arshad

  • Hi Arshad,

    It seems like I'm not able to put it to sleep and then come back out. Once it returns to the dram it just stops. Adding the 100us delay upon return does not seem to fix it. I can execute some assembly code lines to print a b to the terminal, which tells me that it come back out of sram, but I cannot get my next character c to print following the 100us delay. If I don't print the b, then no character tells me that I have returned (it still does not print the c after the delay).

    This problem goes away if I do not go to sleep at all (no deepsleep state machine steps), but if I do sleep, then it will not execute again. I'm not sure what is happening in this case.

    Thanks for the help in advanced