All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-02-21 13:11 ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-02-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-snps-arc, devicetree, Vlad Zakharov,
	Jose Abreu, Michael Turquette, Stephen Boyd, Mark Rutland,
	Rob Herring

AXS10X boards manages it's clocks using various PLLs. These PLL has same
dividers and corresponding control registers mapped to different addresses.
So we add one common driver for such PLLs.

Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
ODIV. Output clock value is managed using these dividers.

We add pre-defined tables with supported rate values and appropriate
configurations of IDIV, FBDIV and ODIV for each value.

As of today we add support for PLLs that generate clock for the
following devices:
 * ARC core on AXC CPU tiles.
 * ARC PGU on ARC SDP Mainboard.
and more to come later.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Mark Rutland <mark.rutland@arm.com>
---
Cc: Rob Herring <robh@kernel.org>
Changes v1..v2
 - Replace '_' with '-' in device tree nodes

 .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
 MAINTAINERS                                        |   6 +
 drivers/clk/axs10x/Makefile                        |   1 +
 drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
 4 files changed, 419 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
 create mode 100644 drivers/clk/axs10x/pll_clock.c

diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
new file mode 100644
index 0000000..5706246
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
@@ -0,0 +1,28 @@
+Binding for the AXS10X Generic PLL clock
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: should be "snps,axs10x-<name>-pll-clock"
+  "snps,axs10x-arc-pll-clock"
+  "snps,axs10x-pgu-pll-clock"
+- reg: should always contain 2 pairs address - length: first for PLL config
+registers and second for corresponding LOCK CGU register.
+- clocks: shall be the input parent clock phandle for the PLL.
+- #clock-cells: from common clock binding; Should always be set to 0.
+
+Example:
+	input-clk: input-clk {
+		clock-frequency = <33333333>;
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+	};
+
+	core-clk: core-clk@80 {
+		compatible = "snps,axs10x-arc-pll-clock";
+		reg = <0x80 0x10 0x100 0x10>;
+		#clock-cells = <0>;
+		clocks = <&input-clk>;
+	};
diff --git a/MAINTAINERS b/MAINTAINERS
index 3960e7f..5805833 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
 F:	arch/arc/boot/dts/ax*
 F:	Documentation/devicetree/bindings/arc/axs10*
 
+SYNOPSYS ARC SDP clock driver
+M:	Vlad Zakharov <vzakhar@synopsys.com>
+S:	Supported
+F:	drivers/clk/axs10x/*
+F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
+
 SYSTEM CONFIGURATION (SYSCON)
 M:	Lee Jones <lee.jones@linaro.org>
 M:	Arnd Bergmann <arnd@arndb.de>
diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
index 01996b8..d747dea 100644
--- a/drivers/clk/axs10x/Makefile
+++ b/drivers/clk/axs10x/Makefile
@@ -1 +1,2 @@
 obj-y += i2s_pll_clock.o
+obj-y += pll_clock.o
diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
new file mode 100644
index 0000000..784a0a2
--- /dev/null
+++ b/drivers/clk/axs10x/pll_clock.c
@@ -0,0 +1,384 @@
+/*
+ * Synopsys AXS10X SDP Generic PLL clock driver
+ *
+ * Copyright (C) 2017 Synopsys
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+/* PLL registers addresses */
+#define PLL_REG_IDIV	0x0
+#define PLL_REG_FBDIV	0x4
+#define PLL_REG_ODIV	0x8
+
+/*
+ * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
+ *  ________________________________________________________________________
+ * |31                15|    14    |   13   |  12  |11         6|5         0|
+ * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
+ * |____________________|__________|________|______|____________|___________|
+ *
+ * Following macros detirmine the way of access to these registers
+ * They should be set up only using the macros.
+ * reg should be and uint32_t variable.
+ */
+
+#define PLL_REG_GET_LOW(reg)			\
+	(((reg) & (0x3F << 0)) >> 0)
+#define PLL_REG_GET_HIGH(reg)			\
+	(((reg) & (0x3F << 6)) >> 6)
+#define PLL_REG_GET_EDGE(reg)			\
+	(((reg) & (BIT(12))) ? 1 : 0)
+#define PLL_REG_GET_BYPASS(reg)			\
+	(((reg) & (BIT(13))) ? 1 : 0)
+#define PLL_REG_GET_NOUPD(reg)			\
+	(((reg) & (BIT(14))) ? 1 : 0)
+#define PLL_REG_GET_PAD(reg)			\
+	(((reg) & (0x1FFFF << 15)) >> 15)
+
+#define PLL_REG_SET_LOW(reg, value)		\
+	{ reg |= (((value) & 0x3F) << 0); }
+#define PLL_REG_SET_HIGH(reg, value)	\
+	{ reg |= (((value) & 0x3F) << 6); }
+#define PLL_REG_SET_EDGE(reg, value)	\
+	{ reg |= (((value) & 0x01) << 12); }
+#define PLL_REG_SET_BYPASS(reg, value)	\
+	{ reg |= (((value) & 0x01) << 13); }
+#define PLL_REG_SET_NOUPD(reg, value)	\
+	{ reg |= (((value) & 0x01) << 14); }
+#define PLL_REG_SET_PAD(reg, value)		\
+	{ reg |= (((value) & 0x1FFFF) << 15); }
+
+#define PLL_LOCK	0x1
+#define PLL_MAX_LOCK_TIME 100 /* 100 us */
+
+struct pll_cfg {
+	u32 rate;
+	u32 idiv;
+	u32 fbdiv;
+	u32 odiv;
+};
+
+struct pll_of_table {
+	unsigned long prate;
+	struct pll_cfg *pll_cfg_table;
+};
+
+struct pll_of_data {
+	struct pll_of_table *pll_table;
+};
+
+static struct pll_of_data pgu_pll_data = {
+	.pll_table = (struct pll_of_table []){
+		{
+			.prate = 27000000,
+			.pll_cfg_table = (struct pll_cfg []){
+				{ 25200000, 1, 84, 90 },
+				{ 50000000, 1, 100, 54 },
+				{ 74250000, 1, 44, 16 },
+				{ },
+			},
+		},
+		/* Used as list limiter */
+		{ },
+	},
+};
+
+static struct pll_of_data arc_pll_data = {
+	.pll_table = (struct pll_of_table []){
+		{
+			.prate = 33333333,
+			.pll_cfg_table = (struct pll_cfg []){
+				{ 33333333,  1, 1,  1 },
+				{ 50000000,  1, 30, 20 },
+				{ 75000000,  2, 45, 10 },
+				{ 90000000,  2, 54, 10 },
+				{ 100000000, 1, 30, 10 },
+				{ 125000000, 2, 45, 6 },
+				{ },
+			},
+		},
+		/* Used as list limiter */
+		{ },
+	},
+};
+
+struct pll_clk {
+	void __iomem *base;
+	void __iomem *lock;
+	const struct pll_of_data *pll_data;
+	struct clk_hw hw;
+	struct device *dev;
+};
+
+static inline void pll_write(struct pll_clk *clk, unsigned int reg,
+		unsigned int val)
+{
+	iowrite32(val, clk->base + reg);
+}
+
+static inline u32 pll_read(struct pll_clk *clk,
+		unsigned int reg)
+{
+	return ioread32(clk->base + reg);
+}
+
+static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct pll_clk, hw);
+}
+
+static inline u32 div_get_value(unsigned int reg)
+{
+	if (PLL_REG_GET_BYPASS(reg))
+		return 1;
+
+	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
+}
+
+static inline u32 encode_div(unsigned int id, int upd)
+{
+	uint32_t div = 0;
+
+	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
+	PLL_REG_SET_HIGH(div, id >> 1);
+	PLL_REG_SET_EDGE(div, id%2);
+	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
+	PLL_REG_SET_NOUPD(div, !upd);
+
+	return div;
+}
+
+static const struct pll_cfg *pll_get_cfg(unsigned long prate,
+		const struct pll_of_table *pll_table)
+{
+	int i;
+
+	for (i = 0; pll_table[i].prate != 0; i++)
+		if (pll_table[i].prate == prate)
+			return pll_table[i].pll_cfg_table;
+
+	return NULL;
+}
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	u64 rate;
+	u32 idiv, fbdiv, odiv;
+	struct pll_clk *clk = to_pll_clk(hw);
+
+	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
+	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
+	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
+
+	rate = (u64)parent_rate * fbdiv;
+	do_div(rate, idiv * odiv);
+
+	return (unsigned long)rate;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate)
+{
+	int i;
+	long best_rate;
+	struct pll_clk *clk = to_pll_clk(hw);
+	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
+			clk->pll_data->pll_table);
+
+	if (!pll_cfg) {
+		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
+		return -EINVAL;
+	}
+
+	if (pll_cfg[0].rate == 0)
+		return -EINVAL;
+
+	best_rate = pll_cfg[0].rate;
+
+	for (i = 1; pll_cfg[i].rate != 0; i++) {
+		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
+			best_rate = pll_cfg[i].rate;
+	}
+
+	return best_rate;
+}
+
+static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	int i;
+	struct pll_clk *clk = to_pll_clk(hw);
+	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
+			clk->pll_data->pll_table);
+
+	if (!pll_cfg) {
+		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
+		return -EINVAL;
+	}
+
+	for (i = 0; pll_cfg[i].rate != 0; i++) {
+		if (pll_cfg[i].rate == rate) {
+			pll_write(clk, PLL_REG_IDIV,
+					encode_div(pll_cfg[i].idiv, 0));
+			pll_write(clk, PLL_REG_FBDIV,
+					encode_div(pll_cfg[i].fbdiv, 0));
+			pll_write(clk, PLL_REG_ODIV,
+					encode_div(pll_cfg[i].odiv, 1));
+
+			/*
+			 * Wait until CGU relocks.
+			 * If after timeout CGU is unlocked yet return error
+			 */
+			udelay(PLL_MAX_LOCK_TIME);
+			if (ioread32(clk->lock) & PLL_LOCK)
+				return 0;
+			else
+				return -ETIMEDOUT;
+		}
+	}
+
+	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
+			parent_rate);
+	return -EINVAL;
+}
+
+static const struct clk_ops pll_ops = {
+	.recalc_rate = pll_recalc_rate,
+	.round_rate = pll_round_rate,
+	.set_rate = pll_set_rate,
+};
+
+static int pll_clk_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const char *parent_name;
+	struct clk *clk;
+	struct pll_clk *pll_clk;
+	struct resource *mem;
+	struct clk_init_data init = { };
+
+	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
+	if (!pll_clk)
+		return -ENOMEM;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pll_clk->base = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(pll_clk->base))
+		return PTR_ERR(pll_clk->base);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	pll_clk->lock = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(pll_clk->lock))
+		return PTR_ERR(pll_clk->base);
+
+	init.name = dev->of_node->name;
+	init.ops = &pll_ops;
+	parent_name = of_clk_get_parent_name(dev->of_node, 0);
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	pll_clk->hw.init = &init;
+	pll_clk->dev = dev;
+	pll_clk->pll_data = of_device_get_match_data(dev);
+
+	if (!pll_clk->pll_data) {
+		dev_err(dev, "No OF match data provided\n");
+			return -EINVAL;
+	}
+
+	clk = devm_clk_register(dev, &pll_clk->hw);
+	if (IS_ERR(clk)) {
+		dev_err(dev, "failed to register %s clock (%ld)\n",
+				init.name, PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
+}
+
+static int pll_clk_remove(struct platform_device *pdev)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+	return 0;
+}
+
+static void __init of_pll_clk_setup(struct device_node *node)
+{
+	const char *parent_name;
+	struct clk *clk;
+	struct pll_clk *pll_clk;
+	struct clk_init_data init = { };
+
+	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+	if (!pll_clk)
+		return;
+
+	pll_clk->base = of_iomap(node, 0);
+	if (!pll_clk->base) {
+		pr_err("failed to map pll div registers\n");
+		iounmap(pll_clk->base);
+		return;
+	}
+
+	pll_clk->lock = of_iomap(node, 1);
+	if (!pll_clk->lock) {
+		pr_err("failed to map pll lock register\n");
+		iounmap(pll_clk->lock);
+		return;
+	}
+
+	init.name = node->name;
+	init.ops = &pll_ops;
+	parent_name = of_clk_get_parent_name(node, 0);
+	init.parent_names = &parent_name;
+	init.num_parents = parent_name ? 1 : 0;
+	pll_clk->hw.init = &init;
+	pll_clk->pll_data = &arc_pll_data;
+
+	clk = clk_register(NULL, &pll_clk->hw);
+	if (IS_ERR(clk)) {
+		pr_err("failed to register %s clock (%ld)\n",
+				node->name, PTR_ERR(clk));
+		kfree(pll_clk);
+		return;
+	}
+
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
+
+static const struct of_device_id pll_clk_id[] = {
+	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
+	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pll_clk_id);
+
+static struct platform_driver pll_clk_driver = {
+	.driver = {
+		.name = "axs10x-pll-clock",
+		.of_match_table = pll_clk_id,
+	},
+	.probe = pll_clk_probe,
+	.remove = pll_clk_remove,
+};
+builtin_platform_driver(pll_clk_driver);
+
+MODULE_AUTHOR("Vlad Zakharov <vzakhar@synopsys.com>");
+MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-02-21 13:11 ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-02-21 13:11 UTC (permalink / raw)
  To: linux-snps-arc

AXS10X boards manages it's clocks using various PLLs. These PLL has same
dividers and corresponding control registers mapped to different addresses.
So we add one common driver for such PLLs.

Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
ODIV. Output clock value is managed using these dividers.

We add pre-defined tables with supported rate values and appropriate
configurations of IDIV, FBDIV and ODIV for each value.

As of today we add support for PLLs that generate clock for the
following devices:
 * ARC core on AXC CPU tiles.
 * ARC PGU on ARC SDP Mainboard.
and more to come later.

Acked-by: Rob Herring <robh at kernel.org>
Signed-off-by: Vlad Zakharov <vzakhar at synopsys.com>
Signed-off-by: Jose Abreu <joabreu at synopsys.com>
Cc: Michael Turquette <mturquette at baylibre.com>
Cc: Stephen Boyd <sboyd at codeaurora.org>
Cc: Mark Rutland <mark.rutland at arm.com>
---
Cc: Rob Herring <robh at kernel.org>
Changes v1..v2
 - Replace '_' with '-' in device tree nodes

 .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
 MAINTAINERS                                        |   6 +
 drivers/clk/axs10x/Makefile                        |   1 +
 drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
 4 files changed, 419 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
 create mode 100644 drivers/clk/axs10x/pll_clock.c

diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
new file mode 100644
index 0000000..5706246
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
@@ -0,0 +1,28 @@
+Binding for the AXS10X Generic PLL clock
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: should be "snps,axs10x-<name>-pll-clock"
+  "snps,axs10x-arc-pll-clock"
+  "snps,axs10x-pgu-pll-clock"
+- reg: should always contain 2 pairs address - length: first for PLL config
+registers and second for corresponding LOCK CGU register.
+- clocks: shall be the input parent clock phandle for the PLL.
+- #clock-cells: from common clock binding; Should always be set to 0.
+
+Example:
+	input-clk: input-clk {
+		clock-frequency = <33333333>;
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+	};
+
+	core-clk: core-clk at 80 {
+		compatible = "snps,axs10x-arc-pll-clock";
+		reg = <0x80 0x10 0x100 0x10>;
+		#clock-cells = <0>;
+		clocks = <&input-clk>;
+	};
diff --git a/MAINTAINERS b/MAINTAINERS
index 3960e7f..5805833 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
 F:	arch/arc/boot/dts/ax*
 F:	Documentation/devicetree/bindings/arc/axs10*
 
+SYNOPSYS ARC SDP clock driver
+M:	Vlad Zakharov <vzakhar at synopsys.com>
+S:	Supported
+F:	drivers/clk/axs10x/*
+F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
+
 SYSTEM CONFIGURATION (SYSCON)
 M:	Lee Jones <lee.jones at linaro.org>
 M:	Arnd Bergmann <arnd at arndb.de>
diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
index 01996b8..d747dea 100644
--- a/drivers/clk/axs10x/Makefile
+++ b/drivers/clk/axs10x/Makefile
@@ -1 +1,2 @@
 obj-y += i2s_pll_clock.o
+obj-y += pll_clock.o
diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
new file mode 100644
index 0000000..784a0a2
--- /dev/null
+++ b/drivers/clk/axs10x/pll_clock.c
@@ -0,0 +1,384 @@
+/*
+ * Synopsys AXS10X SDP Generic PLL clock driver
+ *
+ * Copyright (C) 2017 Synopsys
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+/* PLL registers addresses */
+#define PLL_REG_IDIV	0x0
+#define PLL_REG_FBDIV	0x4
+#define PLL_REG_ODIV	0x8
+
+/*
+ * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
+ *  ________________________________________________________________________
+ * |31                15|    14    |   13   |  12  |11         6|5         0|
+ * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
+ * |____________________|__________|________|______|____________|___________|
+ *
+ * Following macros detirmine the way of access to these registers
+ * They should be set up only using the macros.
+ * reg should be and uint32_t variable.
+ */
+
+#define PLL_REG_GET_LOW(reg)			\
+	(((reg) & (0x3F << 0)) >> 0)
+#define PLL_REG_GET_HIGH(reg)			\
+	(((reg) & (0x3F << 6)) >> 6)
+#define PLL_REG_GET_EDGE(reg)			\
+	(((reg) & (BIT(12))) ? 1 : 0)
+#define PLL_REG_GET_BYPASS(reg)			\
+	(((reg) & (BIT(13))) ? 1 : 0)
+#define PLL_REG_GET_NOUPD(reg)			\
+	(((reg) & (BIT(14))) ? 1 : 0)
+#define PLL_REG_GET_PAD(reg)			\
+	(((reg) & (0x1FFFF << 15)) >> 15)
+
+#define PLL_REG_SET_LOW(reg, value)		\
+	{ reg |= (((value) & 0x3F) << 0); }
+#define PLL_REG_SET_HIGH(reg, value)	\
+	{ reg |= (((value) & 0x3F) << 6); }
+#define PLL_REG_SET_EDGE(reg, value)	\
+	{ reg |= (((value) & 0x01) << 12); }
+#define PLL_REG_SET_BYPASS(reg, value)	\
+	{ reg |= (((value) & 0x01) << 13); }
+#define PLL_REG_SET_NOUPD(reg, value)	\
+	{ reg |= (((value) & 0x01) << 14); }
+#define PLL_REG_SET_PAD(reg, value)		\
+	{ reg |= (((value) & 0x1FFFF) << 15); }
+
+#define PLL_LOCK	0x1
+#define PLL_MAX_LOCK_TIME 100 /* 100 us */
+
+struct pll_cfg {
+	u32 rate;
+	u32 idiv;
+	u32 fbdiv;
+	u32 odiv;
+};
+
+struct pll_of_table {
+	unsigned long prate;
+	struct pll_cfg *pll_cfg_table;
+};
+
+struct pll_of_data {
+	struct pll_of_table *pll_table;
+};
+
+static struct pll_of_data pgu_pll_data = {
+	.pll_table = (struct pll_of_table []){
+		{
+			.prate = 27000000,
+			.pll_cfg_table = (struct pll_cfg []){
+				{ 25200000, 1, 84, 90 },
+				{ 50000000, 1, 100, 54 },
+				{ 74250000, 1, 44, 16 },
+				{ },
+			},
+		},
+		/* Used as list limiter */
+		{ },
+	},
+};
+
+static struct pll_of_data arc_pll_data = {
+	.pll_table = (struct pll_of_table []){
+		{
+			.prate = 33333333,
+			.pll_cfg_table = (struct pll_cfg []){
+				{ 33333333,  1, 1,  1 },
+				{ 50000000,  1, 30, 20 },
+				{ 75000000,  2, 45, 10 },
+				{ 90000000,  2, 54, 10 },
+				{ 100000000, 1, 30, 10 },
+				{ 125000000, 2, 45, 6 },
+				{ },
+			},
+		},
+		/* Used as list limiter */
+		{ },
+	},
+};
+
+struct pll_clk {
+	void __iomem *base;
+	void __iomem *lock;
+	const struct pll_of_data *pll_data;
+	struct clk_hw hw;
+	struct device *dev;
+};
+
+static inline void pll_write(struct pll_clk *clk, unsigned int reg,
+		unsigned int val)
+{
+	iowrite32(val, clk->base + reg);
+}
+
+static inline u32 pll_read(struct pll_clk *clk,
+		unsigned int reg)
+{
+	return ioread32(clk->base + reg);
+}
+
+static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct pll_clk, hw);
+}
+
+static inline u32 div_get_value(unsigned int reg)
+{
+	if (PLL_REG_GET_BYPASS(reg))
+		return 1;
+
+	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
+}
+
+static inline u32 encode_div(unsigned int id, int upd)
+{
+	uint32_t div = 0;
+
+	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
+	PLL_REG_SET_HIGH(div, id >> 1);
+	PLL_REG_SET_EDGE(div, id%2);
+	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
+	PLL_REG_SET_NOUPD(div, !upd);
+
+	return div;
+}
+
+static const struct pll_cfg *pll_get_cfg(unsigned long prate,
+		const struct pll_of_table *pll_table)
+{
+	int i;
+
+	for (i = 0; pll_table[i].prate != 0; i++)
+		if (pll_table[i].prate == prate)
+			return pll_table[i].pll_cfg_table;
+
+	return NULL;
+}
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	u64 rate;
+	u32 idiv, fbdiv, odiv;
+	struct pll_clk *clk = to_pll_clk(hw);
+
+	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
+	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
+	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
+
+	rate = (u64)parent_rate * fbdiv;
+	do_div(rate, idiv * odiv);
+
+	return (unsigned long)rate;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate)
+{
+	int i;
+	long best_rate;
+	struct pll_clk *clk = to_pll_clk(hw);
+	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
+			clk->pll_data->pll_table);
+
+	if (!pll_cfg) {
+		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
+		return -EINVAL;
+	}
+
+	if (pll_cfg[0].rate == 0)
+		return -EINVAL;
+
+	best_rate = pll_cfg[0].rate;
+
+	for (i = 1; pll_cfg[i].rate != 0; i++) {
+		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
+			best_rate = pll_cfg[i].rate;
+	}
+
+	return best_rate;
+}
+
+static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	int i;
+	struct pll_clk *clk = to_pll_clk(hw);
+	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
+			clk->pll_data->pll_table);
+
+	if (!pll_cfg) {
+		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
+		return -EINVAL;
+	}
+
+	for (i = 0; pll_cfg[i].rate != 0; i++) {
+		if (pll_cfg[i].rate == rate) {
+			pll_write(clk, PLL_REG_IDIV,
+					encode_div(pll_cfg[i].idiv, 0));
+			pll_write(clk, PLL_REG_FBDIV,
+					encode_div(pll_cfg[i].fbdiv, 0));
+			pll_write(clk, PLL_REG_ODIV,
+					encode_div(pll_cfg[i].odiv, 1));
+
+			/*
+			 * Wait until CGU relocks.
+			 * If after timeout CGU is unlocked yet return error
+			 */
+			udelay(PLL_MAX_LOCK_TIME);
+			if (ioread32(clk->lock) & PLL_LOCK)
+				return 0;
+			else
+				return -ETIMEDOUT;
+		}
+	}
+
+	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
+			parent_rate);
+	return -EINVAL;
+}
+
+static const struct clk_ops pll_ops = {
+	.recalc_rate = pll_recalc_rate,
+	.round_rate = pll_round_rate,
+	.set_rate = pll_set_rate,
+};
+
+static int pll_clk_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const char *parent_name;
+	struct clk *clk;
+	struct pll_clk *pll_clk;
+	struct resource *mem;
+	struct clk_init_data init = { };
+
+	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
+	if (!pll_clk)
+		return -ENOMEM;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pll_clk->base = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(pll_clk->base))
+		return PTR_ERR(pll_clk->base);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	pll_clk->lock = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(pll_clk->lock))
+		return PTR_ERR(pll_clk->base);
+
+	init.name = dev->of_node->name;
+	init.ops = &pll_ops;
+	parent_name = of_clk_get_parent_name(dev->of_node, 0);
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	pll_clk->hw.init = &init;
+	pll_clk->dev = dev;
+	pll_clk->pll_data = of_device_get_match_data(dev);
+
+	if (!pll_clk->pll_data) {
+		dev_err(dev, "No OF match data provided\n");
+			return -EINVAL;
+	}
+
+	clk = devm_clk_register(dev, &pll_clk->hw);
+	if (IS_ERR(clk)) {
+		dev_err(dev, "failed to register %s clock (%ld)\n",
+				init.name, PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
+}
+
+static int pll_clk_remove(struct platform_device *pdev)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+	return 0;
+}
+
+static void __init of_pll_clk_setup(struct device_node *node)
+{
+	const char *parent_name;
+	struct clk *clk;
+	struct pll_clk *pll_clk;
+	struct clk_init_data init = { };
+
+	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+	if (!pll_clk)
+		return;
+
+	pll_clk->base = of_iomap(node, 0);
+	if (!pll_clk->base) {
+		pr_err("failed to map pll div registers\n");
+		iounmap(pll_clk->base);
+		return;
+	}
+
+	pll_clk->lock = of_iomap(node, 1);
+	if (!pll_clk->lock) {
+		pr_err("failed to map pll lock register\n");
+		iounmap(pll_clk->lock);
+		return;
+	}
+
+	init.name = node->name;
+	init.ops = &pll_ops;
+	parent_name = of_clk_get_parent_name(node, 0);
+	init.parent_names = &parent_name;
+	init.num_parents = parent_name ? 1 : 0;
+	pll_clk->hw.init = &init;
+	pll_clk->pll_data = &arc_pll_data;
+
+	clk = clk_register(NULL, &pll_clk->hw);
+	if (IS_ERR(clk)) {
+		pr_err("failed to register %s clock (%ld)\n",
+				node->name, PTR_ERR(clk));
+		kfree(pll_clk);
+		return;
+	}
+
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
+
+static const struct of_device_id pll_clk_id[] = {
+	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
+	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pll_clk_id);
+
+static struct platform_driver pll_clk_driver = {
+	.driver = {
+		.name = "axs10x-pll-clock",
+		.of_match_table = pll_clk_id,
+	},
+	.probe = pll_clk_probe,
+	.remove = pll_clk_remove,
+};
+builtin_platform_driver(pll_clk_driver);
+
+MODULE_AUTHOR("Vlad Zakharov <vzakhar at synopsys.com>");
+MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 13:18   ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-03 13:18 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: linux-kernel, mturquette, Jose Abreu, devicetree, linux-snps-arc,
	mark.rutland, robh, linux-clk, sboyd

Hi Michael, Stephen,

On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> AXS10X boards manages it's clocks using various PLLs. These PLL has same
> dividers and corresponding control registers mapped to different addresses.
> So we add one common driver for such PLLs.
> 
> Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> ODIV. Output clock value is managed using these dividers.
> 
> We add pre-defined tables with supported rate values and appropriate
> configurations of IDIV, FBDIV and ODIV for each value.
> 
> As of today we add support for PLLs that generate clock for the
> following devices:
>  * ARC core on AXC CPU tiles.
>  * ARC PGU on ARC SDP Mainboard.
> and more to come later.
> 
> Acked-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> ---
> Cc: Rob Herring <robh@kernel.org>
> Changes v1..v2
>  - Replace '_' with '-' in device tree nodes
> 
>  .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
>  MAINTAINERS                                        |   6 +
>  drivers/clk/axs10x/Makefile                        |   1 +
>  drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
>  4 files changed, 419 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
>  create mode 100644 drivers/clk/axs10x/pll_clock.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk@80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;
> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver
> +M:	Vlad Zakharov <vzakhar@synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones@linaro.org>
>  M:	Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers
> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.
> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;
> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};
> +
> +static struct pll_of_data pgu_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)
> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;
> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;
> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);
> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> +
> +static const struct of_device_id pll_clk_id[] = {
> +	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
> +	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pll_clk_id);
> +
> +static struct platform_driver pll_clk_driver = {
> +	.driver = {
> +		.name = "axs10x-pll-clock",
> +		.of_match_table = pll_clk_id,
> +	},
> +	.probe = pll_clk_probe,
> +	.remove = pll_clk_remove,
> +};
> +builtin_platform_driver(pll_clk_driver);
> +
> +MODULE_AUTHOR("Vlad Zakharov <vzakhar@synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
> +MODULE_LICENSE("GPL v2");

Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.

Thanks!

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 13:18   ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-03 13:18 UTC (permalink / raw)
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, Jose Abreu,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-snps-arc-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	mark.rutland-5wv7dgnIgG8, robh-DgEjT+Ai2ygdnm+yROfE0A,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, sboyd-sgV2jX0FEOL9JmXXK+q4OQ

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 15218 bytes --]

