[PATCH v3 3/9] clk: at91: add audio pll clock drivers
From: Quentin Schulz <hidden>
Date: 2017-07-24 08:37:54
Also in:
alsa-devel, linux-clk, linux-devicetree, lkml
Hi Stephen, On 22/07/2017 00:20, Stephen Boyd wrote:
On 07/13, Quentin Schulz wrote:quoted
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@atmel.com> + * Copyright (C) 2017 Free Electrons, + * Quentin Schulz <quentin.schulz@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. [...]
quoted
+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.
quoted
+ + 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.
quoted
+ 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?
quoted
+ 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.
quoted
+} + +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?
quoted
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
[...]
quoted
+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. [...]
quoted
+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.quoted
+ 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.
quoted
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
[...]
quoted
+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. [...]
quoted
+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? [...]
quoted
+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?
quoted
+ + 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