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.

[FAQ] PRU_ICSSG: How to check and set PRU Core Frequency in Linux?

I am using a processor with PRU_ICSSG (AM64x, AM65x). I am using the on-chip Linux core to initialize the PRU_ICSSG. How do I check what frequency the PRU cores are running at? How do I change the PRU core frequency?

Note: these steps document how to use a Linux core to set PRU core frequency. For information on Setting PRU core frequency from CCS, RTOS, or bare metal: Reference [FAQ] PRU_ICSSG: How to check and set PRU Core Frequency in CCS?

  • This response will cover the general steps. Later responses will give specific examples.


    Where does the ICSSG core clock come from?

    For information about how PRU_ICSSG clocks are connected to the rest of the processor, reference the Technical Reference Manual (TRM) of the processor. Start with TRM section "PRU_ICSSG Integration", section "PRU_ICSSG Power and Clock Management", and table "PRU_ICSSG Clocks"

    The actual CORE_CLOCK that is used within an ICSSG is selected from either ICSSGn_CORE_CLK or ICSSGn_ICLK (250MHz, synchronized to interface clock). CORE_CLOCK is selected with register ICSSG_CORE_SYNC_REG, bit CORE_VBUSP_SYNC_EN.

    ICSSGn_CORE_CLK can be selected from different system clocks, depending on the device. ICSSGn_CORE_CLK is selected with register CTRLMMR_ICSSGx_CLKSEL, bit CORE_CLKSEL.


    When are the system clocks configured?

    The system clocks are configured by the Linux kernel during bootup. The Linux devicetree defines the system clock settings.


    How do I check the System Clock Frequencies? 

    First, you need to know the Device IDs and Clock IDs used by TISCI.
    AM64x Device IDs and Clock IDs are defined here: https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am64x/clocks.html
    AM65x Device IDs and Clock IDs are defined here: https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am65x_sr2/clocks.html 

    You can check clock frequencies from the EVM terminal with either the kernel clk_summary, or k3conf. See usage in section "Checking the current PRU Core Clock Frequency".
    # cat /sys/kernel/debug/clk/clk_summary
    # k3conf dump clocks


    How do I change the PRU Core Clock Frequency? 

    One level of clock muxing

    Example clock source: VCLK. See section "Example: One level of clock muxing".

    A single clock mux is represented in the Linux devicetree file with a node. ICSSG clock mux nodes are named icssgX_coreclk_mux and icssgX_iepclk_mux. For this explanation, we will use the terms in this diagram:

    input1 -->|
    ...    -->|
    inputY -->| clk_mux --> output --> PRU core
    ...    -->|
    inputN -->|

    For this clock mux, our clock mux node would look like this:

    clk_mux_node {
        clocks = <input1>,
                 ...
                 <inputY>,
                 ...
                 <inputN>;
        assigned-clocks = <&clk_mux_node>; // this is "output"
        assigned-clock-parents = <inputY>; the clock parent of "output" is selected from input1:inputN
        assigned-clock-rates = <frequency_in_Hz_of_output>; // not needed for fixed frequency sources like VCLK
    }

    This node is pretty simple:
    1) Select one of the inputs to clk_mux (inputY)
    2) Set inputY as the clock-parent of the output of clk_mux
    3) If the clock source can be set to multiple frequencies, specify the correct frequency in assigned-clock-rates


    Multiple levels of clock muxing

    Example clock source: ICSSGn_CORE_CLK. See section "Example: Multiple levels of clock muxing".

    Sometimes the PRU clock source requires multiple levels of muxing. For this explanation, we will use the terms in this diagram:

    in1 -->|                 input1 -->|
    ... -->|                    ... -->|
    inY -->|-> clk_mux_2 --> inputY -->|-> clk_mux --> output --> PRU core
    ... -->|                    ... -->|
    inN -->|                 inputN -->|

    Instead of creating a separate node for clk_mux2, we add that mux information into the clk_mux node:

    clk_mux_node {
        clocks = <input1>,
                 ...
                 <inputY>,
                 ...
                 <inputN>;
        assigned-clocks = <inputY>, <&clk_mux_node>;
        assigned-clock-parents = <inY>, <inputY>; // inY parents inputY, inputY parents &clk_mux_node
        assigned-clock-rates = <frequency_in_Hz_of_output>, <0>; // set inputY to the expected frequency
    }

    Notice that we need to use assigned-clock-rates if the clock source can be set to multiple frequencies.


    Do I need to look for clocking conflicts after setting a clock source with assigned-clock-rates?
     

    It depends on whether that clock source can be used by multiple peripherals within the processor.

    For AM64x PRU Core clock sources, you do NOT need to check for clocking conflicts unless you are using OBSCLK0 output with MAIN_PLL2_HSDIV0_CLKOUT as the clock source. (the PRU Core clock sources with adjustable frequencies do not source any other peripherals). However, if you are setting clock frequencies for other clock sources, you SHOULD check for clocking conflicts.

    See reply "Checking for clocking conflicts" for more details.

  • AM64x example: 

    CORE_CLOCK is selected from either ICSSGn_CORE_CLK or ICSSGn_ICLK (250MHz, synchronized to interface clock). ICSSGn_CORE_CLK selects between MAIN_PLL2_HSDIV0_CLKOUT (225MHz, 300MHz) or MAIN_PLL0_HSDIV9_CLKOUT (200MHz, 250MHz, 333MHz i.e. 1GHz / 3).


    Checking the current PRU Core Clock Frequency

    First, you need to know the Device IDs and Clock IDs used by TISCI. If we check here:
    https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am64x/clocks.html
    Then we see that AM64x ICSSG0 is Device ID 81, and ICSSG1 is Device ID 82.

    Before making any changes, we will check the default settings of the PRU core clocks for ICSSG0. First, we will check the kernel clk_summary:

    # cat /sys/kernel/debug/clk/clk_summary
    
                                     enable  prepare  protect                                duty
       clock                          count    count    count        rate   accuracy phase  cycle
    ---------------------------------------------------------------------------------------------
    ...
     clk:82:20                            0        0        0   250000000          0     0  50000
        30080000.icssg.coreclk-mux        0        0        0   250000000          0     0  50000
           30080000.icssg.iepclk-mux       0        0        0   250000000          0     0  50000
     clk:82:11                            0        0        0   500000000          0     0  50000
    ...
     clk:82:2                             0        0        0   333333333          0     0  50000
     clk:82:1                             0        0        0   225000000          0     0  50000
        clk:82:0                          0        0        0   225000000          0     0  50000
     clk:81:20                            0        0        0   250000000          0     0  50000
        30000000.icssg.coreclk-mux        0        0        0   250000000          0     0  50000
           30000000.icssg.iepclk-mux       0        0        0   250000000          0     0  50000
     clk:81:11                            0        0        0   500000000          0     0  50000

    This tells us that both PRU core clock and IEP timer clock are using the VCLK clock source (Clock ID 20). This is true for ICSSG0 and ICSSG1. We expect VCLK to run at 250MHz, and the clock frequency is confirmed with "rate" = 250000000.

    This clock muxing matches the default k3-am64-main.dtsi devicetree settings (see section "Example: One level of clock muxing").

    You can also use k3conf to dump a list of clocks and their frequencies:

    # k3conf dump clocks
    
    |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
    | Device ID | Clock ID | Clock Name                                                                                           | Status              | Clock Frequency |
    |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
    ...
    |    81     |     0    | DEV_PRU_ICSSG0_CORE_CLK                                                                              | CLK_STATE_READY     | 225000000       |
    ...
    |    81     |    20    | DEV_PRU_ICSSG0_VCLK_CLK                                                                              | CLK_STATE_READY     | 250000000       |
    ...
    |    82     |     0    | DEV_PRU_ICSSG1_CORE_CLK                                                                              | CLK_STATE_READY     | 225000000       |
    ...
    |    82     |    20    | DEV_PRU_ICSSG1_VCLK_CLK                                                                              | CLK_STATE_READY     | 250000000       |
    ...

    Note that in the k3conf list, DEV_PRU_ICSSG0_CORE_CLK is different from CORE_CLK. DEV_PRU_ICSSG0_CORE_CLK is actually input ICSSGn_CORE_CLK. This input clock source is not selected in the default devicetree.


    Setting the PRU core Clock Frequency 

    Example: One level of clock muxing

    We will use the default devicetree settings: 250MHz VCLK is our clock source for ICSSG0 CORE_CLK. VCLK only requires one level of muxing. VCLK only runs at one frequency (250MHz), so assigned-clock-rates is not needed in the Linux devicetree node.

    icssg0_coreclk_mux: coreclk-mux@3c {
    	reg = <0x3c>;
    	#clock-cells = <0>;
    	clocks = <&k3_clks 81 0>,  /* icssg0_core_clk */
    			 <&k3_clks 81 20>; /* icssg0_iclk */
    	assigned-clocks = <&icssg0_coreclk_mux>;
    	assigned-clock-parents = <&k3_clks 81 20>;
    };


    Example: Multiple levels of clock muxing 

    What if we want to run the ICSSG0 PRU cores at 200MHz? This devicetree setting requires multiple levels of muxing, AND assigned-clock-rates.

    icssg0_coreclk_mux: coreclk-mux@3c {
    	reg = <0x3c>;
        #clock-cells = <0>;
        clocks = <&k3_clks 81 0>,  /* icssg0_core_clk */
                 <&k3_clks 81 20>; /* icssg0_iclk */
        assigned-clocks = <&k3_clks 81 0>, <&icssg0_coreclk_mux>;
        assigned-clock-parents = <&k3_clks 81 2>, <&k3_clks 81 0>; /* DEV_PRU_ICSSG1_CORE_CLK_PARENT_POSTDIV4_16FF_MAIN_0_HSDIVOUT9_CLK */
        assigned-clock-rates = <200000000>, <0>; /* 200MHz */
    };

    1) Use https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am64x/clocks.html to determine that MAIN_PLL0_HSDIV9_CLKOUT is Clock ID 2.

    2) Set ICSSG0 Clock ID 2 as the parent clock to ICSSG0 Clock ID 0 (ICSSG0_CORE_CLK)

    3) Set ICSSG0 Clock ID 0 as the parent clock to &icssg0_coreclk_mux (the actual CORE_CLK)

    4) Set MAIN_PLL0_HSDIV9_CLKOUT = 200MHz with assigned-clock-rates

  • Checking for clocking conflicts 

    Some clock sources can provide a clock to multiple peripherals within a device.

    AM64x example:
    MAIN_PLL0_HSDIV6_CLKOUT (200 or 250 MHz) can be used to source ICSSG IEP clock, but it can also source the GTC, the main domain CPTS, the CPSW CPTS, and the PCIE CPTS. So if you set a specific clock frequency for 
    MAIN_PLL0_HSDIV6_CLKOUT with assigned-clock-rates, then you should:
    1) check whether any of those other peripherals are using MAIN_PLL0_HSDIV6_CLKOUT
    2) make sure that you are not trying to set MAIN_PLL0_HSDIV6_CLKOUT to different frequencies in different device tree nodes


    How to check for clocking conflicts? 

    Here is the cleanest method I found so far:

    1) Find the name of the clock source in the TISCI documentation at https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am64x/clocks.html 

    2) Find all the peripherals that can use that clock source

    3) use the kernel clk_summary to check existing clock settings from the terminal


    AM64x example: MAIN_PLL0_HSDIV6_CLKOUT

    1) MAIN_PLL0_HSDIV6_CLKOUT is named MAIN_0_HSDIVOUT6_CLK at https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am64x/clocks.html

    2) By using Ctrl-f, I see that MAIN_0_HSDIVOUT6_CLK is used by:
    AM64X_DEV_CPSW0 (ID = 13) - entry 3
    AM64X_DEV_CPTS0 (ID = 84) - entry 2
    AM64X_DEV_GTC0 (ID = 61) - entry 2
    AM64X_DEV_PCIE0 (ID = 114) - entry 3
    AM64X_DEV_PRU_ICSSG0 (ID = 81) - entry 5
    AM64X_DEV_PRU_ICSSG1 (ID = 82) - entry 5

    3) Now I can run
    # cat /sys/kernel/debug/clk/clk_summary
    and see if any of these clocks are used by a Linux devicetree node:
    clk:13:3
    clk:84:2
    etc.

    Let us take a look at an example:

    # cat /sys/kernel/debug/clk/clk_summary 
                                     enable  prepare  protect                                duty
       clock                          count    count    count        rate   accuracy phase  cycle
    ---------------------------------------------------------------------------------------------
     ...
     clk:82:2                             0        0        0   333333333          0     0  50000
     clk:82:1                             0        0        0   225000000          0     0  50000
        clk:82:0                          0        0        0   300000000          0     0  50000
           30080000.icssg.coreclk-mux       0        0        0   300000000          0     0  50000
              30080000.icssg.iepclk-mux       0        0        0   300000000          0     0  50000

    In this example output, clk:82:2 is not used by a Linux devicetree node.

    However, clk81:1 is used by a Linux devicetree node. The output tells us that
    clk:82:1 is the clock parent of clk:82:0
    clk:82:0 is the clock parent of devicetree node 30080000.icssg.coreclk-mux
    devicetree node 30080000.icssg.coreclk-mux is the clock parent of 
    devicetree node 30080000.icssg.iepclk-mux