Hi Michael, Stephen,

On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> AXS10X boards manages it's clocks using various PLLs. These PLL has same
> dividers and corresponding control registers mapped to different addresses.
> So we add one common driver for such PLLs.
> 
> Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> ODIV. Output clock value is managed using these dividers.
> 
> We add pre-defined tables with supported rate values and appropriate
> configurations of IDIV, FBDIV and ODIV for each value.
> 
> As of today we add support for PLLs that generate clock for the
> following devices:
>  * ARC core on AXC CPU tiles.
>  * ARC PGU on ARC SDP Mainboard.
> and more to come later.
> 
> Acked-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> ---
> Cc: Rob Herring <robh@kernel.org>
> Changes v1..v2
>  - Replace '_' with '-' in device tree nodes
> 
>  .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
>  MAINTAINERS                                        |   6 +
>  drivers/clk/axs10x/Makefile                        |   1 +
>  drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
>  4 files changed, 419 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
>  create mode 100644 drivers/clk/axs10x/pll_clock.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk@80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;
> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver
> +M:	Vlad Zakharov <vzakhar@synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones@linaro.org>
>  M:	Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers
> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.
> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;
> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};
> +
> +static struct pll_of_data pgu_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)
> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;
> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;
> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);
> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> +
> +static const struct of_device_id pll_clk_id[] = {
> +	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
> +	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pll_clk_id);
> +
> +static struct platform_driver pll_clk_driver = {
> +	.driver = {
> +		.name = "axs10x-pll-clock",
> +		.of_match_table = pll_clk_id,
> +	},
> +	.probe = pll_clk_probe,
> +	.remove = pll_clk_remove,
> +};
> +builtin_platform_driver(pll_clk_driver);
> +
> +MODULE_AUTHOR("Vlad Zakharov <vzakhar@synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
> +MODULE_LICENSE("GPL v2");

Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.

Thanks!

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>N‹§²æìr¸›yúèšØb²X¬¶Ç§vØ^–)Þº{.nÇ+‰·zøœzÚÞz)í…æèw*\x1fjg¬±¨\x1e¶‰šŽŠÝ¢j.ïÛ°\½½MŽúgjÌæa×\x02››–' ™©Þ¢¸\f¢·¦j:+v‰¨ŠwèjØm¶Ÿÿ¾\a«‘êçzZ+ƒùšŽŠÝ¢j"ú!¶i

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 13:18   ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-03 13:18 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: linux-kernel, mturquette, Jose Abreu, devicetree, linux-snps-arc,
	mark.rutland, robh, linux-clk, sboyd

