lkml.org 
[lkml]   [2019]   [Jul]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
SubjectRe: [PATCH 3/4] clk: meson: g12a: add notifiers to handle cpu clock change
From
Date
On 30/07/2019 10:37, Jerome Brunet wrote:
> On Mon 29 Jul 2019 at 15:16, Neil Armstrong <narmstrong@baylibre.com> wrote:
>
>> In order to implement clock switching for the CLKID_CPU_CLK and
>> CLKID_CPUB_CLK, notifiers are added on specific points of the
>> clock tree :
>>
>> cpu_clk / cpub_clk
>> | \- cpu_clk_dyn
>> | | \- cpu_clk_premux0
>> | | |- cpu_clk_postmux0
>> | | | |- cpu_clk_dyn0_div
>> | | | \- xtal/fclk_div2/fclk_div3
>> | | \- xtal/fclk_div2/fclk_div3
>> | \- cpu_clk_premux1
>> | |- cpu_clk_postmux1
>> | | |- cpu_clk_dyn1_div
>> | | \- xtal/fclk_div2/fclk_div3
>> | \- xtal/fclk_div2/fclk_div3
>> \ sys_pll / sys1_pll
>>
>> This for each cluster, a single one for G12A, two for G12B.
>>
>> Each cpu_clk_premux1 tree is marked as read-only and CLK_SET_RATE_NO_REPARENT,
>> to be used as "parking" clock in a safe clock frequency.
>>
>> A notifier is added on each cpu_clk_premux0 to detech when CCF want to
>> change the frequency of the cpu_clk_dyn tree.
>> In this notifier, the cpu_clk_premux1 tree is configured to use the xtal
>> clock and then the cpu_clk_dyn is switch to cpu_clk_premux1 while CCF
>> updates the cpu_clk_premux0 tree.
>>
>> A notifier is added on each sys_pll/sys1_pll to detect when CCF wants to
>> change the PLL clock source of the cpu_clk.
>> In this notifier, the cpu_clk is switched to cpu_clk_dyn while CCF
>> updates the sys_pll/sys1_pll frequency.
>>
>> A third small notifier is added on each cpu_clk / cpub_clk and cpu_clk_dyn,
>> add a small delay at PRE_RATE_CHANGE/POST_RATE_CHANGE to let the other
>> notofiers change propagate before changing the cpu_clk_premux0 and sys_pll
>> clock trees.
>>
>> This notifier set permits switching the cpu_clk / cpub_clk without any
>> glitches and using a safe parking clock while switching between sub-GHz
>> clocks using the cpu_clk_dyn tree.
>>
>> This setup has been tested and validated on the Amlogic G12A and G12B
>> SoCs running the arm64 cpuburn at [1] and cycling between all the possible
>> cpufreq translations of each cluster and checking the final frequency using
>> the clock-measurer, script at [2].
>>
>> [1] https://github.com/ssvb/cpuburn-arm/blob/master/cpuburn-a53.S
>> [2] https://gist.github.com/superna9999/d4de964dbc0f84b7d527e1df2ddea25f

Signoff missing.... will fix in v2

