All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yoshinori Sato <ysato@users.sourceforge.jp>
To: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
	linux-sh@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Subject: [PATCH v4 11/22] sh: SH7750/51 CPG Driver
Date: Wed, 29 Jun 2016 13:40:56 +0000	[thread overview]
Message-ID: <1467207667-15768-12-git-send-email-ysato@users.sourceforge.jp> (raw)
In-Reply-To: <1467207667-15768-1-git-send-email-ysato@users.sourceforge.jp>

Convert SH specific clock framework to CCF.

Changes v4
Add acked-by

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../bindings/clock/renesas,sh7750-cpg.txt          |  25 ++
 arch/sh/boards/Kconfig                             |   1 +
 arch/sh/kernel/cpu/Makefile                        |   8 +-
 arch/sh/kernel/cpu/clock.c                         |   6 +-
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   3 +-
 drivers/clk/sh/Kconfig                             |   2 +
 drivers/clk/sh/Makefile                            |   1 +
 drivers/clk/sh/clk-sh7750cpg.c                     | 344 +++++++++++++++++++++
 9 files changed, 387 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt
 create mode 100644 drivers/clk/sh/Kconfig
 create mode 100644 drivers/clk/sh/Makefile
 create mode 100644 drivers/clk/sh/clk-sh7750cpg.c

diff --git a/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt b/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt
new file mode 100644
index 0000000..e763e2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt
@@ -0,0 +1,25 @@
+* Renesas SH7750/51 CPG
+
+Required Properties:
+
+  - compatible: Must be "renesas,sh7750-cpg"
+
+  - clocks: Reference to the parent clocks (xtal or external)
+
+  - #clock-cells: Must be 1
+
+  - reg: Base address and length of the FREQCR
+         and Base address and length of the CLKSTP00 (optional)
+
+  - renesas,mult: PLL1 multiply rate
+
+Example
+-------
+
+        cpg: cpg@ffc00000 {
+                compatible = "renesas,sh7750-cpg";
+                clocks = <&oclk>;
+                #clock-cells = <1>;
+                renesas,mult = <12>;
+                reg = <0xffc00000 32>, <0xfe0a0000 16>;
+        };
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 9e4ccd0..b6ff9df 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -13,6 +13,7 @@ config SH_DEVICE_TREE
 	select CLKSRC_OF
 	select GENERIC_CALIBRATE_DELAY
 	select GENERIC_IOMAP
+	select COMMON_CLK
 	help
 	  Select Board Described by Device Tree to build a kernel that
 	  does not hard-code any board-specific knowledge but instead uses
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index accc7ca..22ad0ee 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -16,6 +16,10 @@ obj-$(CONFIG_ARCH_SHMOBILE)	+= shmobile/
 # Common interfaces.
 
 obj-$(CONFIG_SH_ADC)		+= adc.o
+ifndef CONFIG_COMMON_CLK
 obj-$(CONFIG_SH_CLK_CPG_LEGACY)	+= clock-cpg.o
-
-obj-y	+= irq/ init.o clock.o fpu.o pfc.o proc.o
+endif
+ifndef CONFIG_GENERIC_IRQ_CHIP
+obj-y	+= irq/
+endif
+obj-y	+= init.o clock.o fpu.o pfc.o proc.o
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
index 4187cf4..8e66e23 100644
--- a/arch/sh/kernel/cpu/clock.c
+++ b/arch/sh/kernel/cpu/clock.c
@@ -22,13 +22,15 @@
 
 int __init clk_init(void)
 {
-	int ret;
+	int ret = 0;
 
+#ifndef CONFIG_COMMON_CLK
 	ret = arch_clk_init();
 	if (unlikely(ret)) {
 		pr_err("%s: CPU clock registration failed.\n", __func__);
 		return ret;
 	}
+#endif
 
 	if (sh_mv.mv_clk_init) {
 		ret = sh_mv.mv_clk_init();
@@ -39,11 +41,13 @@ int __init clk_init(void)
 		}
 	}
 
+#ifndef CONFIG_COMMON_CLK
 	/* Kick the child clocks.. */
 	recalculate_root_clocks();
 
 	/* Enable the necessary init clocks */
 	clk_enable_init_clocks();
+#endif
 
 	return ret;
 }
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 98efbfc..60d19d0 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -213,6 +213,7 @@ source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/qcom/Kconfig"
 source "drivers/clk/renesas/Kconfig"
 source "drivers/clk/samsung/Kconfig"
+source "drivers/clk/sh/Kconfig"
 source "drivers/clk/tegra/Kconfig"
 source "drivers/clk/ti/Kconfig"
 
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index dcc5e69..c4bfbb9 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -86,5 +86,6 @@ obj-$(CONFIG_COMMON_CLK_VERSATILE)	+= versatile/
 obj-$(CONFIG_X86)			+= x86/
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
-obj-$(CONFIG_H8300)		+= h8300/
+obj-$(CONFIG_H8300)			+= h8300/
 obj-$(CONFIG_ARC_PLAT_AXS10X)		+= axs10x/
