[PATCH v2 1/6] clk: renesas: rcar-gen3: Add Z clock divider support
Geert Uytterhoeven
geert at linux-m68k.org
Thu Aug 24 05:46:45 PDT 2017
Hi Simon,
On Mon, Aug 21, 2017 at 3:01 PM, Simon Horman
<horms+renesas at verge.net.au> wrote:
> From: Takeshi Kihara <takeshi.kihara.df at renesas.com>
>
> This patch adds Z clock divider support for R-Car Gen3 SoC.
>
> Signed-off-by: Takeshi Kihara <takeshi.kihara.df at renesas.com>
> [simon: divide parent by 2; use bitops macros]
> Signed-off-by: Simon Horman <horms+renesas at verge.net.au>
Thanks for this patch!
> --- a/drivers/clk/renesas/rcar-gen3-cpg.c
> +++ b/drivers/clk/renesas/rcar-gen3-cpg.c
> @@ -13,6 +13,7 @@
> */
>
> #include <linux/bug.h>
> +#include <linux/bitfield.h>
> #include <linux/clk.h>
> #include <linux/clk-provider.h>
> #include <linux/device.h>
> @@ -29,6 +30,139 @@
> #define CPG_PLL2CR 0x002c
> #define CPG_PLL4CR 0x01f4
>
> +/* Z Clock
/*
* Z Clock
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is adjustable. clk->rate = (parent->rate * mult / 32 ) / 2
> + * parent - fixed parent. No clk_set_parent support
> + */
> +#define CPG_FRQCRB 0x00000004
> +#define CPG_FRQCRB_KICK BIT(31)
> +#define CPG_FRQCRC 0x000000e0
> +#define CPG_FRQCRC_ZFC_MASK GENMASK(12, 8)
> +
> +struct cpg_z_clk {
> + struct clk_hw hw;
> + void __iomem *reg;
> + void __iomem *kick_reg;
> +};
> +
> +#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw)
> +
> +static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct cpg_z_clk *zclk = to_z_clk(hw);
> + unsigned long rate;
> + unsigned int mult;
> +
> + mult = 32 - FIELD_GET(CPG_FRQCRC_ZFC_MASK, clk_readl(zclk->reg));
> + /* There is a PLL post-divider of 1/2,
/*
* There is a PLL post-divider of 1/2,
> + * thus the doubling of the divisor below.
> + */
> + rate = div_u64((u64)parent_rate * mult + 16, 32 * 2);
DIV_ROUND_CLOSEST_ULL()
> + /* Round to closest value at 100MHz unit */
> + rate = 100000000 * DIV_ROUND_CLOSEST(rate, 100000000);
Why?
With this rounding, we can no longer distinguish between e.g.
1218750000 and 1265625000 Hz.
> +
> + return rate;
> +}
> +
> +static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *parent_rate)
> +{
> + unsigned long prate = *parent_rate;
> + unsigned int mult;
> +
> + if (!prate)
> + prate = 1;
Can this really happen?
> +
> + mult = div_u64((u64)rate * 32 + prate/2, prate);
mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL, prate);
> + mult = clamp(mult, 1U, 32U);
> +
> + return *parent_rate / 32 * mult;
To avoid losing precision, you should do the multiplication first, using
64-bit math.
> +}
> +
> +static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct cpg_z_clk *zclk = to_z_clk(hw);
> + unsigned int mult;
> + unsigned int i;
> + u32 val, kick;
> +
> + mult = div_u64((u64)rate * 32 + parent_rate/2, parent_rate);
DIV_ROUND_CLOSEST_ULL()
> + /*
> + * Note: There is no HW information about the worst case latency.
> + *
> + * Using experimental measurements, it seems that no more than
> + * ~10 iterations are needed, independently of the CPU rate.
> + * Since this value might be dependent of external xtal rate, pll1
> + * rate or even the other emulation clocks rate, use 1000 as a
another emulated clock rate?
(Yeah, copied from Gen2 ;-)
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
More information about the linux-arm-kernel
mailing list