SGkgTWljaGFlbCwgU3RlcGhlbiwNCg0KT24gVHVlLCAyMDE3LTAyLTIxIGF0IDE2OjExICswMzAw
LCBWbGFkIFpha2hhcm92IHdyb3RlOg0KPiBBWFMxMFggYm9hcmRzIG1hbmFnZXMgaXQncyBjbG9j
a3MgdXNpbmcgdmFyaW91cyBQTExzLiBUaGVzZSBQTEwgaGFzIHNhbWUNCj4gZGl2aWRlcnMgYW5k
IGNvcnJlc3BvbmRpbmcgY29udHJvbCByZWdpc3RlcnMgbWFwcGVkIHRvIGRpZmZlcmVudCBhZGRy
ZXNzZXMuDQo+IFNvIHdlIGFkZCBvbmUgY29tbW9uIGRyaXZlciBmb3Igc3VjaCBQTExzLg0KPiAN
Cj4gRWFjaCBQTEwgb24gQVhTMTBYIGJvYXJkIGNvbnNpc3Qgb2YgdGhyZWUgZGl2aWRlcnM6IElE
SVYsIEZCRElWIGFuZA0KPiBPRElWLiBPdXRwdXQgY2xvY2sgdmFsdWUgaXMgbWFuYWdlZCB1c2lu
ZyB0aGVzZSBkaXZpZGVycy4NCj4gDQo+IFdlIGFkZCBwcmUtZGVmaW5lZCB0YWJsZXMgd2l0aCBz
dXBwb3J0ZWQgcmF0ZSB2YWx1ZXMgYW5kIGFwcHJvcHJpYXRlDQo+IGNvbmZpZ3VyYXRpb25zIG9m
IElESVYsIEZCRElWIGFuZCBPRElWIGZvciBlYWNoIHZhbHVlLg0KPiANCj4gQXMgb2YgdG9kYXkg
d2UgYWRkIHN1cHBvcnQgZm9yIFBMTHMgdGhhdCBnZW5lcmF0ZSBjbG9jayBmb3IgdGhlDQo+IGZv
bGxvd2luZyBkZXZpY2VzOg0KPiDCoCogQVJDIGNvcmUgb24gQVhDIENQVSB0aWxlcy4NCj4gwqAq
IEFSQyBQR1Ugb24gQVJDIFNEUCBNYWluYm9hcmQuDQo+IGFuZCBtb3JlIHRvIGNvbWUgbGF0ZXIu
DQo+IA0KPiBBY2tlZC1ieTogUm9iIEhlcnJpbmcgPHJvYmhAa2VybmVsLm9yZz4NCj4gU2lnbmVk
LW9mZi1ieTogVmxhZCBaYWtoYXJvdiA8dnpha2hhckBzeW5vcHN5cy5jb20+DQo+IFNpZ25lZC1v
ZmYtYnk6IEpvc2UgQWJyZXUgPGpvYWJyZXVAc3lub3BzeXMuY29tPg0KPiBDYzogTWljaGFlbCBU
dXJxdWV0dGUgPG10dXJxdWV0dGVAYmF5bGlicmUuY29tPg0KPiBDYzogU3RlcGhlbiBCb3lkIDxz
Ym95ZEBjb2RlYXVyb3JhLm9yZz4NCj4gQ2M6IE1hcmsgUnV0bGFuZCA8bWFyay5ydXRsYW5kQGFy
bS5jb20+DQo+IC0tLQ0KPiBDYzogUm9iIEhlcnJpbmcgPHJvYmhAa2VybmVsLm9yZz4NCj4gQ2hh
bmdlcyB2MS4udjINCj4gwqAtIFJlcGxhY2UgJ18nIHdpdGggJy0nIGluIGRldmljZSB0cmVlIG5v
ZGVzDQo+IA0KPiDCoC4uLi9kZXZpY2V0cmVlL2JpbmRpbmdzL2Nsb2NrL3NucHMscGxsLWNsb2Nr
LnR4dMKgwqDCoHzCoMKgMjggKysNCj4gwqBNQUlOVEFJTkVSU8KgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgfMKgwqDCoDYgKw0KPiDCoGRyaXZlcnMvY2xrL2F4czEweC9NYWtlZmlsZcKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHzCoMKgwqAxICsNCj4gwqBkcml2
ZXJzL2Nsay9heHMxMHgvcGxsX2Nsb2NrLmPCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqB8IDM4NCArKysrKysrKysrKysrKysrKysrKysNCj4gwqA0IGZpbGVzIGNoYW5n
ZWQsIDQxOSBpbnNlcnRpb25zKCspDQo+IMKgY3JlYXRlIG1vZGUgMTAwNjQ0IERvY3VtZW50YXRp
b24vZGV2aWNldHJlZS9iaW5kaW5ncy9jbG9jay9zbnBzLHBsbC1jbG9jay50eHQNCj4gwqBjcmVh
dGUgbW9kZSAxMDA2NDQgZHJpdmVycy9jbGsvYXhzMTB4L3BsbF9jbG9jay5jDQo+IA0KPiBkaWZm
IC0tZ2l0IGEvRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdzL2Nsb2NrL3NucHMscGxs
LWNsb2NrLnR4dA0KPiBiL0RvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy9jbG9jay9z
bnBzLHBsbC1jbG9jay50eHQNCj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQNCj4gaW5kZXggMDAwMDAw
MC4uNTcwNjI0Ng0KPiAtLS0gL2Rldi9udWxsDQo+ICsrKyBiL0RvY3VtZW50YXRpb24vZGV2aWNl
dHJlZS9iaW5kaW5ncy9jbG9jay9zbnBzLHBsbC1jbG9jay50eHQNCj4gQEAgLTAsMCArMSwyOCBA
QA0KPiArQmluZGluZyBmb3IgdGhlIEFYUzEwWCBHZW5lcmljIFBMTCBjbG9jaw0KPiArDQo+ICtU
aGlzIGJpbmRpbmcgdXNlcyB0aGUgY29tbW9uIGNsb2NrIGJpbmRpbmdbMV0uDQo+ICsNCj4gK1sx
XSBEb2N1bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3MvY2xvY2svY2xvY2stYmluZGluZ3Mu
dHh0DQo+ICsNCj4gK1JlcXVpcmVkIHByb3BlcnRpZXM6DQo+ICstIGNvbXBhdGlibGU6IHNob3Vs
ZCBiZSAic25wcyxheHMxMHgtPG5hbWU+LXBsbC1jbG9jayINCj4gK8KgwqAic25wcyxheHMxMHgt
YXJjLXBsbC1jbG9jayINCj4gK8KgwqAic25wcyxheHMxMHgtcGd1LXBsbC1jbG9jayINCj4gKy0g
cmVnOiBzaG91bGQgYWx3YXlzIGNvbnRhaW4gMiBwYWlycyBhZGRyZXNzIC0gbGVuZ3RoOiBmaXJz
dCBmb3IgUExMIGNvbmZpZw0KPiArcmVnaXN0ZXJzIGFuZCBzZWNvbmQgZm9yIGNvcnJlc3BvbmRp
bmcgTE9DSyBDR1UgcmVnaXN0ZXIuDQo+ICstIGNsb2Nrczogc2hhbGwgYmUgdGhlIGlucHV0IHBh
cmVudCBjbG9jayBwaGFuZGxlIGZvciB0aGUgUExMLg0KPiArLSAjY2xvY2stY2VsbHM6IGZyb20g
Y29tbW9uIGNsb2NrIGJpbmRpbmc7IFNob3VsZCBhbHdheXMgYmUgc2V0IHRvIDAuDQo+ICsNCj4g
K0V4YW1wbGU6DQo+ICsJaW5wdXQtY2xrOiBpbnB1dC1jbGsgew0KPiArCQljbG9jay1mcmVxdWVu
Y3kgPSA8MzMzMzMzMzM+Ow0KPiArCQljb21wYXRpYmxlID0gImZpeGVkLWNsb2NrIjsNCj4gKwkJ
I2Nsb2NrLWNlbGxzID0gPDA+Ow0KPiArCX07DQo+ICsNCj4gKwljb3JlLWNsazogY29yZS1jbGtA
ODAgew0KPiArCQljb21wYXRpYmxlID0gInNucHMsYXhzMTB4LWFyYy1wbGwtY2xvY2siOw0KPiAr
CQlyZWcgPSA8MHg4MCAweDEwIDB4MTAwIDB4MTA+Ow0KPiArCQkjY2xvY2stY2VsbHMgPSA8MD47
DQo+ICsJCWNsb2NrcyA9IDwmaW5wdXQtY2xrPjsNCj4gKwl9Ow0KPiBkaWZmIC0tZ2l0IGEvTUFJ
TlRBSU5FUlMgYi9NQUlOVEFJTkVSUw0KPiBpbmRleCAzOTYwZTdmLi41ODA1ODMzIDEwMDY0NA0K
PiAtLS0gYS9NQUlOVEFJTkVSUw0KPiArKysgYi9NQUlOVEFJTkVSUw0KPiBAQCAtMTE5MTAsNiAr
MTE5MTAsMTIgQEAgRjoJYXJjaC9hcmMvcGxhdC1heHMxMHgNCj4gwqBGOglhcmNoL2FyYy9ib290
L2R0cy9heCoNCj4gwqBGOglEb2N1bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3MvYXJjL2F4
czEwKg0KPiDCoA0KPiArU1lOT1BTWVMgQVJDIFNEUCBjbG9jayBkcml2ZXINCj4gK006CVZsYWQg
WmFraGFyb3YgPHZ6YWtoYXJAc3lub3BzeXMuY29tPg0KPiArUzoJU3VwcG9ydGVkDQo+ICtGOglk
cml2ZXJzL2Nsay9heHMxMHgvKg0KPiArRjoJRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRp
bmdzL2Nsb2NrL3NucHMscGxsLWNsb2NrLnR4dA0KPiArDQo+IMKgU1lTVEVNIENPTkZJR1VSQVRJ
T04gKFNZU0NPTikNCj4gwqBNOglMZWUgSm9uZXMgPGxlZS5qb25lc0BsaW5hcm8ub3JnPg0KPiDC
oE06CUFybmQgQmVyZ21hbm4gPGFybmRAYXJuZGIuZGU+DQo+IGRpZmYgLS1naXQgYS9kcml2ZXJz
L2Nsay9heHMxMHgvTWFrZWZpbGUgYi9kcml2ZXJzL2Nsay9heHMxMHgvTWFrZWZpbGUNCj4gaW5k
ZXggMDE5OTZiOC4uZDc0N2RlYSAxMDA2NDQNCj4gLS0tIGEvZHJpdmVycy9jbGsvYXhzMTB4L01h
a2VmaWxlDQo+ICsrKyBiL2RyaXZlcnMvY2xrL2F4czEweC9NYWtlZmlsZQ0KPiBAQCAtMSArMSwy
IEBADQo+IMKgb2JqLXkgKz0gaTJzX3BsbF9jbG9jay5vDQo+ICtvYmoteSArPSBwbGxfY2xvY2su
bw0KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9jbGsvYXhzMTB4L3BsbF9jbG9jay5jIGIvZHJpdmVy
cy9jbGsvYXhzMTB4L3BsbF9jbG9jay5jDQo+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+IGluZGV4
IDAwMDAwMDAuLjc4NGEwYTINCj4gLS0tIC9kZXYvbnVsbA0KPiArKysgYi9kcml2ZXJzL2Nsay9h
eHMxMHgvcGxsX2Nsb2NrLmMNCj4gQEAgLTAsMCArMSwzODQgQEANCj4gKy8qDQo+ICsgKiBTeW5v
cHN5cyBBWFMxMFggU0RQIEdlbmVyaWMgUExMIGNsb2NrIGRyaXZlcg0KPiArICoNCj4gKyAqIENv
cHlyaWdodCAoQykgMjAxNyBTeW5vcHN5cw0KPiArICoNCj4gKyAqIFRoaXMgZmlsZSBpcyBsaWNl
bnNlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYw0KPiArICogTGlj
ZW5zZSB2ZXJzaW9uIDIuIFRoaXMgcHJvZ3JhbSBpcyBsaWNlbnNlZCAiYXMgaXMiIHdpdGhvdXQg
YW55DQo+ICsgKiB3YXJyYW50eSBvZiBhbnkga2luZCwgd2hldGhlciBleHByZXNzIG9yIGltcGxp
ZWQuDQo+ICsgKi8NCj4gKw0KPiArI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPg0K
PiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2Nsay1wcm92
aWRlci5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2RlbGF5Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgv
ZXJyLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvZGV2aWNlLmg+DQo+ICsjaW5jbHVkZSA8bGludXgv
b2ZfYWRkcmVzcy5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L29mX2RldmljZS5oPg0KPiArI2luY2x1
ZGUgPGxpbnV4L3NsYWIuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9vZi5oPg0KPiArDQo+ICsvKiBQ
TEwgcmVnaXN0ZXJzIGFkZHJlc3NlcyAqLw0KPiArI2RlZmluZSBQTExfUkVHX0lESVYJMHgwDQo+
ICsjZGVmaW5lIFBMTF9SRUdfRkJESVYJMHg0DQo+ICsjZGVmaW5lIFBMTF9SRUdfT0RJVgkweDgN
Cj4gKw0KPiArLyoNCj4gKyAqIEJpdCBmaWVsZHMgb2YgdGhlIFBMTCBJRElWL0ZCRElWL09ESVYg
cmVnaXN0ZXJzOg0KPiArICrCoMKgX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQo+ICsgKiB8MzHCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoDE1fMKgwqDCoMKgMTTCoMKgwqDCoHzCoMKgwqAxM8KgwqDC
oHzCoMKgMTLCoMKgfDExwqDCoMKgwqDCoMKgwqDCoMKgNnw1wqDCoMKgwqDCoMKgwqDCoMKgMHwN
Cj4gKyAqIHwtLS0tLS0tUkVTUlZFRC0tLS0tLXwtTk9VUERBVEUtfC1CWVBBU1MtfC1FREdFLXwt
LUhJR0hUSU1FLS18LS1MT1dUSU1FLS18DQo+ICsgKiB8X19fX19fX19fX19fX19fX19fX198X19f
X19fX19fX3xfX19fX19fX3xfX19fX198X19fX19fX19fX19ffF9fX19fX19fX19ffA0KPiArICoN
Cj4gKyAqIEZvbGxvd2luZyBtYWNyb3MgZGV0aXJtaW5lIHRoZSB3YXkgb2YgYWNjZXNzIHRvIHRo
ZXNlIHJlZ2lzdGVycw0KPiArICogVGhleSBzaG91bGQgYmUgc2V0IHVwIG9ubHkgdXNpbmcgdGhl
IG1hY3Jvcy4NCj4gKyAqIHJlZyBzaG91bGQgYmUgYW5kIHVpbnQzMl90IHZhcmlhYmxlLg0KPiAr
ICovDQo+ICsNCj4gKyNkZWZpbmUgUExMX1JFR19HRVRfTE9XKHJlZykJCQlcDQo+ICsJKCgocmVn
KSAmICgweDNGIDw8IDApKSA+PiAwKQ0KPiArI2RlZmluZSBQTExfUkVHX0dFVF9ISUdIKHJlZykJ
CQlcDQo+ICsJKCgocmVnKSAmICgweDNGIDw8IDYpKSA+PiA2KQ0KPiArI2RlZmluZSBQTExfUkVH
X0dFVF9FREdFKHJlZykJCQlcDQo+ICsJKCgocmVnKSAmIChCSVQoMTIpKSkgPyAxIDogMCkNCj4g
KyNkZWZpbmUgUExMX1JFR19HRVRfQllQQVNTKHJlZykJCQlcDQo+ICsJKCgocmVnKSAmIChCSVQo
MTMpKSkgPyAxIDogMCkNCj4gKyNkZWZpbmUgUExMX1JFR19HRVRfTk9VUEQocmVnKQkJCVwNCj4g
KwkoKChyZWcpICYgKEJJVCgxNCkpKSA/IDEgOiAwKQ0KPiArI2RlZmluZSBQTExfUkVHX0dFVF9Q
QUQocmVnKQkJCVwNCj4gKwkoKChyZWcpICYgKDB4MUZGRkYgPDwgMTUpKSA+PiAxNSkNCj4gKw0K
PiArI2RlZmluZSBQTExfUkVHX1NFVF9MT1cocmVnLCB2YWx1ZSkJCVwNCj4gKwl7IHJlZyB8PSAo
KCh2YWx1ZSkgJiAweDNGKSA8PCAwKTsgfQ0KPiArI2RlZmluZSBQTExfUkVHX1NFVF9ISUdIKHJl
ZywgdmFsdWUpCVwNCj4gKwl7IHJlZyB8PSAoKCh2YWx1ZSkgJiAweDNGKSA8PCA2KTsgfQ0KPiAr
I2RlZmluZSBQTExfUkVHX1NFVF9FREdFKHJlZywgdmFsdWUpCVwNCj4gKwl7IHJlZyB8PSAoKCh2
YWx1ZSkgJiAweDAxKSA8PCAxMik7IH0NCj4gKyNkZWZpbmUgUExMX1JFR19TRVRfQllQQVNTKHJl
ZywgdmFsdWUpCVwNCj4gKwl7IHJlZyB8PSAoKCh2YWx1ZSkgJiAweDAxKSA8PCAxMyk7IH0NCj4g
KyNkZWZpbmUgUExMX1JFR19TRVRfTk9VUEQocmVnLCB2YWx1ZSkJXA0KPiArCXsgcmVnIHw9ICgo
KHZhbHVlKSAmIDB4MDEpIDw8IDE0KTsgfQ0KPiArI2RlZmluZSBQTExfUkVHX1NFVF9QQUQocmVn
LCB2YWx1ZSkJCVwNCj4gKwl7IHJlZyB8PSAoKCh2YWx1ZSkgJiAweDFGRkZGKSA8PCAxNSk7IH0N
Cj4gKw0KPiArI2RlZmluZSBQTExfTE9DSwkweDENCj4gKyNkZWZpbmUgUExMX01BWF9MT0NLX1RJ
TUUgMTAwIC8qIDEwMCB1cyAqLw0KPiArDQo+ICtzdHJ1Y3QgcGxsX2NmZyB7DQo+ICsJdTMyIHJh
dGU7DQo+ICsJdTMyIGlkaXY7DQo+ICsJdTMyIGZiZGl2Ow0KPiArCXUzMiBvZGl2Ow0KPiArfTsN
Cj4gKw0KPiArc3RydWN0IHBsbF9vZl90YWJsZSB7DQo+ICsJdW5zaWduZWQgbG9uZyBwcmF0ZTsN
Cj4gKwlzdHJ1Y3QgcGxsX2NmZyAqcGxsX2NmZ190YWJsZTsNCj4gK307DQo+ICsNCj4gK3N0cnVj
dCBwbGxfb2ZfZGF0YSB7DQo+ICsJc3RydWN0IHBsbF9vZl90YWJsZSAqcGxsX3RhYmxlOw0KPiAr
fTsNCj4gKw0KPiArc3RhdGljIHN0cnVjdCBwbGxfb2ZfZGF0YSBwZ3VfcGxsX2RhdGEgPSB7DQo+
ICsJLnBsbF90YWJsZSA9IChzdHJ1Y3QgcGxsX29mX3RhYmxlIFtdKXsNCj4gKwkJew0KPiArCQkJ
LnByYXRlID0gMjcwMDAwMDAsDQo+ICsJCQkucGxsX2NmZ190YWJsZSA9IChzdHJ1Y3QgcGxsX2Nm
ZyBbXSl7DQo+ICsJCQkJeyAyNTIwMDAwMCwgMSwgODQsIDkwIH0sDQo+ICsJCQkJeyA1MDAwMDAw
MCwgMSwgMTAwLCA1NCB9LA0KPiArCQkJCXsgNzQyNTAwMDAsIDEsIDQ0LCAxNiB9LA0KPiArCQkJ
CXsgfSwNCj4gKwkJCX0sDQo+ICsJCX0sDQo+ICsJCS8qIFVzZWQgYXMgbGlzdCBsaW1pdGVyICov
DQo+ICsJCXsgfSwNCj4gKwl9LA0KPiArfTsNCj4gKw0KPiArc3RhdGljIHN0cnVjdCBwbGxfb2Zf
ZGF0YSBhcmNfcGxsX2RhdGEgPSB7DQo+ICsJLnBsbF90YWJsZSA9IChzdHJ1Y3QgcGxsX29mX3Rh
YmxlIFtdKXsNCj4gKwkJew0KPiArCQkJLnByYXRlID0gMzMzMzMzMzMsDQo+ICsJCQkucGxsX2Nm
Z190YWJsZSA9IChzdHJ1Y3QgcGxsX2NmZyBbXSl7DQo+ICsJCQkJeyAzMzMzMzMzMyzCoMKgMSwg
MSzCoMKgMSB9LA0KPiArCQkJCXsgNTAwMDAwMDAswqDCoDEsIDMwLCAyMCB9LA0KPiArCQkJCXsg
NzUwMDAwMDAswqDCoDIsIDQ1LCAxMCB9LA0KPiArCQkJCXsgOTAwMDAwMDAswqDCoDIsIDU0LCAx
MCB9LA0KPiArCQkJCXsgMTAwMDAwMDAwLCAxLCAzMCwgMTAgfSwNCj4gKwkJCQl7IDEyNTAwMDAw
MCwgMiwgNDUsIDYgfSwNCj4gKwkJCQl7IH0sDQo+ICsJCQl9LA0KPiArCQl9LA0KPiArCQkvKiBV
c2VkIGFzIGxpc3QgbGltaXRlciAqLw0KPiArCQl7IH0sDQo+ICsJfSwNCj4gK307DQo+ICsNCj4g
K3N0cnVjdCBwbGxfY2xrIHsNCj4gKwl2b2lkIF9faW9tZW0gKmJhc2U7DQo+ICsJdm9pZCBfX2lv
bWVtICpsb2NrOw0KPiArCWNvbnN0IHN0cnVjdCBwbGxfb2ZfZGF0YSAqcGxsX2RhdGE7DQo+ICsJ
c3RydWN0IGNsa19odyBodzsNCj4gKwlzdHJ1Y3QgZGV2aWNlICpkZXY7DQo+ICt9Ow0KPiArDQo+
ICtzdGF0aWMgaW5saW5lIHZvaWQgcGxsX3dyaXRlKHN0cnVjdCBwbGxfY2xrICpjbGssIHVuc2ln
bmVkIGludCByZWcsDQo+ICsJCXVuc2lnbmVkIGludCB2YWwpDQo+ICt7DQo+ICsJaW93cml0ZTMy
KHZhbCwgY2xrLT5iYXNlICsgcmVnKTsNCj4gK30NCj4gKw0KPiArc3RhdGljIGlubGluZSB1MzIg
cGxsX3JlYWQoc3RydWN0IHBsbF9jbGsgKmNsaywNCj4gKwkJdW5zaWduZWQgaW50IHJlZykNCj4g
K3sNCj4gKwlyZXR1cm4gaW9yZWFkMzIoY2xrLT5iYXNlICsgcmVnKTsNCj4gK30NCj4gKw0KPiAr
c3RhdGljIGlubGluZSBzdHJ1Y3QgcGxsX2NsayAqdG9fcGxsX2NsayhzdHJ1Y3QgY2xrX2h3ICpo
dykNCj4gK3sNCj4gKwlyZXR1cm4gY29udGFpbmVyX29mKGh3LCBzdHJ1Y3QgcGxsX2NsaywgaHcp
Ow0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW5saW5lIHUzMiBkaXZfZ2V0X3ZhbHVlKHVuc2lnbmVk
IGludCByZWcpDQo+ICt7DQo+ICsJaWYgKFBMTF9SRUdfR0VUX0JZUEFTUyhyZWcpKQ0KPiArCQly
ZXR1cm4gMTsNCj4gKw0KPiArCXJldHVybiAoUExMX1JFR19HRVRfSElHSChyZWcpICsgUExMX1JF
R19HRVRfTE9XKHJlZykpOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW5saW5lIHUzMiBlbmNvZGVf
ZGl2KHVuc2lnbmVkIGludCBpZCwgaW50IHVwZCkNCj4gK3sNCj4gKwl1aW50MzJfdCBkaXYgPSAw
Ow0KPiArDQo+ICsJUExMX1JFR19TRVRfTE9XKGRpdiwgKGlkJTIgPT0gMCkgPyBpZCA+PiAxIDog
KGlkID4+IDEpICsgMSk7DQo+ICsJUExMX1JFR19TRVRfSElHSChkaXYsIGlkID4+IDEpOw0KPiAr
CVBMTF9SRUdfU0VUX0VER0UoZGl2LCBpZCUyKTsNCj4gKwlQTExfUkVHX1NFVF9CWVBBU1MoZGl2
LCBpZCA9PSAxID8gMSA6IDApOw0KPiArCVBMTF9SRUdfU0VUX05PVVBEKGRpdiwgIXVwZCk7DQo+
ICsNCj4gKwlyZXR1cm4gZGl2Ow0KPiArfQ0KPiArDQo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IHBs
bF9jZmcgKnBsbF9nZXRfY2ZnKHVuc2lnbmVkIGxvbmcgcHJhdGUsDQo+ICsJCWNvbnN0IHN0cnVj
dCBwbGxfb2ZfdGFibGUgKnBsbF90YWJsZSkNCj4gK3sNCj4gKwlpbnQgaTsNCj4gKw0KPiArCWZv
ciAoaSA9IDA7IHBsbF90YWJsZVtpXS5wcmF0ZSAhPSAwOyBpKyspDQo+ICsJCWlmIChwbGxfdGFi
bGVbaV0ucHJhdGUgPT0gcHJhdGUpDQo+ICsJCQlyZXR1cm4gcGxsX3RhYmxlW2ldLnBsbF9jZmdf
dGFibGU7DQo+ICsNCj4gKwlyZXR1cm4gTlVMTDsNCj4gK30NCj4gKw0KPiArc3RhdGljIHVuc2ln
bmVkIGxvbmcgcGxsX3JlY2FsY19yYXRlKHN0cnVjdCBjbGtfaHcgKmh3LA0KPiArCQkJdW5zaWdu
ZWQgbG9uZyBwYXJlbnRfcmF0ZSkNCj4gK3sNCj4gKwl1NjQgcmF0ZTsNCj4gKwl1MzIgaWRpdiwg
ZmJkaXYsIG9kaXY7DQo+ICsJc3RydWN0IHBsbF9jbGsgKmNsayA9IHRvX3BsbF9jbGsoaHcpOw0K
PiArDQo+ICsJaWRpdiA9IGRpdl9nZXRfdmFsdWUocGxsX3JlYWQoY2xrLCBQTExfUkVHX0lESVYp
KTsNCj4gKwlmYmRpdiA9IGRpdl9nZXRfdmFsdWUocGxsX3JlYWQoY2xrLCBQTExfUkVHX0ZCRElW
KSk7DQo+ICsJb2RpdiA9IGRpdl9nZXRfdmFsdWUocGxsX3JlYWQoY2xrLCBQTExfUkVHX09ESVYp
KTsNCj4gKw0KPiArCXJhdGUgPSAodTY0KXBhcmVudF9yYXRlICogZmJkaXY7DQo+ICsJZG9fZGl2
KHJhdGUsIGlkaXYgKiBvZGl2KTsNCj4gKw0KPiArCXJldHVybiAodW5zaWduZWQgbG9uZylyYXRl
Ow0KPiArfQ0KPiArDQo+ICtzdGF0aWMgbG9uZyBwbGxfcm91bmRfcmF0ZShzdHJ1Y3QgY2xrX2h3
ICpodywgdW5zaWduZWQgbG9uZyByYXRlLA0KPiArCQkJdW5zaWduZWQgbG9uZyAqcHJhdGUpDQo+
ICt7DQo+ICsJaW50IGk7DQo+ICsJbG9uZyBiZXN0X3JhdGU7DQo+ICsJc3RydWN0IHBsbF9jbGsg
KmNsayA9IHRvX3BsbF9jbGsoaHcpOw0KPiArCWNvbnN0IHN0cnVjdCBwbGxfY2ZnICpwbGxfY2Zn
ID0gcGxsX2dldF9jZmcoKnByYXRlLA0KPiArCQkJY2xrLT5wbGxfZGF0YS0+cGxsX3RhYmxlKTsN
Cj4gKw0KPiArCWlmICghcGxsX2NmZykgew0KPiArCQlkZXZfZXJyKGNsay0+ZGV2LCAiaW52YWxp
ZCBwYXJlbnQgcmF0ZT0lbGRcbiIsICpwcmF0ZSk7DQo+ICsJCXJldHVybiAtRUlOVkFMOw0KPiAr
CX0NCj4gKw0KPiArCWlmIChwbGxfY2ZnWzBdLnJhdGUgPT0gMCkNCj4gKwkJcmV0dXJuIC1FSU5W
QUw7DQo+ICsNCj4gKwliZXN0X3JhdGUgPSBwbGxfY2ZnWzBdLnJhdGU7DQo+ICsNCj4gKwlmb3Ig
KGkgPSAxOyBwbGxfY2ZnW2ldLnJhdGUgIT0gMDsgaSsrKSB7DQo+ICsJCWlmIChhYnMocmF0ZSAt
IHBsbF9jZmdbaV0ucmF0ZSkgPCBhYnMocmF0ZSAtIGJlc3RfcmF0ZSkpDQo+ICsJCQliZXN0X3Jh
dGUgPSBwbGxfY2ZnW2ldLnJhdGU7DQo+ICsJfQ0KPiArDQo+ICsJcmV0dXJuIGJlc3RfcmF0ZTsN
Cj4gK30NCj4gKw0KPiArc3RhdGljIGludCBwbGxfc2V0X3JhdGUoc3RydWN0IGNsa19odyAqaHcs
IHVuc2lnbmVkIGxvbmcgcmF0ZSwNCj4gKwkJCXVuc2lnbmVkIGxvbmcgcGFyZW50X3JhdGUpDQo+
ICt7DQo+ICsJaW50IGk7DQo+ICsJc3RydWN0IHBsbF9jbGsgKmNsayA9IHRvX3BsbF9jbGsoaHcp
Ow0KPiArCWNvbnN0IHN0cnVjdCBwbGxfY2ZnICpwbGxfY2ZnID0gcGxsX2dldF9jZmcocGFyZW50
X3JhdGUsDQo+ICsJCQljbGstPnBsbF9kYXRhLT5wbGxfdGFibGUpOw0KPiArDQo+ICsJaWYgKCFw
bGxfY2ZnKSB7DQo+ICsJCWRldl9lcnIoY2xrLT5kZXYsICJpbnZhbGlkIHBhcmVudCByYXRlPSVs
ZFxuIiwgcGFyZW50X3JhdGUpOw0KPiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gKwl9DQo+ICsNCj4g
Kwlmb3IgKGkgPSAwOyBwbGxfY2ZnW2ldLnJhdGUgIT0gMDsgaSsrKSB7DQo+ICsJCWlmIChwbGxf
Y2ZnW2ldLnJhdGUgPT0gcmF0ZSkgew0KPiArCQkJcGxsX3dyaXRlKGNsaywgUExMX1JFR19JRElW
LA0KPiArCQkJCQllbmNvZGVfZGl2KHBsbF9jZmdbaV0uaWRpdiwgMCkpOw0KPiArCQkJcGxsX3dy
aXRlKGNsaywgUExMX1JFR19GQkRJViwNCj4gKwkJCQkJZW5jb2RlX2RpdihwbGxfY2ZnW2ldLmZi
ZGl2LCAwKSk7DQo+ICsJCQlwbGxfd3JpdGUoY2xrLCBQTExfUkVHX09ESVYsDQo+ICsJCQkJCWVu
Y29kZV9kaXYocGxsX2NmZ1tpXS5vZGl2LCAxKSk7DQo+ICsNCj4gKwkJCS8qDQo+ICsJCQnCoCog
V2FpdCB1bnRpbCBDR1UgcmVsb2Nrcy4NCj4gKwkJCcKgKiBJZiBhZnRlciB0aW1lb3V0IENHVSBp
cyB1bmxvY2tlZCB5ZXQgcmV0dXJuIGVycm9yDQo+ICsJCQnCoCovDQo+ICsJCQl1ZGVsYXkoUExM
X01BWF9MT0NLX1RJTUUpOw0KPiArCQkJaWYgKGlvcmVhZDMyKGNsay0+bG9jaykgJiBQTExfTE9D
SykNCj4gKwkJCQlyZXR1cm4gMDsNCj4gKwkJCWVsc2UNCj4gKwkJCQlyZXR1cm4gLUVUSU1FRE9V
VDsNCj4gKwkJfQ0KPiArCX0NCj4gKw0KPiArCWRldl9lcnIoY2xrLT5kZXYsICJpbnZhbGlkIHJh
dGU9JWxkLCBwYXJlbnRfcmF0ZT0lbGRcbiIsIHJhdGUsDQo+ICsJCQlwYXJlbnRfcmF0ZSk7DQo+
ICsJcmV0dXJuIC1FSU5WQUw7DQo+ICt9DQo+ICsNCj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgY2xr
X29wcyBwbGxfb3BzID0gew0KPiArCS5yZWNhbGNfcmF0ZSA9IHBsbF9yZWNhbGNfcmF0ZSwNCj4g
Kwkucm91bmRfcmF0ZSA9IHBsbF9yb3VuZF9yYXRlLA0KPiArCS5zZXRfcmF0ZSA9IHBsbF9zZXRf
cmF0ZSwNCj4gK307DQo+ICsNCj4gK3N0YXRpYyBpbnQgcGxsX2Nsa19wcm9iZShzdHJ1Y3QgcGxh
dGZvcm1fZGV2aWNlICpwZGV2KQ0KPiArew0KPiArCXN0cnVjdCBkZXZpY2UgKmRldiA9ICZwZGV2
LT5kZXY7DQo+ICsJY29uc3QgY2hhciAqcGFyZW50X25hbWU7DQo+ICsJc3RydWN0IGNsayAqY2xr
Ow0KPiArCXN0cnVjdCBwbGxfY2xrICpwbGxfY2xrOw0KPiArCXN0cnVjdCByZXNvdXJjZSAqbWVt
Ow0KPiArCXN0cnVjdCBjbGtfaW5pdF9kYXRhIGluaXQgPSB7IH07DQo+ICsNCj4gKwlwbGxfY2xr
ID0gZGV2bV9remFsbG9jKGRldiwgc2l6ZW9mKCpwbGxfY2xrKSwgR0ZQX0tFUk5FTCk7DQo+ICsJ
aWYgKCFwbGxfY2xrKQ0KPiArCQlyZXR1cm4gLUVOT01FTTsNCj4gKw0KPiArCW1lbSA9IHBsYXRm
b3JtX2dldF9yZXNvdXJjZShwZGV2LCBJT1JFU09VUkNFX01FTSwgMCk7DQo+ICsJcGxsX2Nsay0+
YmFzZSA9IGRldm1faW9yZW1hcF9yZXNvdXJjZShkZXYsIG1lbSk7DQo+ICsJaWYgKElTX0VSUihw
bGxfY2xrLT5iYXNlKSkNCj4gKwkJcmV0dXJuIFBUUl9FUlIocGxsX2Nsay0+YmFzZSk7DQo+ICsN
Cj4gKwltZW0gPSBwbGF0Zm9ybV9nZXRfcmVzb3VyY2UocGRldiwgSU9SRVNPVVJDRV9NRU0sIDEp
Ow0KPiArCXBsbF9jbGstPmxvY2sgPSBkZXZtX2lvcmVtYXBfcmVzb3VyY2UoZGV2LCBtZW0pOw0K
PiArCWlmIChJU19FUlIocGxsX2Nsay0+bG9jaykpDQo+ICsJCXJldHVybiBQVFJfRVJSKHBsbF9j
bGstPmJhc2UpOw0KPiArDQo+ICsJaW5pdC5uYW1lID0gZGV2LT5vZl9ub2RlLT5uYW1lOw0KPiAr
CWluaXQub3BzID0gJnBsbF9vcHM7DQo+ICsJcGFyZW50X25hbWUgPSBvZl9jbGtfZ2V0X3BhcmVu
dF9uYW1lKGRldi0+b2Zfbm9kZSwgMCk7DQo+ICsJaW5pdC5wYXJlbnRfbmFtZXMgPSAmcGFyZW50
X25hbWU7DQo+ICsJaW5pdC5udW1fcGFyZW50cyA9IDE7DQo+ICsJcGxsX2Nsay0+aHcuaW5pdCA9
ICZpbml0Ow0KPiArCXBsbF9jbGstPmRldiA9IGRldjsNCj4gKwlwbGxfY2xrLT5wbGxfZGF0YSA9
IG9mX2RldmljZV9nZXRfbWF0Y2hfZGF0YShkZXYpOw0KPiArDQo+ICsJaWYgKCFwbGxfY2xrLT5w
bGxfZGF0YSkgew0KPiArCQlkZXZfZXJyKGRldiwgIk5vIE9GIG1hdGNoIGRhdGEgcHJvdmlkZWRc
biIpOw0KPiArCQkJcmV0dXJuIC1FSU5WQUw7DQo+ICsJfQ0KPiArDQo+ICsJY2xrID0gZGV2bV9j
bGtfcmVnaXN0ZXIoZGV2LCAmcGxsX2Nsay0+aHcpOw0KPiArCWlmIChJU19FUlIoY2xrKSkgew0K
PiArCQlkZXZfZXJyKGRldiwgImZhaWxlZCB0byByZWdpc3RlciAlcyBjbG9jayAoJWxkKVxuIiwN
Cj4gKwkJCQlpbml0Lm5hbWUsIFBUUl9FUlIoY2xrKSk7DQo+ICsJCXJldHVybiBQVFJfRVJSKGNs
ayk7DQo+ICsJfQ0KPiArDQo+ICsJcmV0dXJuIG9mX2Nsa19hZGRfcHJvdmlkZXIoZGV2LT5vZl9u
b2RlLCBvZl9jbGtfc3JjX3NpbXBsZV9nZXQsIGNsayk7DQo+ICt9DQo+ICsNCj4gK3N0YXRpYyBp
bnQgcGxsX2Nsa19yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikNCj4gK3sNCj4g
KwlvZl9jbGtfZGVsX3Byb3ZpZGVyKHBkZXYtPmRldi5vZl9ub2RlKTsNCj4gKwlyZXR1cm4gMDsN
Cj4gK30NCj4gKw0KPiArc3RhdGljIHZvaWQgX19pbml0IG9mX3BsbF9jbGtfc2V0dXAoc3RydWN0
IGRldmljZV9ub2RlICpub2RlKQ0KPiArew0KPiArCWNvbnN0IGNoYXIgKnBhcmVudF9uYW1lOw0K
PiArCXN0cnVjdCBjbGsgKmNsazsNCj4gKwlzdHJ1Y3QgcGxsX2NsayAqcGxsX2NsazsNCj4gKwlz
dHJ1Y3QgY2xrX2luaXRfZGF0YSBpbml0ID0geyB9Ow0KPiArDQo+ICsJcGxsX2NsayA9IGt6YWxs
b2Moc2l6ZW9mKCpwbGxfY2xrKSwgR0ZQX0tFUk5FTCk7DQo+ICsJaWYgKCFwbGxfY2xrKQ0KPiAr
CQlyZXR1cm47DQo+ICsNCj4gKwlwbGxfY2xrLT5iYXNlID0gb2ZfaW9tYXAobm9kZSwgMCk7DQo+
ICsJaWYgKCFwbGxfY2xrLT5iYXNlKSB7DQo+ICsJCXByX2VycigiZmFpbGVkIHRvIG1hcCBwbGwg
ZGl2IHJlZ2lzdGVyc1xuIik7DQo+ICsJCWlvdW5tYXAocGxsX2Nsay0+YmFzZSk7DQo+ICsJCXJl
dHVybjsNCj4gKwl9DQo+ICsNCj4gKwlwbGxfY2xrLT5sb2NrID0gb2ZfaW9tYXAobm9kZSwgMSk7
DQo+ICsJaWYgKCFwbGxfY2xrLT5sb2NrKSB7DQo+ICsJCXByX2VycigiZmFpbGVkIHRvIG1hcCBw
bGwgbG9jayByZWdpc3RlclxuIik7DQo+ICsJCWlvdW5tYXAocGxsX2Nsay0+bG9jayk7DQo+ICsJ
CXJldHVybjsNCj4gKwl9DQo+ICsNCj4gKwlpbml0Lm5hbWUgPSBub2RlLT5uYW1lOw0KPiArCWlu
aXQub3BzID0gJnBsbF9vcHM7DQo+ICsJcGFyZW50X25hbWUgPSBvZl9jbGtfZ2V0X3BhcmVudF9u
YW1lKG5vZGUsIDApOw0KPiArCWluaXQucGFyZW50X25hbWVzID0gJnBhcmVudF9uYW1lOw0KPiAr
CWluaXQubnVtX3BhcmVudHMgPSBwYXJlbnRfbmFtZSA/IDEgOiAwOw0KPiArCXBsbF9jbGstPmh3
LmluaXQgPSAmaW5pdDsNCj4gKwlwbGxfY2xrLT5wbGxfZGF0YSA9ICZhcmNfcGxsX2RhdGE7DQo+
ICsNCj4gKwljbGsgPSBjbGtfcmVnaXN0ZXIoTlVMTCwgJnBsbF9jbGstPmh3KTsNCj4gKwlpZiAo
SVNfRVJSKGNsaykpIHsNCj4gKwkJcHJfZXJyKCJmYWlsZWQgdG8gcmVnaXN0ZXIgJXMgY2xvY2sg
KCVsZClcbiIsDQo+ICsJCQkJbm9kZS0+bmFtZSwgUFRSX0VSUihjbGspKTsNCj4gKwkJa2ZyZWUo
cGxsX2Nsayk7DQo+ICsJCXJldHVybjsNCj4gKwl9DQo+ICsNCj4gKwlvZl9jbGtfYWRkX3Byb3Zp
ZGVyKG5vZGUsIG9mX2Nsa19zcmNfc2ltcGxlX2dldCwgY2xrKTsNCj4gK30NCj4gKw0KPiArQ0xL
X09GX0RFQ0xBUkUoYXhzMTB4X3BsbF9jbG9jaywgInNucHMsYXhzMTB4LWFyYy1wbGwtY2xvY2si
LCBvZl9wbGxfY2xrX3NldHVwKTsNCj4gKw0KPiArc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZp
Y2VfaWQgcGxsX2Nsa19pZFtdID0gew0KPiArCXsgLmNvbXBhdGlibGUgPSAic25wcyxheHMxMHgt
YXJjLXBsbC1jbG9jayIsIC5kYXRhID0gJmFyY19wbGxfZGF0YX0sDQo+ICsJeyAuY29tcGF0aWJs
ZSA9ICJzbnBzLGF4czEweC1wZ3UtcGxsLWNsb2NrIiwgLmRhdGEgPSAmcGd1X3BsbF9kYXRhfSwN
Cj4gKwl7IH0sDQo+ICt9Ow0KPiArTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgcGxsX2Nsa19pZCk7
DQo+ICsNCj4gK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIHBsbF9jbGtfZHJpdmVyID0g
ew0KPiArCS5kcml2ZXIgPSB7DQo+ICsJCS5uYW1lID0gImF4czEweC1wbGwtY2xvY2siLA0KPiAr
CQkub2ZfbWF0Y2hfdGFibGUgPSBwbGxfY2xrX2lkLA0KPiArCX0sDQo+ICsJLnByb2JlID0gcGxs
X2Nsa19wcm9iZSwNCj4gKwkucmVtb3ZlID0gcGxsX2Nsa19yZW1vdmUsDQo+ICt9Ow0KPiArYnVp
bHRpbl9wbGF0Zm9ybV9kcml2ZXIocGxsX2Nsa19kcml2ZXIpOw0KPiArDQo+ICtNT0RVTEVfQVVU
SE9SKCJWbGFkIFpha2hhcm92IDx2emFraGFyQHN5bm9wc3lzLmNvbT4iKTsNCj4gK01PRFVMRV9E
RVNDUklQVElPTigiU3lub3BzeXMgQVhTMTBYIFNEUCBHZW5lcmljIFBMTCBDbG9jayBEcml2ZXIi
KTsNCj4gK01PRFVMRV9MSUNFTlNFKCJHUEwgdjIiKTsNCg0KTWF5YmUgeW91IGhhdmUgYW55IGNv
bW1lbnRzIG9yIHJlbWFya3MgYWJvdXQgdGhpcyBwYXRjaD8gQW5kIGlmIHlvdSBkb24ndCBjb3Vs
ZCB5b3UgcGxlYXNlIGFwcGx5IGl0Lg0KDQpUaGFua3MhDQoNCi0tIA0KQmVzdCByZWdhcmRzLA0K
VmxhZCBaYWtoYXJvdiA8dnpha2hhckBzeW5vcHN5cy5jb20+

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 13:18   ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-03 13:18 UTC (permalink / raw)
  To: linux-snps-arc