+obj-$(CONFIG_SUPERH)			+= sh/
diff --git a/drivers/clk/sh/Kconfig b/drivers/clk/sh/Kconfig
new file mode 100644
index 0000000..2090415
--- /dev/null
+++ b/drivers/clk/sh/Kconfig
@@ -0,0 +1,2 @@
+config COMMON_CLK_SH7750
+	bool "CPG driver for SH7750/SH7751"
diff --git a/drivers/clk/sh/Makefile b/drivers/clk/sh/Makefile
new file mode 100644
index 0000000..7ce4da3
--- /dev/null
+++ b/drivers/clk/sh/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_COMMON_CLK_SH7750) += clk-sh7750cpg.o
diff --git a/drivers/clk/sh/clk-sh7750cpg.c b/drivers/clk/sh/clk-sh7750cpg.c
new file mode 100644
index 0000000..a538be4
--- /dev/null
+++ b/drivers/clk/sh/clk-sh7750cpg.c
@@ -0,0 +1,344 @@
+/*
+ * Renesas SH7750/51 clock driver
+ *
+ * Copyright 2016 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/* Available FREQCR settings */
+static const int freqcr_table[] = {
+	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0408,
+	0x0409, 0x040a, 0x040b, 0x040c, 0x0411, 0x0412,
+	0x0413, 0x0414, 0x041a, 0x041b, 0x041c, 0x0423,
+	0x0424, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c,
+	0x0451, 0x0452, 0x0453, 0x0454, 0x045a, 0x045b,
+	0x045c, 0x0463, 0x0464, 0x0491, 0x0492, 0x0493,
+	0x0494, 0x049a, 0x049b, 0x049c, 0x04a3, 0x04a4,
+	0x04da, 0x04db, 0x04dc, 0x04e3, 0x04e4, 0x0523,
+	0x0524, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004,
+	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x0011,
+	0x0012, 0x0013, 0x0014, 0x0019, 0x001a, 0x001b,
+	0x001c, 0x0023, 0x0024, 0x0048, 0x0049, 0x004a,
+	0x004b, 0x004c, 0x0051, 0x0052, 0x0053, 0x0054,
+	0x0059, 0x005a, 0x005b, 0x005c, 0x0063, 0x0064,
+	0x0091, 0x0092, 0x0093, 0x0094, 0x0099, 0x009a,
+	0x009b, 0x009c, 0x00a3, 0x00a4, 0x00d1, 0x00d2,
+	0x00d3, 0x00d4, 0x00d9, 0x00da, 0x00db, 0x00dc,
+	0x00e3, 0x00e4, 0x0123, 0x0124, 0x0163, 0x0164,
+};
+
+struct priv {
+	void __iomem *freqcr;
+	void __iomem *clkstp;
+	int mult;
+	struct clk **clks;
+};
+
+struct cpg_clock {
+	struct clk_hw hw;
+	struct priv *priv;
+	int index;
+};
+
+struct clockname {
+	char *name;
+	int index;
+};
+
+static const struct clockname clocknames[] __initconst = {
+	{ .name = "sci", .index = 0 },
+	{ .name = "rtc", .index = 1 },
+	{ .name = "tmu0", .index = 2 },
+	{ .name = "tmu1", .index = 2 },
+	{ .name = "tmu2", .index = 2 },
+	{ .name = "scif", .index = 3 },
+	{ .name = "dmac", .index = 4 },
+	{ .name = "ubc", .index = 8 },
+	{ .name = "sq", .index = 9 },
+	{ .name = "intc", .index = 16 },
+	{ .name = "tmu3", .index = 17 },
+	{ .name = "tmu4", .index = 17 },
+	{ .name = "pcic", .index = 18 },
+	{ .name = "core", .index = 128 },
+};
+
+static const int iclk_div[] = {1, 2, 3, 4, 6, 8, 0, 0};
+static const int pclk_div[] = {2, 3, 4, 6, 8, 0, 0, 0};
+
+static DEFINE_SPINLOCK(clklock);
+
+#define to_cpg_clock(_hw) container_of(_hw, struct cpg_clock, hw)
+
+static unsigned long pllout(u16 freqcr, unsigned long parent_rate, int mult)
+{
+	if ((freqcr >> 10) & 1)
+		return parent_rate * mult;
+	else
+		return parent_rate;
+}
+
+static unsigned long cpg_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	unsigned long div;
+	u16 freqcr;
+
+	freqcr = ioread16(priv->freqcr);
+	if (cpg_clock->index = 128)
+		div = iclk_div[(freqcr >> 6) & 7];
+	else
+		div = pclk_div[freqcr & 7];
+	return pllout(freqcr, parent_rate, priv->mult) / div;
+}
+
+static u16 get_best_freqcr(unsigned long rate,
+			   unsigned long pclk_rate,
+			   unsigned long parent, int mult)
+{
+	int i;
+	int div;
+	u16 freqcr;
+
+	for (i = 0; i < ARRAY_SIZE(freqcr_table); i++) {
+		freqcr = freqcr_table[i];
+		if (pllout(freqcr, parent, mult) / pclk_div[freqcr & 7]
+		    != pclk_rate)
+			continue;
+		div = iclk_div[(freqcr >> 6) & 7];
+		if (pllout(freqcr, parent, mult) / div < rate)
+			return freqcr;
+	}
+	return 0;
+}
+
+static long cpg_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	unsigned long pclk_rate;
+	u16 freqcr;
+	int div;
+
+	freqcr = ioread16(priv->freqcr);
+	pclk_rate = pllout(freqcr, *prate, priv->mult) / pclk_div[freqcr & 7];
+
+	freqcr = get_best_freqcr(rate, pclk_rate, *prate, priv->mult);
+	if (cpg_clock->index = 128)
+		div = iclk_div[(freqcr >> 6) & 7];
+	else
+		div = pclk_div[freqcr & 7];
+
+	return pllout(freqcr, *prate, priv->mult) / div;
+}
+
+static int cpg_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	unsigned long flags;
+	unsigned long pclk_rate;
+	u16 freqcr, new_freqcr;
+
+	if (cpg_clock->index != 128)
+		return 0;
+
+	freqcr = ioread16(priv->freqcr);
+	pclk_rate = pllout(freqcr, parent_rate, priv->mult) /
+		pclk_div[freqcr & 7];
+
+	new_freqcr = get_best_freqcr(rate, pclk_rate, parent_rate, priv->mult);
+
+	if ((freqcr & 0x0200) = 0 && (new_freqcr & 0x0200) != 0) {
+		/* PLL on */
+		/* required stable time */
+		spin_lock_irqsave(&clklock, flags);
+		iowrite16(0x5a00, priv->freqcr + 8);
+		iowrite16(0xa503, priv->freqcr + 12);
+		iowrite16(new_freqcr, priv->freqcr);
+		spin_unlock_irqrestore(&clklock, flags);
+	} else {
+		/* PLL state no change */
+		/* not required stable time */
+		iowrite16(new_freqcr, priv->freqcr);
+	}
+	return 0;
+}
+
+static int cpg_enable(struct clk_hw *hw)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	u8 stbcr;
+
+	switch ((cpg_clock->index >> 3) & 3) {
+	case 0:
+		/* STBCR */
+		stbcr = ioread8(priv->freqcr + 4);
+		stbcr &= ~(1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 4);
+		break;
+	case 1:
+		/* STBCR2 */
+		stbcr = ioread8(priv->freqcr + 16);
+		stbcr &= ~(1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 16);
+		break;
+	case 2:
+		/* CLKSTPCLR00 */
+		iowrite32(1 << (cpg_clock->index - 16), priv->clkstp + 8);
+		break;
+	}
+	return 0;
+}
+
+static void cpg_disable(struct clk_hw *hw)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	u8 stbcr;
+
+	switch ((cpg_clock->index >> 3) & 3) {
+	case 0:
+		/* STBCR */
+		stbcr = ioread8(priv->freqcr + 4);
+		stbcr |= (1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 4);
+		break;
+	case 1:
+		/* STBCR2 */
+		stbcr = ioread8(priv->freqcr + 16);
+		stbcr |= (1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 16);
+		break;
+	case 2:
+		/* CLKSTP00 */
+		iowrite32(1 << (cpg_clock->index - 16), priv->clkstp);
+		break;
+	}
+}
+
+struct clk *sh7750_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+	struct priv *priv = data;
+	unsigned int idx = clkspec->args[0];
+
+	if (idx >= ARRAY_SIZE(clocknames)) {
+		pr_err("%s: invalid clock index %u\n", __func__, idx);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return priv->clks[idx];
+}
+
+static const struct clk_ops cpg_ops = {
+	.recalc_rate	= cpg_recalc_rate,
+	.round_rate	= cpg_round_rate,
+	.set_rate	= cpg_set_rate,
+	.enable		= cpg_enable,
+	.disable	= cpg_disable,
+};
+
+static struct clk *  __init sh7750_cpg_register(struct device_node *node,
+						const struct clockname *name,
+						const char *parent_name,
+						struct priv *priv)
+{
+	struct cpg_clock *cpg_clock;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	cpg_clock = kzalloc(sizeof(struct cpg_clock), GFP_KERNEL);
+	if (!cpg_clock) {
+		pr_err("%s: failed to alloc memory", name->name);
+		return NULL;
+	}
+
+	init.name = name->name;
+	init.ops = &cpg_ops;
+	init.flags = CLK_IS_BASIC;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	cpg_clock->hw.init = &init;
+	cpg_clock->priv = priv;
+	cpg_clock->index = name->index;
+
+	clk = clk_register(NULL, &cpg_clock->hw);
+	if (IS_ERR(clk)) {
+		pr_err("%s: failed to register %s pll clock (%ld)\n",
+		       __func__, name->name, PTR_ERR(clk));
+		return NULL;
+	}
+	return clk;
+}
+
+static void __init sh7750_cpg_setup(struct device_node *node)
+{
+	const char *parent_name;
+	struct priv *priv;
+	int i;
+
+	priv = kzalloc(sizeof(struct priv), GFP_KERNEL);
+	if (priv = NULL) {
+		pr_err("%s: failed to alloc memory",
+		       node->name);
+		return;
+	}
+	priv->clks = kmalloc_array(sizeof(priv->clks), ARRAY_SIZE(clocknames),
+				   GFP_KERNEL);
+	if (priv->clks = NULL) {
+		pr_err("%s: failed to alloc memory",
+		       node->name);
+		kfree(priv);
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(clocknames); i++)
+		priv->clks[i] = ERR_PTR(-ENOENT);
+
+	priv->freqcr = of_iomap(node, 0);
+	if (priv->freqcr = NULL) {
+		pr_err("%s: failed to map frequenct control register",
+		       node->name);
+		goto free_clock;
+	}
+
+	/* Optional register */
+	priv->clkstp = of_iomap(node, 1);
+
+	of_property_read_u32_index(node, "renesas,mult", 0, &priv->mult);
+
+	parent_name = of_clk_get_parent_name(node, 0);
+
+	for (i = 0; i < ARRAY_SIZE(clocknames); i++) {
+		priv->clks[i] = sh7750_cpg_register(node, &clocknames[i],
+						    parent_name, priv);
+		if (priv->clks[i] = NULL)
+			goto unmap_reg;
+	}
+	of_clk_add_provider(node, sh7750_onecell_get, priv);
+	return;
+
+unmap_reg:
+	if (priv->clkstp)
+		iounmap(priv->clkstp);
+	iounmap(priv->freqcr);
+free_clock:
+	for (i = 0; i < ARRAY_SIZE(clocknames); i++)
+		if (priv->clks[i] != ERR_PTR(-ENOENT) && priv->clks[i])
+			clk_unregister(priv->clks[i]);
+	kfree(priv->clks);
+	kfree(priv);
+}
+
+CLK_OF_DECLARE(sh7750_cpg, "renesas,sh7750-cpg", sh7750_cpg_setup);
-- 
2.7.0


