Renesas R8A7743 CPG/MSSR clock support
cover Renesas R8A7743 CPG/MSSR clock support
Hello.
Here's the set of 3 patches against the 'clk-next' branch of CLK group's
'linux.git' repo. The R8A7743 SoC support will be posted separately later -- this series depend on the Kconfig variable introduced there, however, the DTs in that series will depend on the patch #2 of this series...
[1/3] clk: renesas: cpg-mssr: add common R-Car Gen2 support [2/3] ARM: shmobile: r8a7743: add CPG clock index macros [3/3] clk: renesas: cpg-mssr: add R8A7743 support
MBR, Sergei
PATCH 1/3 clk: renesas: cpg-mssr: add common R-Car Gen2 support
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
--- drivers/clk/renesas/rcar-gen2-cpg.c | 377 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 419 insertions(+)
drivers/clk/renesas/rcar-gen2-cpg.c new file +377 -0
@@ -0,0 +1,377 @@ +/* +* R-Car Gen2 Clock Pulse Generator +* +* Copyright (C) 2016 Cogent Embedded Inc. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published +* by the Free Software Foundation. +*/ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_FRQCRB 0x0004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x0074 +#define CPG_PLL0CR 0x00d8 +#define CPG_FRQCRC 0x00e0 +#define CPG_FRQCRC_ZFC_MASK (0x1f << 8) +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_ADSPCKCR 0x025c +#define CPG_RCANCKCR 0x0270 + +static spinlock_t cpg_lock; + +/* +* 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 +* parent - fixed parent. No clk_set_parent support +*/ + +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 int mult; +unsigned int val; + + val = (readl (zclk->reg) &CPG_FRQCRC_ZFC_MASK ) >>CPG_FRQCRC_ZFC_SHIFT ; + mult =32 - val; + +return div_u64 ((u64 )parent_rate * mult,32 ); +} + +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 ; + + mult =div_u64 ((u64 )rate *32 , prate); + mult =clamp (mult,1U ,32U ); + +return *parent_rate /32 * mult; +} + +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; +u32 val, kick; +unsigned int i; + + mult =div_u64 ((u64 )rate *32 , parent_rate); + mult =clamp (mult,1U ,32U ); + +if (readl (zclk->kick_reg) &CPG_FRQCRB_KICK ) +return -EBUSY ; + + val =readl (zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK ; + val |= (32 - mult) <<CPG_FRQCRC_ZFC_SHIFT ; +writel (val, zclk->reg); + +/* +* Set KICK bit in FRQCRB to update hardware setting and wait for +* clock change completion. +*/ + kick =readl (zclk->kick_reg); + kick |=CPG_FRQCRB_KICK ; +writel (kick, zclk->kick_reg); + +/* +* 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 on external xtal rate, pll1 +* rate or even the other emulation clocks rate, use 1000 as a +* "super" safe value. +*/ +for (i =1000 ; i; i--) { +if (!(readl (zclk->kick_reg) &CPG_FRQCRB_KICK )) +return 0 ; + +cpu_relax (); + } + +return -ETIMEDOUT ; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk *__init cpg_z_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_init_data init; +struct cpg_z_clk *zclk; +struct clk *clk; + + zclk =kzalloc (sizeof (*zclk),GFP_KERNEL ); +if (!zclk) +return ERR_PTR (-ENOMEM ); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags =0 ; + init.parent_names = &parent_name; + init.num_parents =1 ; + + zclk->reg = base +CPG_FRQCRC ; + zclk->kick_reg = base +CPG_FRQCRB ; + zclk->hw.init = &init; + + clk =clk_register (NULL , &zclk->hw); +if (IS_ERR (clk)) +kfree (zclk); + +return clk; +} + +static struct clk *__init cpg_rcan_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_fixed_factor *fixed; +struct clk_gate *gate; +struct clk *clk; + + fixed =kzalloc (sizeof (*fixed),GFP_KERNEL ); +if (!fixed) +return ERR_PTR (-ENOMEM ); + + fixed->mult =1 ; + fixed->div =6 ; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (fixed); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_RCANCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (fixed); + } + +return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + {1 ,3 }, {2 ,4 }, {3 ,6 }, {4 ,8 }, + {5 ,12 }, {6 ,16 }, {7 ,18 }, {8 ,24 }, + {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static struct clk *__init cpg_adsp_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_divider *div; +struct clk_gate *gate; +struct clk *clk; + + div =kzalloc (sizeof (*div),GFP_KERNEL ); +if (!div) +return ERR_PTR (-ENOMEM ); + + div->reg = base +CPG_ADSPCKCR ; + div->width =4 ; + div->table = cpg_adsp_div_table; + div->lock = &cpg_lock; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (div); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_ADSPCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (div); + } + +return clk; +} + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + {0 ,2 }, {1 ,3 }, {2 ,4 }, {3 ,6 }, + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {12 ,10 }, + {0 ,0 }, +}; + +static const struct rcar_gen2_cpg_pll_config *cpg_pll_config__initdata ; +static u32 cpg_mode__initdata ; + +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +const struct clk_div_table *table =NULL ; +const struct clk *parent; +const char *parent_name; +unsigned int mult =1 ; +unsigned int div =1 ; +unsigned int shift; +u32 value; + + parent = clks[core->parent]; +if (IS_ERR (parent)) +return ERR_CAST (parent); + + parent_name =__clk_get_name (parent); + +switch (core->type) { +/* R-Car Gen2 */ +case CLK_TYPE_GEN2_MAIN : + div = cpg_pll_config->extal_div; +break ; + +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock. Register it as a +* fixed factor clock for now as there's no generic multiplier +* clock implementation and we currently have no need to change +* the multiplier value. +*/ + value =readl (base +CPG_PLL0CR ); + mult = (((value >>24 ) &0x7f ) +1 ); +break ; + +case CLK_TYPE_GEN2_PLL1 : + mult = cpg_pll_config->pll1_mult /2 ; +break ; + +case CLK_TYPE_GEN2_PLL3 : + mult = cpg_pll_config->pll3_mult; +break ; + +case CLK_TYPE_GEN2_Z : +return cpg_z_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_LB : + div = cpg_mode &BIT (18 ) ?36 :24 ; +break ; + +case CLK_TYPE_GEN2_ADSP : +return cpg_adsp_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_SDH : + table = cpg_sdh_div_table; + shift =8 ; +break ; + +case CLK_TYPE_GEN2_SD0 : + table = cpg_sd01_div_table; + shift =4 ; +break ; + +case CLK_TYPE_GEN2_SD1 : + table = cpg_sd01_div_table; + shift =0 ; +break ; + +case CLK_TYPE_GEN2_QSPI : + div = (cpg_mode & (BIT (3 ) |BIT (2 ) |BIT (1 ))) ==BIT (2 ) ? +8 :10 ; +break ; + +case CLK_TYPE_GEN2_RCAN : +return cpg_rcan_clk_register (core->name, parent_name, base); + +default : +return ERR_PTR (-EINVAL ); + } + +if (!table) +return clk_register_fixed_factor (NULL , core->name, parent_name, +0 , mult, div); +else +return clk_register_divider_table (NULL , core->name, + parent_name,0 , + base +CPG_SDCKCR , shift,4 , +0 , table, &cpg_lock); +} + +/* +* Reset register definitions. +*/ +#define MODEMR 0xe6160060 + +u32 __init rcar_gen2_read_modemr (void ) +{ +void __iomem *modemr =ioremap_nocache (MODEMR ,4 ); +u32 mode; + +BUG_ON (!modemr); + mode =ioread32 (modemr); +iounmap (modemr); + +return mode; +} + +int __init rcar_gen2_cpg_init (const struct rcar_gen2_cpg_pll_config *config) +{ + cpg_pll_config = config; + +spin_lock_init (&cpg_lock); + +return 0 ; +}
drivers/clk/renesas/rcar-gen2-cpg.h new file +42 -0
42 lines not on this page · raw message
Re: [PATCH 1/3] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Quoted 7 lines · Sergei Shtylyov
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
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
Re: [PATCH 1/3] clk: renesas: cpg-mssr: add common R-Car Gen2 support
In reply to Sergei ShtylyovQuoted 18 lines · Sergei Shtylyov
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+ +/* +* Reset register definitions. +*/ +#define MODEMR 0xe6160060 + +u32 __init rcar_gen2_read_modemr (void )
drivers/clk/renesas/rcar-gen2-cpg.c line 368
Quoted 10 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +10
-0+{ +void __iomem *modemr =ioremap_nocache (MODEMR ,4 ); +u32 mode; + +BUG_ON (!modemr); + mode =ioread32 (modemr); +iounmap (modemr); + +return mode; +}
Once we have "[PATCH v4 00/23] soc: renesas: Add R-Car RST driver for obtaining mode pin state" in place, we can make the RST node in DT mandatory for SoCs using CPG/MSSR, and drop this helper function.
drivers/clk/renesas/rcar-gen2-cpg.h
Quoted 5 lines
drivers/clk/renesas/rcar-gen2-cpg.h new file +1
-0@@ -0,0 +1,42 @@ +u32 rcar_gen2_read_modemr (void );
Likewise.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
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
PATCH 2/3 ARM: shmobile: r8a7743: add CPG clock index macros
In reply to Sergei ShtylyovAdd macros usable by the device tree sources to reference the R8A7743 CPG clocks by index.
--- include/dt-bindings/clock/r8a7743-cpg-mssr.h | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
include/dt-bindings/clock/r8a7743-cpg-mssr.h new file +43 -0
@@ -0,0 +1,43 @@ +/* +* Copyright (C) 2016 Cogent Embedded Inc. +* +* 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. +*/ +#ifndef __DT_BINDINGS_CLOCK_R8A7743_CPG_MSSR_H__ +#define __DT_BINDINGS_CLOCK_R8A7743_CPG_MSSR_H__ + +#include <dt-bindings/clock/renesas-cpg-mssr.h> + +/* r8a7743 CPG Core Clocks */ +#define R8A7743_CLK_Z 0 +#define R8A7743_CLK_ZG 1 +#define R8A7743_CLK_ZTR 2 +#define R8A7743_CLK_ZTRD2 3 +#define R8A7743_CLK_ZT 4 +#define R8A7743_CLK_ZX 5 +#define R8A7743_CLK_ZS 6 +#define R8A7743_CLK_HP 7 +#define R8A7743_CLK_B 8 +#define R8A7743_CLK_LB 9 +#define R8A7743_CLK_P 10 +#define R8A7743_CLK_CL 11 +#define R8A7743_CLK_M2 12 +#define R8A7743_CLK_ZB3 13 +#define R8A7743_CLK_ZB3D2 14 +#define R8A7743_CLK_DDR 15 +#define R8A7743_CLK_SDH 16 +#define R8A7743_CLK_SD0 17 +#define R8A7743_CLK_SD2 18 +#define R8A7743_CLK_SD3 19 +#define R8A7743_CLK_MMC0 20 +#define R8A7743_CLK_MP 21 +#define R8A7743_CLK_QSPI 22 +#define R8A7743_CLK_CP 23 +#define R8A7743_CLK_RCAN 24 +#define R8A7743_CLK_R 25 +#define R8A7743_CLK_OSC 26 + +#endif /* __DT_BINDINGS_CLOCK_R8A7743_CPG_MSSR_H__ */
Re: [PATCH 2/3] ARM: shmobile: r8a7743: add CPG clock index macros
Hello.
Quoted 4 lines · Sergei Shtylyov
Add macros usable by the device tree sources to reference the R8A7743 CPG clocks by index.
Signed-off-by Sergei Shtylyov <[email protected]>
Probably makes ense to merge this patch to both the clk and renesas trees
in order to satisfy the dependencies both ways... what do you think, Simon?
MBR, Sergei
Re: [PATCH 2/3] ARM: shmobile: r8a7743: add CPG clock index macros
In reply to Sergei ShtylyovHello.
Quoted 4 lines · Sergei Shtylyov
Add macros usable by the device tree sources to reference the R8A7743 CPG clocks by index.
Signed-off-by Sergei Shtylyov <[email protected]>
Probably makes sense to merge this patch to both the clk and renesas trees
in order to satisfy the dependencies both ways... what do you think, Simon?
MBR, Sergei
Re: [PATCH 2/3] ARM: shmobile: r8a7743: add CPG clock index macros
Hi Sergei,
Quoted 9 lines · Sergei Shtylyov
Quoted 4 lines · Sergei Shtylyov
Add macros usable by the device tree sources to reference the R8A7743 CPG clocks by index.
Signed-off-by Sergei Shtylyov <[email protected]>Probably makes sense to merge this patch to both the clk and renesas
trees in order to satisfy the dependencies both ways... what do you think, Simon?
Yes, this is another case where both the clock maintainers and Simon should merge my clk-renesas-for-v4.10 tree, when It has received (a future version of) this series.
But that's to be handled after v4.9-rc1...
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
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
PATCH 3/3 clk: renesas: cpg-mssr: add R8A7743 support
In reply to Sergei ShtylyovAdd RZ/G1M (R8A7743) Clock Pulse Generator / Module Standby and Software Reset support, using the CPG/MSSR driver core and the common R-Car Gen2 (and RZ/G) code.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
--- drivers/clk/renesas/Kconfig | 1 drivers/clk/renesas/Makefile | 1 drivers/clk/renesas/r8a7743-cpg-mssr.c | 268 +++++++++++++++++++++++++++++++++ drivers/clk/renesas/renesas-cpg-mssr.c | 6 drivers/clk/renesas/renesas-cpg-mssr.h | 1 5 files changed, 277 insertions(+)
drivers/clk/renesas/Kconfig +1 -0
@@ -1,5 +1,6 @@ config CLK_RENESAS_CPG_MSSR bool + default y if ARCH_R8A7743 default y if ARCH_R8A7795 default y if ARCH_R8A7796
drivers/clk/renesas/Makefile +1 -0
@@ -2,6 +2,7 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o clk-div6.o obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7743) += r8a7743-cpg-mssr.o rcar-gen2-cpg.o obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o clk-div6.o
drivers/clk/renesas/r8a7743-cpg-mssr.c new file +268 -0
268 lines not on this page · raw message
drivers/clk/renesas/renesas-cpg-mssr.c +6 -0
@@ -503,6 +503,12 @@ static int __init cpg_mssr_add_clk_domai }static const struct of_device_id cpg_mssr_match[] = { +#ifdef CONFIG_ARCH_R8A7743 + { + .compatible ="renesas,r8a7743-cpg-mssr" , + .data = &r8a7743_cpg_mssr_info, + }, +#endif #ifdef CONFIG_ARCH_R8A7795 { .compatible ="renesas,r8a7795-cpg-mssr" ,
drivers/clk/renesas/renesas-cpg-mssr.h +1 -0
@@ -130,6 +130,7 @@ struct cpg_mssr_info { struct clk **clks, void __iomem *base); }; +extern const struct cpg_mssr_info r8a7743_cpg_mssr_info;extern const struct cpg_mssr_info r8a7795_cpg_mssr_info;extern const struct cpg_mssr_info r8a7796_cpg_mssr_info;#endif
Hello.
Quoted 6 lines · Sergei Shtylyov
Here's the set of 3 patches against the 'clk-next' branch of CLK group's
'linux.git' repo. The R8A7743 SoC support will be posted separately later -- this series depend on theKconfigvariable introduced there, however, the DTs in that series will depend on the patch #2 of this series...[1/3] clk: renesas: cpg-mssr: add common R-Car Gen2 support
This will probably have to be fixed up to handle the absence of the PLL0CR
register oin the low end SoC (like RZ/G1E)...
Quoted 2 lines
[2/3] ARM: shmobile: r8a7743: add CPG clock index macros [3/3] clk: renesas: cpg-mssr: add R8A7743 support
I'm splitting the above 2 patches into a new series and will repost it RSN.
MBR, Sergei
PATCH v2 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support
In reply to Sergei ShtylyovAdd the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.
Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- used CPG_FRQCRC_ZFC_SHIFT to #define CPG_FRQCRC_ZFC_MASK;
- removed rcar_gen2_read_modemr();
- added Geert's tag.
drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)
drivers/clk/renesas/rcar-gen2-cpg.c new file +369 -0
@@ -0,0 +1,369 @@ +/* +* R-Car Gen2 Clock Pulse Generator +* +* Copyright (C) 2016 Cogent Embedded Inc. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published +* by the Free Software Foundation. +*/ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_FRQCRB 0x0004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x0074 +#define CPG_PLL0CR 0x00d8 +#define CPG_PLL0CR_STC_SHIFT 24 +#define CPG_PLL0CR_STC_MASK (0x7f << CPG_PLL0CR_STC_SHIFT) +#define CPG_FRQCRC 0x00e0 +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_FRQCRC_ZFC_MASK (0x1f << CPG_FRQCRC_ZFC_SHIFT) +#define CPG_ADSPCKCR 0x025c +#define CPG_RCANCKCR 0x0270 + +static spinlock_t cpg_lock; + +/* +* 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 +* parent - fixed parent. No clk_set_parent support +*/ + +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 int mult; +unsigned int val; + + val = (readl (zclk->reg) &CPG_FRQCRC_ZFC_MASK ) >>CPG_FRQCRC_ZFC_SHIFT ; + mult =32 - val; + +return div_u64 ((u64 )parent_rate * mult,32 ); +} + +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 ; + + mult =div_u64 ((u64 )rate *32 , prate); + mult =clamp (mult,1U ,32U ); + +return *parent_rate /32 * mult; +} + +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; +u32 val, kick; +unsigned int i; + + mult =div_u64 ((u64 )rate *32 , parent_rate); + mult =clamp (mult,1U ,32U ); + +if (readl (zclk->kick_reg) &CPG_FRQCRB_KICK ) +return -EBUSY ; + + val =readl (zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK ; + val |= (32 - mult) <<CPG_FRQCRC_ZFC_SHIFT ; +writel (val, zclk->reg); + +/* +* Set KICK bit in FRQCRB to update hardware setting and wait for +* clock change completion. +*/ + kick =readl (zclk->kick_reg); + kick |=CPG_FRQCRB_KICK ; +writel (kick, zclk->kick_reg); + +/* +* 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 on external xtal rate, pll1 +* rate or even the other emulation clocks rate, use 1000 as a +* "super" safe value. +*/ +for (i =1000 ; i; i--) { +if (!(readl (zclk->kick_reg) &CPG_FRQCRB_KICK )) +return 0 ; + +cpu_relax (); + } + +return -ETIMEDOUT ; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk *__init cpg_z_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_init_data init; +struct cpg_z_clk *zclk; +struct clk *clk; + + zclk =kzalloc (sizeof (*zclk),GFP_KERNEL ); +if (!zclk) +return ERR_PTR (-ENOMEM ); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags =0 ; + init.parent_names = &parent_name; + init.num_parents =1 ; + + zclk->reg = base +CPG_FRQCRC ; + zclk->kick_reg = base +CPG_FRQCRB ; + zclk->hw.init = &init; + + clk =clk_register (NULL , &zclk->hw); +if (IS_ERR (clk)) +kfree (zclk); + +return clk; +} + +static struct clk *__init cpg_rcan_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_fixed_factor *fixed; +struct clk_gate *gate; +struct clk *clk; + + fixed =kzalloc (sizeof (*fixed),GFP_KERNEL ); +if (!fixed) +return ERR_PTR (-ENOMEM ); + + fixed->mult =1 ; + fixed->div =6 ; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (fixed); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_RCANCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (fixed); + } + +return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + {1 ,3 }, {2 ,4 }, {3 ,6 }, {4 ,8 }, + {5 ,12 }, {6 ,16 }, {7 ,18 }, {8 ,24 }, + {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static struct clk *__init cpg_adsp_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_divider *div; +struct clk_gate *gate; +struct clk *clk; + + div =kzalloc (sizeof (*div),GFP_KERNEL ); +if (!div) +return ERR_PTR (-ENOMEM ); + + div->reg = base +CPG_ADSPCKCR ; + div->width =4 ; + div->table = cpg_adsp_div_table; + div->lock = &cpg_lock; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (div); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_ADSPCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (div); + } + +return clk; +} + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + {0 ,2 }, {1 ,3 }, {2 ,4 }, {3 ,6 }, + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {12 ,10 }, + {0 ,0 }, +}; + +static const struct rcar_gen2_cpg_pll_config *cpg_pll_config__initdata ; +static u32 cpg_mode__initdata ; + +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +const struct clk_div_table *table =NULL ; +const struct clk *parent; +const char *parent_name; +unsigned int mult =1 ; +unsigned int div =1 ; +unsigned int shift; + + parent = clks[core->parent]; +if (IS_ERR (parent)) +return ERR_CAST (parent); + + parent_name =__clk_get_name (parent); + +switch (core->type) { +/* R-Car Gen2 */ +case CLK_TYPE_GEN2_MAIN : + div = cpg_pll_config->extal_div; +break ; + +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car E2. +* Register the PLL0 clock as a fixed factor clock for now as +* there's no generic multiplier clock implementation and we +* currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */ + div =3 ; + }else { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = ((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ; + } +break ; + +case CLK_TYPE_GEN2_PLL1 : + mult = cpg_pll_config->pll1_mult /2 ; +break ; + +case CLK_TYPE_GEN2_PLL3 : + mult = cpg_pll_config->pll3_mult; +break ; + +case CLK_TYPE_GEN2_Z : +return cpg_z_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_LB : + div = cpg_mode &BIT (18 ) ?36 :24 ; +break ; + +case CLK_TYPE_GEN2_ADSP : +return cpg_adsp_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_SDH : + table = cpg_sdh_div_table; + shift =8 ; +break ; + +case CLK_TYPE_GEN2_SD0 : + table = cpg_sd01_div_table; + shift =4 ; +break ; + +case CLK_TYPE_GEN2_SD1 : + table = cpg_sd01_div_table; + shift =0 ; +break ; + +case CLK_TYPE_GEN2_QSPI : + div = (cpg_mode & (BIT (3 ) |BIT (2 ) |BIT (1 ))) ==BIT (2 ) ? +8 :10 ; +break ; + +case CLK_TYPE_GEN2_RCAN : +return cpg_rcan_clk_register (core->name, parent_name, base); + +default : +return ERR_PTR (-EINVAL ); + } + +if (!table) +return clk_register_fixed_factor (NULL , core->name, parent_name, +0 , mult, div); +else +return clk_register_divider_table (NULL , core->name, + parent_name,0 , + base +CPG_SDCKCR , shift,4 , +0 , table, &cpg_lock); +} + +int __init rcar_gen2_cpg_init (const struct rcar_gen2_cpg_pll_config *config) +{ + cpg_pll_config = config; + +spin_lock_init (&cpg_lock); + +return 0 ; +}
drivers/clk/renesas/rcar-gen2-cpg.h new file +42 -0
42 lines not on this page · raw message
Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Hi Sergei,
Quoted 30 lines · Sergei Shtylyov
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>Reviewed-by Geert Uytterhoeven <[email protected]>---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================drivers/clk/renesas/rcar-gen2-cpg.c new file +0
-0@@ -0,0 +1,369 @@
drivers/clk/renesas/rcar-gen2-cpg.c line 290
Quoted 10 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +9
-0+struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car E2.
... and V2H.
drivers/clk/renesas/rcar-gen2-cpg.c line 297
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+* Register the PLL0 clock as a fixed factor clock for now as +* there's no generic multiplier clock implementation and we +* currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */
... and V2H.
drivers/clk/renesas/rcar-gen2-cpg.c line 304
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+ div =3 ; + }else { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = ((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ; + }
Looks OK to me. I assume you've verified the Z2 clock frequency in /sys/kernel/debug/clk/clk_summary?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
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
Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Hi Sergei,
Quoted 53 lines · Geert Uytterhoeven
Quoted 30 lines · Sergei Shtylyov
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>Reviewed-by Geert Uytterhoeven <[email protected]>---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================drivers/clk/renesas/rcar-gen2-cpg.c new file +0
-0@@ -0,0 +1,369 @@drivers/clk/renesas/rcar-gen2-cpg.c line 290
Quoted 10 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +9
-0+struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car E2. ... and V2H.
drivers/clk/renesas/rcar-gen2-cpg.c line 297
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+* Register the PLL0 clock as a fixed factor clock for now as +* there's no generic multiplier clock implementation and we +* currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */
Quoted 3 lines
... and V2H.
Quoted 1 line
+ div = 3;
After seeing the r8a7745 driver, I think it would be better to use a divider value of 1 here (dropping the need for this branch), and handle the /3 in the SoC-specific driver.
This would make it more similar to the other SoCs here (div = 1 in the else branch below), and to similar clocks in the SoC-specific driver (e.g. DEF_FIXED("zg", ..., 3, 1) in r8a7743-cpg-mssr.c).
Quoted 9 lines
drivers/clk/renesas/rcar-gen2-cpg.c line 304
Quoted 6 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +6
-0+ }else { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = ((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ; + }Looks OK to me. I assume you've verified the Z2 clock frequency in
/sys/kernel/debug/clk/clk_summary?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
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
Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Quoted 53 lines · Geert Uytterhoeven
Quoted 53 lines
Quoted 30 lines
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>Reviewed-by Geert Uytterhoeven <[email protected]>---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================drivers/clk/renesas/rcar-gen2-cpg.c new file +0
-0@@ -0,0 +1,369 @@drivers/clk/renesas/rcar-gen2-cpg.c line 290
Quoted 10 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +9
-0+struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car E2. ... and V2H.
drivers/clk/renesas/rcar-gen2-cpg.c line 297
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+* Register the PLL0 clock as a fixed factor clock for now as +* there's no generic multiplier clock implementation and we +* currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */ ... and V2H.
Quoted 8 lines
Quoted 1 line
Quoted 1 line
+ div = 3;
After seeing the r8a7745 driver, I think it would be better to use a divider value of 1 here (dropping the need for this branch), and handle the /3 in the SoC-specific driver.
This would make it more similar to the other SoCs here (div = 1 in the else branch below), and to similar clocks in the SoC-specific driver (e.g.DEF_FIXED("zg", ..., 3, 1)inr8a7743-cpg-mssr.c).
We'll then have to lie about the PLL0 output freq? I don't like it...
MBR, Sergei
Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Hi Sergei,
Quoted 9 lines · Sergei Shtylyov
Quoted 8 lines · Geert Uytterhoeven
Quoted 8 lines
Quoted 8 lines
Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +2
-0@@ -0,0 +1,369 @@ +struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev, + const struct cpg_core_c=
lk
Quoted 28 lines
Quoted 28 lines
Quoted 28 lines
Quoted 11 lines
*core,
+ const structcpg_mssr_info*info,drivers/clk/renesas/rcar-gen2-cpg.c new file +6
-0+struct clk **clks, + void __iomem *base) +{ + case CLK_TYPE_GEN2_PLL0: +/* +* PLL0 is a configurable multiplier clock except on Quoted 1 line
R-Car E2.
... and V2H.
Quoted 10 lines
+ * Register the PLL0 clock as a fixed factor clock for
now as
+ * there's no generic multiplier clock implementation
and we
+ * currently have no need to change the multiplier
value.drivers/clk/renesas/rcar-gen2-cpg.c new file +4
-0+*/ + mult =3D cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
... and V2H.
Quoted 1 line
+ div =3D 3;
After seeing the r8a7745 driver, I think it would be better to use a divider
value of 1 here (dropping the need for this branch), and handle the /3 i=
n
Quoted 8 lines
Quoted 6 lines
the
SoC-specific driver.
This would make it more similar to the other SoCs here (div =3D 1 in the else
branch below), and to similar clocks in the SoC-specific driver (e.g.DEF_FIXED("zg", ..., 3, 1)inr8a7743-cpg-mssr.c).We'll then have to lie about the PLL0 output freq? I don't like it...
Do we? The divisor is not part of PLL0. The datasheet says:
- For V2H/E2:
"PLL circuit 0 multiplies EXTAL or EXTAL/2 clock."
- For the other SoCs:
"PLL circuit 0 multiplies EXTAL or EXTAL/2 clock. The
multiplication ratio is set by the PLL0CR."
Division is done _after_ PLL0, using the SYS-CPU clock divider.
- For e.g. E2:
"The SYS-CPU clock divider 1 divides PLL0 output clock. This
divider generates the AP-System core clocks (Z2=CF=86)."
AFAIU, this SYS-CPU clock divider is "/3" on E2.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k= .org
In personal conversations with technical people, I call myself a hacker. Bu= t
when I'm talking to journalists I just say "programmer" or something like t= hat.
-- Linus Torvalds
Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Quoted 75 lines · Geert Uytterhoeven
Quoted 65 lines
Quoted 55 lines
Quoted 50 lines
Quoted 33 lines
Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================drivers/clk/renesas/rcar-gen2-cpg.c new file +2
-0@@ -0,0 +1,369 @@ +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, + const struct cpg_core_clk*core,
+ const structcpg_mssr_info*info,drivers/clk/renesas/rcar-gen2-cpg.c new file +6
-0+struct clk **clks, + void __iomem *base) +{ + case CLK_TYPE_GEN2_PLL0: +/* +* PLL0 is a configurable multiplier clock except on Quoted 1 line
R-Car E2.
... and V2H.
drivers/clk/renesas/rcar-gen2-cpg.c line 297
Quoted 10 lines
+ * Register the PLL0 clock as a fixed factor clock for
now as
+ * there's no generic multiplier clock implementation
and we
+ * currently have no need to change the multiplier
value.drivers/clk/renesas/rcar-gen2-cpg.c new file +4
-0+*/ + mult = cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */ Quoted 3 lines
... and V2H.
Quoted 1 line
+ div = 3;
Quoted 9 lines
After seeing the r8a7745 driver, I think it would be better to use a divider
value of 1 here (dropping the need for this branch), and handle the /3 in the
SoC-specific driver.
This would make it more similar to the other SoCs here (div = 1 in the else
branch below), and to similar clocks in the SoC-specific driver (e.g.DEF_FIXED("zg", ..., 3, 1)inr8a7743-cpg-mssr.c).Quoted 1 line
We'll then have to lie about the PLL0 output freq? I don't like it...
Do we? The divisor is not part of PLL0. The datasheet says:
- For V2H/E2:
"PLL circuit 0 multiplies EXTAL or EXTAL/2 clock."
- For the other SoCs:
"PLL circuit 0 multiplies EXTAL or EXTAL/2 clock. The
multiplication ratio is set by the PLL0CR."
Quoted 7 lines
Division is done _after_ PLL0, using the SYS-CPU clock divider.
- For e.g. E2:
"The SYS-CPU clock divider 1 divides PLL0 output clock. This
divider generates the AP-System core clocks (Z2φ)."AFAIU, this SYS-CPU clock divider is "/3" on E2.
The figures 7.1* picture it the other way: PLL0/VCO*1/3 is the output of
PLL0, according to them. Quite the same way as PLL1 freq gets divided by 2 before being output. Maybe they're wrong tho...
Quoted 3 lines
Gr{oetje,eeting}s,
Geert
MBR, Sergei
Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support
In reply to Geert UytterhoevenHello.
Quoted 37 lines · Geert Uytterhoeven
Quoted 36 lines
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>Reviewed-by Geert Uytterhoeven <[email protected]>---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.drivers/clk/renesas/rcar-gen2-cpg.c | 369 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 411 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================drivers/clk/renesas/rcar-gen2-cpg.c new file +6
-0@@ -0,0 +1,369 @@ +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{
Quoted 5 lines
drivers/clk/renesas/rcar-gen2-cpg.c line 290
Quoted 3 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +3
-0+case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car E2. ... and V2H.
Indeed. But at least on E2 PLL0CR.STC can be read...
Quoted 9 lines
drivers/clk/renesas/rcar-gen2-cpg.c line 297
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+* Register the PLL0 clock as a fixed factor clock for now as +* there's no generic multiplier clock implementation and we +* currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (mult) { +/* PLL0 is VCO/3 on R-Car E2 */ ... and V2H.
No, it's still VCO/2 on V2H, according to the manual. Perhaps I'll have to
store the PLL0 divisor separately (and maybe even instead of the PLL0 multiplier).
Quoted 10 lines
drivers/clk/renesas/rcar-gen2-cpg.c line 304
Quoted 7 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +7
-0+ div =3 ; + }else { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = ((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ; + }Looks OK to me. I assume you've verified the Z2 clock frequency in
/sys/kernel/debug/clk/clk_summary?
Not yet. :-/
Quoted 3 lines
Gr{oetje,eeting}s,
Geert
MBR, Sergei
PATCH v3 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support
In reply to Sergei ShtylyovAdd the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
---
Changes in version 3:
- remove the divisor logic from the PLL0 related code;
- also mention R-Car V2H as not having the PLL0CR register.
Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- used CPG_FRQCRC_ZFC_SHIFT to #define CPG_FRQCRC_ZFC_MASK;
- removed rcar_gen2_read_modemr();
- added Geert's tag.
drivers/clk/renesas/rcar-gen2-cpg.c | 366 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 408 insertions(+)
drivers/clk/renesas/rcar-gen2-cpg.c new file +366 -0
@@ -0,0 +1,366 @@ +/* +* R-Car Gen2 Clock Pulse Generator +* +* Copyright (C) 2016 Cogent Embedded Inc. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published +* by the Free Software Foundation. +*/ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_FRQCRB 0x0004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x0074 +#define CPG_PLL0CR 0x00d8 +#define CPG_PLL0CR_STC_SHIFT 24 +#define CPG_PLL0CR_STC_MASK (0x7f << CPG_PLL0CR_STC_SHIFT) +#define CPG_FRQCRC 0x00e0 +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_FRQCRC_ZFC_MASK (0x1f << CPG_FRQCRC_ZFC_SHIFT) +#define CPG_ADSPCKCR 0x025c +#define CPG_RCANCKCR 0x0270 + +static spinlock_t cpg_lock; + +/* +* 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 +* parent - fixed parent. No clk_set_parent support +*/ + +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 int mult; +unsigned int val; + + val = (readl (zclk->reg) &CPG_FRQCRC_ZFC_MASK ) >>CPG_FRQCRC_ZFC_SHIFT ; + mult =32 - val; + +return div_u64 ((u64 )parent_rate * mult,32 ); +} + +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 ; + + mult =div_u64 ((u64 )rate *32 , prate); + mult =clamp (mult,1U ,32U ); + +return *parent_rate /32 * mult; +} + +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; +u32 val, kick; +unsigned int i; + + mult =div_u64 ((u64 )rate *32 , parent_rate); + mult =clamp (mult,1U ,32U ); + +if (readl (zclk->kick_reg) &CPG_FRQCRB_KICK ) +return -EBUSY ; + + val =readl (zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK ; + val |= (32 - mult) <<CPG_FRQCRC_ZFC_SHIFT ; +writel (val, zclk->reg); + +/* +* Set KICK bit in FRQCRB to update hardware setting and wait for +* clock change completion. +*/ + kick =readl (zclk->kick_reg); + kick |=CPG_FRQCRB_KICK ; +writel (kick, zclk->kick_reg); + +/* +* 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 on external xtal rate, pll1 +* rate or even the other emulation clocks rate, use 1000 as a +* "super" safe value. +*/ +for (i =1000 ; i; i--) { +if (!(readl (zclk->kick_reg) &CPG_FRQCRB_KICK )) +return 0 ; + +cpu_relax (); + } + +return -ETIMEDOUT ; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk *__init cpg_z_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_init_data init; +struct cpg_z_clk *zclk; +struct clk *clk; + + zclk =kzalloc (sizeof (*zclk),GFP_KERNEL ); +if (!zclk) +return ERR_PTR (-ENOMEM ); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags =0 ; + init.parent_names = &parent_name; + init.num_parents =1 ; + + zclk->reg = base +CPG_FRQCRC ; + zclk->kick_reg = base +CPG_FRQCRB ; + zclk->hw.init = &init; + + clk =clk_register (NULL , &zclk->hw); +if (IS_ERR (clk)) +kfree (zclk); + +return clk; +} + +static struct clk *__init cpg_rcan_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_fixed_factor *fixed; +struct clk_gate *gate; +struct clk *clk; + + fixed =kzalloc (sizeof (*fixed),GFP_KERNEL ); +if (!fixed) +return ERR_PTR (-ENOMEM ); + + fixed->mult =1 ; + fixed->div =6 ; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (fixed); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_RCANCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (fixed); + } + +return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + {1 ,3 }, {2 ,4 }, {3 ,6 }, {4 ,8 }, + {5 ,12 }, {6 ,16 }, {7 ,18 }, {8 ,24 }, + {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static struct clk *__init cpg_adsp_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_divider *div; +struct clk_gate *gate; +struct clk *clk; + + div =kzalloc (sizeof (*div),GFP_KERNEL ); +if (!div) +return ERR_PTR (-ENOMEM ); + + div->reg = base +CPG_ADSPCKCR ; + div->width =4 ; + div->table = cpg_adsp_div_table; + div->lock = &cpg_lock; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (div); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_ADSPCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (div); + } + +return clk; +} + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + {0 ,2 }, {1 ,3 }, {2 ,4 }, {3 ,6 }, + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {12 ,10 }, + {0 ,0 }, +}; + +static const struct rcar_gen2_cpg_pll_config *cpg_pll_config__initdata ; +static u32 cpg_mode__initdata ; + +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +const struct clk_div_table *table =NULL ; +const struct clk *parent; +const char *parent_name; +unsigned int mult =1 ; +unsigned int div =1 ; +unsigned int shift; + + parent = clks[core->parent]; +if (IS_ERR (parent)) +return ERR_CAST (parent); + + parent_name =__clk_get_name (parent); + +switch (core->type) { +/* R-Car Gen2 */ +case CLK_TYPE_GEN2_MAIN : + div = cpg_pll_config->extal_div; +break ; + +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car +* V2H/E2. Register the PLL0 clock as a fixed factor clock for +* now as there's no generic multiplier clock implementation and +* we currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (!mult) { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = (((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ) *2 ; + } +break ; + +case CLK_TYPE_GEN2_PLL1 : + mult = cpg_pll_config->pll1_mult /2 ; +break ; + +case CLK_TYPE_GEN2_PLL3 : + mult = cpg_pll_config->pll3_mult; +break ; + +case CLK_TYPE_GEN2_Z : +return cpg_z_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_LB : + div = cpg_mode &BIT (18 ) ?36 :24 ; +break ; + +case CLK_TYPE_GEN2_ADSP : +return cpg_adsp_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_SDH : + table = cpg_sdh_div_table; + shift =8 ; +break ; + +case CLK_TYPE_GEN2_SD0 : + table = cpg_sd01_div_table; + shift =4 ; +break ; + +case CLK_TYPE_GEN2_SD1 : + table = cpg_sd01_div_table; + shift =0 ; +break ; + +case CLK_TYPE_GEN2_QSPI : + div = (cpg_mode & (BIT (3 ) |BIT (2 ) |BIT (1 ))) ==BIT (2 ) ? +8 :10 ; +break ; + +case CLK_TYPE_GEN2_RCAN : +return cpg_rcan_clk_register (core->name, parent_name, base); + +default : +return ERR_PTR (-EINVAL ); + } + +if (!table) +return clk_register_fixed_factor (NULL , core->name, parent_name, +0 , mult, div); +else +return clk_register_divider_table (NULL , core->name, + parent_name,0 , + base +CPG_SDCKCR , shift,4 , +0 , table, &cpg_lock); +} + +int __init rcar_gen2_cpg_init (const struct rcar_gen2_cpg_pll_config *config) +{ + cpg_pll_config = config; + +spin_lock_init (&cpg_lock); + +return 0 ; +}
drivers/clk/renesas/rcar-gen2-cpg.h new file +42 -0
42 lines not on this page · raw message
PATCH RESEND v3 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support
In reply to Sergei ShtylyovAdd the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.
Changes in version 3:
- remove the divisor logic from the PLL0 related code;
- also mention R-Car V2H as not having the PLL0CR register.
Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- used CPG_FRQCRC_ZFC_SHIFT to #define CPG_FRQCRC_ZFC_MASK;
- removed rcar_gen2_read_modemr();
- added Geert's tag.
drivers/clk/renesas/rcar-gen2-cpg.c | 366 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 42 ++++ 2 files changed, 408 insertions(+)
drivers/clk/renesas/rcar-gen2-cpg.c new file +366 -0
@@ -0,0 +1,366 @@ +/* +* R-Car Gen2 Clock Pulse Generator +* +* Copyright (C) 2016 Cogent Embedded Inc. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published +* by the Free Software Foundation. +*/ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_FRQCRB 0x0004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x0074 +#define CPG_PLL0CR 0x00d8 +#define CPG_PLL0CR_STC_SHIFT 24 +#define CPG_PLL0CR_STC_MASK (0x7f << CPG_PLL0CR_STC_SHIFT) +#define CPG_FRQCRC 0x00e0 +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_FRQCRC_ZFC_MASK (0x1f << CPG_FRQCRC_ZFC_SHIFT) +#define CPG_ADSPCKCR 0x025c +#define CPG_RCANCKCR 0x0270 + +static spinlock_t cpg_lock; + +/* +* 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 +* parent - fixed parent. No clk_set_parent support +*/ + +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 int mult; +unsigned int val; + + val = (readl (zclk->reg) &CPG_FRQCRC_ZFC_MASK ) >>CPG_FRQCRC_ZFC_SHIFT ; + mult =32 - val; + +return div_u64 ((u64 )parent_rate * mult,32 ); +} + +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 ; + + mult =div_u64 ((u64 )rate *32 , prate); + mult =clamp (mult,1U ,32U ); + +return *parent_rate /32 * mult; +} + +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; +u32 val, kick; +unsigned int i; + + mult =div_u64 ((u64 )rate *32 , parent_rate); + mult =clamp (mult,1U ,32U ); + +if (readl (zclk->kick_reg) &CPG_FRQCRB_KICK ) +return -EBUSY ; + + val =readl (zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK ; + val |= (32 - mult) <<CPG_FRQCRC_ZFC_SHIFT ; +writel (val, zclk->reg); + +/* +* Set KICK bit in FRQCRB to update hardware setting and wait for +* clock change completion. +*/ + kick =readl (zclk->kick_reg); + kick |=CPG_FRQCRB_KICK ; +writel (kick, zclk->kick_reg); + +/* +* 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 on external xtal rate, pll1 +* rate or even the other emulation clocks rate, use 1000 as a +* "super" safe value. +*/ +for (i =1000 ; i; i--) { +if (!(readl (zclk->kick_reg) &CPG_FRQCRB_KICK )) +return 0 ; + +cpu_relax (); + } + +return -ETIMEDOUT ; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk *__init cpg_z_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_init_data init; +struct cpg_z_clk *zclk; +struct clk *clk; + + zclk =kzalloc (sizeof (*zclk),GFP_KERNEL ); +if (!zclk) +return ERR_PTR (-ENOMEM ); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags =0 ; + init.parent_names = &parent_name; + init.num_parents =1 ; + + zclk->reg = base +CPG_FRQCRC ; + zclk->kick_reg = base +CPG_FRQCRB ; + zclk->hw.init = &init; + + clk =clk_register (NULL , &zclk->hw); +if (IS_ERR (clk)) +kfree (zclk); + +return clk; +} + +static struct clk *__init cpg_rcan_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_fixed_factor *fixed; +struct clk_gate *gate; +struct clk *clk; + + fixed =kzalloc (sizeof (*fixed),GFP_KERNEL ); +if (!fixed) +return ERR_PTR (-ENOMEM ); + + fixed->mult =1 ; + fixed->div =6 ; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (fixed); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_RCANCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (fixed); + } + +return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + {1 ,3 }, {2 ,4 }, {3 ,6 }, {4 ,8 }, + {5 ,12 }, {6 ,16 }, {7 ,18 }, {8 ,24 }, + {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static struct clk *__init cpg_adsp_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_divider *div; +struct clk_gate *gate; +struct clk *clk; + + div =kzalloc (sizeof (*div),GFP_KERNEL ); +if (!div) +return ERR_PTR (-ENOMEM ); + + div->reg = base +CPG_ADSPCKCR ; + div->width =4 ; + div->table = cpg_adsp_div_table; + div->lock = &cpg_lock; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (div); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_ADSPCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (div); + } + +return clk; +} + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + {0 ,2 }, {1 ,3 }, {2 ,4 }, {3 ,6 }, + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {12 ,10 }, + {0 ,0 }, +}; + +static const struct rcar_gen2_cpg_pll_config *cpg_pll_config__initdata ; +static u32 cpg_mode__initdata ; + +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +const struct clk_div_table *table =NULL ; +const struct clk *parent; +const char *parent_name; +unsigned int mult =1 ; +unsigned int div =1 ; +unsigned int shift; + + parent = clks[core->parent]; +if (IS_ERR (parent)) +return ERR_CAST (parent); + + parent_name =__clk_get_name (parent); + +switch (core->type) { +/* R-Car Gen2 */ +case CLK_TYPE_GEN2_MAIN : + div = cpg_pll_config->extal_div; +break ; + +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car +* V2H/E2. Register the PLL0 clock as a fixed factor clock for +* now as there's no generic multiplier clock implementation and +* we currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; +if (!mult) { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = (((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ) *2 ; + } +break ; + +case CLK_TYPE_GEN2_PLL1 : + mult = cpg_pll_config->pll1_mult /2 ; +break ; + +case CLK_TYPE_GEN2_PLL3 : + mult = cpg_pll_config->pll3_mult; +break ; + +case CLK_TYPE_GEN2_Z : +return cpg_z_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_LB : + div = cpg_mode &BIT (18 ) ?36 :24 ; +break ; + +case CLK_TYPE_GEN2_ADSP : +return cpg_adsp_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_SDH : + table = cpg_sdh_div_table; + shift =8 ; +break ; + +case CLK_TYPE_GEN2_SD0 : + table = cpg_sd01_div_table; + shift =4 ; +break ; + +case CLK_TYPE_GEN2_SD1 : + table = cpg_sd01_div_table; + shift =0 ; +break ; + +case CLK_TYPE_GEN2_QSPI : + div = (cpg_mode & (BIT (3 ) |BIT (2 ) |BIT (1 ))) ==BIT (2 ) ? +8 :10 ; +break ; + +case CLK_TYPE_GEN2_RCAN : +return cpg_rcan_clk_register (core->name, parent_name, base); + +default : +return ERR_PTR (-EINVAL ); + } + +if (!table) +return clk_register_fixed_factor (NULL , core->name, parent_name, +0 , mult, div); +else +return clk_register_divider_table (NULL , core->name, + parent_name,0 , + base +CPG_SDCKCR , shift,4 , +0 , table, &cpg_lock); +} + +int __init rcar_gen2_cpg_init (const struct rcar_gen2_cpg_pll_config *config) +{ + cpg_pll_config = config; + +spin_lock_init (&cpg_lock); + +return 0 ; +}
drivers/clk/renesas/rcar-gen2-cpg.h new file +42 -0
42 lines not on this page · raw message
PATCH v4 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support
In reply to Sergei ShtylyovAdd the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.
Changes in version 4:
- added a static variable for the PLL0 divisor and a parameter of the same kind to rcar_gen2_cpg_init(), used that variable in rcar_gen2_cpg_clk_register().
Changes in version 3:
- removed the divisor logic from the PLL0 related code;
- also mentioned R-Car V2H as not having the PLL0CR register.
Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- used CPG_FRQCRC_ZFC_SHIFT to #define CPG_FRQCRC_ZFC_MASK;
- removed rcar_gen2_read_modemr();
- added Geert's tag.
drivers/clk/renesas/rcar-gen2-cpg.c | 370 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 43 ++++ 2 files changed, 413 insertions(+)
drivers/clk/renesas/rcar-gen2-cpg.c new file +370 -0
@@ -0,0 +1,370 @@ +/* +* R-Car Gen2 Clock Pulse Generator +* +* Copyright (C) 2016 Cogent Embedded Inc. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published +* by the Free Software Foundation. +*/ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_FRQCRB 0x0004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x0074 +#define CPG_PLL0CR 0x00d8 +#define CPG_PLL0CR_STC_SHIFT 24 +#define CPG_PLL0CR_STC_MASK (0x7f << CPG_PLL0CR_STC_SHIFT) +#define CPG_FRQCRC 0x00e0 +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_FRQCRC_ZFC_MASK (0x1f << CPG_FRQCRC_ZFC_SHIFT) +#define CPG_ADSPCKCR 0x025c +#define CPG_RCANCKCR 0x0270 + +static spinlock_t cpg_lock; + +/* +* 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 +* parent - fixed parent. No clk_set_parent support +*/ + +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 int mult; +unsigned int val; + + val = (readl (zclk->reg) &CPG_FRQCRC_ZFC_MASK ) >>CPG_FRQCRC_ZFC_SHIFT ; + mult =32 - val; + +return div_u64 ((u64 )parent_rate * mult,32 ); +} + +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 ; + + mult =div_u64 ((u64 )rate *32 , prate); + mult =clamp (mult,1U ,32U ); + +return *parent_rate /32 * mult; +} + +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; +u32 val, kick; +unsigned int i; + + mult =div_u64 ((u64 )rate *32 , parent_rate); + mult =clamp (mult,1U ,32U ); + +if (readl (zclk->kick_reg) &CPG_FRQCRB_KICK ) +return -EBUSY ; + + val =readl (zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK ; + val |= (32 - mult) <<CPG_FRQCRC_ZFC_SHIFT ; +writel (val, zclk->reg); + +/* +* Set KICK bit in FRQCRB to update hardware setting and wait for +* clock change completion. +*/ + kick =readl (zclk->kick_reg); + kick |=CPG_FRQCRB_KICK ; +writel (kick, zclk->kick_reg); + +/* +* 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 on external xtal rate, pll1 +* rate or even the other emulation clocks rate, use 1000 as a +* "super" safe value. +*/ +for (i =1000 ; i; i--) { +if (!(readl (zclk->kick_reg) &CPG_FRQCRB_KICK )) +return 0 ; + +cpu_relax (); + } + +return -ETIMEDOUT ; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk *__init cpg_z_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_init_data init; +struct cpg_z_clk *zclk; +struct clk *clk; + + zclk =kzalloc (sizeof (*zclk),GFP_KERNEL ); +if (!zclk) +return ERR_PTR (-ENOMEM ); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags =0 ; + init.parent_names = &parent_name; + init.num_parents =1 ; + + zclk->reg = base +CPG_FRQCRC ; + zclk->kick_reg = base +CPG_FRQCRB ; + zclk->hw.init = &init; + + clk =clk_register (NULL , &zclk->hw); +if (IS_ERR (clk)) +kfree (zclk); + +return clk; +} + +static struct clk *__init cpg_rcan_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_fixed_factor *fixed; +struct clk_gate *gate; +struct clk *clk; + + fixed =kzalloc (sizeof (*fixed),GFP_KERNEL ); +if (!fixed) +return ERR_PTR (-ENOMEM ); + + fixed->mult =1 ; + fixed->div =6 ; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (fixed); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_RCANCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (fixed); + } + +return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + {1 ,3 }, {2 ,4 }, {3 ,6 }, {4 ,8 }, + {5 ,12 }, {6 ,16 }, {7 ,18 }, {8 ,24 }, + {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static struct clk *__init cpg_adsp_clk_register (const char *name, +const char *parent_name, +void __iomem *base) +{ +struct clk_divider *div; +struct clk_gate *gate; +struct clk *clk; + + div =kzalloc (sizeof (*div),GFP_KERNEL ); +if (!div) +return ERR_PTR (-ENOMEM ); + + div->reg = base +CPG_ADSPCKCR ; + div->width =4 ; + div->table = cpg_adsp_div_table; + div->lock = &cpg_lock; + + gate =kzalloc (sizeof (*gate),GFP_KERNEL ); +if (!gate) { +kfree (div); +return ERR_PTR (-ENOMEM ); + } + + gate->reg = base +CPG_ADSPCKCR ; + gate->bit_idx =8 ; + gate->flags =CLK_GATE_SET_TO_DISABLE ; + gate->lock = &cpg_lock; + + clk =clk_register_composite (NULL , name, &parent_name,1 ,NULL ,NULL , + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops,0 ); +if (IS_ERR (clk)) { +kfree (gate); +kfree (div); + } + +return clk; +} + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + {0 ,2 }, {1 ,3 }, {2 ,4 }, {3 ,6 }, + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {0 ,0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + {4 ,8 }, {5 ,12 }, {6 ,16 }, {7 ,18 }, + {8 ,24 }, {10 ,36 }, {11 ,48 }, {12 ,10 }, + {0 ,0 }, +}; + +static const struct rcar_gen2_cpg_pll_config *cpg_pll_config__initdata ; +static unsigned int cpg_pll0_div__initdata ; +static u32 cpg_mode__initdata ; + +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +const struct clk_div_table *table =NULL ; +const struct clk *parent; +const char *parent_name; +unsigned int mult =1 ; +unsigned int div =1 ; +unsigned int shift; + + parent = clks[core->parent]; +if (IS_ERR (parent)) +return ERR_CAST (parent); + + parent_name =__clk_get_name (parent); + +switch (core->type) { +/* R-Car Gen2 */ +case CLK_TYPE_GEN2_MAIN : + div = cpg_pll_config->extal_div; +break ; + +case CLK_TYPE_GEN2_PLL0 : +/* +* PLL0 is a configurable multiplier clock except on R-Car +* V2H/E2. Register the PLL0 clock as a fixed factor clock for +* now as there's no generic multiplier clock implementation and +* we currently have no need to change the multiplier value. +*/ + mult = cpg_pll_config->pll0_mult; + div = cpg_pll0_div; +if (!mult) { +u32 pll0cr =readl (base +CPG_PLL0CR ); + + mult = (((pll0cr &CPG_PLL0CR_STC_MASK ) >> +CPG_PLL0CR_STC_SHIFT ) +1 ) *2 ; + } +break ; + +case CLK_TYPE_GEN2_PLL1 : + mult = cpg_pll_config->pll1_mult /2 ; +break ; + +case CLK_TYPE_GEN2_PLL3 : + mult = cpg_pll_config->pll3_mult; +break ; + +case CLK_TYPE_GEN2_Z : +return cpg_z_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_LB : + div = cpg_mode &BIT (18 ) ?36 :24 ; +break ; + +case CLK_TYPE_GEN2_ADSP : +return cpg_adsp_clk_register (core->name, parent_name, base); + +case CLK_TYPE_GEN2_SDH : + table = cpg_sdh_div_table; + shift =8 ; +break ; + +case CLK_TYPE_GEN2_SD0 : + table = cpg_sd01_div_table; + shift =4 ; +break ; + +case CLK_TYPE_GEN2_SD1 : + table = cpg_sd01_div_table; + shift =0 ; +break ; + +case CLK_TYPE_GEN2_QSPI : + div = (cpg_mode & (BIT (3 ) |BIT (2 ) |BIT (1 ))) ==BIT (2 ) ? +8 :10 ; +break ; + +case CLK_TYPE_GEN2_RCAN : +return cpg_rcan_clk_register (core->name, parent_name, base); + +default : +return ERR_PTR (-EINVAL ); + } + +if (!table) +return clk_register_fixed_factor (NULL , core->name, parent_name, +0 , mult, div); +else +return clk_register_divider_table (NULL , core->name, + parent_name,0 , + base +CPG_SDCKCR , shift,4 , +0 , table, &cpg_lock); +} + +int __init rcar_gen2_cpg_init (const struct rcar_gen2_cpg_pll_config *config, +unsigned int pll0_div) +{ + cpg_pll_config = config; + cpg_pll0_div = pll0_div; + +spin_lock_init (&cpg_lock); + +return 0 ; +}
drivers/clk/renesas/rcar-gen2-cpg.h new file +43 -0
43 lines not on this page · raw message
Re: [PATCH v4] clk: renesas: cpg-mssr: add common R-Car Gen2 support
Hi Sergei,
Quoted 9 lines · Sergei Shtylyov
Add the common R-Car Gen2 (and RZ/G) Clock Pulse Generator / Module Standby and Software Reset support code, using the CPG/MSSR driver core.
Based on the proof-of-concept R8A7791 CPG/MSSR patch by Geert Uytterhoeven <[email protected]>.
Signed-off-by Sergei Shtylyov <[email protected]>Reviewed-by Geert Uytterhoeven <[email protected]>
Quoted 6 lines
---
This patch is against the 'clk-next' branch of CLK group's 'linux.git' repo.Changes in version 4:
- added a static variable for the PLL0 divisor and a parameter of the same kind torcar_gen2_cpg_init(), used that variable inrcar_gen2_cpg_clk_register().
Thanks, I've verified correct operation by using your r8a7743 driver on r8a7791/koelsch, and comparing /sys/kernel/debug/clk/clk_summary.
Quoted 20 lines
Changes in version 3:
- removed the divisor logic from the PLL0 related code;
- also mentioned R-Car V2H as not having the PLL0CR register.Changes in version 2:
- added support for non-existing PLL0CR;
- removed the function reading the mode pins;
- added/used the #define's for PLL0CR.STC;
- usedCPG_FRQCRC_ZFC_SHIFTto #defineCPG_FRQCRC_ZFC_MASK;
- removedrcar_gen2_read_modemr();
- added Geert's tag.drivers/clk/renesas/rcar-gen2-cpg.c | 370 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen2-cpg.h | 43 ++++ 2 files changed, 413 insertions(+)Index:
linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================
drivers/clk/renesas/rcar-gen2-cpg.c
Quoted 2 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +1
-0+static u32 cpg_mode__initdata ;
cpg_mode is never set.
drivers/clk/renesas/rcar-gen2-cpg.c line 318
Quoted 12 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +10
-0+ +struct clk *__init rcar_gen2_cpg_clk_register (struct device *dev, +const struct cpg_core_clk *core, +const struct cpg_mssr_info *info, +struct clk **clks, +void __iomem *base) +{ +switch (core->type) { +case CLK_TYPE_GEN2_LB : + div = cpg_mode &BIT (18 ) ?36 :24 ;
Hence div may be incorrect here (it wasn't for me)...
drivers/clk/renesas/rcar-gen2-cpg.c line 341
Quoted 3 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +3
-0+case CLK_TYPE_GEN2_QSPI : + div = (cpg_mode & (BIT (3 ) |BIT (2 ) |BIT (1 ))) ==BIT (2 ) ? +8 :10 ;
... and here (it was for me).
drivers/clk/renesas/rcar-gen2-cpg.c line 362
Quoted 2 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +2
-0+int __init rcar_gen2_cpg_init (const struct rcar_gen2_cpg_pll_config *config, +unsigned int pll0_div)
I'll add a "u32 mode" parameter here, and pass it from r8a774[35]_cpg_mssr_init().
drivers/clk/renesas/rcar-gen2-cpg.c line 365
Quoted 3 lines
drivers/clk/renesas/rcar-gen2-cpg.c new file +3
-0+{ + cpg_pll_config = config; + cpg_pll0_div = pll0_div;
cpg_mode = mode;
No need to resend, fixing it up myself.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
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