Hi Michael, Stephen,

On Tue, 2017-02-21@16:11 +0300, Vlad Zakharov wrote:
> AXS10X boards manages it's clocks using various PLLs. These PLL has same
> dividers and corresponding control registers mapped to different addresses.
> So we add one common driver for such PLLs.
> 
> Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> ODIV. Output clock value is managed using these dividers.
> 
> We add pre-defined tables with supported rate values and appropriate
> configurations of IDIV, FBDIV and ODIV for each value.
> 
> As of today we add support for PLLs that generate clock for the
> following devices:
> ?* ARC core on AXC CPU tiles.
> ?* ARC PGU on ARC SDP Mainboard.
> and more to come later.
> 
> Acked-by: Rob Herring <robh at kernel.org>
> Signed-off-by: Vlad Zakharov <vzakhar at synopsys.com>
> Signed-off-by: Jose Abreu <joabreu at synopsys.com>
> Cc: Michael Turquette <mturquette at baylibre.com>
> Cc: Stephen Boyd <sboyd at codeaurora.org>
> Cc: Mark Rutland <mark.rutland at arm.com>
> ---
> Cc: Rob Herring <robh at kernel.org>
> Changes v1..v2
> ?- Replace '_' with '-' in device tree nodes
> 
> ?.../devicetree/bindings/clock/snps,pll-clock.txt???|??28 ++
> ?MAINTAINERS????????????????????????????????????????|???6 +
> ?drivers/clk/axs10x/Makefile????????????????????????|???1 +
> ?drivers/clk/axs10x/pll_clock.c?????????????????????| 384 +++++++++++++++++++++
> ?4 files changed, 419 insertions(+)
> ?create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> ?create mode 100644 drivers/clk/axs10x/pll_clock.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +??"snps,axs10x-arc-pll-clock"
> +??"snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk at 80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;
> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
> ?F:	arch/arc/boot/dts/ax*
> ?F:	Documentation/devicetree/bindings/arc/axs10*
> ?
> +SYNOPSYS ARC SDP clock driver
> +M:	Vlad Zakharov <vzakhar at synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
> ?SYSTEM CONFIGURATION (SYSCON)
> ?M:	Lee Jones <lee.jones at linaro.org>
> ?M:	Arnd Bergmann <arnd at arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
> ?obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *??________________________________________________________________________
> + * |31????????????????15|????14????|???13???|??12??|11?????????6|5?????????0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers
> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.
> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;
> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};
> +
> +static struct pll_of_data pgu_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,??1, 1,??1 },
> +				{ 50000000,??1, 30, 20 },
> +				{ 75000000,??2, 45, 10 },
> +				{ 90000000,??2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)
> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;
> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			?* Wait until CGU relocks.
> +			?* If after timeout CGU is unlocked yet return error
> +			?*/
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;
> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);
> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> +
> +static const struct of_device_id pll_clk_id[] = {
> +	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
> +	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pll_clk_id);
> +
> +static struct platform_driver pll_clk_driver = {
> +	.driver = {
> +		.name = "axs10x-pll-clock",
> +		.of_match_table = pll_clk_id,
> +	},
> +	.probe = pll_clk_probe,
> +	.remove = pll_clk_remove,
> +};
> +builtin_platform_driver(pll_clk_driver);
> +
> +MODULE_AUTHOR("Vlad Zakharov <vzakhar at synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
> +MODULE_LICENSE("GPL v2");

Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.

Thanks!

-- 
Best regards,
Vlad Zakharov <vzakhar at synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 23:50     ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-03-03 23:50 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: Michael Turquette, linux-kernel, Jose Abreu, devicetree,
	linux-snps-arc, mark.rutland, robh, linux-clk

On 03/03, Vlad Zakharov wrote:
> Hi Michael, Stephen,
> 
> On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > dividers and corresponding control registers mapped to different addresses.
> > So we add one common driver for such PLLs.
> > 
> > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > ODIV. Output clock value is managed using these dividers.
> > 
> > We add pre-defined tables with supported rate values and appropriate
> > configurations of IDIV, FBDIV and ODIV for each value.
> > 
> > As of today we add support for PLLs that generate clock for the
> > following devices:
> >  * ARC core on AXC CPU tiles.
> >  * ARC PGU on ARC SDP Mainboard.
> > and more to come later.
> > 
> > Acked-by: Rob Herring <robh@kernel.org>
> > Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> > Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> > Cc: Michael Turquette <mturquette@baylibre.com>
> > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> 
> Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> 

I haven't reviewed it yet. The merge window is upon us right now
so I'll probably get to going through the queue this weekend/next
week.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 23:50     ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-03-03 23:50 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: Michael Turquette, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	Jose Abreu, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-snps-arc-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	mark.rutland-5wv7dgnIgG8, robh-DgEjT+Ai2ygdnm+yROfE0A,
	linux-clk-u79uwXL29TY76Z2rM5mHXA

On 03/03, Vlad Zakharov wrote:
> Hi Michael, Stephen,
> 
> On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > dividers and corresponding control registers mapped to different addresses.
> > So we add one common driver for such PLLs.
> > 
> > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > ODIV. Output clock value is managed using these dividers.
> > 
> > We add pre-defined tables with supported rate values and appropriate
> > configurations of IDIV, FBDIV and ODIV for each value.
> > 
> > As of today we add support for PLLs that generate clock for the
> > following devices:
> >  * ARC core on AXC CPU tiles.
> >  * ARC PGU on ARC SDP Mainboard.
> > and more to come later.
> > 
> > Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > Signed-off-by: Vlad Zakharov <vzakhar-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> > Signed-off-by: Jose Abreu <joabreu-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> > Cc: Michael Turquette <mturquette-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> > Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> > Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> 
> Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> 

I haven't reviewed it yet. The merge window is upon us right now
so I'll probably get to going through the queue this weekend/next
week.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 23:50     ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-03-03 23:50 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: Michael Turquette, linux-kernel, Jose Abreu, devicetree,
	linux-snps-arc, mark.rutland, robh, linux-clk

On 03/03, Vlad Zakharov wrote:
> Hi Michael, Stephen,
> 
> On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > dividers and corresponding control registers mapped to different addresses.
> > So we add one common driver for such PLLs.
> > 
> > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > ODIV. Output clock value is managed using these dividers.
> > 
> > We add pre-defined tables with supported rate values and appropriate
> > configurations of IDIV, FBDIV and ODIV for each value.
> > 
> > As of today we add support for PLLs that generate clock for the
> > following devices:
> >  * ARC core on AXC CPU tiles.
> >  * ARC PGU on ARC SDP Mainboard.
> > and more to come later.
> > 
> > Acked-by: Rob Herring <robh@kernel.org>
> > Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> > Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> > Cc: Michael Turquette <mturquette@baylibre.com>
> > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> 
> Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> 

I haven't reviewed it yet. The merge window is upon us right now
so I'll probably get to going through the queue this weekend/next
week.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-03 23:50     ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-03-03 23:50 UTC (permalink / raw)
  To: linux-snps-arc

On 03/03, Vlad Zakharov wrote:
> Hi Michael, Stephen,
> 
> On Tue, 2017-02-21@16:11 +0300, Vlad Zakharov wrote:
> > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > dividers and corresponding control registers mapped to different addresses.
> > So we add one common driver for such PLLs.
> > 
> > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > ODIV. Output clock value is managed using these dividers.
> > 
> > We add pre-defined tables with supported rate values and appropriate
> > configurations of IDIV, FBDIV and ODIV for each value.
> > 
> > As of today we add support for PLLs that generate clock for the
> > following devices:
> > ?* ARC core on AXC CPU tiles.
> > ?* ARC PGU on ARC SDP Mainboard.
> > and more to come later.
> > 
> > Acked-by: Rob Herring <robh at kernel.org>
> > Signed-off-by: Vlad Zakharov <vzakhar at synopsys.com>
> > Signed-off-by: Jose Abreu <joabreu at synopsys.com>
> > Cc: Michael Turquette <mturquette at baylibre.com>
> > Cc: Stephen Boyd <sboyd at codeaurora.org>
> > Cc: Mark Rutland <mark.rutland at arm.com>
> 
> Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> 

I haven't reviewed it yet. The merge window is upon us right now
so I'll probably get to going through the queue this weekend/next
week.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
  2017-03-03 23:50     ` Stephen Boyd
  (?)
  (?)
@ 2017-03-29 11:20       ` Vlad Zakharov
  -1 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-29 11:20 UTC (permalink / raw)
  To: sboyd, mturquette, Vladislav.Zakharov
  Cc: robh, linux-kernel, Jose.Abreu, mark.rutland, linux-clk,
	devicetree, linux-snps-arc

Hi Stephen, Michael,

On Fri, 2017-03-03 at 15:50 -0800, Stephen Boyd wrote:
> On 03/03, Vlad Zakharov wrote:
> > 
> > Hi Michael, Stephen,
> > 
> > On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> > > 
> > > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > > dividers and corresponding control registers mapped to different addresses.
> > > So we add one common driver for such PLLs.
> > > 
> > > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > > ODIV. Output clock value is managed using these dividers.
> > > 
> > > We add pre-defined tables with supported rate values and appropriate
> > > configurations of IDIV, FBDIV and ODIV for each value.
> > > 
> > > As of today we add support for PLLs that generate clock for the
> > > following devices:
> > >  * ARC core on AXC CPU tiles.
> > >  * ARC PGU on ARC SDP Mainboard.
> > > and more to come later.
> > > 
> > > Acked-by: Rob Herring <robh@kernel.org>
> > > Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> > > Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> > > Cc: Michael Turquette <mturquette@baylibre.com>
> > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > Cc: Mark Rutland <mark.rutland@arm.com>
> > 
> > Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> > 
> 
> I haven't reviewed it yet. The merge window is upon us right now
> so I'll probably get to going through the queue this weekend/next
> week.
> 

Please treat this message as a polite reminder to review my patch.
It is required for some subsystems on our boards, e.g. for ARC PGU.

Thanks.

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-29 11:20       ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-29 11:20 UTC (permalink / raw)
  To: sboyd, mturquette, Vladislav.Zakharov
  Cc: Jose.Abreu, robh, devicetree, linux-kernel, mark.rutland,
	linux-snps-arc, linux-clk

Hi Stephen, Michael,

On Fri, 2017-03-03 at 15:50 -0800, Stephen Boyd wrote:
> On 03/03, Vlad Zakharov wrote:
> > 
> > Hi Michael, Stephen,
> > 
> > On Tue, 2017-02-21 at 16:11 +0300, Vlad Zakharov wrote:
> > > 
> > > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > > dividers and corresponding control registers mapped to different addresses.
> > > So we add one common driver for such PLLs.
> > > 
> > > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > > ODIV. Output clock value is managed using these dividers.
> > > 
> > > We add pre-defined tables with supported rate values and appropriate
> > > configurations of IDIV, FBDIV and ODIV for each value.
> > > 
> > > As of today we add support for PLLs that generate clock for the
> > > following devices:
> > >  * ARC core on AXC CPU tiles.
> > >  * ARC PGU on ARC SDP Mainboard.
> > > and more to come later.
> > > 
> > > Acked-by: Rob Herring <robh@kernel.org>
> > > Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> > > Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> > > Cc: Michael Turquette <mturquette@baylibre.com>
> > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > Cc: Mark Rutland <mark.rutland@arm.com>
> > 
> > Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> > 
> 
> I haven't reviewed it yet. The merge window is upon us right now
> so I'll probably get to going through the queue this weekend/next
> week.
> 

Please treat this message as a polite reminder to review my patch.
It is required for some subsystems on our boards, e.g. for ARC PGU.

Thanks.

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>
_______________________________________________
linux-snps-arc mailing list
linux-snps-arc@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-snps-arc

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-29 11:20       ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-29 11:20 UTC (permalink / raw)
  To: sboyd, mturquette, Vladislav.Zakharov
  Cc: robh, linux-kernel, Jose.Abreu, mark.rutland, linux-clk,
	devicetree, linux-snps-arc

SGkgU3RlcGhlbiwgTWljaGFlbCwNCg0KT24gRnJpLCAyMDE3LTAzLTAzIGF0IDE1OjUwIC0wODAw
LCBTdGVwaGVuIEJveWQgd3JvdGU6DQo+IE9uIDAzLzAzLCBWbGFkIFpha2hhcm92IHdyb3RlOg0K
PiA+IA0KPiA+IEhpIE1pY2hhZWwsIFN0ZXBoZW4sDQo+ID4gDQo+ID4gT24gVHVlLCAyMDE3LTAy
LTIxIGF0IDE2OjExICswMzAwLCBWbGFkIFpha2hhcm92IHdyb3RlOg0KPiA+ID4gDQo+ID4gPiBB
WFMxMFggYm9hcmRzIG1hbmFnZXMgaXQncyBjbG9ja3MgdXNpbmcgdmFyaW91cyBQTExzLiBUaGVz
ZSBQTEwgaGFzIHNhbWUNCj4gPiA+IGRpdmlkZXJzIGFuZCBjb3JyZXNwb25kaW5nIGNvbnRyb2wg
cmVnaXN0ZXJzIG1hcHBlZCB0byBkaWZmZXJlbnQgYWRkcmVzc2VzLg0KPiA+ID4gU28gd2UgYWRk
IG9uZSBjb21tb24gZHJpdmVyIGZvciBzdWNoIFBMTHMuDQo+ID4gPiANCj4gPiA+IEVhY2ggUExM
IG9uIEFYUzEwWCBib2FyZCBjb25zaXN0IG9mIHRocmVlIGRpdmlkZXJzOiBJRElWLCBGQkRJViBh
bmQNCj4gPiA+IE9ESVYuIE91dHB1dCBjbG9jayB2YWx1ZSBpcyBtYW5hZ2VkIHVzaW5nIHRoZXNl
IGRpdmlkZXJzLg0KPiA+ID4gDQo+ID4gPiBXZSBhZGQgcHJlLWRlZmluZWQgdGFibGVzIHdpdGgg
c3VwcG9ydGVkIHJhdGUgdmFsdWVzIGFuZCBhcHByb3ByaWF0ZQ0KPiA+ID4gY29uZmlndXJhdGlv
bnMgb2YgSURJViwgRkJESVYgYW5kIE9ESVYgZm9yIGVhY2ggdmFsdWUuDQo+ID4gPiANCj4gPiA+
IEFzIG9mIHRvZGF5IHdlIGFkZCBzdXBwb3J0IGZvciBQTExzIHRoYXQgZ2VuZXJhdGUgY2xvY2sg
Zm9yIHRoZQ0KPiA+ID4gZm9sbG93aW5nIGRldmljZXM6DQo+ID4gPiDCoCogQVJDIGNvcmUgb24g
QVhDIENQVSB0aWxlcy4NCj4gPiA+IMKgKiBBUkMgUEdVIG9uIEFSQyBTRFAgTWFpbmJvYXJkLg0K
PiA+ID4gYW5kIG1vcmUgdG8gY29tZSBsYXRlci4NCj4gPiA+IA0KPiA+ID4gQWNrZWQtYnk6IFJv
YiBIZXJyaW5nIDxyb2JoQGtlcm5lbC5vcmc+DQo+ID4gPiBTaWduZWQtb2ZmLWJ5OiBWbGFkIFph
a2hhcm92IDx2emFraGFyQHN5bm9wc3lzLmNvbT4NCj4gPiA+IFNpZ25lZC1vZmYtYnk6IEpvc2Ug
QWJyZXUgPGpvYWJyZXVAc3lub3BzeXMuY29tPg0KPiA+ID4gQ2M6IE1pY2hhZWwgVHVycXVldHRl
IDxtdHVycXVldHRlQGJheWxpYnJlLmNvbT4NCj4gPiA+IENjOiBTdGVwaGVuIEJveWQgPHNib3lk
QGNvZGVhdXJvcmEub3JnPg0KPiA+ID4gQ2M6IE1hcmsgUnV0bGFuZCA8bWFyay5ydXRsYW5kQGFy
bS5jb20+DQo+ID4gDQo+ID4gTWF5YmUgeW91IGhhdmUgYW55IGNvbW1lbnRzIG9yIHJlbWFya3Mg
YWJvdXQgdGhpcyBwYXRjaD8gQW5kIGlmIHlvdSBkb24ndCBjb3VsZCB5b3UgcGxlYXNlIGFwcGx5
IGl0Lg0KPiA+IA0KPiANCj4gSSBoYXZlbid0IHJldmlld2VkIGl0IHlldC4gVGhlIG1lcmdlIHdp
bmRvdyBpcyB1cG9uIHVzIHJpZ2h0IG5vdw0KPiBzbyBJJ2xsIHByb2JhYmx5IGdldCB0byBnb2lu
ZyB0aHJvdWdoIHRoZSBxdWV1ZSB0aGlzIHdlZWtlbmQvbmV4dA0KPiB3ZWVrLg0KPiANCg0KUGxl
YXNlIHRyZWF0IHRoaXMgbWVzc2FnZSBhcyBhIHBvbGl0ZSByZW1pbmRlciB0byByZXZpZXcgbXkg
cGF0Y2guDQpJdCBpcyByZXF1aXJlZCBmb3Igc29tZSBzdWJzeXN0ZW1zIG9uIG91ciBib2FyZHMs
IGUuZy4gZm9yIEFSQyBQR1UuDQoNClRoYW5rcy4NCg0KLS0gDQpCZXN0IHJlZ2FyZHMsDQpWbGFk
IFpha2hhcm92IDx2emFraGFyQHN5bm9wc3lzLmNvbT4=

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-03-29 11:20       ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-03-29 11:20 UTC (permalink / raw)
  To: linux-snps-arc

Hi Stephen, Michael,

On Fri, 2017-03-03@15:50 -0800, Stephen Boyd wrote:
> On 03/03, Vlad Zakharov wrote:
> > 
> > Hi Michael, Stephen,
> > 
> > On Tue, 2017-02-21@16:11 +0300, Vlad Zakharov wrote:
> > > 
> > > AXS10X boards manages it's clocks using various PLLs. These PLL has same
> > > dividers and corresponding control registers mapped to different addresses.
> > > So we add one common driver for such PLLs.
> > > 
> > > Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> > > ODIV. Output clock value is managed using these dividers.
> > > 
> > > We add pre-defined tables with supported rate values and appropriate
> > > configurations of IDIV, FBDIV and ODIV for each value.
> > > 
> > > As of today we add support for PLLs that generate clock for the
> > > following devices:
> > > ?* ARC core on AXC CPU tiles.
> > > ?* ARC PGU on ARC SDP Mainboard.
> > > and more to come later.
> > > 
> > > Acked-by: Rob Herring <robh at kernel.org>
> > > Signed-off-by: Vlad Zakharov <vzakhar at synopsys.com>
> > > Signed-off-by: Jose Abreu <joabreu at synopsys.com>
> > > Cc: Michael Turquette <mturquette at baylibre.com>
> > > Cc: Stephen Boyd <sboyd at codeaurora.org>
> > > Cc: Mark Rutland <mark.rutland at arm.com>
> > 
> > Maybe you have any comments or remarks about this patch? And if you don't could you please apply it.
> > 
> 
> I haven't reviewed it yet. The merge window is upon us right now
> so I'll probably get to going through the queue this weekend/next
> week.
> 

Please treat this message as a polite reminder to review my patch.
It is required for some subsystems on our boards, e.g. for ARC PGU.

Thanks.

-- 
Best regards,
Vlad Zakharov <vzakhar at synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-03 10:54   ` Jose Abreu
  0 siblings, 0 replies; 30+ messages in thread
From: Jose Abreu @ 2017-04-03 10:54 UTC (permalink / raw)
  To: Vlad Zakharov, linux-clk
  Cc: linux-kernel, linux-snps-arc, devicetree, Michael Turquette,
	Stephen Boyd, Mark Rutland, Rob Herring

Hi Vlad,


On 21-02-2017 13:11, Vlad Zakharov wrote:
> AXS10X boards manages it's clocks using various PLLs. These PLL has same
> dividers and corresponding control registers mapped to different addresses.
> So we add one common driver for such PLLs.
>
> Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> ODIV. Output clock value is managed using these dividers.
>
> We add pre-defined tables with supported rate values and appropriate
> configurations of IDIV, FBDIV and ODIV for each value.
>
> As of today we add support for PLLs that generate clock for the
> following devices:
>  * ARC core on AXC CPU tiles.
>  * ARC PGU on ARC SDP Mainboard.
> and more to come later.
>
> Acked-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Vlad Zakharov <vzakhar@synopsys.com>
> Signed-off-by: Jose Abreu <joabreu@synopsys.com>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> ---
> Cc: Rob Herring <robh@kernel.org>
> Changes v1..v2
>  - Replace '_' with '-' in device tree nodes
>
>  .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
>  MAINTAINERS                                        |   6 +
>  drivers/clk/axs10x/Makefile                        |   1 +
>  drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
>  4 files changed, 419 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
>  create mode 100644 drivers/clk/axs10x/pll_clock.c
>
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk@80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;
> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver
> +M:	Vlad Zakharov <vzakhar@synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones@linaro.org>
>  M:	Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers
> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.
> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;
> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};
> +
> +static struct pll_of_data pgu_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)
> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;

"uint32_t" -> "u32"

> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;
> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);

Typo: should be "return PTR_ERR(pll_clk->lock);"

> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> +
> +static const struct of_device_id pll_clk_id[] = {
> +	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
> +	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pll_clk_id);
> +
> +static struct platform_driver pll_clk_driver = {
> +	.driver = {
> +		.name = "axs10x-pll-clock",
> +		.of_match_table = pll_clk_id,
> +	},
> +	.probe = pll_clk_probe,
> +	.remove = pll_clk_remove,
> +};
> +builtin_platform_driver(pll_clk_driver);
> +
> +MODULE_AUTHOR("Vlad Zakharov <vzakhar@synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-03 10:54   ` Jose Abreu
  0 siblings, 0 replies; 30+ messages in thread
From: Jose Abreu @ 2017-04-03 10:54 UTC (permalink / raw)
  To: Vlad Zakharov, linux-clk-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-snps-arc-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Michael Turquette,
	Stephen Boyd, Mark Rutland, Rob Herring

Hi Vlad,


On 21-02-2017 13:11, Vlad Zakharov wrote:
> AXS10X boards manages it's clocks using various PLLs. These PLL has same
> dividers and corresponding control registers mapped to different addresses.
> So we add one common driver for such PLLs.
>
> Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> ODIV. Output clock value is managed using these dividers.
>
> We add pre-defined tables with supported rate values and appropriate
> configurations of IDIV, FBDIV and ODIV for each value.
>
> As of today we add support for PLLs that generate clock for the
> following devices:
>  * ARC core on AXC CPU tiles.
>  * ARC PGU on ARC SDP Mainboard.
> and more to come later.
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Vlad Zakharov <vzakhar-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Jose Abreu <joabreu-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> Cc: Michael Turquette <mturquette-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> ---
> Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Changes v1..v2
>  - Replace '_' with '-' in device tree nodes
>
>  .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
>  MAINTAINERS                                        |   6 +
>  drivers/clk/axs10x/Makefile                        |   1 +
>  drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
>  4 files changed, 419 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
>  create mode 100644 drivers/clk/axs10x/pll_clock.c
>
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk@80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;
> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver
> +M:	Vlad Zakharov <vzakhar-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>  M:	Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers
> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.
> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;
> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};
> +
> +static struct pll_of_data pgu_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)
> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;