WARNING: multiple messages have this Message-ID (diff)
From: Yoshinori Sato <ysato@users.sourceforge.jp>
To: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
	linux-sh@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Subject: [PATCH v4 11/22] sh: SH7750/51 CPG Driver
Date: Wed, 29 Jun 2016 22:40:56 +0900	[thread overview]
Message-ID: <1467207667-15768-12-git-send-email-ysato@users.sourceforge.jp> (raw)
In-Reply-To: <1467207667-15768-1-git-send-email-ysato@users.sourceforge.jp>

Convert SH specific clock framework to CCF.

Changes v4
Add acked-by

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../bindings/clock/renesas,sh7750-cpg.txt          |  25 ++
 arch/sh/boards/Kconfig                             |   1 +
 arch/sh/kernel/cpu/Makefile                        |   8 +-
 arch/sh/kernel/cpu/clock.c                         |   6 +-
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   3 +-
 drivers/clk/sh/Kconfig                             |   2 +
 drivers/clk/sh/Makefile                            |   1 +
 drivers/clk/sh/clk-sh7750cpg.c                     | 344 +++++++++++++++++++++
 9 files changed, 387 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt
 create mode 100644 drivers/clk/sh/Kconfig
 create mode 100644 drivers/clk/sh/Makefile
 create mode 100644 drivers/clk/sh/clk-sh7750cpg.c

