Skip to contentSkip to conversation

Renesas R8A7743 CPG/MSSR clock support

PATCH Superseded Sergei Shtylyov 2 participants 21 messages Oct 5, 2016 – Nov 10, 2016

Subsystems (4)

v1 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

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

#1 lore raw

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
Index: renesas/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================
@@ -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

Reviewed-by on v1 1/3
#2 lore raw

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]>.

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

#3 lore raw

Re: [PATCH 1/3] clk: renesas: cpg-mssr: add common R-Car Gen2 support

In reply to Sergei Shtylyov
Quoted 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]>.

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

#4 lore raw

PATCH 2/3 ARM: shmobile: r8a7743: add CPG clock index macros

In reply to Sergei Shtylyov

Add 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
Index: renesas/include/dt-bindings/clock/r8a7743-cpg-mssr.h
===================================================================
@@ -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__ */
#5 lore raw

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.

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

#6 lore raw

Re: [PATCH 2/3] ARM: shmobile: r8a7743: add CPG clock index macros

In reply to Sergei Shtylyov

Hello.

Quoted 4 lines · Sergei Shtylyov

Add macros usable by the device tree sources to reference the R8A7743 CPG clocks by index.

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

#7 lore raw

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.

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

#8 lore raw

PATCH 3/3 clk: renesas: cpg-mssr: add R8A7743 support

In reply to Sergei Shtylyov

Add 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
Index: renesas/drivers/clk/renesas/Kconfig
===================================================================
@@ -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
Index: renesas/drivers/clk/renesas/Makefile
===================================================================
@@ -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
Index: renesas/drivers/clk/renesas/renesas-cpg-mssr.c
===================================================================
@@ -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
Index: renesas/drivers/clk/renesas/renesas-cpg-mssr.h
===================================================================
@@ -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
#9 lore raw
In reply to Sergei Shtylyov

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 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

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

#10 lore raw

PATCH v2 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support

In reply to 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]>.

---
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
Index: linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================
@@ -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

#11 lore raw

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]>.

---
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(+)

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

#12 lore raw

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]>.

---
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(+)

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

#13 lore raw

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]>.

---
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(+)

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) in r8a7743-cpg-mssr.c).

We'll then have to lie about the PLL0 output freq? I don't like it...

MBR, Sergei

#14 lore raw

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;
- used CPG_FRQCRC_ZFC_SHIFT to #define CPG_FRQCRC_ZFC_MASK;
- removed rcar_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 struct
cpg_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) in r8a7743-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

#15 lore raw

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;
- 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(+)

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 struct
cpg_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) in r8a7743-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

#16 lore raw

Re: [PATCH v2] clk: renesas: cpg-mssr: add common R-Car Gen2 support

In reply to Geert Uytterhoeven

Hello.

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]>.

---
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(+)

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

#17 lore raw

PATCH v3 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support

In reply to 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]>.

---
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
Index: renesas/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================
@@ -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

#18 lore raw

PATCH RESEND v3 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support

In reply to 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]>.

---
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
Index: linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================
@@ -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

#19 lore raw

PATCH v4 1/1 clk: renesas: cpg-mssr: add common R-Car Gen2 support

In reply to 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]>.

---
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
Index: linux/drivers/clk/renesas/rcar-gen2-cpg.c
===================================================================
@@ -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

#20 lore raw

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]>.

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 to rcar_gen2_cpg_init(), used that variable in rcar_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;
- 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(+)

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