"uint32_t" -> "u32"

> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;
> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);

Typo: should be "return PTR_ERR(pll_clk->lock);"

> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> +
> +static const struct of_device_id pll_clk_id[] = {
> +	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
> +	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pll_clk_id);
> +
> +static struct platform_driver pll_clk_driver = {
> +	.driver = {
> +		.name = "axs10x-pll-clock",
> +		.of_match_table = pll_clk_id,
> +	},
> +	.probe = pll_clk_probe,
> +	.remove = pll_clk_remove,
> +};
> +builtin_platform_driver(pll_clk_driver);
> +
> +MODULE_AUTHOR("Vlad Zakharov <vzakhar-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>");
> +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
> +MODULE_LICENSE("GPL v2");

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-03 10:54   ` Jose Abreu
  0 siblings, 0 replies; 30+ messages in thread
From: Jose Abreu @ 2017-04-03 10:54 UTC (permalink / raw)
  To: linux-snps-arc

Hi Vlad,


On 21-02-2017 13:11, Vlad Zakharov wrote:
> AXS10X boards manages it's clocks using various PLLs. These PLL has same
> dividers and corresponding control registers mapped to different addresses.
> So we add one common driver for such PLLs.
>
> Each PLL on AXS10X board consist of three dividers: IDIV, FBDIV and
> ODIV. Output clock value is managed using these dividers.
>
> We add pre-defined tables with supported rate values and appropriate
> configurations of IDIV, FBDIV and ODIV for each value.
>
> As of today we add support for PLLs that generate clock for the
> following devices:
>  * ARC core on AXC CPU tiles.
>  * ARC PGU on ARC SDP Mainboard.
> and more to come later.
>
> Acked-by: Rob Herring <robh at kernel.org>
> Signed-off-by: Vlad Zakharov <vzakhar at synopsys.com>
> Signed-off-by: Jose Abreu <joabreu at synopsys.com>
> Cc: Michael Turquette <mturquette at baylibre.com>
> Cc: Stephen Boyd <sboyd at codeaurora.org>
> Cc: Mark Rutland <mark.rutland at arm.com>
> ---
> Cc: Rob Herring <robh at kernel.org>
> Changes v1..v2
>  - Replace '_' with '-' in device tree nodes
>
>  .../devicetree/bindings/clock/snps,pll-clock.txt   |  28 ++
>  MAINTAINERS                                        |   6 +
>  drivers/clk/axs10x/Makefile                        |   1 +
>  drivers/clk/axs10x/pll_clock.c                     | 384 +++++++++++++++++++++
>  4 files changed, 419 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/snps,pll-clock.txt
>  create mode 100644 drivers/clk/axs10x/pll_clock.c
>
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk at 80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;
> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver
> +M:	Vlad Zakharov <vzakhar at synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones at linaro.org>
>  M:	Arnd Bergmann <arnd at arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers
> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.
> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;
> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};
> +
> +static struct pll_of_data pgu_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {
> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)
> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));
> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;

"uint32_t" -> "u32"

> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;
> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);

Typo: should be "return PTR_ERR(pll_clk->lock);"

> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> +
> +static const struct of_device_id pll_clk_id[] = {
> +	{ .compatible = "snps,axs10x-arc-pll-clock", .data = &arc_pll_data},
> +	{ .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_data},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pll_clk_id);
> +
> +static struct platform_driver pll_clk_driver = {
> +	.driver = {
> +		.name = "axs10x-pll-clock",
> +		.of_match_table = pll_clk_id,
> +	},
> +	.probe = pll_clk_probe,
> +	.remove = pll_clk_remove,
> +};
> +builtin_platform_driver(pll_clk_driver);
> +
> +MODULE_AUTHOR("Vlad Zakharov <vzakhar at synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-05  1:35   ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-04-05  1:35 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: linux-clk, linux-kernel, linux-snps-arc, devicetree, Jose Abreu,
	Michael Turquette, Mark Rutland, Rob Herring

On 02/21, Vlad Zakharov wrote:
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk@80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;

Can you add the braces around register pairs in the example?
Makes it clearer with a quick glance.

> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver

This also includes the existing driver there. Jose can ack this?

> +M:	Vlad Zakharov <vzakhar@synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones@linaro.org>
>  M:	Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers

s/detirmine/determine/

> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.

s/and/an/?

> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;

const?

> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};

Why the structure for another structure pointer? Just use
pll_of_table for now?

> +
> +static struct pll_of_data pgu_pll_data = {

const?

> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,

Can this be another clk in the framework instead of hardcoding
the parent rate?

> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },

There's only ever one, so I'm confused why we're making a list.

> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {

const?

> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)

reg can go on the same line as clk?

> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));

Useless parenthesis.

> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;
> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;

Useless cast?

> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;

Can just be a return without the else part.

> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);
> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);

Can you please use the clk_hw based provider and clk registration
functions?

> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);

Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
driver need to probe and also have this of declare happen? Is the
PLL special and needs to be used for the timers?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-05  1:35   ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-04-05  1:35 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-snps-arc-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Jose Abreu, Michael Turquette,
	Mark Rutland, Rob Herring

On 02/21, Vlad Zakharov wrote:
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk@80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;

Can you add the braces around register pairs in the example?
Makes it clearer with a quick glance.

> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver

This also includes the existing driver there. Jose can ack this?

> +M:	Vlad Zakharov <vzakhar-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>  M:	Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers

s/detirmine/determine/

> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.

s/and/an/?

> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;

const?

> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};

Why the structure for another structure pointer? Just use
pll_of_table for now?

> +
> +static struct pll_of_data pgu_pll_data = {

const?

> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,

Can this be another clk in the framework instead of hardcoding
the parent rate?

> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },

There's only ever one, so I'm confused why we're making a list.

> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {

const?

> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)

reg can go on the same line as clk?

> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));

Useless parenthesis.

> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;
> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;

Useless cast?

> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;

Can just be a return without the else part.

> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);
> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);

Can you please use the clk_hw based provider and clk registration
functions?

> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);

Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
driver need to probe and also have this of declare happen? Is the
PLL special and needs to be used for the timers?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-05  1:35   ` Stephen Boyd
  0 siblings, 0 replies; 30+ messages in thread