diff --git a/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt b/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt
new file mode 100644
index 0000000..e763e2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.txt
@@ -0,0 +1,25 @@
+* Renesas SH7750/51 CPG
+
+Required Properties:
+
+  - compatible: Must be "renesas,sh7750-cpg"
+
+  - clocks: Reference to the parent clocks (xtal or external)
+
+  - #clock-cells: Must be 1
+
+  - reg: Base address and length of the FREQCR
+         and Base address and length of the CLKSTP00 (optional)
+
+  - renesas,mult: PLL1 multiply rate
+
+Example
+-------
+
+        cpg: cpg@ffc00000 {
+                compatible = "renesas,sh7750-cpg";
+                clocks = <&oclk>;
+                #clock-cells = <1>;
+                renesas,mult = <12>;
+                reg = <0xffc00000 32>, <0xfe0a0000 16>;
+        };
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 9e4ccd0..b6ff9df 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -13,6 +13,7 @@ config SH_DEVICE_TREE
 	select CLKSRC_OF
 	select GENERIC_CALIBRATE_DELAY
 	select GENERIC_IOMAP
+	select COMMON_CLK
 	help
 	  Select Board Described by Device Tree to build a kernel that
 	  does not hard-code any board-specific knowledge but instead uses
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index accc7ca..22ad0ee 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -16,6 +16,10 @@ obj-$(CONFIG_ARCH_SHMOBILE)	+= shmobile/
 # Common interfaces.
 
 obj-$(CONFIG_SH_ADC)		+= adc.o