>> ---
>> drivers/clk/meson/g12a.c | 567 +++++++++++++++++++++++++++++++++++----
>> 1 file changed, 521 insertions(+), 46 deletions(-)
>>
>> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
>> index e4957fd9f91f..23162310c7ee 100644
>> --- a/drivers/clk/meson/g12a.c
>> +++ b/drivers/clk/meson/g12a.c
>> @@ -14,6 +14,7 @@
>> #include <linux/init.h>
>> #include <linux/of_device.h>
>> #include <linux/platform_device.h>
>> +#include <linux/clk.h>
>>
>> #include "clk-mpll.h"
>> #include "clk-pll.h"
>> @@ -88,16 +89,9 @@ static struct clk_regmap g12a_fixed_pll = {
>> },
>> };
>>
>> -/*
>> - * Internal sys pll emulation configuration parameters
>> - */
>> -static const struct reg_sequence g12a_sys_init_regs[] = {
>> - { .reg = HHI_SYS_PLL_CNTL1, .def = 0x00000000 },
>> - { .reg = HHI_SYS_PLL_CNTL2, .def = 0x00000000 },
>> - { .reg = HHI_SYS_PLL_CNTL3, .def = 0x48681c00 },
>> - { .reg = HHI_SYS_PLL_CNTL4, .def = 0x88770290 },
>> - { .reg = HHI_SYS_PLL_CNTL5, .def = 0x39272000 },
>> - { .reg = HHI_SYS_PLL_CNTL6, .def = 0x56540000 },
>> +static const struct pll_mult_range g12a_sys_pll_mult_range = {
>> + .min = 128,
>> + .max = 250,
>> };
>
> The init sequence is removed, I suppose you were concerned about
> glitching the clock on startup ?
>
> Without the init sequence, it will inherit whatever is left by the
> bootloader. We have seen in the past that this is not desirable.
>
> I'm mostly concerned about CNTL3 to CNTL6. Should we apply the sequence
> on .set_rate() instead ? It should be safe then ?
>

This PLL (and the SYS1_PLL) cannot be disabled (thus re-initialized) until
the cpu has been parked on a safe clock, and the default beahavior of the
current G12A & G12B BL2 is to setup this PLL and the SYS1_PLL to start with
the cores at 1,2GHz. So we can consider theses PLLs has been correctly
initialized, thus removing the init code and still be safe.

>>
>
> [...]
>
>>
>> @@ -364,16 +366,50 @@ static struct clk_regmap g12a_cpu_clk_premux1 = {
>> },
>> .hw.init = &(struct clk_init_data){
>> .name = "cpu_clk_dyn1_sel",
>> - .ops = &clk_regmap_mux_ro_ops,
>> + .ops = &clk_regmap_mux_ops,
>> .parent_data = (const struct clk_parent_data []) {
>> { .fw_name = "xtal", },
>> { .hw = &g12a_fclk_div2.hw },
>> { .hw = &g12a_fclk_div3.hw },
>> },
>> .num_parents = 3,
>> + /* This sub-tree is used a parking clock */
>> + .flags = CLK_SET_RATE_NO_REPARENT
>> },
>> };
>>
>> +#define SYS_CPU_DYN_ENABLE BIT(26)
>> +
>> +/* This divider uses bit 26 to take change in account */
>> +static int g12a_cpu_clk_mux0_div_set_rate(struct clk_hw *hw, unsigned long rate,
>> + unsigned long parent_rate)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
>> + unsigned int val;
>> + int ret;
>> +
>> + ret = divider_get_val(rate, parent_rate, div->table, div->width,
>> + div->flags);
>> + if (ret < 0)
>> + return ret;
>> +
>> + val = (unsigned int)ret << div->shift;
>> +
>> + regmap_update_bits(clk->map, HHI_SYS_CPU_CLK_CNTL0,
>> + SYS_CPU_DYN_ENABLE, SYS_CPU_DYN_ENABLE);
>> +
>> + return regmap_update_bits(clk->map, div->offset,
>> + clk_div_mask(div->width) << div->shift |
>> + SYS_CPU_DYN_ENABLE, val);
>> +};
>> +
>> +const struct clk_ops g12a_cpu_clk_mux0_div_ops = {
>> + .recalc_rate = clk_regmap_div_recalc_rate,
>> + .round_rate = clk_regmap_div_round_rate,
>> + .set_rate = g12a_cpu_clk_mux0_div_set_rate,
>> +};
>
> I would prefer if we could keep the clock drivers and clock controllers
> separated.
>
> Could you move the above above in another file ?

Yup, done