From: Stephen Boyd @ 2017-04-05  1:35 UTC (permalink / raw)
  To: linux-snps-arc

On 02/21, Vlad Zakharov wrote:
> diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> new file mode 100644
> index 0000000..5706246
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> @@ -0,0 +1,28 @@
> +Binding for the AXS10X Generic PLL clock
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible: should be "snps,axs10x-<name>-pll-clock"
> +  "snps,axs10x-arc-pll-clock"
> +  "snps,axs10x-pgu-pll-clock"
> +- reg: should always contain 2 pairs address - length: first for PLL config
> +registers and second for corresponding LOCK CGU register.
> +- clocks: shall be the input parent clock phandle for the PLL.
> +- #clock-cells: from common clock binding; Should always be set to 0.
> +
> +Example:
> +	input-clk: input-clk {
> +		clock-frequency = <33333333>;
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +	};
> +
> +	core-clk: core-clk at 80 {
> +		compatible = "snps,axs10x-arc-pll-clock";
> +		reg = <0x80 0x10 0x100 0x10>;

Can you add the braces around register pairs in the example?
Makes it clearer with a quick glance.

> +		#clock-cells = <0>;
> +		clocks = <&input-clk>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3960e7f..5805833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11910,6 +11910,12 @@ F:	arch/arc/plat-axs10x
>  F:	arch/arc/boot/dts/ax*
>  F:	Documentation/devicetree/bindings/arc/axs10*
>  
> +SYNOPSYS ARC SDP clock driver

This also includes the existing driver there. Jose can ack this?

> +M:	Vlad Zakharov <vzakhar at synopsys.com>
> +S:	Supported
> +F:	drivers/clk/axs10x/*
> +F:	Documentation/devicetree/bindings/clock/snps,pll-clock.txt
> +
>  SYSTEM CONFIGURATION (SYSCON)
>  M:	Lee Jones <lee.jones at linaro.org>
>  M:	Arnd Bergmann <arnd at arndb.de>
> diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile
> index 01996b8..d747dea 100644
> --- a/drivers/clk/axs10x/Makefile
> +++ b/drivers/clk/axs10x/Makefile
> @@ -1 +1,2 @@
>  obj-y += i2s_pll_clock.o
> +obj-y += pll_clock.o
> diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
> new file mode 100644
> index 0000000..784a0a2
> --- /dev/null
> +++ b/drivers/clk/axs10x/pll_clock.c
> @@ -0,0 +1,384 @@
> +/*
> + * Synopsys AXS10X SDP Generic PLL clock driver
> + *
> + * Copyright (C) 2017 Synopsys
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +/* PLL registers addresses */
> +#define PLL_REG_IDIV	0x0
> +#define PLL_REG_FBDIV	0x4
> +#define PLL_REG_ODIV	0x8
> +
> +/*
> + * Bit fields of the PLL IDIV/FBDIV/ODIV registers:
> + *  ________________________________________________________________________
> + * |31                15|    14    |   13   |  12  |11         6|5         0|
> + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--|
> + * |____________________|__________|________|______|____________|___________|
> + *
> + * Following macros detirmine the way of access to these registers

s/detirmine/determine/

> + * They should be set up only using the macros.
> + * reg should be and uint32_t variable.

s/and/an/?

> + */
> +
> +#define PLL_REG_GET_LOW(reg)			\
> +	(((reg) & (0x3F << 0)) >> 0)
> +#define PLL_REG_GET_HIGH(reg)			\
> +	(((reg) & (0x3F << 6)) >> 6)
> +#define PLL_REG_GET_EDGE(reg)			\
> +	(((reg) & (BIT(12))) ? 1 : 0)
> +#define PLL_REG_GET_BYPASS(reg)			\
> +	(((reg) & (BIT(13))) ? 1 : 0)
> +#define PLL_REG_GET_NOUPD(reg)			\
> +	(((reg) & (BIT(14))) ? 1 : 0)
> +#define PLL_REG_GET_PAD(reg)			\
> +	(((reg) & (0x1FFFF << 15)) >> 15)
> +
> +#define PLL_REG_SET_LOW(reg, value)		\
> +	{ reg |= (((value) & 0x3F) << 0); }
> +#define PLL_REG_SET_HIGH(reg, value)	\
> +	{ reg |= (((value) & 0x3F) << 6); }
> +#define PLL_REG_SET_EDGE(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 12); }
> +#define PLL_REG_SET_BYPASS(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 13); }
> +#define PLL_REG_SET_NOUPD(reg, value)	\
> +	{ reg |= (((value) & 0x01) << 14); }
> +#define PLL_REG_SET_PAD(reg, value)		\
> +	{ reg |= (((value) & 0x1FFFF) << 15); }
> +
> +#define PLL_LOCK	0x1
> +#define PLL_MAX_LOCK_TIME 100 /* 100 us */
> +
> +struct pll_cfg {
> +	u32 rate;
> +	u32 idiv;
> +	u32 fbdiv;
> +	u32 odiv;
> +};
> +
> +struct pll_of_table {
> +	unsigned long prate;
> +	struct pll_cfg *pll_cfg_table;

const?

> +};
> +
> +struct pll_of_data {
> +	struct pll_of_table *pll_table;
> +};

Why the structure for another structure pointer? Just use
pll_of_table for now?

> +
> +static struct pll_of_data pgu_pll_data = {

const?

> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 27000000,

Can this be another clk in the framework instead of hardcoding
the parent rate?

> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 25200000, 1, 84, 90 },
> +				{ 50000000, 1, 100, 54 },
> +				{ 74250000, 1, 44, 16 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },

There's only ever one, so I'm confused why we're making a list.

> +	},
> +};
> +
> +static struct pll_of_data arc_pll_data = {

const?

> +	.pll_table = (struct pll_of_table []){
> +		{
> +			.prate = 33333333,
> +			.pll_cfg_table = (struct pll_cfg []){
> +				{ 33333333,  1, 1,  1 },
> +				{ 50000000,  1, 30, 20 },
> +				{ 75000000,  2, 45, 10 },
> +				{ 90000000,  2, 54, 10 },
> +				{ 100000000, 1, 30, 10 },
> +				{ 125000000, 2, 45, 6 },
> +				{ },
> +			},
> +		},
> +		/* Used as list limiter */
> +		{ },
> +	},
> +};
> +
> +struct pll_clk {
> +	void __iomem *base;
> +	void __iomem *lock;
> +	const struct pll_of_data *pll_data;
> +	struct clk_hw hw;
> +	struct device *dev;
> +};
> +
> +static inline void pll_write(struct pll_clk *clk, unsigned int reg,
> +		unsigned int val)
> +{
> +	iowrite32(val, clk->base + reg);
> +}
> +
> +static inline u32 pll_read(struct pll_clk *clk,
> +		unsigned int reg)

reg can go on the same line as clk?

> +{
> +	return ioread32(clk->base + reg);
> +}
> +
> +static inline struct pll_clk *to_pll_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct pll_clk, hw);
> +}
> +
> +static inline u32 div_get_value(unsigned int reg)
> +{
> +	if (PLL_REG_GET_BYPASS(reg))
> +		return 1;
> +
> +	return (PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg));

Useless parenthesis.

> +}
> +
> +static inline u32 encode_div(unsigned int id, int upd)
> +{
> +	uint32_t div = 0;
> +
> +	PLL_REG_SET_LOW(div, (id%2 == 0) ? id >> 1 : (id >> 1) + 1);
> +	PLL_REG_SET_HIGH(div, id >> 1);
> +	PLL_REG_SET_EDGE(div, id%2);
> +	PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0);
> +	PLL_REG_SET_NOUPD(div, !upd);
> +
> +	return div;
> +}
> +
> +static const struct pll_cfg *pll_get_cfg(unsigned long prate,
> +		const struct pll_of_table *pll_table)
> +{
> +	int i;
> +
> +	for (i = 0; pll_table[i].prate != 0; i++)
> +		if (pll_table[i].prate == prate)
> +			return pll_table[i].pll_cfg_table;
> +
> +	return NULL;
> +}
> +
> +static unsigned long pll_recalc_rate(struct clk_hw *hw,
> +			unsigned long parent_rate)
> +{
> +	u64 rate;
> +	u32 idiv, fbdiv, odiv;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +
> +	idiv = div_get_value(pll_read(clk, PLL_REG_IDIV));
> +	fbdiv = div_get_value(pll_read(clk, PLL_REG_FBDIV));
> +	odiv = div_get_value(pll_read(clk, PLL_REG_ODIV));
> +
> +	rate = (u64)parent_rate * fbdiv;
> +	do_div(rate, idiv * odiv);
> +
> +	return (unsigned long)rate;

Useless cast?

> +}
> +
> +static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *prate)
> +{
> +	int i;
> +	long best_rate;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(*prate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
> +		return -EINVAL;
> +	}
> +
> +	if (pll_cfg[0].rate == 0)
> +		return -EINVAL;
> +
> +	best_rate = pll_cfg[0].rate;
> +
> +	for (i = 1; pll_cfg[i].rate != 0; i++) {
> +		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
> +			best_rate = pll_cfg[i].rate;
> +	}
> +
> +	return best_rate;
> +}
> +
> +static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			unsigned long parent_rate)
> +{
> +	int i;
> +	struct pll_clk *clk = to_pll_clk(hw);
> +	const struct pll_cfg *pll_cfg = pll_get_cfg(parent_rate,
> +			clk->pll_data->pll_table);
> +
> +	if (!pll_cfg) {
> +		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; pll_cfg[i].rate != 0; i++) {
> +		if (pll_cfg[i].rate == rate) {
> +			pll_write(clk, PLL_REG_IDIV,
> +					encode_div(pll_cfg[i].idiv, 0));
> +			pll_write(clk, PLL_REG_FBDIV,
> +					encode_div(pll_cfg[i].fbdiv, 0));
> +			pll_write(clk, PLL_REG_ODIV,
> +					encode_div(pll_cfg[i].odiv, 1));
> +
> +			/*
> +			 * Wait until CGU relocks.
> +			 * If after timeout CGU is unlocked yet return error
> +			 */
> +			udelay(PLL_MAX_LOCK_TIME);
> +			if (ioread32(clk->lock) & PLL_LOCK)
> +				return 0;
> +			else
> +				return -ETIMEDOUT;

Can just be a return without the else part.

> +		}
> +	}
> +
> +	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
> +			parent_rate);
> +	return -EINVAL;
> +}
> +
> +static const struct clk_ops pll_ops = {
> +	.recalc_rate = pll_recalc_rate,
> +	.round_rate = pll_round_rate,
> +	.set_rate = pll_set_rate,
> +};
> +
> +static int pll_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct resource *mem;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return -ENOMEM;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pll_clk->base = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->base))
> +		return PTR_ERR(pll_clk->base);
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	pll_clk->lock = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(pll_clk->lock))
> +		return PTR_ERR(pll_clk->base);
> +
> +	init.name = dev->of_node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(dev->of_node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = 1;
> +	pll_clk->hw.init = &init;
> +	pll_clk->dev = dev;
> +	pll_clk->pll_data = of_device_get_match_data(dev);
> +
> +	if (!pll_clk->pll_data) {
> +		dev_err(dev, "No OF match data provided\n");
> +			return -EINVAL;
> +	}
> +
> +	clk = devm_clk_register(dev, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "failed to register %s clock (%ld)\n",
> +				init.name, PTR_ERR(clk));
> +		return PTR_ERR(clk);
> +	}
> +
> +	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
> +}
> +
> +static int pll_clk_remove(struct platform_device *pdev)
> +{
> +	of_clk_del_provider(pdev->dev.of_node);
> +	return 0;
> +}
> +
> +static void __init of_pll_clk_setup(struct device_node *node)
> +{
> +	const char *parent_name;
> +	struct clk *clk;
> +	struct pll_clk *pll_clk;
> +	struct clk_init_data init = { };
> +
> +	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
> +	if (!pll_clk)
> +		return;
> +
> +	pll_clk->base = of_iomap(node, 0);
> +	if (!pll_clk->base) {
> +		pr_err("failed to map pll div registers\n");
> +		iounmap(pll_clk->base);
> +		return;
> +	}
> +
> +	pll_clk->lock = of_iomap(node, 1);
> +	if (!pll_clk->lock) {
> +		pr_err("failed to map pll lock register\n");
> +		iounmap(pll_clk->lock);
> +		return;
> +	}
> +
> +	init.name = node->name;
> +	init.ops = &pll_ops;
> +	parent_name = of_clk_get_parent_name(node, 0);
> +	init.parent_names = &parent_name;
> +	init.num_parents = parent_name ? 1 : 0;
> +	pll_clk->hw.init = &init;
> +	pll_clk->pll_data = &arc_pll_data;
> +
> +	clk = clk_register(NULL, &pll_clk->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("failed to register %s clock (%ld)\n",
> +				node->name, PTR_ERR(clk));
> +		kfree(pll_clk);
> +		return;
> +	}
> +
> +	of_clk_add_provider(node, of_clk_src_simple_get, clk);

Can you please use the clk_hw based provider and clk registration
functions?

> +}
> +
> +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);

Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
driver need to probe and also have this of declare happen? Is the
PLL special and needs to be used for the timers?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
  2017-04-05  1:35   ` Stephen Boyd
  (?)
  (?)
@ 2017-04-05 16:06     ` Vlad Zakharov
  -1 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-05 16:06 UTC (permalink / raw)
  To: sboyd
  Cc: mark.rutland, linux-kernel, Jose.Abreu, mturquette, devicetree,
	linux-clk, linux-snps-arc

Hi Stephen,

On Tue, 2017-04-04 at 18:35 -0700, Stephen Boyd wrote:
> > +     .pll_table = (struct pll_of_table []){
> > +             {
> > +                     .prate = 27000000,
> 
> Can this be another clk in the framework instead of hardcoding
> the parent rate?

In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching for
the correct table comparing .parent_node field with real hardware parent clock frequency:
---------------------------------->8------------------------------------
for (i = 0; pll_table[i].prate != 0; i++)
    if (pll_table[i].prate == prate)
        return pll_table[i].pll_cfg_table;
---------------------------------->8------------------------------------

> 
> > +                     .pll_cfg_table = (struct pll_cfg []){
> > +                             { 25200000, 1, 84, 90 },
> > +                             { 50000000, 1, 100, 54 },
> > +                             { 74250000, 1, 44, 16 },
> > +                             { },
> > +                     },
> > +             },
> > +             /* Used as list limiter */
> > +             { },
> 
> There's only ever one, so I'm confused why we're making a list.

By this patch we only add support of core arc pll and pgu pll and today they are clocked by the only parent clocks
introduced here. But other plls on axs10x may be driven by different or configurable clocks, so in such cases we will
have more than one entry in this list. And we are going to add more supported plls to this driver in the nearest future.

> > +
> > +     clk = clk_register(NULL, &pll_clk->hw);
> > +     if (IS_ERR(clk)) {
> > +             pr_err("failed to register %s clock (%ld)\n",
> > +                             node->name, PTR_ERR(clk));
> > +             kfree(pll_clk);
> > +             return;
> > +     }
> > +
> > +     of_clk_add_provider(node, of_clk_src_simple_get, clk);
> 
> Can you please use the clk_hw based provider and clk registration
> functions?

Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk registration
functions please? In which cases they are preferred? 

> 
> > +}
> > +
> > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> 
> Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> driver need to probe and also have this of declare happen? Is the
> PLL special and needs to be used for the timers?

It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
drive PGU clock frequency and other subsystems and so we add usual probe func.

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-05 16:06     ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-05 16:06 UTC (permalink / raw)
  To: sboyd
  Cc: mark.rutland, linux-kernel, Jose.Abreu, mturquette, devicetree,
	linux-clk, linux-snps-arc

Hi Stephen,

On Tue, 2017-04-04 at 18:35 -0700, Stephen Boyd wrote:
> > +     .pll_table = (struct pll_of_table []){
> > +             {
> > +                     .prate = 27000000,
> 
> Can this be another clk in the framework instead of hardcoding
> the parent rate?

In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching for
the correct table comparing .parent_node field with real hardware parent clock frequency:
---------------------------------->8------------------------------------
for (i = 0; pll_table[i].prate != 0; i++)
    if (pll_table[i].prate == prate)
        return pll_table[i].pll_cfg_table;
---------------------------------->8------------------------------------

> 
> > +                     .pll_cfg_table = (struct pll_cfg []){
> > +                             { 25200000, 1, 84, 90 },
> > +                             { 50000000, 1, 100, 54 },
> > +                             { 74250000, 1, 44, 16 },
> > +                             { },
> > +                     },
> > +             },
> > +             /* Used as list limiter */
> > +             { },
> 
> There's only ever one, so I'm confused why we're making a list.

By this patch we only add support of core arc pll and pgu pll and today they are clocked by the only parent clocks
introduced here. But other plls on axs10x may be driven by different or configurable clocks, so in such cases we will
have more than one entry in this list. And we are going to add more supported plls to this driver in the nearest future.

> > +
> > +     clk = clk_register(NULL, &pll_clk->hw);
> > +     if (IS_ERR(clk)) {
> > +             pr_err("failed to register %s clock (%ld)\n",
> > +                             node->name, PTR_ERR(clk));
> > +             kfree(pll_clk);
> > +             return;
> > +     }
> > +
> > +     of_clk_add_provider(node, of_clk_src_simple_get, clk);
> 
> Can you please use the clk_hw based provider and clk registration
> functions?

Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk registration
functions please? In which cases they are preferred? 

> 
> > +}
> > +
> > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> 
> Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> driver need to probe and also have this of declare happen? Is the
> PLL special and needs to be used for the timers?

It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
drive PGU clock frequency and other subsystems and so we add usual probe func.

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-05 16:06     ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-05 16:06 UTC (permalink / raw)
  To: sboyd
  Cc: mark.rutland, linux-kernel, Jose.Abreu, mturquette, devicetree,
	linux-clk, linux-snps-arc

