setting divider for mmdc_ch1_axi_clk

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

setting divider for mmdc_ch1_axi_clk

1,070 Views
simonbraunschmi
Contributor I

Hi

I have troubles setting up the LVDS pixel clock to frequencies below 38.79 Mhz, compare also the discussion in "LVDS pixel clock on i.MX6" [1].

To allow lower frequencies, I came up with the idea to derive the LDB clock from mmdc_ch1_axi_clk and in turn derive mmdc_ch1_axi_clk from

pll2_pfd_352M (PLL2,PFD2).

mmdc_ch1_axi_clk itself does not seem to be used by other modules in the kernel 3.0.35_4.0.0, and the current setup is to derive the clock from pll2_pfd_352M directly, so this seems possible.

This would allow to use both the divider for mmdc_ch1_axi_clk (/1 .. /8) and the PFD to generate the clock.

Example:

to generate a clock near 33.26 MHz as needed in [1], pll2_pfd_352M is set to 475.2 MHz and the divider for mmdc_ch1_axi_clk is set to 2. We thus obtain

475200000/2 MHz ^= LVDS bitrate 237.6 Mbit/s

237.6 MHz/7 ^= LVDS Pixelclock 33.94 MHz

The Problem:

I have everything set up already in code, to reparent the mmdc_ch1_axi_clk and to derive the nearest clock frequency by combining the divider and the PFD. As per the manual, after changing the bits at MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK (the divider), you have to wait for MXC_CCM_CDHIPR_MMDC_CH0_PODF_BUSY to toggle to zero again, compare "18.5.1.5.6 Divider change handshake" .

I used the following code to wait for the bit, but it never toggles to zero.

        if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR)
             & MXC_CCM_CDHIPR_PERIPH2_CLK_SEL_BUSY), SPIN_DELAY))
                panic("_clk_mmdc_ch1_axi_set_parent failed\n");

I also tried other kinds of delay-loops, I can see that the bit does not toggle to zero for as long as 3 seconds.

But when I disable the handshaking,

       reg = __raw_readl(MXC_CCM_CCDR);
       reg |= MXC_CCM_CCDR_MMDC_CH1_HS_MASK;
       __raw_writel(reg,MXC_CCM_CCDR);

the WAIT(condition,delay)-loop above succeeds (it looks like the bit is then not ever set to indicate "wait-for-handshake"), and I can even observe the desired clock on LVDS, as I checked with a scope.

My 3 questions:

Can you help me understand why the handshake obviously does not finish?

Do the module or modules that derive their clock from mmdc_ch1_axi_clk have to be in a specific condition before attempting to change to divider, so that the handshake can succeed?

Would it be OK to just delay for some amount of time (how long typically?), instead of waiting for the handshake-indication?

related discussion:

[1] LVDS pixel clock on i.MX6

     https://community.freescale.com/message/331739


Best Regards,

Simon Braunschmidt

Labels (1)
0 Kudos
3 Replies

623 Views
AnsonHuang
NXP Employee
NXP Employee

Hi,Simon

     1. Can you dump the registers value before and after you change the parent of periph2_clk, I need to know the register values of CCM_CBCMR, CCM_CBCDR, also, when you try to switch the parent of periph2_clk, do you follow the flow? You need to switch periph2_clk_sel to 1b'1 first, then set the pre_periph2_clk_sel to 2b'10(352M PFD), then switch periph2_clk_sel back to 1b'0.

     2. I think there is no specific requirement for the handshake of mmdc_ch1_axi_clk;

     3. It is better to wait for the handshake, our timeout is set to 1.2ms, so if you are sure the clock setting already be done but there is no handshake, I think waiting for 2ms is enough, but it would be better to use handshake, I didn't meet handshake issue before.

623 Views
simonbraunschmi
Contributor I

Hello Yongcai,

Thanks for your reply!

Sorry for the confusion, the WAIT() statement that I showed, to wait for MXC_CCM_CDHIPR_PERIPH2_CLK_SEL_BUSY,

was the wrong one. periph2 clock switching seems to work fine for my case.

I checked the setting of the CCM_CBCMR, CCM_CBCDR before and after as you recommended to confirm this.

