mbox series

[v5,0/5] add Amlogic A1 clock controller driver

Message ID 20191227094606.143637-1-jian.hu@amlogic.com
Headers show
Series add Amlogic A1 clock controller driver | expand

Message

Jian Hu Dec. 27, 2019, 9:46 a.m. UTC
add support for Amlogic A1 clock driver, the clock includes 
three parts: peripheral clocks, pll clocks, CPU clocks.
sys pll and CPU clocks will be sent in next patch.

Changes since v4 at [5]:
- change yaml GPL
- drop meson-eeclk.c patch, add probe function in each driver
- add CLK_IS_CRITICAL for sys_clk clock, drop the flag for sys_a and sys_b
- add new parm for pll, add protection for rst parm
- drop flag for a1_fixed_pll
- remove the same comment for fclk_div, add "refer to"
- add critical flag for a1_sys_clk
- remove rtc table
- rename a1_dspa_en_dspa and a1_dspb_en_dspb
- remove useless comment

Changes since v3 at [3]:
-fix reparenting orphan failed, it depends on jerome's patch [4]
-fix changelist in v3 about reparenting orphan
-remove the dts patch 

Changes since v2 at [2]:
-add probe function for A1
-seperate the clock driver into two patch
-change some clock flags and ops
-add support for a1 PLL ops
-add A1 clock node
-fix reparenting orphan clock failed, registering xtal_fixpll
 and xtal_hifipll after the provider registration, it is not
 a best way.

Changes since v1 at [1]:
-place A1 config alphabetically
-add actual reason for RO ops, CLK_IS_CRITICAL, CLK_IGNORE_UNUSED
-separate the driver into two driver: peripheral and pll driver
-delete CLK_IGNORE_UNUSED flag for pwm b/c/d/e/f clock, dsp clock
-delete the change in Kconfig.platforms, address to Kevin alone
-remove the useless comments
-modify the meson pll driver to support A1 PLLs

[1] https://lkml.kernel.org/r/1569411888-98116-1-git-send-email-jian.hu@amlogic.com
[2] https://lkml.kernel.org/r/1571382865-41978-1-git-send-email-jian.hu@amlogic.com
[3] https://lkml.kernel.org/r/20191129144605.182774-1-jian.hu@amlogic.com
[4] https://lkml.kernel.org/r/20191203080805.104628-1-jbrunet@baylibre.com
[5] https://lkml.kernel.org/r/20191206074052.15557-1-jian.hu@amlogic.com

Jian Hu (5):
  dt-bindings: clock: meson: add A1 PLL clock controller bindings
  clk: meson: add support for A1 PLL clock ops
  clk: meson: a1: add support for Amlogic A1 PLL clock driver
  dt-bindings: clock: meson: add A1 peripheral clock controller bindings
  clk: meson: a1: add support for Amlogic A1 Peripheral clock driver

 .../bindings/clock/amlogic,a1-clkc.yaml       |   67 +
 .../bindings/clock/amlogic,a1-pll-clkc.yaml   |   54 +
 drivers/clk/meson/Kconfig                     |   18 +
 drivers/clk/meson/Makefile                    |    2 +
 drivers/clk/meson/a1-pll.c                    |  374 +++
 drivers/clk/meson/a1-pll.h                    |   56 +
 drivers/clk/meson/a1.c                        | 2263 +++++++++++++++++
 drivers/clk/meson/a1.h                        |  120 +
 drivers/clk/meson/clk-pll.c                   |   40 +-
 drivers/clk/meson/clk-pll.h                   |    2 +
 include/dt-bindings/clock/a1-clkc.h           |   98 +
 include/dt-bindings/clock/a1-pll-clkc.h       |   16 +
 12 files changed, 3105 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-clkc.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-pll-clkc.yaml
 create mode 100644 drivers/clk/meson/a1-pll.c
 create mode 100644 drivers/clk/meson/a1-pll.h
 create mode 100644 drivers/clk/meson/a1.c
 create mode 100644 drivers/clk/meson/a1.h
 create mode 100644 include/dt-bindings/clock/a1-clkc.h
 create mode 100644 include/dt-bindings/clock/a1-pll-clkc.h

Comments