SGkgU3RlcGhlbiwNCg0KT24gVHVlLCAyMDE3LTA0LTA0IGF0IDE4OjM1IC0wNzAwLCBTdGVwaGVu
IEJveWQgd3JvdGU6DQo+ID4gK8KgwqDCoMKgwqAucGxsX3RhYmxlID0gKHN0cnVjdCBwbGxfb2Zf
dGFibGUgW10pew0KPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHsNCj4gPiArwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgLnByYXRlID0gMjcwMDAwMDAsDQo+
IA0KPiBDYW4gdGhpcyBiZSBhbm90aGVyIGNsayBpbiB0aGUgZnJhbWV3b3JrIGluc3RlYWQgb2Yg
aGFyZGNvZGluZw0KPiB0aGUgcGFyZW50IHJhdGU/DQoNCkluIGZhY3QgdGhlcmUgaXMgYW5vdGhl
ciBjbGsgaW4gdGhlIGZyYW1ld29yayB0aGF0IHJlcHJlc2VudHMgdGhpcyBwYXJlbnQgY2xvY2su
IEJ1dCB0aGlzIGZpZWxkIGlzIG5lZWRlZCB0byBnZXQNCmFwcHJvcHJpYXRlIHBsbF9jZmdfdGFi
bGUgYXMgaXQgZGVwZW5kcyBvbiBwYXJlbnQgY2xvY2sgZnJlcXVlbmN5LiBCZWxvdyBpbiBwbGxf
Y2ZnX2dldCBmdW5jdGlvbiB3ZSBhcmUgc2VhcmNoaW5nIGZvcg0KdGhlIGNvcnJlY3QgdGFibGUg
Y29tcGFyaW5nIC5wYXJlbnRfbm9kZSBmaWVsZCB3aXRoIHJlYWwgaGFyZHdhcmUgcGFyZW50IGNs
b2NrIGZyZXF1ZW5jeToNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+OC0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KZm9yIChpID0gMDsgcGxsX3RhYmxlW2ld
LnByYXRlICE9IDA7IGkrKykNCsKgIMKgIGlmIChwbGxfdGFibGVbaV0ucHJhdGUgPT0gcHJhdGUp
DQrCoCDCoCDCoCDCoCByZXR1cm4gcGxsX3RhYmxlW2ldLnBsbF9jZmdfdGFibGU7DQotLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPjgtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0NCg0KPiANCj4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgLnBsbF9jZmdfdGFibGUgPSAoc3RydWN0IHBsbF9jZmcgW10pew0KPiA+ICvCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgeyAyNTIw
MDAwMCwgMSwgODQsIDkwIH0sDQo+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqB7IDUwMDAwMDAwLCAxLCAxMDAsIDU0IH0sDQo+ID4g
K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqB7IDc0MjUwMDAwLCAxLCA0NCwgMTYgfSwNCj4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHsgfSwNCj4gPiArwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgfSwNCj4gPiArwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqB9LA0KPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoC8qIFVzZWQgYXMgbGlz
dCBsaW1pdGVyICovDQo+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgeyB9LA0KPiANCj4g
VGhlcmUncyBvbmx5IGV2ZXIgb25lLCBzbyBJJ20gY29uZnVzZWQgd2h5IHdlJ3JlIG1ha2luZyBh
IGxpc3QuDQoNCkJ5IHRoaXMgcGF0Y2ggd2Ugb25seSBhZGQgc3VwcG9ydCBvZiBjb3JlIGFyYyBw
bGwgYW5kIHBndSBwbGwgYW5kIHRvZGF5IHRoZXkgYXJlIGNsb2NrZWQgYnkgdGhlIG9ubHkgcGFy
ZW50IGNsb2Nrcw0KaW50cm9kdWNlZCBoZXJlLiBCdXQgb3RoZXIgcGxscyBvbiBheHMxMHggbWF5
IGJlIGRyaXZlbiBieSBkaWZmZXJlbnQgb3IgY29uZmlndXJhYmxlIGNsb2Nrcywgc28gaW4gc3Vj
aCBjYXNlcyB3ZSB3aWxsDQpoYXZlIG1vcmUgdGhhbiBvbmUgZW50cnkgaW4gdGhpcyBsaXN0LiBB
bmQgd2UgYXJlIGdvaW5nIHRvIGFkZCBtb3JlIHN1cHBvcnRlZCBwbGxzIHRvIHRoaXMgZHJpdmVy
IGluIHRoZSBuZWFyZXN0IGZ1dHVyZS4NCg0KPiA+ICsNCj4gPiArwqDCoMKgwqDCoGNsayA9IGNs
a19yZWdpc3RlcihOVUxMLCAmcGxsX2Nsay0+aHcpOw0KPiA+ICvCoMKgwqDCoMKgaWYgKElTX0VS
UihjbGspKSB7DQo+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcHJfZXJyKCJmYWlsZWQg
dG8gcmVnaXN0ZXIgJXMgY2xvY2sgKCVsZClcbiIsDQo+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBub2RlLT5uYW1lLCBQVFJfRVJS
KGNsaykpOw0KPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGtmcmVlKHBsbF9jbGspOw0K
PiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJldHVybjsNCj4gPiArwqDCoMKgwqDCoH0N
Cj4gPiArDQo+ID4gK8KgwqDCoMKgwqBvZl9jbGtfYWRkX3Byb3ZpZGVyKG5vZGUsIG9mX2Nsa19z
cmNfc2ltcGxlX2dldCwgY2xrKTsNCj4gDQo+IENhbiB5b3UgcGxlYXNlIHVzZSB0aGUgY2xrX2h3
IGJhc2VkIHByb3ZpZGVyIGFuZCBjbGsgcmVnaXN0cmF0aW9uDQo+IGZ1bmN0aW9ucz8NCg0KU3Vy
ZS4gQ291bGQgeW91IGJlIHNvIGtpbmQgdG8gZXhwbGFpbiB3aGF0IGlzIHRoZSBkaWZmZXJlbmNl
IGJldHdlZW4gaHcgYW5kIG5vbi1odyBiYXNlZCBwcm92aWRlciBhbmQgY2xrIHJlZ2lzdHJhdGlv
bg0KZnVuY3Rpb25zIHBsZWFzZT8gSW4gd2hpY2ggY2FzZXMgdGhleSBhcmUgcHJlZmVycmVkP8Kg
DQoNCj4gDQo+ID4gK30NCj4gPiArDQo+ID4gK0NMS19PRl9ERUNMQVJFKGF4czEweF9wbGxfY2xv
Y2ssICJzbnBzLGF4czEweC1hcmMtcGxsLWNsb2NrIiwgb2ZfcGxsX2Nsa19zZXR1cCk7DQo+IA0K
PiBEb2VzIHRoaXMgbmVlZCB0byBiZSBDTEtfT0ZfREVDTEFSRV9EUklWRVI/IEkgbWVhbiBkb2Vz
IHRoZQ0KPiBkcml2ZXIgbmVlZCB0byBwcm9iZSBhbmQgYWxzbyBoYXZlIHRoaXMgb2YgZGVjbGFy
ZSBoYXBwZW4/IElzIHRoZQ0KPiBQTEwgc3BlY2lhbCBhbmQgbmVlZHMgdG8gYmUgdXNlZCBmb3Ig
dGhlIHRpbWVycz8NCg0KSXQgaXMgc3BlY2lhbCBhbmQgaXMgdXNlZCBmb3IgdGhlIHRpbWVycywg
c28gd2UgaGF2ZSB0byBDTEtfT0ZfREVDTEFSRSBpdC4gT24gdGhlIG90aGVyIGhhbmQgc2ltaWxh
ciBwbGwgaXMgdXNlZCB0bw0KZHJpdmUgUEdVIGNsb2NrIGZyZXF1ZW5jeSBhbmQgb3RoZXIgc3Vi
c3lzdGVtcyBhbmQgc28gd2UgYWRkIHVzdWFsIHByb2JlIGZ1bmMuDQoNCi0tIA0KQmVzdCByZWdh
cmRzLA0KVmxhZCBaYWtoYXJvdiA8dnpha2hhckBzeW5vcHN5cy5jb20+

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-05 16:06     ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-05 16:06 UTC (permalink / raw)
  To: linux-snps-arc

Hi Stephen,

On Tue, 2017-04-04@18:35 -0700, Stephen Boyd wrote:
> > +?????.pll_table = (struct pll_of_table []){
> > +?????????????{
> > +?????????????????????.prate = 27000000,
> 
> Can this be another clk in the framework instead of hardcoding
> the parent rate?

In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching for
the correct table comparing .parent_node field with real hardware parent clock frequency:
---------------------------------->8------------------------------------
for (i = 0; pll_table[i].prate != 0; i++)
? ? if (pll_table[i].prate == prate)
? ? ? ? return pll_table[i].pll_cfg_table;
---------------------------------->8------------------------------------

> 
> > +?????????????????????.pll_cfg_table = (struct pll_cfg []){
> > +?????????????????????????????{ 25200000, 1, 84, 90 },
> > +?????????????????????????????{ 50000000, 1, 100, 54 },
> > +?????????????????????????????{ 74250000, 1, 44, 16 },
> > +?????????????????????????????{ },
> > +?????????????????????},
> > +?????????????},
> > +?????????????/* Used as list limiter */
> > +?????????????{ },
> 
> There's only ever one, so I'm confused why we're making a list.

By this patch we only add support of core arc pll and pgu pll and today they are clocked by the only parent clocks
introduced here. But other plls on axs10x may be driven by different or configurable clocks, so in such cases we will
have more than one entry in this list. And we are going to add more supported plls to this driver in the nearest future.

> > +
> > +?????clk = clk_register(NULL, &pll_clk->hw);
> > +?????if (IS_ERR(clk)) {
> > +?????????????pr_err("failed to register %s clock (%ld)\n",
> > +?????????????????????????????node->name, PTR_ERR(clk));
> > +?????????????kfree(pll_clk);
> > +?????????????return;
> > +?????}
> > +
> > +?????of_clk_add_provider(node, of_clk_src_simple_get, clk);
> 
> Can you please use the clk_hw based provider and clk registration
> functions?

Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk registration
functions please? In which cases they are preferred??

> 
> > +}
> > +
> > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> 
> Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> driver need to probe and also have this of declare happen? Is the
> PLL special and needs to be used for the timers?

It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
drive PGU clock frequency and other subsystems and so we add usual probe func.

-- 
Best regards,
Vlad Zakharov <vzakhar at synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
  2017-04-05 16:06     ` Vlad Zakharov
  (?)
@ 2017-04-19 16:49       ` sboyd
  -1 siblings, 0 replies; 30+ messages in thread
From: sboyd @ 2017-04-19 16:49 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: mark.rutland, linux-kernel, Jose.Abreu, mturquette, devicetree,
	linux-clk, linux-snps-arc

On 04/05, Vlad Zakharov wrote:
> Hi Stephen,
> 
> On Tue, 2017-04-04 at 18:35 -0700, Stephen Boyd wrote:
> > > +     .pll_table = (struct pll_of_table []){
> > > +             {
> > > +                     .prate = 27000000,
> > 
> > Can this be another clk in the framework instead of hardcoding
> > the parent rate?
> 
> In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
> appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching for
> the correct table comparing .parent_node field with real hardware parent clock frequency:
> ---------------------------------->8------------------------------------
> for (i = 0; pll_table[i].prate != 0; i++)
>     if (pll_table[i].prate == prate)
>         return pll_table[i].pll_cfg_table;
> ---------------------------------->8------------------------------------

When is that done though? During round_rate and recalc_rate the
parent frequency is passed into the function, so it should be
possible to use that if the tree is properly expressed.

> 
> > 
> > > +                     .pll_cfg_table = (struct pll_cfg []){
> > > +                             { 25200000, 1, 84, 90 },
> > > +                             { 50000000, 1, 100, 54 },
> > > +                             { 74250000, 1, 44, 16 },
> > > +                             { },
> > > +                     },
> > > +             },
> > > +             /* Used as list limiter */
> > > +             { },
> > 
> > There's only ever one, so I'm confused why we're making a list.
> 
> By this patch we only add support of core arc pll and pgu pll and today they are clocked by the only parent clocks
> introduced here. But other plls on axs10x may be driven by different or configurable clocks, so in such cases we will
> have more than one entry in this list. And we are going to add more supported plls to this driver in the nearest future.

Ok.

> 
> > > +
> > > +     clk = clk_register(NULL, &pll_clk->hw);
> > > +     if (IS_ERR(clk)) {
> > > +             pr_err("failed to register %s clock (%ld)\n",
> > > +                             node->name, PTR_ERR(clk));
> > > +             kfree(pll_clk);
> > > +             return;
> > > +     }
> > > +
> > > +     of_clk_add_provider(node, of_clk_src_simple_get, clk);
> > 
> > Can you please use the clk_hw based provider and clk registration
> > functions?
> 
> Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk registration
> functions please? In which cases they are preferred? 
> 

We're trying to split the consumer and provider APIs along struct
clk_hw and struct clk respectively. If we can have drivers only
registers clk_hw pointers and never get back anything but an
error code, then we can force consumers to always go through the
clk_get() family of APIs. Then we can easily tell who is a
provider, who is a consumer, and who is a provider + a consumer.
Right now this isn't always clear cut because clk_hw has access
to struct clk, and also clk_register() returns a clk pointer, but
it doesn't really get used by anything in a provider driver,
unless provider drivers are doing something with the consumer
API.

> > 
> > > +}
> > > +
> > > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> > 
> > Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> > driver need to probe and also have this of declare happen? Is the
> > PLL special and needs to be used for the timers?
> 
> It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
> drive PGU clock frequency and other subsystems and so we add usual probe func.
> 

Presumably we'll have different compatible strings for the
different PLLs then? CLK_OF_DECLARE() will make it so that the
device node that matches never gets a ->probe() from a
platform_driver called on it. If you want it to be called twice,
then you need to use CLK_OF_DECLARE_DRIVER() instead.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-19 16:49       ` sboyd
  0 siblings, 0 replies; 30+ messages in thread
From: sboyd @ 2017-04-19 16:49 UTC (permalink / raw)
  To: Vlad Zakharov
  Cc: mark.rutland, linux-kernel, Jose.Abreu, mturquette, devicetree,
	linux-clk, linux-snps-arc

On 04/05, Vlad Zakharov wrote:
> Hi Stephen,
> 
> On Tue, 2017-04-04 at 18:35 -0700, Stephen Boyd wrote:
> > > +     .pll_table = (struct pll_of_table []){
> > > +             {
> > > +                     .prate = 27000000,
> > 
> > Can this be another clk in the framework instead of hardcoding
> > the parent rate?
> 
> In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
> appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching for
> the correct table comparing .parent_node field with real hardware parent clock frequency:
> ---------------------------------->8------------------------------------
> for (i = 0; pll_table[i].prate != 0; i++)
>     if (pll_table[i].prate == prate)
>         return pll_table[i].pll_cfg_table;
> ---------------------------------->8------------------------------------

When is that done though? During round_rate and recalc_rate the
parent frequency is passed into the function, so it should be
possible to use that if the tree is properly expressed.

> 
> > 
> > > +                     .pll_cfg_table = (struct pll_cfg []){
> > > +                             { 25200000, 1, 84, 90 },
> > > +                             { 50000000, 1, 100, 54 },
> > > +                             { 74250000, 1, 44, 16 },
> > > +                             { },
> > > +                     },
> > > +             },
> > > +             /* Used as list limiter */
> > > +             { },
> > 
> > There's only ever one, so I'm confused why we're making a list.
> 
> By this patch we only add support of core arc pll and pgu pll and today they are clocked by the only parent clocks
> introduced here. But other plls on axs10x may be driven by different or configurable clocks, so in such cases we will
> have more than one entry in this list. And we are going to add more supported plls to this driver in the nearest future.

Ok.

> 
> > > +
> > > +     clk = clk_register(NULL, &pll_clk->hw);
> > > +     if (IS_ERR(clk)) {
> > > +             pr_err("failed to register %s clock (%ld)\n",
> > > +                             node->name, PTR_ERR(clk));
> > > +             kfree(pll_clk);
> > > +             return;
> > > +     }
> > > +
> > > +     of_clk_add_provider(node, of_clk_src_simple_get, clk);
> > 
> > Can you please use the clk_hw based provider and clk registration
> > functions?
> 
> Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk registration
> functions please? In which cases they are preferred? 
> 

We're trying to split the consumer and provider APIs along struct
clk_hw and struct clk respectively. If we can have drivers only
registers clk_hw pointers and never get back anything but an
error code, then we can force consumers to always go through the
clk_get() family of APIs. Then we can easily tell who is a
provider, who is a consumer, and who is a provider + a consumer.
Right now this isn't always clear cut because clk_hw has access
to struct clk, and also clk_register() returns a clk pointer, but
it doesn't really get used by anything in a provider driver,
unless provider drivers are doing something with the consumer
API.

> > 
> > > +}
> > > +
> > > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> > 
> > Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> > driver need to probe and also have this of declare happen? Is the
> > PLL special and needs to be used for the timers?
> 
> It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
> drive PGU clock frequency and other subsystems and so we add usual probe func.
> 

Presumably we'll have different compatible strings for the
different PLLs then? CLK_OF_DECLARE() will make it so that the
device node that matches never gets a ->probe() from a
platform_driver called on it. If you want it to be called twice,
then you need to use CLK_OF_DECLARE_DRIVER() instead.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-19 16:49       ` sboyd
  0 siblings, 0 replies; 30+ messages in thread
From: sboyd @ 2017-04-19 16:49 UTC (permalink / raw)
  To: linux-snps-arc

On 04/05, Vlad Zakharov wrote:
> Hi Stephen,
> 
> On Tue, 2017-04-04@18:35 -0700, Stephen Boyd wrote:
> > > +?????.pll_table = (struct pll_of_table []){
> > > +?????????????{
> > > +?????????????????????.prate = 27000000,
> > 
> > Can this be another clk in the framework instead of hardcoding
> > the parent rate?
> 
> In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
> appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching for
> the correct table comparing .parent_node field with real hardware parent clock frequency:
> ---------------------------------->8------------------------------------
> for (i = 0; pll_table[i].prate != 0; i++)
> ? ? if (pll_table[i].prate == prate)
> ? ? ? ? return pll_table[i].pll_cfg_table;
> ---------------------------------->8------------------------------------

When is that done though? During round_rate and recalc_rate the
parent frequency is passed into the function, so it should be
possible to use that if the tree is properly expressed.

> 
> > 
> > > +?????????????????????.pll_cfg_table = (struct pll_cfg []){
> > > +?????????????????????????????{ 25200000, 1, 84, 90 },
> > > +?????????????????????????????{ 50000000, 1, 100, 54 },
> > > +?????????????????????????????{ 74250000, 1, 44, 16 },
> > > +?????????????????????????????{ },
> > > +?????????????????????},
> > > +?????????????},
> > > +?????????????/* Used as list limiter */
> > > +?????????????{ },
> > 
> > There's only ever one, so I'm confused why we're making a list.
> 
> By this patch we only add support of core arc pll and pgu pll and today they are clocked by the only parent clocks
> introduced here. But other plls on axs10x may be driven by different or configurable clocks, so in such cases we will
> have more than one entry in this list. And we are going to add more supported plls to this driver in the nearest future.

Ok.

> 
> > > +
> > > +?????clk = clk_register(NULL, &pll_clk->hw);
> > > +?????if (IS_ERR(clk)) {
> > > +?????????????pr_err("failed to register %s clock (%ld)\n",
> > > +?????????????????????????????node->name, PTR_ERR(clk));
> > > +?????????????kfree(pll_clk);
> > > +?????????????return;
> > > +?????}
> > > +
> > > +?????of_clk_add_provider(node, of_clk_src_simple_get, clk);
> > 
> > Can you please use the clk_hw based provider and clk registration
> > functions?
> 
> Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk registration
> functions please? In which cases they are preferred??
> 

We're trying to split the consumer and provider APIs along struct
clk_hw and struct clk respectively. If we can have drivers only
registers clk_hw pointers and never get back anything but an
error code, then we can force consumers to always go through the
clk_get() family of APIs. Then we can easily tell who is a
provider, who is a consumer, and who is a provider + a consumer.
Right now this isn't always clear cut because clk_hw has access
to struct clk, and also clk_register() returns a clk pointer, but
it doesn't really get used by anything in a provider driver,
unless provider drivers are doing something with the consumer
API.

> > 
> > > +}
> > > +
> > > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> > 
> > Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> > driver need to probe and also have this of declare happen? Is the
> > PLL special and needs to be used for the timers?
> 
> It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
> drive PGU clock frequency and other subsystems and so we add usual probe func.
> 

Presumably we'll have different compatible strings for the
different PLLs then? CLK_OF_DECLARE() will make it so that the
device node that matches never gets a ->probe() from a
platform_driver called on it. If you want it to be called twice,
then you need to use CLK_OF_DECLARE_DRIVER() instead.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
  2017-04-19 16:49       ` sboyd
  (?)
@ 2017-04-20 15:13         ` Vlad Zakharov
  -1 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-20 15:13 UTC (permalink / raw)
  To: sboyd
  Cc: mark.rutland, linux-kernel, mturquette, Jose.Abreu, linux-clk,
	linux-snps-arc

Hi Stephen,