+ifndef CONFIG_COMMON_CLK
 obj-$(CONFIG_SH_CLK_CPG_LEGACY)	+= clock-cpg.o
-
-obj-y	+= irq/ init.o clock.o fpu.o pfc.o proc.o
+endif
+ifndef CONFIG_GENERIC_IRQ_CHIP
+obj-y	+= irq/
+endif
+obj-y	+= init.o clock.o fpu.o pfc.o proc.o
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
index 4187cf4..8e66e23 100644
--- a/arch/sh/kernel/cpu/clock.c
+++ b/arch/sh/kernel/cpu/clock.c
@@ -22,13 +22,15 @@
 
 int __init clk_init(void)
 {
-	int ret;
+	int ret = 0;
 
+#ifndef CONFIG_COMMON_CLK
 	ret = arch_clk_init();
 	if (unlikely(ret)) {
 		pr_err("%s: CPU clock registration failed.\n", __func__);
 		return ret;
 	}
+#endif
 
 	if (sh_mv.mv_clk_init) {
 		ret = sh_mv.mv_clk_init();
@@ -39,11 +41,13 @@ int __init clk_init(void)
 		}
 	}
 
+#ifndef CONFIG_COMMON_CLK
 	/* Kick the child clocks.. */
 	recalculate_root_clocks();
 
 	/* Enable the necessary init clocks */
 	clk_enable_init_clocks();
+#endif
 
 	return ret;
 }
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 98efbfc..60d19d0 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -213,6 +213,7 @@ source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/qcom/Kconfig"
 source "drivers/clk/renesas/Kconfig"
 source "drivers/clk/samsung/Kconfig"
+source "drivers/clk/sh/Kconfig"
 source "drivers/clk/tegra/Kconfig"
 source "drivers/clk/ti/Kconfig"
 
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index dcc5e69..c4bfbb9 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -86,5 +86,6 @@ obj-$(CONFIG_COMMON_CLK_VERSATILE)	+= versatile/
 obj-$(CONFIG_X86)			+= x86/
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
-obj-$(CONFIG_H8300)		+= h8300/
+obj-$(CONFIG_H8300)			+= h8300/
 obj-$(CONFIG_ARC_PLAT_AXS10X)		+= axs10x/