Martin Blumenstingl Dec. 27, 2019, 4:53 p.m. UTC | #1
Hi Jian,

On Fri, Dec 27, 2019 at 10:46 AM Jian Hu <jian.hu@amlogic.com> wrote:
[...]
> @@ -294,9 +298,12 @@ static int meson_clk_pll_is_enabled(struct clk_hw *hw)
>  {
>         struct clk_regmap *clk = to_clk_regmap(hw);
>         struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
> +       int ret = 0;
>
> -       if (meson_parm_read(clk->map, &pll->rst) ||
> -           !meson_parm_read(clk->map, &pll->en) ||
> +       if (MESON_PARM_APPLICABLE(&pll->rst))
> +               ret = meson_parm_read(clk->map, &pll->rst);
> +
> +       if (ret || !meson_parm_read(clk->map, &pll->en) ||
>             !meson_parm_read(clk->map, &pll->l))
>                 return 0;
I had to read this part twice to understand what it's doing because I
misunderstood what "ret" is used for (I thought that some "return ret"
is missing)
my proposal to make it easier to read:
...
if (MESON_PARM_APPLICABLE(&pll->rst) &&
    meson_parm_read(clk->map, &pll->rst))
  return 0;

if (!meson_parm_read(clk->map, &pll->en) ||
    !meson_parm_read(clk->map, &pll->l))
                 return 0;
...

please let me know what you think about this

> @@ -321,6 +328,23 @@ static int meson_clk_pll_enable(struct clk_hw *hw)
>         /* do nothing if the PLL is already enabled */
>         if (clk_hw_is_enabled(hw))
>                 return 0;
> +       /*
> +        * Compared with the previous SoCs, self-adaption module current
> +        * is newly added for A1, keep the new power-on sequence to enable the
> +        * PLL.
> +        */
> +       if (MESON_PARM_APPLICABLE(&pll->current_en)) {
> +               /* Enable the pll */
> +               meson_parm_write(clk->map, &pll->en, 1);
> +               udelay(10);
> +               /* Enable the pll self-adaption module current */
> +               meson_parm_write(clk->map, &pll->current_en, 1);
> +               udelay(40);
> +               /* Enable lock detect module */
> +               meson_parm_write(clk->map, &pll->l_detect, 1);
> +               meson_parm_write(clk->map, &pll->l_detect, 0);
> +               goto out;
> +       }
in all other functions you are skipping the pll->rst register by
checking for MESON_PARM_APPLICABLE(&pll->rst)
I like that because it's a pattern which is easy to follow

do you think we can make this part consistent with that?
I'm thinking of something like this (not compile-tested and I dropped
all comments, just so you get the idea):
...
if (MESON_PARM_APPLICABLE(&pll->rst)
  meson_parm_write(clk->map, &pll->rst, 1);

meson_parm_write(clk->map, &pll->en, 1);

if (MESON_PARM_APPLICABLE(&pll->rst))
  meson_parm_write(clk->map, &pll->rst, 0);

if (MESON_PARM_APPLICABLE(&pll->current_en))
  meson_parm_write(clk->map, &pll->current_en, 1);

if (MESON_PARM_APPLICABLE(&pll->l_detect)) {
  meson_parm_write(clk->map, &pll->l_detect, 1);
  meson_parm_write(clk->map, &pll->l_detect, 0);
}

if (meson_clk_pll_wait_lock(hw))
...

I see two (and a half) benefits here:
- if there's a PLL with neither the pll->current_en nor the pll->rst
registers then you get support for this implementation for free
- the if (MESON_PARM_APPLICABLE(...)) pattern is already used in the
driver, but only for one register (in your example when
MESON_PARM_APPLICABLE(&pll->current_en) exists you also modify the
pll->l_detect register, which I did not expect)
- only counts half: no use of "goto", which in my opinion makes it
very easy to read (just read from top to bottom, checking each "if")


Martin
Jian Hu Jan. 9, 2020, 6:55 a.m. UTC | #2
Hi Martin

Thanks for your review

On 2019/12/28 0:53, Martin Blumenstingl wrote:
> Hi Jian,
> 
> On Fri, Dec 27, 2019 at 10:46 AM Jian Hu <jian.hu@amlogic.com> wrote:
> [...]
>> @@ -294,9 +298,12 @@ static int meson_clk_pll_is_enabled(struct clk_hw *hw)
>>   {
>>          struct clk_regmap *clk = to_clk_regmap(hw);
>>          struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
>> +       int ret = 0;
>>
>> -       if (meson_parm_read(clk->map, &pll->rst) ||
>> -           !meson_parm_read(clk->map, &pll->en) ||
>> +       if (MESON_PARM_APPLICABLE(&pll->rst))
>> +               ret = meson_parm_read(clk->map, &pll->rst);
>> +
>> +       if (ret || !meson_parm_read(clk->map, &pll->en) ||
>>              !meson_parm_read(clk->map, &pll->l))
>>                  return 0;
> I had to read this part twice to understand what it's doing because I
> misunderstood what "ret" is used for (I thought that some "return ret"
> is missing)
> my proposal to make it easier to read:
> ...
> if (MESON_PARM_APPLICABLE(&pll->rst) &&
>      meson_parm_read(clk->map, &pll->rst))
>    return 0;
> 
> if (!meson_parm_read(clk->map, &pll->en) ||
>      !meson_parm_read(clk->map, &pll->l))
>                   return 0;
> ...
> 
> please let me know what you think about this
I was intended to use 'ret' to store the return value of pll->rst.

If pll->rst exists, it will get it. Otherwise, the ret will be zero.

Your proposal is a good way for it. I will use it.
> 
>> @@ -321,6 +328,23 @@ static int meson_clk_pll_enable(struct clk_hw *hw)
>>          /* do nothing if the PLL is already enabled */
>>          if (clk_hw_is_enabled(hw))
>>                  return 0;
>> +       /*
>> +        * Compared with the previous SoCs, self-adaption module current
>> +        * is newly added for A1, keep the new power-on sequence to enable the
>> +        * PLL.
>> +        */
>> +       if (MESON_PARM_APPLICABLE(&pll->current_en)) {
>> +               /* Enable the pll */
>> +               meson_parm_write(clk->map, &pll->en, 1);
>> +               udelay(10);
>> +               /* Enable the pll self-adaption module current */
>> +               meson_parm_write(clk->map, &pll->current_en, 1);
>> +               udelay(40);
>> +               /* Enable lock detect module */
>> +               meson_parm_write(clk->map, &pll->l_detect, 1);
>> +               meson_parm_write(clk->map, &pll->l_detect, 0);
>> +               goto out;
>> +       }
> in all other functions you are skipping the pll->rst register by
> checking for MESON_PARM_APPLICABLE(&pll->rst)
> I like that because it's a pattern which is easy to follow
> 
> do you think we can make this part consistent with that?
> I'm thinking of something like this (not compile-tested and I dropped
> all comments, just so you get the idea):
It is a good idea. I will test it.
> ...
> if (MESON_PARM_APPLICABLE(&pll->rst)
>    meson_parm_write(clk->map, &pll->rst, 1);
> 
> meson_parm_write(clk->map, &pll->en, 1);
> 
> if (MESON_PARM_APPLICABLE(&pll->rst))
>    meson_parm_write(clk->map, &pll->rst, 0);
> 
> if (MESON_PARM_APPLICABLE(&pll->current_en))
>    meson_parm_write(clk->map, &pll->current_en, 1);
> 
> if (MESON_PARM_APPLICABLE(&pll->l_detect)) {
>    meson_parm_write(clk->map, &pll->l_detect, 1);
>    meson_parm_write(clk->map, &pll->l_detect, 0);
> }
> 
> if (meson_clk_pll_wait_lock(hw))
> ...
> 
> I see two (and a half) benefits here:
> - if there's a PLL with neither the pll->current_en nor the pll->rst
> registers then you get support for this implementation for free
> - the if (MESON_PARM_APPLICABLE(...)) pattern is already used in the
> driver, but only for one register (in your example when
> MESON_PARM_APPLICABLE(&pll->current_en) exists you also modify the
> pll->l_detect register, which I did not expect)
> - only counts half: no use of "goto", which in my opinion makes it
> very easy to read (just read from top to bottom, checking each "if")
> 
I see, I will verify it.
> 
> Martin
> 
> .
>