On Wed, 2017-04-19 at 09:49 -0700, sboyd@codeaurora.org wrote:
> On 04/05, Vlad Zakharov wrote:
> > 
> > Hi Stephen,
> > 
> > On Tue, 2017-04-04 at 18:35 -0700, Stephen Boyd wrote:
> > > 
> > > > 
> > > > +     .pll_table = (struct pll_of_table []){
> > > > +             {
> > > > +                     .prate = 27000000,
> > > 
> > > Can this be another clk in the framework instead of hardcoding
> > > the parent rate?
> > 
> > In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
> > appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching
> > for
> > the correct table comparing .parent_node field with real hardware parent clock frequency:
> > ---------------------------------->8------------------------------------
> > for (i = 0; pll_table[i].prate != 0; i++)
> >     if (pll_table[i].prate == prate)
> >         return pll_table[i].pll_cfg_table;
> > ---------------------------------->8------------------------------------
> 
> When is that done though? During round_rate and recalc_rate the
> parent frequency is passed into the function, so it should be
> possible to use that if the tree is properly expressed.
> 

I think that we haven't understood each other correctly.
Anyway I have checked out our hardware documentations and find out that in fact for today's version of hardware we don't
have situations when such plls can be driven by different parent clock. So this approach is not required at the moment.
I am going to simplify my driver so it will reflect current version of hardware and if anything changes I will better
provide a new patch.

> > Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk
> > registration
> > functions please? In which cases they are preferred? 
> > 
> 
> We're trying to split the consumer and provider APIs along struct
> clk_hw and struct clk respectively. If we can have drivers only
> registers clk_hw pointers and never get back anything but an
> error code, then we can force consumers to always go through the
> clk_get() family of APIs. Then we can easily tell who is a
> provider, who is a consumer, and who is a provider + a consumer.
> Right now this isn't always clear cut because clk_hw has access
> to struct clk, and also clk_register() returns a clk pointer, but
> it doesn't really get used by anything in a provider driver,
> unless provider drivers are doing something with the consumer
> API.

I am sorry for my foolish questions, but as I understand I should use hw-based functions for clk providers but it is not
still clear enough for me when should I use non-hw functions? Does any drivers need struct clk when they are initiating
or probing themselves? And what clk consumer can be used for?

> 
> > 
> > > 
> > > 
> > > > 
> > > > +}
> > > > +
> > > > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> > > 
> > > Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> > > driver need to probe and also have this of declare happen? Is the
> > > PLL special and needs to be used for the timers?
> > 
> > It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
> > drive PGU clock frequency and other subsystems and so we add usual probe func.
> > 
> 
> Presumably we'll have different compatible strings for the
> different PLLs then? CLK_OF_DECLARE() will make it so that the
> device node that matches never gets a ->probe() from a
> platform_driver called on it. If you want it to be called twice,
> then you need to use CLK_OF_DECLARE_DRIVER() instead.
> 

In fact we don't need it to be called twice. And as you can see I do practically the same things in setup and probe
functions. So maybe I can somehow avoid this code duplication? 
I mean that this driver is going either to be used for the timers or as a simple platform driver for clocking some
peripheras. So for the first case I have written a setup function to CLK_OF_DECLARE my driver and for the second case
I've written usual probe function. Maybe I am mistaken and it is not the right way?

Thank you for you answers. 

-- 
Best regards,
Vlad Zakharov <vzakhar@synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-20 15:13         ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-20 15:13 UTC (permalink / raw)
  To: sboyd
  Cc: mark.rutland, linux-kernel, mturquette, Jose.Abreu, linux-clk,
	linux-snps-arc

SGkgU3RlcGhlbiwNCg0KT24gV2VkLCAyMDE3LTA0LTE5IGF0IDA5OjQ5IC0wNzAwLCBzYm95ZEBj
b2RlYXVyb3JhLm9yZyB3cm90ZToNCj4gT24gMDQvMDUsIFZsYWQgWmFraGFyb3Ygd3JvdGU6DQo+
ID4gDQo+ID4gSGkgU3RlcGhlbiwNCj4gPiANCj4gPiBPbiBUdWUsIDIwMTctMDQtMDQgYXQgMTg6
MzUgLTA3MDAsIFN0ZXBoZW4gQm95ZCB3cm90ZToNCj4gPiA+IA0KPiA+ID4gPiANCj4gPiA+ID4g
K8KgwqDCoMKgwqAucGxsX3RhYmxlID0gKHN0cnVjdCBwbGxfb2ZfdGFibGUgW10pew0KPiA+ID4g
PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqB7DQo+ID4gPiA+ICvCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAucHJhdGUgPSAyNzAwMDAwMCwNCj4gPiA+IA0KPiA+
ID4gQ2FuIHRoaXMgYmUgYW5vdGhlciBjbGsgaW4gdGhlIGZyYW1ld29yayBpbnN0ZWFkIG9mIGhh
cmRjb2RpbmcNCj4gPiA+IHRoZSBwYXJlbnQgcmF0ZT8NCj4gPiANCj4gPiBJbiBmYWN0IHRoZXJl
IGlzIGFub3RoZXIgY2xrIGluIHRoZSBmcmFtZXdvcmsgdGhhdCByZXByZXNlbnRzIHRoaXMgcGFy
ZW50IGNsb2NrLiBCdXQgdGhpcyBmaWVsZCBpcyBuZWVkZWQgdG8gZ2V0DQo+ID4gYXBwcm9wcmlh
dGUgcGxsX2NmZ190YWJsZSBhcyBpdCBkZXBlbmRzIG9uIHBhcmVudCBjbG9jayBmcmVxdWVuY3ku
IEJlbG93IGluIHBsbF9jZmdfZ2V0IGZ1bmN0aW9uIHdlIGFyZSBzZWFyY2hpbmcNCj4gPiBmb3IN
Cj4gPiB0aGUgY29ycmVjdCB0YWJsZSBjb21wYXJpbmcgLnBhcmVudF9ub2RlIGZpZWxkIHdpdGgg
cmVhbCBoYXJkd2FyZSBwYXJlbnQgY2xvY2sgZnJlcXVlbmN5Og0KPiA+IC0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0+OC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LQ0KPiA+IGZvciAoaSA9IDA7IHBsbF90YWJsZVtpXS5wcmF0ZSAhPSAwOyBpKyspDQo+ID4gwqAg
wqAgaWYgKHBsbF90YWJsZVtpXS5wcmF0ZSA9PSBwcmF0ZSkNCj4gPiDCoCDCoCDCoCDCoCByZXR1
cm4gcGxsX3RhYmxlW2ldLnBsbF9jZmdfdGFibGU7DQo+ID4gLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLT44LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQo+IA0K
PiBXaGVuIGlzIHRoYXQgZG9uZSB0aG91Z2g/IER1cmluZyByb3VuZF9yYXRlIGFuZCByZWNhbGNf
cmF0ZSB0aGUNCj4gcGFyZW50IGZyZXF1ZW5jeSBpcyBwYXNzZWQgaW50byB0aGUgZnVuY3Rpb24s
IHNvIGl0IHNob3VsZCBiZQ0KPiBwb3NzaWJsZSB0byB1c2UgdGhhdCBpZiB0aGUgdHJlZSBpcyBw
cm9wZXJseSBleHByZXNzZWQuDQo+IA0KDQpJIHRoaW5rIHRoYXQgd2UgaGF2ZW4ndCB1bmRlcnN0
b29kIGVhY2ggb3RoZXIgY29ycmVjdGx5Lg0KQW55d2F5IEkgaGF2ZSBjaGVja2VkIG91dCBvdXIg
aGFyZHdhcmUgZG9jdW1lbnRhdGlvbnMgYW5kIGZpbmQgb3V0IHRoYXQgaW4gZmFjdCBmb3IgdG9k
YXkncyB2ZXJzaW9uIG9mIGhhcmR3YXJlIHdlIGRvbid0DQpoYXZlIHNpdHVhdGlvbnMgd2hlbiBz
dWNoIHBsbHMgY2FuIGJlIGRyaXZlbiBieSBkaWZmZXJlbnQgcGFyZW50IGNsb2NrLiBTbyB0aGlz
IGFwcHJvYWNoIGlzIG5vdCByZXF1aXJlZCBhdCB0aGUgbW9tZW50Lg0KSSBhbSBnb2luZyB0byBz
aW1wbGlmeSBteSBkcml2ZXIgc28gaXQgd2lsbCByZWZsZWN0IGN1cnJlbnQgdmVyc2lvbiBvZiBo
YXJkd2FyZSBhbmQgaWYgYW55dGhpbmcgY2hhbmdlcyBJIHdpbGwgYmV0dGVyDQpwcm92aWRlIGEg
bmV3IHBhdGNoLg0KDQo+ID4gU3VyZS4gQ291bGQgeW91IGJlIHNvIGtpbmQgdG8gZXhwbGFpbiB3
aGF0IGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gaHcgYW5kIG5vbi1odyBiYXNlZCBwcm92aWRl
ciBhbmQgY2xrDQo+ID4gcmVnaXN0cmF0aW9uDQo+ID4gZnVuY3Rpb25zIHBsZWFzZT8gSW4gd2hp
Y2ggY2FzZXMgdGhleSBhcmUgcHJlZmVycmVkP8KgDQo+ID4gDQo+IA0KPiBXZSdyZSB0cnlpbmcg
dG8gc3BsaXQgdGhlIGNvbnN1bWVyIGFuZCBwcm92aWRlciBBUElzIGFsb25nIHN0cnVjdA0KPiBj
bGtfaHcgYW5kIHN0cnVjdCBjbGsgcmVzcGVjdGl2ZWx5LiBJZiB3ZSBjYW4gaGF2ZSBkcml2ZXJz
IG9ubHkNCj4gcmVnaXN0ZXJzIGNsa19odyBwb2ludGVycyBhbmQgbmV2ZXIgZ2V0IGJhY2sgYW55
dGhpbmcgYnV0IGFuDQo+IGVycm9yIGNvZGUsIHRoZW4gd2UgY2FuIGZvcmNlIGNvbnN1bWVycyB0
byBhbHdheXMgZ28gdGhyb3VnaCB0aGUNCj4gY2xrX2dldCgpIGZhbWlseSBvZiBBUElzLiBUaGVu
IHdlIGNhbiBlYXNpbHkgdGVsbCB3aG8gaXMgYQ0KPiBwcm92aWRlciwgd2hvIGlzIGEgY29uc3Vt
ZXIsIGFuZCB3aG8gaXMgYSBwcm92aWRlciArIGEgY29uc3VtZXIuDQo+IFJpZ2h0IG5vdyB0aGlz
IGlzbid0IGFsd2F5cyBjbGVhciBjdXQgYmVjYXVzZSBjbGtfaHcgaGFzIGFjY2Vzcw0KPiB0byBz
dHJ1Y3QgY2xrLCBhbmQgYWxzbyBjbGtfcmVnaXN0ZXIoKSByZXR1cm5zIGEgY2xrIHBvaW50ZXIs
IGJ1dA0KPiBpdCBkb2Vzbid0IHJlYWxseSBnZXQgdXNlZCBieSBhbnl0aGluZyBpbiBhIHByb3Zp
ZGVyIGRyaXZlciwNCj4gdW5sZXNzIHByb3ZpZGVyIGRyaXZlcnMgYXJlIGRvaW5nIHNvbWV0aGlu
ZyB3aXRoIHRoZSBjb25zdW1lcg0KPiBBUEkuDQoNCkkgYW0gc29ycnkgZm9yIG15IGZvb2xpc2gg
cXVlc3Rpb25zLCBidXQgYXMgSSB1bmRlcnN0YW5kIEkgc2hvdWxkIHVzZSBody1iYXNlZCBmdW5j
dGlvbnMgZm9yIGNsayBwcm92aWRlcnMgYnV0IGl0IGlzIG5vdA0Kc3RpbGwgY2xlYXIgZW5vdWdo
IGZvciBtZSB3aGVuIHNob3VsZCBJIHVzZSBub24taHcgZnVuY3Rpb25zPyBEb2VzIGFueSBkcml2
ZXJzIG5lZWQgc3RydWN0IGNsayB3aGVuIHRoZXkgYXJlIGluaXRpYXRpbmcNCm9yIHByb2Jpbmcg
dGhlbXNlbHZlcz8gQW5kIHdoYXQgY2xrIGNvbnN1bWVyIGNhbiBiZSB1c2VkIGZvcj8NCg0KPiAN
Cj4gPiANCj4gPiA+IA0KPiA+ID4gDQo+ID4gPiA+IA0KPiA+ID4gPiArfQ0KPiA+ID4gPiArDQo+
ID4gPiA+ICtDTEtfT0ZfREVDTEFSRShheHMxMHhfcGxsX2Nsb2NrLCAic25wcyxheHMxMHgtYXJj
LXBsbC1jbG9jayIsIG9mX3BsbF9jbGtfc2V0dXApOw0KPiA+ID4gDQo+ID4gPiBEb2VzIHRoaXMg
bmVlZCB0byBiZSBDTEtfT0ZfREVDTEFSRV9EUklWRVI/IEkgbWVhbiBkb2VzIHRoZQ0KPiA+ID4g
ZHJpdmVyIG5lZWQgdG8gcHJvYmUgYW5kIGFsc28gaGF2ZSB0aGlzIG9mIGRlY2xhcmUgaGFwcGVu
PyBJcyB0aGUNCj4gPiA+IFBMTCBzcGVjaWFsIGFuZCBuZWVkcyB0byBiZSB1c2VkIGZvciB0aGUg
dGltZXJzPw0KPiA+IA0KPiA+IEl0IGlzIHNwZWNpYWwgYW5kIGlzIHVzZWQgZm9yIHRoZSB0aW1l
cnMsIHNvIHdlIGhhdmUgdG8gQ0xLX09GX0RFQ0xBUkUgaXQuIE9uIHRoZSBvdGhlciBoYW5kIHNp
bWlsYXIgcGxsIGlzIHVzZWQgdG8NCj4gPiBkcml2ZSBQR1UgY2xvY2sgZnJlcXVlbmN5IGFuZCBv
dGhlciBzdWJzeXN0ZW1zIGFuZCBzbyB3ZSBhZGQgdXN1YWwgcHJvYmUgZnVuYy4NCj4gPiANCj4g
DQo+IFByZXN1bWFibHkgd2UnbGwgaGF2ZSBkaWZmZXJlbnQgY29tcGF0aWJsZSBzdHJpbmdzIGZv
ciB0aGUNCj4gZGlmZmVyZW50IFBMTHMgdGhlbj8gQ0xLX09GX0RFQ0xBUkUoKSB3aWxsIG1ha2Ug
aXQgc28gdGhhdCB0aGUNCj4gZGV2aWNlIG5vZGUgdGhhdCBtYXRjaGVzIG5ldmVyIGdldHMgYSAt
PnByb2JlKCkgZnJvbSBhDQo+IHBsYXRmb3JtX2RyaXZlciBjYWxsZWQgb24gaXQuIElmIHlvdSB3
YW50IGl0IHRvIGJlIGNhbGxlZCB0d2ljZSwNCj4gdGhlbiB5b3UgbmVlZCB0byB1c2UgQ0xLX09G
X0RFQ0xBUkVfRFJJVkVSKCkgaW5zdGVhZC4NCj4gDQoNCkluIGZhY3Qgd2UgZG9uJ3QgbmVlZCBp
dCB0byBiZSBjYWxsZWQgdHdpY2UuIEFuZCBhcyB5b3UgY2FuIHNlZSBJIGRvIHByYWN0aWNhbGx5
IHRoZSBzYW1lIHRoaW5ncyBpbiBzZXR1cCBhbmQgcHJvYmUNCmZ1bmN0aW9ucy4gU28gbWF5YmUg
SSBjYW4gc29tZWhvdyBhdm9pZCB0aGlzIGNvZGUgZHVwbGljYXRpb24/wqANCkkgbWVhbiB0aGF0
IHRoaXMgZHJpdmVyIGlzIGdvaW5nIGVpdGhlciB0byBiZSB1c2VkIGZvciB0aGUgdGltZXJzIG9y
IGFzIGEgc2ltcGxlIHBsYXRmb3JtIGRyaXZlciBmb3IgY2xvY2tpbmcgc29tZQ0KcGVyaXBoZXJh
cy4gU28gZm9yIHRoZSBmaXJzdCBjYXNlIEkgaGF2ZSB3cml0dGVuIGEgc2V0dXAgZnVuY3Rpb24g
dG8gQ0xLX09GX0RFQ0xBUkUgbXkgZHJpdmVyIGFuZCBmb3IgdGhlIHNlY29uZCBjYXNlDQpJJ3Zl
IHdyaXR0ZW4gdXN1YWwgcHJvYmUgZnVuY3Rpb24uIE1heWJlIEkgYW0gbWlzdGFrZW4gYW5kIGl0
IGlzIG5vdCB0aGUgcmlnaHQgd2F5Pw0KDQpUaGFuayB5b3UgZm9yIHlvdSBhbnN3ZXJzLsKgDQoN
Ci0tIA0KQmVzdCByZWdhcmRzLA0KVmxhZCBaYWtoYXJvdiA8dnpha2hhckBzeW5vcHN5cy5jb20+

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] clk/axs10x: introduce AXS10X pll driver
@ 2017-04-20 15:13         ` Vlad Zakharov
  0 siblings, 0 replies; 30+ messages in thread
From: Vlad Zakharov @ 2017-04-20 15:13 UTC (permalink / raw)
  To: linux-snps-arc

Hi Stephen,

On Wed, 2017-04-19@09:49 -0700, sboyd@codeaurora.org wrote:
> On 04/05, Vlad Zakharov wrote:
> > 
> > Hi Stephen,
> > 
> > On Tue, 2017-04-04@18:35 -0700, Stephen Boyd wrote:
> > > 
> > > > 
> > > > +?????.pll_table = (struct pll_of_table []){
> > > > +?????????????{
> > > > +?????????????????????.prate = 27000000,
> > > 
> > > Can this be another clk in the framework instead of hardcoding
> > > the parent rate?
> > 
> > In fact there is another clk in the framework that represents this parent clock. But this field is needed to get
> > appropriate pll_cfg_table as it depends on parent clock frequency. Below in pll_cfg_get function we are searching
> > for
> > the correct table comparing .parent_node field with real hardware parent clock frequency:
> > ---------------------------------->8------------------------------------
> > for (i = 0; pll_table[i].prate != 0; i++)
> > ? ? if (pll_table[i].prate == prate)
> > ? ? ? ? return pll_table[i].pll_cfg_table;
> > ---------------------------------->8------------------------------------
> 
> When is that done though? During round_rate and recalc_rate the
> parent frequency is passed into the function, so it should be
> possible to use that if the tree is properly expressed.
> 

I think that we haven't understood each other correctly.
Anyway I have checked out our hardware documentations and find out that in fact for today's version of hardware we don't
have situations when such plls can be driven by different parent clock. So this approach is not required at the moment.
I am going to simplify my driver so it will reflect current version of hardware and if anything changes I will better
provide a new patch.

> > Sure. Could you be so kind to explain what is the difference between hw and non-hw based provider and clk
> > registration
> > functions please? In which cases they are preferred??
> > 
> 
> We're trying to split the consumer and provider APIs along struct
> clk_hw and struct clk respectively. If we can have drivers only
> registers clk_hw pointers and never get back anything but an
> error code, then we can force consumers to always go through the
> clk_get() family of APIs. Then we can easily tell who is a
> provider, who is a consumer, and who is a provider + a consumer.
> Right now this isn't always clear cut because clk_hw has access
> to struct clk, and also clk_register() returns a clk pointer, but
> it doesn't really get used by anything in a provider driver,
> unless provider drivers are doing something with the consumer
> API.

I am sorry for my foolish questions, but as I understand I should use hw-based functions for clk providers but it is not
still clear enough for me when should I use non-hw functions? Does any drivers need struct clk when they are initiating
or probing themselves? And what clk consumer can be used for?

> 
> > 
> > > 
> > > 
> > > > 
> > > > +}
> > > > +
> > > > +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", of_pll_clk_setup);
> > > 
> > > Does this need to be CLK_OF_DECLARE_DRIVER? I mean does the
> > > driver need to probe and also have this of declare happen? Is the
> > > PLL special and needs to be used for the timers?
> > 
> > It is special and is used for the timers, so we have to CLK_OF_DECLARE it. On the other hand similar pll is used to
> > drive PGU clock frequency and other subsystems and so we add usual probe func.
> > 
> 
> Presumably we'll have different compatible strings for the
> different PLLs then? CLK_OF_DECLARE() will make it so that the
> device node that matches never gets a ->probe() from a
> platform_driver called on it. If you want it to be called twice,
> then you need to use CLK_OF_DECLARE_DRIVER() instead.
> 

In fact we don't need it to be called twice. And as you can see I do practically the same things in setup and probe
functions. So maybe I can somehow avoid this code duplication??
I mean that this driver is going either to be used for the timers or as a simple platform driver for clocking some
peripheras. So for the first case I have written a setup function to CLK_OF_DECLARE my driver and for the second case
I've written usual probe function. Maybe I am mistaken and it is not the right way?

Thank you for you answers.?

-- 
Best regards,
Vlad Zakharov <vzakhar at synopsys.com>

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2017-04-20 15:13 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-21 13:11 [PATCH v2] clk/axs10x: introduce AXS10X pll driver Vlad Zakharov
2017-02-21 13:11 ` Vlad Zakharov
2017-03-03 13:18 ` Vlad Zakharov
2017-03-03 13:18   ` Vlad Zakharov
2017-03-03 13:18   ` Vlad Zakharov
2017-03-03 13:18   ` Vlad Zakharov
2017-03-03 23:50   ` Stephen Boyd
2017-03-03 23:50     ` Stephen Boyd
2017-03-03 23:50     ` Stephen Boyd
2017-03-03 23:50     ` Stephen Boyd
2017-03-29 11:20     ` Vlad Zakharov
2017-03-29 11:20       ` Vlad Zakharov
2017-03-29 11:20       ` Vlad Zakharov
2017-03-29 11:20       ` Vlad Zakharov
2017-04-03 10:54 ` Jose Abreu
2017-04-03 10:54   ` Jose Abreu
2017-04-03 10:54   ` Jose Abreu
2017-04-05  1:35 ` Stephen Boyd
2017-04-05  1:35   ` Stephen Boyd
2017-04-05  1:35   ` Stephen Boyd
2017-04-05 16:06   ` Vlad Zakharov
2017-04-05 16:06     ` Vlad Zakharov
2017-04-05 16:06     ` Vlad Zakharov
2017-04-05 16:06     ` Vlad Zakharov
2017-04-19 16:49     ` sboyd
2017-04-19 16:49       ` sboyd
2017-04-19 16:49       ` sboyd
2017-04-20 15:13       ` Vlad Zakharov
2017-04-20 15:13         ` Vlad Zakharov
2017-04-20 15:13         ` Vlad Zakharov

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.