+obj-$(CONFIG_SUPERH)			+= sh/
diff --git a/drivers/clk/sh/Kconfig b/drivers/clk/sh/Kconfig
new file mode 100644
index 0000000..2090415
--- /dev/null
+++ b/drivers/clk/sh/Kconfig
@@ -0,0 +1,2 @@
+config COMMON_CLK_SH7750
+	bool "CPG driver for SH7750/SH7751"
diff --git a/drivers/clk/sh/Makefile b/drivers/clk/sh/Makefile
new file mode 100644
index 0000000..7ce4da3
--- /dev/null
+++ b/drivers/clk/sh/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_COMMON_CLK_SH7750) += clk-sh7750cpg.o
diff --git a/drivers/clk/sh/clk-sh7750cpg.c b/drivers/clk/sh/clk-sh7750cpg.c
new file mode 100644
index 0000000..a538be4
--- /dev/null
+++ b/drivers/clk/sh/clk-sh7750cpg.c
@@ -0,0 +1,344 @@
+/*
+ * Renesas SH7750/51 clock driver
+ *
+ * Copyright 2016 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/* Available FREQCR settings */
+static const int freqcr_table[] = {
+	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0408,
+	0x0409, 0x040a, 0x040b, 0x040c, 0x0411, 0x0412,
+	0x0413, 0x0414, 0x041a, 0x041b, 0x041c, 0x0423,
+	0x0424, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c,
+	0x0451, 0x0452, 0x0453, 0x0454, 0x045a, 0x045b,
+	0x045c, 0x0463, 0x0464, 0x0491, 0x0492, 0x0493,
+	0x0494, 0x049a, 0x049b, 0x049c, 0x04a3, 0x04a4,
+	0x04da, 0x04db, 0x04dc, 0x04e3, 0x04e4, 0x0523,
+	0x0524, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004,
+	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x0011,
+	0x0012, 0x0013, 0x0014, 0x0019, 0x001a, 0x001b,
+	0x001c, 0x0023, 0x0024, 0x0048, 0x0049, 0x004a,
+	0x004b, 0x004c, 0x0051, 0x0052, 0x0053, 0x0054,
+	0x0059, 0x005a, 0x005b, 0x005c, 0x0063, 0x0064,
+	0x0091, 0x0092, 0x0093, 0x0094, 0x0099, 0x009a,
+	0x009b, 0x009c, 0x00a3, 0x00a4, 0x00d1, 0x00d2,
+	0x00d3, 0x00d4, 0x00d9, 0x00da, 0x00db, 0x00dc,
+	0x00e3, 0x00e4, 0x0123, 0x0124, 0x0163, 0x0164,
+};
+
+struct priv {
+	void __iomem *freqcr;
+	void __iomem *clkstp;
+	int mult;
+	struct clk **clks;
+};
+
+struct cpg_clock {
+	struct clk_hw hw;
+	struct priv *priv;
+	int index;
+};
+
+struct clockname {
+	char *name;
+	int index;
+};
+
+static const struct clockname clocknames[] __initconst = {
+	{ .name = "sci", .index = 0 },
+	{ .name = "rtc", .index = 1 },
+	{ .name = "tmu0", .index = 2 },
+	{ .name = "tmu1", .index = 2 },
+	{ .name = "tmu2", .index = 2 },
+	{ .name = "scif", .index = 3 },
+	{ .name = "dmac", .index = 4 },
+	{ .name = "ubc", .index = 8 },
+	{ .name = "sq", .index = 9 },
+	{ .name = "intc", .index = 16 },
+	{ .name = "tmu3", .index = 17 },
+	{ .name = "tmu4", .index = 17 },
+	{ .name = "pcic", .index = 18 },
+	{ .name = "core", .index = 128 },
+};
+
+static const int iclk_div[] = {1, 2, 3, 4, 6, 8, 0, 0};
+static const int pclk_div[] = {2, 3, 4, 6, 8, 0, 0, 0};
+
+static DEFINE_SPINLOCK(clklock);
+
+#define to_cpg_clock(_hw) container_of(_hw, struct cpg_clock, hw)
+
+static unsigned long pllout(u16 freqcr, unsigned long parent_rate, int mult)
+{
+	if ((freqcr >> 10) & 1)
+		return parent_rate * mult;
+	else
+		return parent_rate;
+}
+
+static unsigned long cpg_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	unsigned long div;
+	u16 freqcr;
+
+	freqcr = ioread16(priv->freqcr);
+	if (cpg_clock->index == 128)
+		div = iclk_div[(freqcr >> 6) & 7];
+	else
+		div = pclk_div[freqcr & 7];
+	return pllout(freqcr, parent_rate, priv->mult) / div;
+}
+
+static u16 get_best_freqcr(unsigned long rate,
+			   unsigned long pclk_rate,
+			   unsigned long parent, int mult)
+{
+	int i;
+	int div;
+	u16 freqcr;
+
+	for (i = 0; i < ARRAY_SIZE(freqcr_table); i++) {
+		freqcr = freqcr_table[i];
+		if (pllout(freqcr, parent, mult) / pclk_div[freqcr & 7]
+		    != pclk_rate)
+			continue;
+		div = iclk_div[(freqcr >> 6) & 7];
+		if (pllout(freqcr, parent, mult) / div < rate)
+			return freqcr;
+	}
+	return 0;
+}
+
+static long cpg_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	unsigned long pclk_rate;
+	u16 freqcr;
+	int div;
+
+	freqcr = ioread16(priv->freqcr);
+	pclk_rate = pllout(freqcr, *prate, priv->mult) / pclk_div[freqcr & 7];
+
+	freqcr = get_best_freqcr(rate, pclk_rate, *prate, priv->mult);
+	if (cpg_clock->index == 128)
+		div = iclk_div[(freqcr >> 6) & 7];
+	else
+		div = pclk_div[freqcr & 7];
+
+	return pllout(freqcr, *prate, priv->mult) / div;
+}
+
+static int cpg_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	unsigned long flags;
+	unsigned long pclk_rate;
+	u16 freqcr, new_freqcr;
+
+	if (cpg_clock->index != 128)
+		return 0;
+
+	freqcr = ioread16(priv->freqcr);
+	pclk_rate = pllout(freqcr, parent_rate, priv->mult) /
+		pclk_div[freqcr & 7];
+
+	new_freqcr = get_best_freqcr(rate, pclk_rate, parent_rate, priv->mult);
+
+	if ((freqcr & 0x0200) == 0 && (new_freqcr & 0x0200) != 0) {
+		/* PLL on */
+		/* required stable time */
+		spin_lock_irqsave(&clklock, flags);
+		iowrite16(0x5a00, priv->freqcr + 8);
+		iowrite16(0xa503, priv->freqcr + 12);
+		iowrite16(new_freqcr, priv->freqcr);
+		spin_unlock_irqrestore(&clklock, flags);
+	} else {
+		/* PLL state no change */
+		/* not required stable time */
+		iowrite16(new_freqcr, priv->freqcr);
+	}
+	return 0;
+}
+
+static int cpg_enable(struct clk_hw *hw)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	u8 stbcr;
+
+	switch ((cpg_clock->index >> 3) & 3) {
+	case 0:
+		/* STBCR */
+		stbcr = ioread8(priv->freqcr + 4);
+		stbcr &= ~(1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 4);
+		break;
+	case 1:
+		/* STBCR2 */
+		stbcr = ioread8(priv->freqcr + 16);
+		stbcr &= ~(1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 16);
+		break;
+	case 2:
+		/* CLKSTPCLR00 */
+		iowrite32(1 << (cpg_clock->index - 16), priv->clkstp + 8);
+		break;
+	}
+	return 0;
+}
+
+static void cpg_disable(struct clk_hw *hw)
+{
+	struct cpg_clock *cpg_clock = to_cpg_clock(hw);
+	struct priv *priv = cpg_clock->priv;
+	u8 stbcr;
+
+	switch ((cpg_clock->index >> 3) & 3) {
+	case 0:
+		/* STBCR */
+		stbcr = ioread8(priv->freqcr + 4);
+		stbcr |= (1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 4);
+		break;
+	case 1:
+		/* STBCR2 */
+		stbcr = ioread8(priv->freqcr + 16);
+		stbcr |= (1 << (cpg_clock->index & 7));
+		iowrite8(stbcr, priv->freqcr + 16);
+		break;
+	case 2:
+		/* CLKSTP00 */
+		iowrite32(1 << (cpg_clock->index - 16), priv->clkstp);
+		break;
+	}
+}
+
+struct clk *sh7750_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+	struct priv *priv = data;
+	unsigned int idx = clkspec->args[0];
+
+	if (idx >= ARRAY_SIZE(clocknames)) {
+		pr_err("%s: invalid clock index %u\n", __func__, idx);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return priv->clks[idx];
+}
+
+static const struct clk_ops cpg_ops = {
+	.recalc_rate	= cpg_recalc_rate,
+	.round_rate	= cpg_round_rate,
+	.set_rate	= cpg_set_rate,
+	.enable		= cpg_enable,
+	.disable	= cpg_disable,
+};
+
+static struct clk *  __init sh7750_cpg_register(struct device_node *node,
+						const struct clockname *name,
+						const char *parent_name,
+						struct priv *priv)
+{
+	struct cpg_clock *cpg_clock;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	cpg_clock = kzalloc(sizeof(struct cpg_clock), GFP_KERNEL);
+	if (!cpg_clock) {
+		pr_err("%s: failed to alloc memory", name->name);
+		return NULL;
+	}
+
+	init.name = name->name;
+	init.ops = &cpg_ops;
+	init.flags = CLK_IS_BASIC;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	cpg_clock->hw.init = &init;
+	cpg_clock->priv = priv;
+	cpg_clock->index = name->index;
+
+	clk = clk_register(NULL, &cpg_clock->hw);
+	if (IS_ERR(clk)) {
+		pr_err("%s: failed to register %s pll clock (%ld)\n",
+		       __func__, name->name, PTR_ERR(clk));
+		return NULL;
+	}
+	return clk;
+}
+
+static void __init sh7750_cpg_setup(struct device_node *node)
+{
+	const char *parent_name;
+	struct priv *priv;
+	int i;
+
+	priv = kzalloc(sizeof(struct priv), GFP_KERNEL);
+	if (priv == NULL) {
+		pr_err("%s: failed to alloc memory",
+		       node->name);
+		return;
+	}
+	priv->clks = kmalloc_array(sizeof(priv->clks), ARRAY_SIZE(clocknames),
+				   GFP_KERNEL);
+	if (priv->clks == NULL) {
+		pr_err("%s: failed to alloc memory",
+		       node->name);
+		kfree(priv);
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(clocknames); i++)
+		priv->clks[i] = ERR_PTR(-ENOENT);
+
+	priv->freqcr = of_iomap(node, 0);
+	if (priv->freqcr == NULL) {
+		pr_err("%s: failed to map frequenct control register",
+		       node->name);
+		goto free_clock;
+	}
+
+	/* Optional register */
+	priv->clkstp = of_iomap(node, 1);
+
+	of_property_read_u32_index(node, "renesas,mult", 0, &priv->mult);
+
+	parent_name = of_clk_get_parent_name(node, 0);
+
+	for (i = 0; i < ARRAY_SIZE(clocknames); i++) {
+		priv->clks[i] = sh7750_cpg_register(node, &clocknames[i],
+						    parent_name, priv);
+		if (priv->clks[i] == NULL)
+			goto unmap_reg;
+	}
+	of_clk_add_provider(node, sh7750_onecell_get, priv);
+	return;
+
+unmap_reg:
+	if (priv->clkstp)
+		iounmap(priv->clkstp);
+	iounmap(priv->freqcr);
+free_clock:
+	for (i = 0; i < ARRAY_SIZE(clocknames); i++)
+		if (priv->clks[i] != ERR_PTR(-ENOENT) && priv->clks[i])
+			clk_unregister(priv->clks[i]);
+	kfree(priv->clks);
+	kfree(priv);
+}
+
+CLK_OF_DECLARE(sh7750_cpg, "renesas,sh7750-cpg", sh7750_cpg_setup);
-- 
2.7.0

  parent reply	other threads:[~2016-06-29 13:40 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-29 13:40 [PATCH v4 00/22] sh: LANDISK and R2Dplus convert to device tree Yoshinori Sato
2016-06-29 13:40 ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 01/22] sh: Add sh-specific early_init_dt_reserve_memory_arch Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 02/22] sh: More early unflatten device tree Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 03/22] sh: set preset_lpj Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 04/22] sh: Use P1SEGADDR Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 05/22] sh: command line passing chosen/bootargs in devicetree Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 06/22] sh: FDT address save before bank change Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 07/22] sh: Passing FDT address on zImage Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 08/22] sh: Disable board specific code on device tree mode Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 09/22] sh: Use GENERIC_IOMAP " Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 10/22] sh: Add board specific initialize of of-generic Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` Yoshinori Sato [this message]
2016-06-29 13:40   ` [PATCH v4 11/22] sh: SH7750/51 CPG Driver Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 12/22] sh: Add PCI host bridge driver for SH7751 Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 14:57   ` Arnd Bergmann
2016-06-29 14:57     ` Arnd Bergmann
2016-06-30 14:52     ` Yoshinori Sato
2016-06-30 14:52       ` Yoshinori Sato
2016-07-01  1:43   ` Rob Herring
2016-07-01  1:43     ` Rob Herring
2016-06-29 13:40 ` [PATCH v4 13/22] sh: irqchip: SH7751 IRQCHIP Driver Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-06-29 13:40 ` [PATCH v4 14/22] sh: SH7751 core dtsi Yoshinori Sato
2016-06-29 13:40   ` Yoshinori Sato
2016-07-01  8:57   ` Geert Uytterhoeven
2016-07-01  8:57     ` Geert Uytterhoeven
2016-07-03 11:34     ` Yoshinori Sato
2016-07-03 11:34       ` Yoshinori Sato
2016-07-04  7:15   ` Geert Uytterhoeven
2016-07-04  7:15     ` Geert Uytterhoeven
     [not found]     ` <CAMuHMdXPfP+A4PFy81bRoq4Va=5JpgCz2M3D6icWf0zCGTw8pA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-07-04 16:12       ` Yoshinori Sato
