[PATCH v3 3/9] clk: at91: add audio pll clock drivers
Quentin Schulz
quentin.schulz at free-electrons.com
Mon Jul 24 01:37:33 PDT 2017
Hi Stephen,
On 22/07/2017 00:20, Stephen Boyd wrote:
> On 07/13, Quentin Schulz wrote:
>> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
>> new file mode 100644
>> index 000000000000..10dd6d625696
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
>> @@ -0,0 +1,206 @@
>> +/*
>> + * Copyright (C) 2016 Atmel Corporation,
>> + * Nicolas Ferre <nicolas.ferre at atmel.com>
>> + * Copyright (C) 2017 Free Electrons,
>> + * Quentin Schulz <quentin.schulz at free-electrons.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>
> Used?
>
Not really, I need slab.h for kzalloc tough which was included by clkdev.
[...]
>> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
>> + unsigned long parent_rate)
>> +{
>> + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
>> + u8 tmp_div;
>> +
>> + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> + rate, parent_rate);
>> +
>> + if (!rate)
>> + return -EINVAL;
>
> This happens?
>
I don't know, but it's better to do this quick check rather than being
exposed to a division by zero IMHO. Nothing in clk_ops states that the
rate given to set_rate is non-zero, so I made sure this can't happen.
>> +
>> + tmp_div = parent_rate / rate;
>> + if (tmp_div % 3 == 0) {
>> + apad_ck->qdaudio = tmp_div / 3;
>> + apad_ck->div = 3;
>> + } else {
>> + apad_ck->qdaudio = tmp_div / 2;
>> + apad_ck->div = 2;
>> + }[...]
>> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
>> +{
>> + struct clk_audio_pad *apad_ck;
>> + struct clk_init_data init;
>
> Best to initialize to { } just in case we add something later.
>
ACK.
>> + struct regmap *regmap;
>> + const char *parent_name;
>> + const char *name = np->name;
>> + int ret;
>> +
>> + parent_name = of_clk_get_parent_name(np, 0);
>> +
>> + of_property_read_string(np, "clock-output-names", &name);
>> +
>> + regmap = syscon_node_to_regmap(of_get_parent(np));
>> + if (IS_ERR(regmap))
>> + return;
>> +
>> + apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
>> + if (!apad_ck)
>> + return;
>> +
>> + init.name = name;
>> + init.ops = &audio_pll_pad_ops;
>> + init.parent_names = (parent_name ? &parent_name : NULL);
>
> Use of_clk_parent_fill()?
>
[Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
[Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]
+ const char *parent_names[1];
[...]
+ of_clk_parent_fill(np, parent_names, 1);
+ init.parent_names = parent_names;
Is it what you're asking?
>> + init.num_parents = 1;
>> + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> + CLK_SET_RATE_PARENT;
>> +
>> + apad_ck->hw.init = &init;
>> + apad_ck->regmap = regmap;
>> +
>> + ret = clk_hw_register(NULL, &apad_ck->hw);
>> + if (ret)
>> + kfree(apad_ck);
>> + else
>> + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
>
> Maybe we should make this register sequence a helper function.
> Seems common.
>
I can put such an helper in an header if this is what you meant.
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
>> + "atmel,sama5d2-clk-audio-pll-pad",
>> + of_sama5d2_clk_audio_pll_pad_setup);
>
> We can't have a device driver for this?
>
Could you elaborate please?
>> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
>> new file mode 100644
>> index 000000000000..7b0847ed7a4b
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
[...]
>> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
>> + unsigned long parent_rate)
>> +{
>> + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
>> +
>> + if (!rate)
>> + return -EINVAL;
>> +
>> + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> + rate, parent_rate);
>> +
>> + apmc_ck->qdpmc = parent_rate / rate - 1;
>
> Hopefully rate isn't 1 or that goes undefined.
>
Thanks to operator precedence, the only check to do is rate != 0 (done
few lines above). Division has precedence over substraction.
[...]
>> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
>> +{
>> + struct clk_audio_pmc *apmc_ck;
>> + struct clk_init_data init;
>> + struct regmap *regmap;
>> + const char *parent_name;
>> + const char *name = np->name;
>> + int ret;
>> +
>> + parent_name = of_clk_get_parent_name(np, 0);
>> +
>> + of_property_read_string(np, "clock-output-names", &name);
>> +
>> + regmap = syscon_node_to_regmap(of_get_parent(np));
>> + if (IS_ERR(regmap))
>> + return;
>> +
>> + apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
>> + if (!apmc_ck)
>> + return;
>> +
>> + init.name = name;
>> + init.ops = &audio_pll_pmc_ops;
>> + init.parent_names = (parent_name ? &parent_name : NULL);
>
> This feels repetitive.
>
>> + init.num_parents = 1;
>> + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> + CLK_SET_RATE_PARENT;
>> +
>> + apmc_ck->hw.init = &init;
>> + apmc_ck->regmap = regmap;
>> +
>> + ret = clk_hw_register(NULL, &apmc_ck->hw);
>> + if (ret)
>> + kfree(apmc_ck);
>> + else
>> + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
>> + "atmel,sama5d2-clk-audio-pll-pmc",
>> + of_sama5d2_clk_audio_pll_pmc_setup);
>
> Very
>
Basically, both share almost the same code but have different formulae
for the rate.
>> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
>> new file mode 100644
>> index 000000000000..efc2cb58da85
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll.c
[...]
>> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
>> + unsigned long nd, unsigned long fracr)
>> +{
>> + unsigned long long fr = (unsigned long long)parent_rate *
>> + (unsigned long long)fracr;
>
> We only need one cast here?
>
Indeed, I'll remove the casting of fracr.
[...]
>> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> + unsigned long *parent_rate)
>> +{
>> + long best_rate = -EINVAL;
>> + unsigned long fracr, nd;
>> + int ret;
>> +
>> + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
>> + *parent_rate);
>> +
>> + if (rate < AUDIO_PLL_FOUT_MIN)
>> + rate = AUDIO_PLL_FOUT_MIN;
>> + else if (rate > AUDIO_PLL_FOUT_MAX)
>> + rate = AUDIO_PLL_FOUT_MAX;
>
> Use clamp. Also, did you want to use determine_rate callback and
> clamp the requested rate range?
>
Didn't know this one, thanks!
I want determine_rate to return a valid rate for the pll so I clamp the
requested rate range in this one. In set_rate, I just tell the user that
any requested rate outside of the valid range is invalid. Does that
answer your question?
[...]
>> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
>> +{
>> + struct clk_audio_frac *fck;
>> + struct clk_init_data init;
>> + struct regmap *regmap;
>> + const char *parent_name;
>> + const char *name = np->name;
>> + int ret;
>> +
>> + parent_name = of_clk_get_parent_name(np, 0);
>> +
>> + of_property_read_string(np, "clock-output-names", &name);
>
> Any way to not rely on clock-output-names?
>
I guess we could use the name of the DT node (as it's done in the
variable initialization block above) and not override it by
clock-output-names?
>> +
>> + regmap = syscon_node_to_regmap(of_get_parent(np));
>> + if (IS_ERR(regmap))
>> + return;
>> +
>> + fck = kzalloc(sizeof(*fck), GFP_KERNEL);
>
> This variable name looks like f*ck, perhaps name it something
> else. frac?
Sure.
[...]
Thanks,
Quentin
--
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
More information about the linux-arm-kernel
mailing list