>
>> +
>
> [...]
>
>>
>> +/* This divider uses bit 26 to take change in account */
>> +static int g12b_cpub_clk_mux0_div_set_rate(struct clk_hw *hw,
>> + unsigned long rate,
>> + unsigned long parent_rate)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
>> + unsigned int val;
>> + int ret;
>> +
>> + ret = divider_get_val(rate, parent_rate, div->table, div->width,
>> + div->flags);
>> + if (ret < 0)
>> + return ret;
>> +
>> + val = (unsigned int)ret << div->shift;
>> +
>> + regmap_update_bits(clk->map, HHI_SYS_CPUB_CLK_CNTL,
>
> Unless I missed something, this function is same as the g12a with the
> exception of the register address.
>
> It seems this clock could have its own clock type and its own data
> structure to store the 'dyn enable' register parameter.

Yup done

>
>> + SYS_CPU_DYN_ENABLE, SYS_CPU_DYN_ENABLE);
>> +
>> + return regmap_update_bits(clk->map, div->offset,
>> + clk_div_mask(div->width) << div->shift |
>> + SYS_CPU_DYN_ENABLE, val);
>> +};
>> +
>> +static const struct clk_ops g12b_cpub_clk_mux0_div_ops = {
>> + .recalc_rate = clk_regmap_div_recalc_rate,
>> + .round_rate = clk_regmap_div_round_rate,
>> + .set_rate = g12b_cpub_clk_mux0_div_set_rate,
>> +};
>> +
>
> [...]
>
>> +
>> +static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
>> + unsigned long event, void *data)
>> +{
>> + struct g12a_cpu_clk_postmux_nb_data *nb_data =
>> + container_of(nb, struct g12a_cpu_clk_postmux_nb_data, nb);
>> +
>> + switch (event) {
>> + case PRE_RATE_CHANGE:
>> + /*
>> + * This notifier means cpu_clk_postmux0 clock will be changed
>> + * to feed cpu_clk, this is the current path :
>> + * cpu_clk
>> + * \- cpu_clk_dyn
>> + * \- cpu_clk_postmux0
>> + * \- cpu_clk_muxX_div
>> + * \- cpu_clk_premux0
>> + * \- fclk_div3 or fclk_div2
>> + * OR
>> + * \- cpu_clk_premux0
>> + * \- fclk_div3 or fclk_div2
>> + */
>> +
>> + /* Setup cpu_clk_premux1 to xtal */
>> + clk_hw_set_parent(nb_data->cpu_clk_premux1,
>> + nb_data->xtal);
>> +
>> + /* Setup cpu_clk_postmux1 to bypass divider */
>> + clk_hw_set_parent(nb_data->cpu_clk_postmux1,
>> + nb_data->cpu_clk_premux1);
>> +
>> + /* Switch to parking clk on cpu_clk_postmux1 */
>> + clk_hw_set_parent(nb_data->cpu_clk_dyn,
>> + nb_data->cpu_clk_postmux1);
>> +
>> + /*
>> + * Now, cpu_clk is 24MHz in the current path :
>> + * cpu_clk
>> + * \- cpu_clk_dyn
>> + * \- cpu_clk_postmux1
>> + * \- cpu_clk_premux1
>> + * \- xtal
>> + */
>> +
>> + udelay(100);
>
> Just curious about the this 100us delay. It seems fairly long, even at
> 24MHz. In your stress tests, have you tried shorter delays ? 10us maybe ?

Honestly no, I took the values from Amlogic implementation to be safe for
the first implementation, and I gave all my test scripts and utilities for
others to re-run them to eventually reduce these values to 10us in a second
time.

>
>> +
>> + return NOTIFY_OK;
>> +
>> + case POST_RATE_CHANGE:
>> + /*
>> + * The cpu_clk_postmux0 has ben updated, now switch back
>> + * cpu_clk_dyn to cpu_clk_postmux0 and take the changes
>> + * in account.
>> + */
>> +
>> + /* Configure cpu_clk_dyn back to cpu_clk_postmux0 */
>> + clk_hw_set_parent(nb_data->cpu_clk_dyn,
>> + nb_data->cpu_clk_postmux0);
>> +
>> + /*
>> + * new path :
>> + * cpu_clk
>> + * \- cpu_clk_dyn
>> + * \- cpu_clk_postmux0
>> + * \- cpu_clk_muxX_div
>> + * \- cpu_clk_premux0
>> + * \- fclk_div3 or fclk_div2
>> + * OR
>> + * \- cpu_clk_premux0
>> + * \- fclk_div3 or fclk_div2
>> + */
>> +
>> + udelay(100);
>> +
>> + return NOTIFY_OK;
>> +
>> + default:
>> + return NOTIFY_DONE;
>> + }
>> +}
>> +

Thanks,
Neil

\
 
 \ /
  Last update: 2019-07-30 17:15    [W:0.081 / U:0.024 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site