2016-07-04 16:12         ` Yoshinori Sato
2016-07-04 16:12         ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 15/22] sh: Move common PCI stuff to arch/sh/kernel Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 16/22] pci: pci_config_window move to linux/pci.h Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 17/22] pci: PCI_HOST_GENERIC enable for SH Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 18/22] sh: Add separate DTB build rule Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 19/22] sh: IO-DATA HDL-U (a,k.a landisk) IRQCHIP driver Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 13:43   ` John Paul Adrian Glaubitz
2016-06-29 13:43     ` John Paul Adrian Glaubitz
2016-07-01  1:48   ` Rob Herring
2016-07-01  1:48     ` Rob Herring
2016-07-03 11:36     ` Yoshinori Sato
2016-07-03 11:36       ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 20/22] sh: IO-DATA HDL-U (a,k.a landisk) DeviceTree Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 21/22] sh: Renesas RTS7751R2Dplus (a,k.a R2Dplus) IRQCHIP Driver Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-07-01  1:53   ` Rob Herring
2016-07-01  1:53     ` Rob Herring
2016-07-03 11:35     ` Yoshinori Sato
2016-07-03 11:35       ` Yoshinori Sato
2016-06-29 13:41 ` [PATCH v4 22/22] sh: Renesas RTS7751R2Dplus (a,k.a R2Dplus) DeviceTree Yoshinori Sato
2016-06-29 13:41   ` Yoshinori Sato
2016-06-29 14:16   ` Sergei Shtylyov
2016-06-29 14:16     ` Sergei Shtylyov
2016-06-30 14:47     ` Yoshinori Sato
2016-06-30 14:47       ` Yoshinori Sato

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1467207667-15768-12-git-send-email-ysato@users.sourceforge.jp \
    --to=ysato@users.sourceforge.jp \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sh@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.