How to change CCM_CSCMR2[7:2] (CAN_PODF) using clock API from the module

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

How to change CCM_CSCMR2[7:2] (CAN_PODF) using clock API from the module

1,705 Views
krzysztof_blasz
Contributor I

Hello all,

I look for rationale of design of imx_clk_divider(), choice of CLK_SET_RATE_PARENT for such clock instances like CAN_PODF.

and here is the line of my interest from clk-imx6ul.c:

787b4271a6a0c (Frank Li             2015-07-10 02:09:42 +0800 302)     clks[IMX6UL_CLK_CAN_PODF]    = imx_clk_divider("can_podf",       "can_sel",        base + 0x20, 2,  6);

At 1st I want to be confident of that I read assumptions right and my understanding of above statement is like following:

1.1. "can_sel" stands for parent clock instance to "can_podf",

1.2. CLK_SET_RATE_PARENT stands for that changes to "can_podf" will be propagated to clock mutliplexer as

    depicted in i.MX 6ULL Applications Processor Reference Manual, Rev. 1, 11/2017 on page 630, section 18: CCM

I want to find a right API way from perspective of loadable kernel driver module to change value of the CAM_PODF divisor only.

1.3 And because of the way clks[IMX6UL_CLK_CAN_PODF] was created it seems that multiplexer may be affected too even that it is not related to change of the part of "18.6.9 CCM Serial Clock Multiplexer Register 2 (CCM_CSCMR2)" for CAN_PODF

I conducted following test by adding this code to clk-imx6ul.c:

    clks[IMX6UL_CLK_CAN_PODF]    = imx_clk_divider("can_podf",       "can_sel",        base + 0x20, 2,  6);

printk("%s:%d \"can_podf\" %p\n", __FUNCTION__, __LINE__, clks[IMX6UL_CLK_CAN_PODF]);
printk("%s:%d clk_get(\"can_podf\") %p\n", __FUNCTION__, __LINE__, clk_get(NULL, "can_podf"));
    {
        struct clk *can_podf = clks[IMX6UL_CLK_CAN_PODF];

            int rate = clk_get_rate(can_podf);
            printk("%s:%d clk_podf rate  %u\n", __FUNCTION__, __LINE__, rate);

            clk_set_rate(can_podf, 1);

            rate = clk_get_rate(can_podf);
            printk("%s:%d ->1: clk_podf rate  %u\n", __FUNCTION__, __LINE__, rate);
    }

My goal was to change the podf only not messing up with the CCM_CSCMR2 register directly.

And here is kernel log from running the test:

[    0.000000] imx6ul_clocks_init:299 "can_podf" 88010dc0
[    0.000000] imx6ul_clocks_init:300 clk_get("can_podf") fffffffe
[    0.000000] imx6ul_clocks_init:305 clk_podf rate  0
[    0.000000] Division by zero in kernel.
[    0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.9.11 #17
[    0.000000] Hardware name: Freescale i.MX6 UltraLite (Device Tree)
[    0.000000] [<8010ed88>] (unwind_backtrace) from [<8010b390>] (show_stack+0x10/0x14)
[    0.000000] [<8010b390>] (show_stack) from [<803ad944>] (dump_stack+0x78/0x8c)
[    0.000000] [<803ad944>] (dump_stack) from [<803ab970>] (Ldiv0_64+0x8/0x18)
[    0.000000] [<803ab970>] (Ldiv0_64) from [<80427b0c>] (divider_get_val+0x15c/0x17c)
[    0.000000] [<80427b0c>] (divider_get_val) from [<80427b54>] (clk_divider_set_rate+0x28/0xc0)
[    0.000000] [<80427b54>] (clk_divider_set_rate) from [<804265dc>] (clk_change_rate+0x194/0x248)
[    0.000000] [<804265dc>] (clk_change_rate) from [<804266f8>] (clk_core_set_rate_nolock+0x68/0xb0)
[    0.000000] [<804266f8>] (clk_core_set_rate_nolock) from [<80426760>] (clk_set_rate+0x20/0x30)
[    0.000000] [<80426760>] (clk_set_rate) from [<80e33274>] (imx6ul_clocks_init+0x1930/0x3aa8)
[    0.000000] [<80e33274>] (imx6ul_clocks_init) from [<80e1ea04>] (of_clk_init+0x15c/0x1e8)
[    0.000000] [<80e1ea04>] (of_clk_init) from [<80e04528>] (time_init+0x24/0x2c)
[    0.000000] [<80e04528>] (time_init) from [<80e00b34>] (start_kernel+0x254/0x388)
[    0.000000] [<80e00b34>] (start_kernel) from [<8000807c>] (0x8000807c)
[    0.000000] imx6ul_clocks_init:310 ->1: clk_podf rate  0

2.1. Surprisingly clk_get(NULL||&dev, "can_podf") returns ERR_PTR;

   How can I get the pointer to "can_podf" from the driver's module ?

2.2 clk_get_rate() on can_podf instance returns 0 which is invalid because default divider settings is "/2" ie CCM_CSCMR2[7:2] = 1 thus I expected to see 1 or 2 as result but 0.

2.3 clk_set_rate(, 1) causes integer division by 0.

        I reckon that if whole "common clock" is designed correctly then this "0" division stands for mistake in clk-imx6ul.c

        see also claim 1.3. multiplexer and divider are chained so there is parent relationship indeed but I reckon that these two hardware modules are usually independent from each other, at least I would like to have a facility to select clock source and divisor number, unless there is some well designed abstract clock instance which can set both multiplexer and divider with regard to final flexcan frequency: e.g. clk_set_rate(the_abstract_clock, 24e6 or 30e6 or some other value from finite set with regard to section 18.3 CCM Clock Tree of i.MX 6ULL Applications Processor Reference Manual, Rev. 1, 11/2017). It seems there is no such abstract clock working.

2.4 clk_set_rate() was not successful likely.

I look for solution which will allow to change flexcan clock from default 30MHz, ie pll3_sw_clk clock (60M) /2 to my desired value from perspective of kernel driver module and this solution should not depend on some "quirks" made to clk-imx6ul.c

Labels (1)
0 Kudos
1 Reply

1,459 Views
krzysztof_blasz
Contributor I

It turned out that using clk_set_rate() too early, ie just right after instance of the CAN_PODF divider was created, ends up with that division by 0.

if the "clock" freq. setting is moved to the end of all clocks initialization then the CSCMR2[7:2] has expected value.

[    0.000000] imx6ul_clocks_init:532 clk_podf rate  30000000, CCM_CSCMR2 13192c06
[    0.000000] imx6ul_clocks_init:537 clk_podf rate  60000000, CCM_CSCMR2 13192c02

Furthermore it is possible to use clk_set_rate(clk_get(  ,"per"), freq) from the driver and following clk_get_rate() confirms that the change was successful.

0 Kudos