I did not actually implement the switch to (periph2_clk_sel=1b'1), then back to (periph2_clk_sel=1b'0). Is this explained in the PRM? "18.5.1.5.3 PLL clock change" only talks about PLLs.

I even observed the problem with the handshake of mmdc_ch1_axi_clk when I did not reparent the periph2 at all.

For the record, the WAIT() statement that does no return, waiting for handshake, is actually

        if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR)

                & MXC_CCM_CDHIPR_MMDC_CH1_PODF_BUSY), SPIN_DELAY))

                        panic("_clk_mmdc_ch1_axi_set_rate failed\n");

I now implemented the divider change for mmdc_ch1 as follows, with 2ms delay as per your recommendation:

diff --git a/arch/arm/mach-mx6/clock.c b/arch/arm/mach-mx6/clock.c

index 2941e8c..7f80a48 100644

--- a/arch/arm/mach-mx6/clock.c

+++ b/arch/arm/mach-mx6/clock.c

@@ -1825,9 +1825,38 @@ static int _clk_mmdc_ch1_axi_set_rate(struct clk *clk, unsigned long rate)

        reg |= (div - 1) << MXC_CCM_CBCDR_MMDC_CH1_PODF_OFFSET;

        __raw_writel(reg, MXC_CCM_CBCDR);

+#ifdef CONFIG_FB_MX6_LDB_352M_MMDC_CH1

+       /*

+        * TODO

+        * > we observe that the WAIT() for MXC_CCM_CDHIPR_MMDC_CH1_PODF_BUSY

+        *   does not return

+        * -> so we disable handshake for mmdc_ch1,

+        *    else clock_set (setting the divider) would not return,

+        *    waiting forever (at least until panic()) for the handshake

+        * -> it also does not help to just wait unconditionally,

+        *    without deactivating the handshake,

+        *    in this case the divider would not be applied

+        * => for now, we only apply this hack when we actually use mmdc_ch1

+        *    for LVDS

+        *

+        * TODO: investigate*/

+       reg = __raw_readl(MXC_CCM_CCDR);

+       reg |= MXC_CCM_CCDR_MMDC_CH1_HS_MASK;

+       __raw_writel(reg,MXC_CCM_CCDR);

+

+       /* unconditionally wait 2ms for signal to stabilize,

+        * compare https://community.freescale.com/thread/309091

+        */

+       WAIT((false),2000000);

+

+       reg = __raw_readl(MXC_CCM_CCDR);

+       reg &= ~MXC_CCM_CCDR_MMDC_CH1_HS_MASK;

+       __raw_writel(reg,MXC_CCM_CCDR);

+#else /* CONFIG_FB_MX6_LDB_352M_MMDC_CH1 */

        if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR)

                & MXC_CCM_CDHIPR_MMDC_CH1_PODF_BUSY), SPIN_DELAY))

                        panic("_clk_mmdc_ch1_axi_set_rate failed\n");

+#endif /* CONFIG_FB_MX6_LDB_352M_MMDC_CH1 */

        return 0;

}

0 Kudos

623 Views
AnsonHuang
NXP Employee
NXP Employee

Hi, Simon

     I think you should follow the flows, as the MUX of CBCDR periph2_clk_sel is a glitchless mux, but the MUX of CBCMR pre_periph2_clk_sel is not. So, everytime you want to change the parent of pre_periph2_clk, you need to switch the periph2_clk_sel to 1'b1 first, then change the MUX of CBCMR to set the pre_periph2_clk_sel, then switch back periph2_clk_sel to 1'b0. This is the safe flow. Changing the non-glitchless MUX could cause unexpected result, there may be no output of MUX.

     And, when you switch the periph2_clk_sel, you should check the handshake of periph2_clk_sel_busy bit. When you change the divider of mmdc_ch1_axi, you should change the handshake of MMDC_CH1_PODF_BUSY. Simply using a 2mS delay is not recommended! Checking handshake is the right approach.

     The glitchless rule is not just for PLLs. Everytime you change a clk's parent, and if there is children node enabled in this clk, you should first switch its children to a safe clk source by glitchless MUX, then change this clk's parent, then switch its children clk back.