linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
@ 2018-10-21 13:10 A.s. Dong
  2018-10-21 13:10 ` [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE " A.s. Dong
                   ` (9 more replies)
  0 siblings, 10 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:10 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong

This is a rebased version of below patch series against latest clk tree.
[PATCH RESEND V3 0/9] clk: add imx7ulp clk support
https://lkml.org/lkml/2018/3/16/310

It only updates the license to SPDX format as well as a minor fix of
pllv4.

This patch series intends to add imx7ulp clk support.

i.MX7ULP Clock functions are under joint control of the System
Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
modules, and Core Mode Controller (CMC)1 blocks

The clocking scheme provides clear separation between M4 domain
and A7 domain. Except for a few clock sources shared between two
domains, such as the System Oscillator clock, the Slow IRC (SIRC),
and and the Fast IRC clock (FIRCLK), clock sources and clock
management are separated and contained within each domain.

M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.

Note: this series only adds A7 clock domain support as M4 clock
domain will be handled by M4 seperately.

Change Log:
v3->v4:
 * update after changing scg and pcc into separete nodes according to
   Rob's suggestion
v2->v3:
 * Patch 1 changed on: 1) split normal and gate ops 2) fix the possible racy
   Others no changes.

v1->v2:
 * add enable/disable for the type of CLK_DIVIDER_ZERO_GATE dividers
 * use clk_hw apis to register clocks
 * use of_clk_add_hw_provider
 * split the clocks register process into two parts: early part for possible
   timers clocks registered by CLK_OF_DECLARE_DRIVER and the later part for
   the left normal peripheral clocks registered by a platform driver.

Dong Aisheng (9):
  clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support
  clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support
  clk: imx: add pllv4 support
  clk: imx: add pfdv2 support
  clk: imx: add composite clk support
  dt-bindings: clock: add imx7ulp clock binding doc
  clk: imx: make mux parent strings const
  clk: imx: implement new clk_hw based APIs
  clk: imx: add imx7ulp clk driver

 .../devicetree/bindings/clock/imx7ulp-clock.txt    |  87 +++++++++
 drivers/clk/clk-divider.c                          | 152 +++++++++++++++
 drivers/clk/clk-fractional-divider.c               |  10 +
 drivers/clk/imx/Makefile                           |   6 +-
 drivers/clk/imx/clk-busy.c                         |   2 +-
 drivers/clk/imx/clk-composite.c                    |  85 +++++++++
 drivers/clk/imx/clk-fixup-mux.c                    |   2 +-
 drivers/clk/imx/clk-imx7ulp.c                      | 209 +++++++++++++++++++++
 drivers/clk/imx/clk-pfdv2.c                        | 201 ++++++++++++++++++++
 drivers/clk/imx/clk-pllv4.c                        | 182 ++++++++++++++++++
 drivers/clk/imx/clk.c                              |  22 +++
 drivers/clk/imx/clk.h                              |  92 ++++++++-
 include/dt-bindings/clock/imx7ulp-clock.h          | 109 +++++++++++
 include/linux/clk-provider.h                       |  17 ++
 14 files changed, 1166 insertions(+), 10 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
 create mode 100644 drivers/clk/imx/clk-composite.c
 create mode 100644 drivers/clk/imx/clk-imx7ulp.c
 create mode 100644 drivers/clk/imx/clk-pfdv2.c
 create mode 100644 drivers/clk/imx/clk-pllv4.c
 create mode 100644 include/dt-bindings/clock/imx7ulp-clock.h

-- 
2.7.4


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

* [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
@ 2018-10-21 13:10 ` A.s. Dong
  2018-11-05  0:59   ` Michael Turquette
  2018-10-21 13:10 ` [PATCH RESEND V4 2/9] clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support A.s. Dong
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:10 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

For dividers with zero indicating clock is disabled, instead of giving a
warning each time like "clkx: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not
set" in exist code, we'd like to introduce enable/disable function for it.
e.g.
000b - Clock disabled
001b - Divide by 1
010b - Divide by 2
...

Set rate when the clk is disabled will cache the rate request and only
when the clk is enabled will the driver actually program the hardware to
have the requested divider value. Similarly, when the clk is disabled we'll
write a 0 there, but when the clk is enabled we'll restore whatever rate
(divider) was chosen last.

It does mean that recalc rate will be sort of odd, because when the clk is
off it will return 0, and when the clk is on it will return the right rate.
So to make things work, we'll need to return the cached rate in recalc rate
when the clk is off and read the hardware when the clk is on.

NOTE for the default off divider, the recalc rate will still return 0 as
there's still no proper preset rate. Enable such divider will give user
a reminder error message.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * no changes
v2->v3:
 * split normal and gate ops
 * fix the possible racy
v1->v2:
 * add enable/disable for the type of CLK_DIVIDER_ZERO_GATE dividers
---
 drivers/clk/clk-divider.c    | 152 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h |   9 +++
 2 files changed, 161 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index b6234a5..b3566fd 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -122,6 +122,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 
 	div = _get_div(table, val, flags, width);
 	if (!div) {
+		if (flags & CLK_DIVIDER_ZERO_GATE)
+			return 0;
+
 		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			clk_hw_get_name(hw));
@@ -145,6 +148,34 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
 				   divider->flags, divider->width);
 }
 
+static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned long flags = 0;
+	unsigned int val;
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	if (!clk_hw_is_enabled(hw)) {
+		val = divider->cached_val;
+	} else {
+		val = clk_readl(divider->reg) >> divider->shift;
+		val &= clk_div_mask(divider->width);
+	}
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+
+	return divider_recalc_rate(hw, parent_rate, val, divider->table,
+				   divider->flags, divider->width);
+}
+
 static bool _is_valid_table_div(const struct clk_div_table *table,
 							 unsigned int div)
 {
@@ -437,6 +468,108 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 	return 0;
 }
 
+static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned long flags = 0;
+	int value;
+	u32 val;
+
+	value = divider_get_val(rate, parent_rate, divider->table,
+				divider->width, divider->flags);
+	if (value < 0)
+		return value;
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	if (clk_hw_is_enabled(hw)) {
+		if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
+			val = clk_div_mask(divider->width) << (divider->shift + 16);
+		} else {
+			val = clk_readl(divider->reg);
+			val &= ~(clk_div_mask(divider->width) << divider->shift);
+		}
+		val |= (u32)value << divider->shift;
+		clk_writel(val, divider->reg);
+	} else {
+		divider->cached_val = value;
+	}
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+
+	return 0;
+}
+
+static int clk_divider_enable(struct clk_hw *hw)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned long flags = 0;
+	u32 val;
+
+	if (!divider->cached_val) {
+		pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
+		return -EINVAL;
+	}
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	/* restore div val */
+	val = clk_readl(divider->reg);
+	val |= divider->cached_val << divider->shift;
+	clk_writel(val, divider->reg);
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+
+	return 0;
+}
+
+static void clk_divider_disable(struct clk_hw *hw)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned long flags = 0;
+	u32 val;
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	/* store the current div val */
+	val = clk_readl(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+	divider->cached_val = val;
+	clk_writel(0, divider->reg);
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+}
+
+static int clk_divider_is_enabled(struct clk_hw *hw)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	u32 val;
+
+	val = clk_readl(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+
+	return val ? 1 : 0;
+}
+
 const struct clk_ops clk_divider_ops = {
 	.recalc_rate = clk_divider_recalc_rate,
 	.round_rate = clk_divider_round_rate,
@@ -444,6 +577,16 @@ const struct clk_ops clk_divider_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_divider_ops);
 
+const struct clk_ops clk_divider_gate_ops = {
+	.recalc_rate = clk_divider_gate_recalc_rate,
+	.round_rate = clk_divider_round_rate,
+	.set_rate = clk_divider_gate_set_rate,
+	.enable = clk_divider_enable,
+	.disable = clk_divider_disable,
+	.is_enabled = clk_divider_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_divider_gate_ops);
+
 const struct clk_ops clk_divider_ro_ops = {
 	.recalc_rate = clk_divider_recalc_rate,
 	.round_rate = clk_divider_round_rate,
@@ -459,6 +602,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
 	struct clk_divider *div;
 	struct clk_hw *hw;
 	struct clk_init_data init;
+	u32 val;
 	int ret;
 
 	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
@@ -476,6 +620,8 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
 	init.name = name;
 	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
 		init.ops = &clk_divider_ro_ops;
+	else if (clk_divider_flags & CLK_DIVIDER_ZERO_GATE)
+		init.ops = &clk_divider_gate_ops;
 	else
 		init.ops = &clk_divider_ops;
 	init.flags = flags | CLK_IS_BASIC;
@@ -491,6 +637,12 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
 	div->hw.init = &init;
 	div->table = table;
 
+	if (div->flags & CLK_DIVIDER_ZERO_GATE) {
+		val = clk_readl(reg) >> shift;
+		val &= clk_div_mask(width);
+		div->cached_val = val;
+	}
+
 	/* register the clock */
 	hw = &div->hw;
 	ret = clk_hw_register(dev, hw);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 08b1aa7..08f135a 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -387,6 +387,7 @@ struct clk_div_table {
  * @shift:	shift to the divider bit field
  * @width:	width of the divider bit field
  * @table:	array of value/divider pairs, last entry should have div = 0
+ * @cached_val: cached div hw value used for CLK_DIVIDER_ZERO_GATE
  * @lock:	register lock
  *
  * Clock with an adjustable divider affecting its output frequency.  Implements
@@ -415,6 +416,12 @@ struct clk_div_table {
  * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like CLK_DIVIDER_ONE_BASED
  *	except when the value read from the register is zero, the divisor is
  *	2^width of the field.
+ * CLK_DIVIDER_ZERO_GATE - For dividers which are like CLK_DIVIDER_ONE_BASED
+ *	when the value read from the register is zero, it means the divisor
+ *	is gated. For this case, the cached_val will be used to store the
+ *	intermediate div for the normal rate operation, like set_rate/get_rate/
+ *	recalc_rate. When the divider is ungated, the driver will actually
+ *	program the hardware to have the requested divider value.
  */
 struct clk_divider {
 	struct clk_hw	hw;
@@ -423,6 +430,7 @@ struct clk_divider {
 	u8		width;
 	u8		flags;
 	const struct clk_div_table	*table;
+	u32		cached_val;
 	spinlock_t	*lock;
 };
 
@@ -436,6 +444,7 @@ struct clk_divider {
 #define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
 #define CLK_DIVIDER_READ_ONLY		BIT(5)
 #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
+#define CLK_DIVIDER_ZERO_GATE		BIT(7)
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;
-- 
2.7.4


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

* [PATCH RESEND V4 2/9] clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
  2018-10-21 13:10 ` [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE " A.s. Dong
@ 2018-10-21 13:10 ` A.s. Dong
  2018-10-21 13:10 ` [PATCH RESEND V4 3/9] clk: imx: add pllv4 support A.s. Dong
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:10 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

Adding CLK_FRAC_DIVIDER_ZERO_BASED flag to indicate the numerator and
denominator value in register are start from 0.

This can be used to support frac dividers like below:
Divider output clock = Divider input clock x [(frac +1) / (div +1)]
where frac/div in register is:
000b - Divide by 1.
001b - Divide by 2.
010b - Divide by 3.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * no changes
v2->v3:
 * no changes
v1->v2:
 * improve comments suggested by Stephen
---
 drivers/clk/clk-fractional-divider.c | 10 ++++++++++
 include/linux/clk-provider.h         |  8 ++++++++
 2 files changed, 18 insertions(+)

diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index fdf625f..7ccde6b 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -40,6 +40,11 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
 	m = (val & fd->mmask) >> fd->mshift;
 	n = (val & fd->nmask) >> fd->nshift;
 
+	if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
+		m++;
+		n++;
+	}
+
 	if (!n || !m)
 		return parent_rate;
 
@@ -103,6 +108,11 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
 			GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
 			&m, &n);
 
+	if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
+		m--;
+		n--;
+	}
+
 	if (fd->lock)
 		spin_lock_irqsave(fd->lock, flags);
 	else
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 08f135a..90d7c26 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -603,6 +603,12 @@ void clk_hw_unregister_fixed_factor(struct clk_hw *hw);
  * @lock:	register lock
  *
  * Clock with adjustable fractional divider affecting its output frequency.
+ *
+ * Flags:
+ * CLK_FRAC_DIVIDER_ZERO_BASED - by default the numerator and denominator
+ *	is the value read from the register. If CLK_FRAC_DIVIDER_ZERO_BASED
+ *	is set then the numerator and denominator are both the value read
+ *	plus one.
  */
 struct clk_fractional_divider {
 	struct clk_hw	hw;
@@ -622,6 +628,8 @@ struct clk_fractional_divider {
 
 #define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
 
+#define CLK_FRAC_DIVIDER_ZERO_BASED		BIT(0)
+
 extern const struct clk_ops clk_fractional_divider_ops;
 struct clk *clk_register_fractional_divider(struct device *dev,
 		const char *name, const char *parent_name, unsigned long flags,
-- 
2.7.4


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

* [PATCH RESEND V4 3/9] clk: imx: add pllv4 support
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
  2018-10-21 13:10 ` [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE " A.s. Dong
  2018-10-21 13:10 ` [PATCH RESEND V4 2/9] clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support A.s. Dong
@ 2018-10-21 13:10 ` A.s. Dong
  2018-10-21 13:11 ` [PATCH RESEND V4 4/9] clk: imx: add pfdv2 support A.s. Dong
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:10 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

pllv4 is designed for System Clock Generation (SCG) module observed
in IMX ULP SoC series. e.g. i.MX7ULP.

The SCG modules generates clock used to derive processor, system,
peripheral bus and external memory interface clocks while this patch
intends to support the PLL part.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Anson Huang <Anson.Huang@nxp.com>
Cc: Bai Ping <ping.bai@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * no changes
v2->v3:
 * no changes
v1->v2:
 * remove clk_pllv4_is_enabled() check in set_rate, instead it will
   be handled by core later.
 * use readl_poll_timeout
 * use clk_hw_register instead of clk_register
 * other minor changes
---
 drivers/clk/imx/Makefile    |   1 +
 drivers/clk/imx/clk-pllv4.c | 182 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/imx/clk.h       |   3 +
 3 files changed, 186 insertions(+)
 create mode 100644 drivers/clk/imx/clk-pllv4.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 8c3baa7..bfe31bf 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -11,6 +11,7 @@ obj-y += \
 	clk-pllv1.o \
 	clk-pllv2.o \
 	clk-pllv3.o \
+	clk-pllv4.o \
 	clk-pfd.o
 
 obj-$(CONFIG_SOC_IMX1)   += clk-imx1.o
diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c
new file mode 100644
index 0000000..67c64c7
--- /dev/null
+++ b/drivers/clk/imx/clk-pllv4.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ *
+ * Author: Dong Aisheng <aisheng.dong@nxp.com>
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+
+/* PLL Control Status Register (xPLLCSR) */
+#define PLL_CSR_OFFSET		0x0
+#define PLL_VLD			BIT(24)
+#define PLL_EN			BIT(0)
+
+/* PLL Configuration Register (xPLLCFG) */
+#define PLL_CFG_OFFSET		0x08
+#define BP_PLL_MULT		16
+#define BM_PLL_MULT		(0x7f << 16)
+
+/* PLL Numerator Register (xPLLNUM) */
+#define PLL_NUM_OFFSET		0x10
+
+/* PLL Denominator Register (xPLLDENOM) */
+#define PLL_DENOM_OFFSET	0x14
+
+struct clk_pllv4 {
+	struct clk_hw	hw;
+	void __iomem	*base;
+};
+
+/* Valid PLL MULT Table */
+static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
+
+#define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
+
+#define LOCK_TIMEOUT_US		USEC_PER_MSEC
+
+static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
+{
+	u32 csr;
+
+	return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
+				  csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
+}
+
+static int clk_pllv4_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pllv4 *pll = to_clk_pllv4(hw);
+
+	if (readl_relaxed(pll->base) & PLL_EN)
+		return 1;
+
+	return 0;
+}
+
+static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
+					   unsigned long parent_rate)
+{
+	struct clk_pllv4 *pll = to_clk_pllv4(hw);
+	u32 div;
+
+	div = readl_relaxed(pll->base + PLL_CFG_OFFSET);
+	div &= BM_PLL_MULT;
+	div >>= BP_PLL_MULT;
+
+	return parent_rate * div;
+}
+
+static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
+				 unsigned long *prate)
+{
+	unsigned long parent_rate = *prate;
+	unsigned long round_rate, i;
+
+	for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
+		round_rate = parent_rate * pllv4_mult_table[i];
+		if (rate >= round_rate)
+			return round_rate;
+	}
+
+	return round_rate;
+}
+
+static bool clk_pllv4_is_valid_mult(unsigned int mult)
+{
+	int i;
+
+	/* check if mult is in valid MULT table */
+	for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
+		if (pllv4_mult_table[i] == mult)
+			return true;
+	}
+
+	return false;
+}
+
+static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long parent_rate)
+{
+	struct clk_pllv4 *pll = to_clk_pllv4(hw);
+	u32 val, mult;
+
+	mult = rate / parent_rate;
+
+	if (!clk_pllv4_is_valid_mult(mult))
+		return -EINVAL;
+
+	val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
+	val &= ~BM_PLL_MULT;
+	val |= mult << BP_PLL_MULT;
+	writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
+
+	return 0;
+}
+
+static int clk_pllv4_enable(struct clk_hw *hw)
+{
+	u32 val;
+	struct clk_pllv4 *pll = to_clk_pllv4(hw);
+
+	val = readl_relaxed(pll->base);
+	val |= PLL_EN;
+	writel_relaxed(val, pll->base);
+
+	return clk_pllv4_wait_lock(pll);
+}
+
+static void clk_pllv4_disable(struct clk_hw *hw)
+{
+	u32 val;
+	struct clk_pllv4 *pll = to_clk_pllv4(hw);
+
+	val = readl_relaxed(pll->base);
+	val &= ~PLL_EN;
+	writel_relaxed(val, pll->base);
+}
+
+static const struct clk_ops clk_pllv4_ops = {
+	.recalc_rate	= clk_pllv4_recalc_rate,
+	.round_rate	= clk_pllv4_round_rate,
+	.set_rate	= clk_pllv4_set_rate,
+	.enable		= clk_pllv4_enable,
+	.disable	= clk_pllv4_disable,
+	.is_enabled	= clk_pllv4_is_enabled,
+};
+
+struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name,
+			  void __iomem *base)
+{
+	struct clk_pllv4 *pll;
+	struct clk_hw *hw;
+	struct clk_init_data init;
+	int ret;
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->base = base;
+
+	init.name = name;
+	init.ops = &clk_pllv4_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	pll->hw.init = &init;
+
+	hw = &pll->hw;
+	ret = clk_hw_register(NULL, hw);
+	if (ret) {
+		kfree(pll);
+		hw = ERR_PTR(ret);
+	}
+
+	return hw;
+}
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 8076ec0..2fb4f1d 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -42,6 +42,9 @@ enum imx_pllv3_type {
 struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
 		const char *parent_name, void __iomem *base, u32 div_mask);
 
+struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name,
+			     void __iomem *base);
+
 struct clk *clk_register_gate2(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		void __iomem *reg, u8 bit_idx, u8 cgr_val,
-- 
2.7.4


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

* [PATCH RESEND V4 4/9] clk: imx: add pfdv2 support
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (2 preceding siblings ...)
  2018-10-21 13:10 ` [PATCH RESEND V4 3/9] clk: imx: add pllv4 support A.s. Dong
@ 2018-10-21 13:11 ` A.s. Dong
  2018-10-21 13:11 ` [PATCH RESEND V4 5/9] clk: imx: add composite clk support A.s. Dong
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

The pfdv2 is designed for PLL Fractional Divide (PFD) observed in System
Clock Generation (SCG) module in IMX ULP SoC series. e.g. i.MX7ULP.

NOTE pfdv2 can only be operated when clk is gated.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Anson Huang <Anson.Huang@nxp.com>
Cc: Bai Ping <ping.bai@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * no changes
v2->v3:
 * no changes
v1->v2:
 * change to readl_poll_timeout
 * add pfd lock to protect share reg access between rate and enable/disable
   operations and multiple pfd instances.
 * use clk_hw_register
---
 drivers/clk/imx/Makefile    |   3 +-
 drivers/clk/imx/clk-pfdv2.c | 201 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/imx/clk.h       |   3 +
 3 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/imx/clk-pfdv2.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index bfe31bf..e5b0d42 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -12,7 +12,8 @@ obj-y += \
 	clk-pllv2.o \
 	clk-pllv3.o \
 	clk-pllv4.o \
-	clk-pfd.o
+	clk-pfd.o \
+	clk-pfdv2.o
 
 obj-$(CONFIG_SOC_IMX1)   += clk-imx1.o
 obj-$(CONFIG_SOC_IMX21)  += clk-imx21.o
diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c
new file mode 100644
index 0000000..afb2904
--- /dev/null
+++ b/drivers/clk/imx/clk-pfdv2.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ *
+ * Author: Dong Aisheng <aisheng.dong@nxp.com>
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+
+/**
+ * struct clk_pfdv2 - IMX PFD clock
+ * @clk_hw:	clock source
+ * @reg:	PFD register address
+ * @gate_bit:	Gate bit offset
+ * @vld_bit:	Valid bit offset
+ * @frac_off:	PLL Fractional Divider offset
+ */
+
+struct clk_pfdv2 {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	u8		gate_bit;
+	u8		vld_bit;
+	u8		frac_off;
+};
+
+#define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
+
+#define CLK_PFDV2_FRAC_MASK 0x3f
+
+#define LOCK_TIMEOUT_US		USEC_PER_MSEC
+
+static DEFINE_SPINLOCK(pfd_lock);
+
+static int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
+{
+	u32 val;
+
+	return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit,
+				  0, LOCK_TIMEOUT_US);
+}
+
+static int clk_pfdv2_enable(struct clk_hw *hw)
+{
+	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&pfd_lock, flags);
+	val = readl_relaxed(pfd->reg);
+	val &= ~pfd->gate_bit;
+	writel_relaxed(val, pfd->reg);
+	spin_unlock_irqrestore(&pfd_lock, flags);
+
+	return clk_pfdv2_wait(pfd);
+}
+
+static void clk_pfdv2_disable(struct clk_hw *hw)
+{
+	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&pfd_lock, flags);
+	val = readl_relaxed(pfd->reg);
+	val |= pfd->gate_bit;
+	writel_relaxed(val, pfd->reg);
+	spin_unlock_irqrestore(&pfd_lock, flags);
+}
+
+static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw,
+					   unsigned long parent_rate)
+{
+	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
+	u64 tmp = parent_rate;
+	u8 frac;
+
+	frac = (readl_relaxed(pfd->reg) >> pfd->frac_off)
+		& CLK_PFDV2_FRAC_MASK;
+
+	if (!frac) {
+		pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
+			 clk_hw_get_name(hw));
+		return 0;
+	}
+
+	tmp *= 18;
+	do_div(tmp, frac);
+
+	return tmp;
+}
+
+static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate,
+				 unsigned long *prate)
+{
+	u64 tmp = *prate;
+	u8 frac;
+
+	tmp = tmp * 18 + rate / 2;
+	do_div(tmp, rate);
+	frac = tmp;
+
+	if (frac < 12)
+		frac = 12;
+	else if (frac > 35)
+		frac = 35;
+
+	tmp = *prate;
+	tmp *= 18;
+	do_div(tmp, frac);
+
+	return tmp;
+}
+
+static int clk_pfdv2_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
+
+	if (readl_relaxed(pfd->reg) & pfd->gate_bit)
+		return 0;
+
+	return 1;
+}
+
+static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long parent_rate)
+{
+	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
+	unsigned long flags;
+	u64 tmp = parent_rate;
+	u32 val;
+	u8 frac;
+
+	tmp = tmp * 18 + rate / 2;
+	do_div(tmp, rate);
+	frac = tmp;
+	if (frac < 12)
+		frac = 12;
+	else if (frac > 35)
+		frac = 35;
+
+	spin_lock_irqsave(&pfd_lock, flags);
+	val = readl_relaxed(pfd->reg);
+	val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off);
+	val |= frac << pfd->frac_off;
+	writel_relaxed(val, pfd->reg);
+	spin_unlock_irqrestore(&pfd_lock, flags);
+
+	return 0;
+}
+
+static const struct clk_ops clk_pfdv2_ops = {
+	.enable		= clk_pfdv2_enable,
+	.disable	= clk_pfdv2_disable,
+	.recalc_rate	= clk_pfdv2_recalc_rate,
+	.round_rate	= clk_pfdv2_round_rate,
+	.set_rate	= clk_pfdv2_set_rate,
+	.is_enabled     = clk_pfdv2_is_enabled,
+};
+
+struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
+			     void __iomem *reg, u8 idx)
+{
+	struct clk_init_data init;
+	struct clk_pfdv2 *pfd;
+	struct clk_hw *hw;
+	int ret;
+
+	WARN_ON(idx > 3);
+
+	pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
+	if (!pfd)
+		return ERR_PTR(-ENOMEM);
+
+	pfd->reg = reg;
+	pfd->gate_bit = 1 << ((idx + 1) * 8 - 1);
+	pfd->vld_bit = pfd->gate_bit - 1;
+	pfd->frac_off = idx * 8;
+
+	init.name = name;
+	init.ops = &clk_pfdv2_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	pfd->hw.init = &init;
+
+	hw = &pfd->hw;
+	ret = clk_hw_register(NULL, hw);
+	if (ret) {
+		kfree(pfd);
+		hw = ERR_PTR(ret);
+	}
+
+	return hw;
+}
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 2fb4f1d..a5a9374 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -60,6 +60,9 @@ struct clk *imx_clk_gate_exclusive(const char *name, const char *parent,
 struct clk *imx_clk_pfd(const char *name, const char *parent_name,
 		void __iomem *reg, u8 idx);
 
+struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
+			     void __iomem *reg, u8 idx);
+
 struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
 				 void __iomem *reg, u8 shift, u8 width,
 				 void __iomem *busy_reg, u8 busy_shift);
-- 
2.7.4


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

* [PATCH RESEND V4 5/9] clk: imx: add composite clk support
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (3 preceding siblings ...)
  2018-10-21 13:11 ` [PATCH RESEND V4 4/9] clk: imx: add pfdv2 support A.s. Dong
@ 2018-10-21 13:11 ` A.s. Dong
  2018-10-21 13:11 ` [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc A.s. Dong
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

The imx composite clk is designed for Peripheral Clock Control (PCC)
module observed in IMX ULP SoC series. e.g. i.MX7ULP.

NOTE pcc can only be operated when clk is gated.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Anson Huang <Anson.Huang@nxp.com>
Cc: Bai Ping <ping.bai@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * no changes
v2->v3:
 * no changes
v1->v2:
 * remove an unneeded blank line change
 * use clk_hw_register
---
 drivers/clk/imx/Makefile        |  1 +
 drivers/clk/imx/clk-composite.c | 85 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/imx/clk.h           |  6 +++
 3 files changed, 92 insertions(+)
 create mode 100644 drivers/clk/imx/clk-composite.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index e5b0d42..f4da12c 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -4,6 +4,7 @@ obj-y += \
 	clk.o \
 	clk-busy.o \
 	clk-cpu.o \
+	clk-composite.o \
 	clk-fixup-div.o \
 	clk-fixup-mux.o \
 	clk-gate-exclusive.o \
diff --git a/drivers/clk/imx/clk-composite.c b/drivers/clk/imx/clk-composite.c
new file mode 100644
index 0000000..297974b
--- /dev/null
+++ b/drivers/clk/imx/clk-composite.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#define PCG_PCS_SHIFT	24
+#define PCG_PCS_MASK	0x7
+#define PCG_CGC_SHIFT	30
+#define PCG_FRAC_SHIFT	3
+#define PCG_FRAC_WIDTH	1
+#define PCG_FRAC_MASK	BIT(3)
+#define PCG_PCD_SHIFT	0
+#define PCG_PCD_WIDTH	3
+#define PCG_PCD_MASK	0x7
+
+struct clk_hw *imx_clk_composite(const char *name,
+			      const char * const *parent_names,
+			      int num_parents, bool mux_present,
+			      bool rate_present, bool gate_present,
+			      void __iomem *reg)
+{
+	struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL;
+	struct clk_fractional_divider *fd = NULL;
+	struct clk_gate *gate = NULL;
+	struct clk_mux *mux = NULL;
+	struct clk_hw *hw;
+
+	if (mux_present) {
+		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+		if (!mux)
+			return ERR_PTR(-ENOMEM);
+		mux_hw = &mux->hw;
+		mux->reg = reg;
+		mux->shift = PCG_PCS_SHIFT;
+		mux->mask = PCG_PCS_MASK;
+	}
+
+	if (rate_present) {
+		fd = kzalloc(sizeof(*fd), GFP_KERNEL);
+		if (!fd) {
+			kfree(mux);
+			return ERR_PTR(-ENOMEM);
+		}
+		fd_hw = &fd->hw;
+		fd->reg = reg;
+		fd->mshift = PCG_FRAC_SHIFT;
+		fd->mwidth = PCG_FRAC_WIDTH;
+		fd->mmask  = PCG_FRAC_MASK;
+		fd->nshift = PCG_PCD_SHIFT;
+		fd->nwidth = PCG_PCD_WIDTH;
+		fd->nmask = PCG_PCD_MASK;
+		fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
+	}
+
+	if (gate_present) {
+		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+		if (!gate) {
+			kfree(mux);
+			kfree(fd);
+			return ERR_PTR(-ENOMEM);
+		}
+		gate_hw = &gate->hw;
+		gate->reg = reg;
+		gate->bit_idx = PCG_CGC_SHIFT;
+	}
+
+	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
+				       mux_hw, &clk_mux_ops, fd_hw,
+				       &clk_fractional_divider_ops, gate_hw,
+				       &clk_gate_ops, CLK_SET_RATE_GATE |
+				       CLK_SET_PARENT_GATE);
+	if (IS_ERR(hw)) {
+		kfree(mux);
+		kfree(fd);
+		kfree(gate);
+	}
+
+	return hw;
+}
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index a5a9374..bc43f68 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -71,6 +71,12 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
 			     u8 width, void __iomem *busy_reg, u8 busy_shift,
 			     const char **parent_names, int num_parents);
 
+struct clk_hw *imx_clk_composite(const char *name,
+				 const char * const *parent_names,
+				 int num_parents, bool mux_present,
+				 bool rate_present, bool gate_present,
+				 void __iomem *reg);
+
 struct clk *imx_clk_fixup_divider(const char *name, const char *parent,
 				  void __iomem *reg, u8 shift, u8 width,
 				  void (*fixup)(u32 *val));
-- 
2.7.4


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

* [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (4 preceding siblings ...)
  2018-10-21 13:11 ` [PATCH RESEND V4 5/9] clk: imx: add composite clk support A.s. Dong
@ 2018-10-21 13:11 ` A.s. Dong
  2018-10-22 22:16   ` Rob Herring
  2018-10-21 13:11 ` [PATCH RESEND V4 7/9] clk: imx: make mux parent strings const A.s. Dong
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Rob Herring,
	Mark Rutland, Stephen Boyd, devicetree

i.MX7ULP Clock functions are under joint control of the System
Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
modules, and Core Mode Controller (CMC)1 blocks

Note IMX7ULP has two clock domains: M4 and A7. This binding doc
is only for A7 clock domain.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: devicetree@vger.kernel.org
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Anson Huang <Anson.Huang@nxp.com>
Cc: Bai Ping <ping.bai@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * make scg, pcc separate nodes according to Rob's suggestion
v2->v3:
 * no changes
v1->v2: no changes
---
 .../devicetree/bindings/clock/imx7ulp-clock.txt    |  87 ++++++++++++++++
 include/dt-bindings/clock/imx7ulp-clock.h          | 109 +++++++++++++++++++++
 2 files changed, 196 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
 create mode 100644 include/dt-bindings/clock/imx7ulp-clock.h

diff --git a/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt b/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
new file mode 100644
index 0000000..2239383
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
@@ -0,0 +1,87 @@
+* Clock bindings for Freescale i.MX7ULP
+
+i.MX7ULP Clock functions are under joint control of the System
+Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
+modules, and Core Mode Controller (CMC)1 blocks
+
+The clocking scheme provides clear separation between M4 domain
+and A7 domain. Except for a few clock sources shared between two
+domains, such as the System Oscillator clock, the Slow IRC (SIRC),
+and and the Fast IRC clock (FIRCLK), clock sources and clock
+management are separated and contained within each domain.
+
+M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
+A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.
+
+Note: this binding doc is only for A7 clock domain.
+
+System Clock Generation (SCG) modules:
+---------------------------------------------------------------------
+The System Clock Generation (SCG) is responsible for clock generation
+and distribution across this device. Functions performed by the SCG
+include: clock reference selection, generation of clock used to derive
+processor, system, peripheral bus and external memory interface clocks,
+source selection for peripheral clocks and control of power saving
+clock gating mode.
+
+Required properties:
+
+- compatible:	Should be "fsl,imx7ulp-scg1".
+- reg : 	Should contain registers location and length.
+- #clock-cells:	Should be <1>.
+- clocks:	Should contain the fixed input clocks.
+- clock-name:   Should contain the following clock names:"rosc", "sosc",
+		"sirc", "firc", "upll", "mpll".
+
+Peripheral Clock Control (PCC) modules:
+---------------------------------------------------------------------
+The Peripheral Clock Control (PCC) is responsible for clock selection,
+optional division and clock gating mode for peripherals in their
+respected power domain
+
+Required properties:
+- compatible:	Should be "fsl,imx7ulp-pcc2" or "fsl,imx7ulp-pcc3".
+- reg : 	Should contain registers location and length.
+- #clock-cells:	Should be <1>.
+
+The clock consumer should specify the desired clock by having the clock
+ID in its "clocks" phandle cell.
+See include/dt-bindings/clock/imx7ulp-clock.h
+for the full list of i.MX7ULP clock IDs of each module.
+
+Examples:
+
+#include <dt-bindings/clock/imx7ulp-clock.h>
+
+scg1: scg1@403e0000 {
+	compatible = "fsl,imx7ulp-scg1;
+	reg = <0x403e0000 0x10000>;
+	clocks = <&rosc>, <&sosc>, <&sirc>,
+		 <&firc>, <&upll>, <&mpll>;
+	clock-names = "rosc", "sosc", "sirc",
+		      "firc", "upll", "mpll";
+	#clock-cells = <1>;
+};
+
+pcc2: pcc2@403f0000 {
+	compatible = "fsl,imx7ulp-pcc2";
+	reg = <0x403f0000 0x10000>;
+	#clock-cells = <1>;
+};
+
+pcc3: pcc3@40b30000 {
+	compatible = "fsl,imx7ulp-pcc3";
+	reg = <0x40b30000 0x10000>;
+	#clock-cells = <1>;
+};
+
+usdhc1: usdhc@40380000 {
+	compatible = "fsl,imx7ulp-usdhc";
+	reg = <0x40380000 0x10000>;
+	interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>,
+		 <&scg1 IMX7ULP_CLK_NIC1_DIV>,
+		 <&pcc2 IMX7ULP_CLK_USDHC1>;
+	clock-names ="ipg", "ahb", "per";
+	bus-width = <4>;
+};
diff --git a/include/dt-bindings/clock/imx7ulp-clock.h b/include/dt-bindings/clock/imx7ulp-clock.h
new file mode 100644
index 0000000..008c5ee
--- /dev/null
+++ b/include/dt-bindings/clock/imx7ulp-clock.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ *
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_IMX7ULP_H
+#define __DT_BINDINGS_CLOCK_IMX7ULP_H
+
+/* SCG1 */
+
+#define IMX7ULP_CLK_DUMMY		0
+#define IMX7ULP_CLK_ROSC		1
+#define IMX7ULP_CLK_SOSC		2
+#define IMX7ULP_CLK_FIRC		3
+#define IMX7ULP_CLK_SPLL_PRE_SEL	4
+#define IMX7ULP_CLK_SPLL_PRE_DIV	5
+#define IMX7ULP_CLK_SPLL		6
+#define IMX7ULP_CLK_SPLL_POST_DIV1	7
+#define IMX7ULP_CLK_SPLL_POST_DIV2	8
+#define IMX7ULP_CLK_SPLL_PFD0		9
+#define IMX7ULP_CLK_SPLL_PFD1		10
+#define IMX7ULP_CLK_SPLL_PFD2		11
+#define IMX7ULP_CLK_SPLL_PFD3		12
+#define IMX7ULP_CLK_SPLL_PFD_SEL	13
+#define IMX7ULP_CLK_SPLL_SEL		14
+#define IMX7ULP_CLK_APLL_PRE_SEL	15
+#define IMX7ULP_CLK_APLL_PRE_DIV	16
+#define IMX7ULP_CLK_APLL		17
+#define IMX7ULP_CLK_APLL_POST_DIV1	18
+#define IMX7ULP_CLK_APLL_POST_DIV2	19
+#define IMX7ULP_CLK_APLL_PFD0		20
+#define IMX7ULP_CLK_APLL_PFD1		21
+#define IMX7ULP_CLK_APLL_PFD2		22
+#define IMX7ULP_CLK_APLL_PFD3		23
+#define IMX7ULP_CLK_APLL_PFD_SEL	24
+#define IMX7ULP_CLK_APLL_SEL		25
+#define IMX7ULP_CLK_UPLL		26
+#define IMX7ULP_CLK_SYS_SEL		27
+#define IMX7ULP_CLK_CORE_DIV		28
+#define IMX7ULP_CLK_BUS_DIV		29
+#define IMX7ULP_CLK_PLAT_DIV		30
+#define IMX7ULP_CLK_DDR_SEL		31
+#define IMX7ULP_CLK_DDR_DIV		32
+#define IMX7ULP_CLK_NIC_SEL		33
+#define IMX7ULP_CLK_NIC0_DIV		34
+#define IMX7ULP_CLK_GPU_DIV		35
+#define IMX7ULP_CLK_NIC1_DIV		36
+#define IMX7ULP_CLK_NIC1_BUS_DIV	37
+#define IMX7ULP_CLK_NIC1_EXT_DIV	38
+#define IMX7ULP_CLK_MIPI_PLL		39
+#define IMX7ULP_CLK_SIRC		40
+#define IMX7ULP_CLK_SOSC_BUS_CLK	41
+#define IMX7ULP_CLK_FIRC_BUS_CLK	42
+#define IMX7ULP_CLK_SPLL_BUS_CLK	43
+
+#define IMX7ULP_CLK_SCG1_END		44
+
+/* PCC2 */
+#define IMX7ULP_CLK_DMA1		0
+#define IMX7ULP_CLK_RGPIO2P1		1
+#define IMX7ULP_CLK_FLEXBUS		2
+#define IMX7ULP_CLK_SEMA42_1		3
+#define IMX7ULP_CLK_DMA_MUX1		4
+#define IMX7ULP_CLK_SNVS		5
+#define IMX7ULP_CLK_CAAM		6
+#define IMX7ULP_CLK_LPTPM4		7
+#define IMX7ULP_CLK_LPTPM5		8
+#define IMX7ULP_CLK_LPIT1		9
+#define IMX7ULP_CLK_LPSPI2		10
+#define IMX7ULP_CLK_LPSPI3		11
+#define IMX7ULP_CLK_LPI2C4		12
+#define IMX7ULP_CLK_LPI2C5		13
+#define IMX7ULP_CLK_LPUART4		14
+#define IMX7ULP_CLK_LPUART5		15
+#define IMX7ULP_CLK_FLEXIO1		16
+#define IMX7ULP_CLK_USB0		17
+#define IMX7ULP_CLK_USB1		18
+#define IMX7ULP_CLK_USB_PHY		19
+#define IMX7ULP_CLK_USB_PL301		20
+#define IMX7ULP_CLK_USDHC0		21
+#define IMX7ULP_CLK_USDHC1		22
+#define IMX7ULP_CLK_WDG1		23
+#define IMX7ULP_CLK_WDG2		24
+
+#define IMX7ULP_CLK_PCC2_END		25
+
+/* PCC3 */
+#define IMX7ULP_CLK_LPTPM6		0
+#define IMX7ULP_CLK_LPTPM7		1
+#define IMX7ULP_CLK_LPI2C6		2
+#define IMX7ULP_CLK_LPI2C7		3
+#define IMX7ULP_CLK_LPUART6		4
+#define IMX7ULP_CLK_LPUART7		5
+#define IMX7ULP_CLK_VIU			6
+#define IMX7ULP_CLK_DSI			7
+#define IMX7ULP_CLK_LCDIF		8
+#define IMX7ULP_CLK_MMDC		9
+#define IMX7ULP_CLK_PCTLC		10
+#define IMX7ULP_CLK_PCTLD		11
+#define IMX7ULP_CLK_PCTLE		12
+#define IMX7ULP_CLK_PCTLF		13
+#define IMX7ULP_CLK_GPU3D		14
+#define IMX7ULP_CLK_GPU2D		15
+
+#define IMX7ULP_CLK_PCC3_END		16
+
+#endif /* __DT_BINDINGS_CLOCK_IMX7ULP_H */
-- 
2.7.4


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

* [PATCH RESEND V4 7/9] clk: imx: make mux parent strings const
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (5 preceding siblings ...)
  2018-10-21 13:11 ` [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc A.s. Dong
@ 2018-10-21 13:11 ` A.s. Dong
  2018-10-21 13:11 ` [PATCH RESEND V4 8/9] clk: imx: implement new clk_hw based APIs A.s. Dong
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

As the commit 2893c379461a ("clk: make strings in parent name arrays
const"), let's make the parent strings const, otherwise we may meet
the following warning when compiling:

drivers/clk/imx/clk-imx7ulp.c: In function 'imx7ulp_clocks_init':
drivers/clk/imx/clk-imx7ulp.c:73:35: warning: passing argument 5 of
	'imx_clk_mux_flags' discards 'const' qualifier from pointer target type

  clks[IMX7ULP_CLK_APLL_PRE_SEL] = imx_clk_mux_flags("apll_pre_sel", base + 0x508, 0,
	1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
                                   ^
In file included from drivers/clk/imx/clk-imx7ulp.c:23:0:
drivers/clk/imx/clk.h:200:27: note: expected 'const char **' but argument is
 of type 'const char * const*'
...

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v1->v4: no changes
---
 drivers/clk/imx/clk-busy.c      |  2 +-
 drivers/clk/imx/clk-fixup-mux.c |  2 +-
 drivers/clk/imx/clk.h           | 18 +++++++++++-------
 3 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c
index 9903652..e695622 100644
--- a/drivers/clk/imx/clk-busy.c
+++ b/drivers/clk/imx/clk-busy.c
@@ -154,7 +154,7 @@ static const struct clk_ops clk_busy_mux_ops = {
 
 struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
 			     u8 width, void __iomem *busy_reg, u8 busy_shift,
-			     const char **parent_names, int num_parents)
+			     const char * const *parent_names, int num_parents)
 {
 	struct clk_busy_mux *busy;
 	struct clk *clk;
diff --git a/drivers/clk/imx/clk-fixup-mux.c b/drivers/clk/imx/clk-fixup-mux.c
index c9b327e..44817c1 100644
--- a/drivers/clk/imx/clk-fixup-mux.c
+++ b/drivers/clk/imx/clk-fixup-mux.c
@@ -70,7 +70,7 @@ static const struct clk_ops clk_fixup_mux_ops = {
 };
 
 struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
-			      u8 shift, u8 width, const char **parents,
+			      u8 shift, u8 width, const char * const *parents,
 			      int num_parents, void (*fixup)(u32 *val))
 {
 	struct clk_fixup_mux *fixup_mux;
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index bc43f68..7fca912 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -69,7 +69,7 @@ struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
 
 struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
 			     u8 width, void __iomem *busy_reg, u8 busy_shift,
-			     const char **parent_names, int num_parents);
+			     const char * const *parent_names, int num_parents);
 
 struct clk_hw *imx_clk_composite(const char *name,
 				 const char * const *parent_names,
@@ -82,7 +82,7 @@ struct clk *imx_clk_fixup_divider(const char *name, const char *parent,
 				  void (*fixup)(u32 *val));
 
 struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
-			      u8 shift, u8 width, const char **parents,
+			      u8 shift, u8 width, const char * const *parents,
 			      int num_parents, void (*fixup)(u32 *val));
 
 static inline struct clk *imx_clk_fixed(const char *name, int rate)
@@ -91,7 +91,8 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate)
 }
 
 static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
-		u8 shift, u8 width, const char **parents, int num_parents)
+			u8 shift, u8 width, const char * const *parents,
+			int num_parents)
 {
 	return clk_register_mux(NULL, name, parents, num_parents,
 			CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg,
@@ -204,7 +205,8 @@ static inline struct clk *imx_clk_gate4(const char *name, const char *parent,
 }
 
 static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
-		u8 shift, u8 width, const char **parents, int num_parents)
+			u8 shift, u8 width, const char * const *parents,
+			int num_parents)
 {
 	return clk_register_mux(NULL, name, parents, num_parents,
 			CLK_SET_RATE_NO_REPARENT, reg, shift,
@@ -212,7 +214,8 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
 }
 
 static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
-		u8 shift, u8 width, const char **parents, int num_parents)
+			u8 shift, u8 width, const char * const *parents,
+			int num_parents)
 {
 	return clk_register_mux(NULL, name, parents, num_parents,
 			CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE,
@@ -220,8 +223,9 @@ static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
 }
 
 static inline struct clk *imx_clk_mux_flags(const char *name,
-		void __iomem *reg, u8 shift, u8 width, const char **parents,
-		int num_parents, unsigned long flags)
+			void __iomem *reg, u8 shift, u8 width,
+			const char * const *parents, int num_parents,
+			unsigned long flags)
 {
 	return clk_register_mux(NULL, name, parents, num_parents,
 			flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0,
-- 
2.7.4


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

* [PATCH RESEND V4 8/9] clk: imx: implement new clk_hw based APIs
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (6 preceding siblings ...)
  2018-10-21 13:11 ` [PATCH RESEND V4 7/9] clk: imx: make mux parent strings const A.s. Dong
@ 2018-10-21 13:11 ` A.s. Dong
  2018-10-21 13:11 ` [PATCH RESEND V4 9/9] clk: imx: add imx7ulp clk driver A.s. Dong
  2018-10-21 13:15 ` [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong

Clock providers are recommended to use the new struct clk_hw based API,
so implement IMX clk_hw based provider helpers functions to the new
approach.

Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v2->v4:
 * no changes
v1->v2:  new patches
---
 drivers/clk/imx/clk.c | 22 ++++++++++++++++++
 drivers/clk/imx/clk.h | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+)

diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c
index 9074e69..1efed86 100644
--- a/drivers/clk/imx/clk.c
+++ b/drivers/clk/imx/clk.c
@@ -18,6 +18,16 @@ void __init imx_check_clocks(struct clk *clks[], unsigned int count)
 			       i, PTR_ERR(clks[i]));
 }
 
+void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count)
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++)
+		if (IS_ERR(clks[i]))
+			pr_err("i.MX clk %u: register failed with %ld\n",
+			       i, PTR_ERR(clks[i]));
+}
+
 static struct clk * __init imx_obtain_fixed_clock_from_dt(const char *name)
 {
 	struct of_phandle_args phandle;
@@ -49,6 +59,18 @@ struct clk * __init imx_obtain_fixed_clock(
 	return clk;
 }
 
+struct clk_hw * __init imx_obtain_fixed_clk_hw(struct device_node *np,
+					       const char *name)
+{
+	struct clk *clk;
+
+	clk = of_clk_get_by_name(np, name);
+	if (IS_ERR(clk))
+		return ERR_PTR(-ENOENT);
+
+	return __clk_get_hw(clk);
+}
+
 /*
  * This fixups the register CCM_CSCMR1 write value.
  * The write/read/divider values of the aclk_podf field
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 7fca912..d3fcaa5 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -8,6 +8,7 @@
 extern spinlock_t imx_ccm_lock;
 
 void imx_check_clocks(struct clk *clks[], unsigned int count);
+void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count);
 void imx_register_uart_clocks(struct clk ** const clks[]);
 
 extern void imx_cscmr1_fixup(u32 *val);
@@ -54,6 +55,9 @@ struct clk *clk_register_gate2(struct device *dev, const char *name,
 struct clk * imx_obtain_fixed_clock(
 			const char *name, unsigned long rate);
 
+struct clk_hw *imx_obtain_fixed_clk_hw(struct device_node *np,
+				       const char *name);
+
 struct clk *imx_clk_gate_exclusive(const char *name, const char *parent,
 	 void __iomem *reg, u8 shift, u32 exclusive_mask);
 
@@ -90,6 +94,16 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate)
 	return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
 }
 
+static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate)
+{
+	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
+static inline struct clk_hw *imx_get_clk_hw_fixed(const char *name, int rate)
+{
+	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
 static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
 			u8 shift, u8 width, const char * const *parents,
 			int num_parents)
@@ -113,6 +127,15 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent,
 			reg, shift, width, 0, &imx_ccm_lock);
 }
 
+static inline struct clk_hw *imx_clk_hw_divider(const char *name,
+						const char *parent,
+						void __iomem *reg, u8 shift,
+						u8 width)
+{
+	return clk_hw_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+				       reg, shift, width, 0, &imx_ccm_lock);
+}
+
 static inline struct clk *imx_clk_divider_flags(const char *name,
 		const char *parent, void __iomem *reg, u8 shift, u8 width,
 		unsigned long flags)
@@ -121,6 +144,15 @@ static inline struct clk *imx_clk_divider_flags(const char *name,
 			reg, shift, width, 0, &imx_ccm_lock);
 }
 
+static inline struct clk_hw *imx_clk_hw_divider_flags(const char *name,
+						   const char *parent,
+						   void __iomem *reg, u8 shift,
+						   u8 width, unsigned long flags)
+{
+	return clk_hw_register_divider(NULL, name, parent, flags,
+				       reg, shift, width, 0, &imx_ccm_lock);
+}
+
 static inline struct clk *imx_clk_divider2(const char *name, const char *parent,
 		void __iomem *reg, u8 shift, u8 width)
 {
@@ -143,6 +175,13 @@ static inline struct clk *imx_clk_gate_flags(const char *name, const char *paren
 			shift, 0, &imx_ccm_lock);
 }
 
+static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *parent,
+					     void __iomem *reg, u8 shift)
+{
+	return clk_hw_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+				    shift, 0, &imx_ccm_lock);
+}
+
 static inline struct clk *imx_clk_gate_dis(const char *name, const char *parent,
 		void __iomem *reg, u8 shift)
 {
@@ -222,6 +261,17 @@ static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
 			reg, shift, width, 0, &imx_ccm_lock);
 }
 
+static inline struct clk_hw *imx_clk_hw_mux2(const char *name, void __iomem *reg,
+					     u8 shift, u8 width,
+					     const char * const *parents,
+					     int num_parents)
+{
+	return clk_hw_register_mux(NULL, name, parents, num_parents,
+				   CLK_SET_RATE_NO_REPARENT |
+				   CLK_OPS_PARENT_ENABLE,
+				   reg, shift, width, 0, &imx_ccm_lock);
+}
+
 static inline struct clk *imx_clk_mux_flags(const char *name,
 			void __iomem *reg, u8 shift, u8 width,
 			const char * const *parents, int num_parents,
@@ -232,6 +282,18 @@ static inline struct clk *imx_clk_mux_flags(const char *name,
 			&imx_ccm_lock);
 }
 
+static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name,
+						  void __iomem *reg, u8 shift,
+						  u8 width,
+						  const char * const *parents,
+						  int num_parents,
+						  unsigned long flags)
+{
+	return clk_hw_register_mux(NULL, name, parents, num_parents,
+				   flags | CLK_SET_RATE_NO_REPARENT,
+				   reg, shift, width, 0, &imx_ccm_lock);
+}
+
 struct clk *imx_clk_cpu(const char *name, const char *parent_name,
 		struct clk *div, struct clk *mux, struct clk *pll,
 		struct clk *step);
-- 
2.7.4


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

* [PATCH RESEND V4 9/9] clk: imx: add imx7ulp clk driver
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (7 preceding siblings ...)
  2018-10-21 13:11 ` [PATCH RESEND V4 8/9] clk: imx: implement new clk_hw based APIs A.s. Dong
@ 2018-10-21 13:11 ` A.s. Dong
  2018-10-21 13:15 ` [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
  9 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:11 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

i.MX7ULP Clock functions are under joint control of the System
Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
modules, and Core Mode Controller (CMC)1 blocks

The clocking scheme provides clear separation between M4 domain
and A7 domain. Except for a few clock sources shared between two
domains, such as the System Oscillator clock, the Slow IRC (SIRC),
and and the Fast IRC clock (FIRCLK), clock sources and clock
management are separated and contained within each domain.

M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.

This driver only adds clock support in A7 domain.

Note that most clocks required to be operated when gated, e.g. pll,
pfd, pcc. And more special cases that scs/ddr/nic mux selecting
different clock source requires that clock to be enabled first,
then we need set CLK_OPS_PARENT_ENABLE flag for them properly.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Anson Huang <Anson.Huang@nxp.com>
Cc: Bai Ping <ping.bai@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v3->v4:
 * update after changing scg and pcc into separete nodes according to
   Rob's suggestion
v2->v3:
 * no changes
v1->v2:
 * use of_clk_add_hw_provider instead
 * split the clocks register process into two parts: early part for possible
   timers clocks registered by CLK_OF_DECLARE_DRIVER and the later part for
   the left normal peripheral clocks registered by a platform driver.
---
 drivers/clk/imx/Makefile      |   1 +
 drivers/clk/imx/clk-imx7ulp.c | 209 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 210 insertions(+)
 create mode 100644 drivers/clk/imx/clk-imx7ulp.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index f4da12c..983c0a5 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -29,4 +29,5 @@ obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o
 obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o
 obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o
 obj-$(CONFIG_SOC_IMX7D)  += clk-imx7d.o
+obj-$(CONFIG_SOC_IMX7ULP) += clk-imx7ulp.o
 obj-$(CONFIG_SOC_VF610)  += clk-vf610.o
diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c
new file mode 100644
index 0000000..33dedca
--- /dev/null
+++ b/drivers/clk/imx/clk-imx7ulp.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ *
+ * Author: Dong Aisheng <aisheng.dong@nxp.com>
+ *
+ */
+
+#include <dt-bindings/clock/imx7ulp-clock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+static const char * const pll_pre_sels[]	= { "sosc", "firc", };
+static const char * const spll_pfd_sels[]	= { "spll_pfd0", "spll_pfd1", "spll_pfd2", "spll_pfd3", };
+static const char * const spll_sels[]		= { "spll", "spll_pfd_sel", };
+static const char * const apll_pfd_sels[]	= { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", };
+static const char * const apll_sels[]		= { "apll", "apll_pfd_sel", };
+static const char * const scs_sels[]		= { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "upll", };
+static const char * const ddr_sels[]		= { "apll_pfd_sel", "upll", };
+static const char * const nic_sels[]		= { "firc", "ddr_clk", };
+static const char * const periph_plat_sels[]	= { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", };
+static const char * const periph_bus_sels[]	= { "dummy", "sosc_bus_clk", "mpll", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", };
+
+static void __init imx7ulp_clk_scg1_init(struct device_node *np)
+{
+	struct clk_hw_onecell_data *clk_data;
+	struct clk_hw **clks;
+	void __iomem *base;
+
+	clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
+			   IMX7ULP_CLK_SCG1_END, GFP_KERNEL);
+	if (!clk_data)
+		return;
+
+	clk_data->num = IMX7ULP_CLK_SCG1_END;
+	clks = clk_data->hws;
+
+	clks[IMX7ULP_CLK_DUMMY]		= imx_clk_hw_fixed("dummy", 0);
+
+	clks[IMX7ULP_CLK_ROSC]		= imx_obtain_fixed_clk_hw(np, "rosc");
+	clks[IMX7ULP_CLK_SOSC]		= imx_obtain_fixed_clk_hw(np, "sosc");
+	clks[IMX7ULP_CLK_SIRC]		= imx_obtain_fixed_clk_hw(np, "sirc");
+	clks[IMX7ULP_CLK_FIRC]		= imx_obtain_fixed_clk_hw(np, "firc");
+	clks[IMX7ULP_CLK_MIPI_PLL]	= imx_obtain_fixed_clk_hw(np, "mpll");
+	clks[IMX7ULP_CLK_UPLL]		= imx_obtain_fixed_clk_hw(np, "upll");
+
+	/* SCG1 */
+	base = of_iomap(np, 0);
+	WARN_ON(!base);
+
+	/* NOTE: xPLL config can't be changed when xPLL is enabled */
+	clks[IMX7ULP_CLK_APLL_PRE_SEL]	= imx_clk_hw_mux_flags("apll_pre_sel", base + 0x508, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_SPLL_PRE_SEL]	= imx_clk_hw_mux_flags("spll_pre_sel", base + 0x608, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
+
+	/*							   name		    parent_name	   reg			shift	width	flags */
+	clks[IMX7ULP_CLK_APLL_PRE_DIV]	= imx_clk_hw_divider_flags("apll_pre_div", "apll_pre_sel", base + 0x508,	8,	3,	CLK_SET_RATE_GATE);
+	clks[IMX7ULP_CLK_SPLL_PRE_DIV]	= imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608,	8,	3,	CLK_SET_RATE_GATE);
+
+	/*						name	 parent_name	 base */
+	clks[IMX7ULP_CLK_APLL]		= imx_clk_pllv4("apll",  "apll_pre_div", base + 0x500);
+	clks[IMX7ULP_CLK_SPLL]		= imx_clk_pllv4("spll",  "spll_pre_div", base + 0x600);
+
+	/* APLL PFDs */
+	clks[IMX7ULP_CLK_APLL_PFD0]	= imx_clk_pfdv2("apll_pfd0", "apll", base + 0x50c, 0);
+	clks[IMX7ULP_CLK_APLL_PFD1]	= imx_clk_pfdv2("apll_pfd1", "apll", base + 0x50c, 1);
+	clks[IMX7ULP_CLK_APLL_PFD2]	= imx_clk_pfdv2("apll_pfd2", "apll", base + 0x50c, 2);
+	clks[IMX7ULP_CLK_APLL_PFD3]	= imx_clk_pfdv2("apll_pfd3", "apll", base + 0x50c, 3);
+
+	/* SPLL PFDs */
+	clks[IMX7ULP_CLK_SPLL_PFD0]	= imx_clk_pfdv2("spll_pfd0", "spll", base + 0x60C, 0);
+	clks[IMX7ULP_CLK_SPLL_PFD1]	= imx_clk_pfdv2("spll_pfd1", "spll", base + 0x60C, 1);
+	clks[IMX7ULP_CLK_SPLL_PFD2]	= imx_clk_pfdv2("spll_pfd2", "spll", base + 0x60C, 2);
+	clks[IMX7ULP_CLK_SPLL_PFD3]	= imx_clk_pfdv2("spll_pfd3", "spll", base + 0x60C, 3);
+
+	/* PLL Mux */
+	clks[IMX7ULP_CLK_APLL_PFD_SEL]	= imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_SPLL_PFD_SEL]	= imx_clk_hw_mux_flags("spll_pfd_sel", base + 0x608, 14, 2, spll_pfd_sels, ARRAY_SIZE(spll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_APLL_SEL]	= imx_clk_hw_mux_flags("apll_sel", base + 0x508, 1, 1, apll_sels, ARRAY_SIZE(apll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_SPLL_SEL]	= imx_clk_hw_mux_flags("spll_sel", base + 0x608, 1, 1, spll_sels, ARRAY_SIZE(spll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+
+	clks[IMX7ULP_CLK_SPLL_BUS_CLK]	= clk_hw_register_divider(NULL, "spll_bus_clk", "spll_sel", CLK_SET_RATE_GATE, base + 0x604, 8, 3, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+
+	/* scs/ddr/nic select different clock source requires that clock to be enabled first */
+	clks[IMX7ULP_CLK_SYS_SEL]	= imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels));
+	clks[IMX7ULP_CLK_NIC_SEL]	= imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels));
+	clks[IMX7ULP_CLK_DDR_SEL]	= imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 1, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
+
+	clks[IMX7ULP_CLK_CORE_DIV]	= imx_clk_hw_divider_flags("divcore",	"scs_sel",  base + 0x14, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+	clks[IMX7ULP_CLK_DDR_DIV]	= clk_hw_register_divider(NULL, "ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3,
+								  CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+
+	clks[IMX7ULP_CLK_NIC0_DIV]	= imx_clk_hw_divider_flags("nic0_clk",		"nic_sel",  base + 0x40, 24, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+	clks[IMX7ULP_CLK_NIC1_DIV]	= imx_clk_hw_divider_flags("nic1_clk",		"nic0_clk", base + 0x40, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+	clks[IMX7ULP_CLK_NIC1_BUS_DIV]	= imx_clk_hw_divider_flags("nic1_bus_clk",	"nic1_clk", base + 0x40, 4,  4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+	clks[IMX7ULP_CLK_GPU_DIV]	= imx_clk_hw_divider("gpu_clk", "nic0_clk", base + 0x40, 20, 4);
+
+	clks[IMX7ULP_CLK_SOSC_BUS_CLK]	= clk_hw_register_divider(NULL, "sosc_bus_clk", "sosc", 0, base + 0x104, 8, 3,
+								  CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+	clks[IMX7ULP_CLK_FIRC_BUS_CLK]	= clk_hw_register_divider(NULL, "firc_bus_clk", "firc", 0, base + 0x304, 8, 3,
+								  CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+
+	imx_check_clk_hws(clks, clk_data->num);
+
+	of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+}
+CLK_OF_DECLARE(imx7ulp_clk_scg1, "fsl,imx7ulp-scg1", imx7ulp_clk_scg1_init);
+
+static void __init imx7ulp_clk_pcc2_init(struct device_node *np)
+{
+	struct clk_hw_onecell_data *clk_data;
+	struct clk_hw **clks;
+	void __iomem *base;
+
+	clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
+			   IMX7ULP_CLK_SCG1_END, GFP_KERNEL);
+	if (!clk_data)
+		return;
+
+	clk_data->num = IMX7ULP_CLK_PCC2_END;
+	clks = clk_data->hws;
+
+	/* PCC2 */
+	base = of_iomap(np, 0);
+	WARN_ON(!base);
+
+	clks[IMX7ULP_CLK_DMA1]		= imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30);
+	clks[IMX7ULP_CLK_RGPIO2P1]	= imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30);
+	clks[IMX7ULP_CLK_DMA_MUX1]	= imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30);
+	clks[IMX7ULP_CLK_SNVS]		= imx_clk_hw_gate("snvs", "nic1_bus_clk", base + 0x8c, 30);
+	clks[IMX7ULP_CLK_CAAM]		= imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30);
+	clks[IMX7ULP_CLK_LPTPM4]	= imx_clk_composite("lptpm4",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
+	clks[IMX7ULP_CLK_LPTPM5]	= imx_clk_composite("lptpm5",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
+	clks[IMX7ULP_CLK_LPIT1]		= imx_clk_composite("lpit1",   periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
+	clks[IMX7ULP_CLK_LPSPI2]	= imx_clk_composite("lpspi2",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa4);
+	clks[IMX7ULP_CLK_LPSPI3]	= imx_clk_composite("lpspi3",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa8);
+	clks[IMX7ULP_CLK_LPI2C4]	= imx_clk_composite("lpi2c4",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xac);
+	clks[IMX7ULP_CLK_LPI2C5]	= imx_clk_composite("lpi2c5",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb0);
+	clks[IMX7ULP_CLK_LPUART4]	= imx_clk_composite("lpuart4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb4);
+	clks[IMX7ULP_CLK_LPUART5]	= imx_clk_composite("lpuart5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb8);
+	clks[IMX7ULP_CLK_FLEXIO1]	= imx_clk_composite("flexio1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xc4);
+	clks[IMX7ULP_CLK_USB0]		= imx_clk_composite("usb0",    periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xcc);
+	clks[IMX7ULP_CLK_USB1]		= imx_clk_composite("usb1",    periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xd0);
+	clks[IMX7ULP_CLK_USB_PHY]	= imx_clk_hw_gate("usb_phy", "nic1_bus_clk", base + 0xd4, 30);
+	clks[IMX7ULP_CLK_USDHC0]	= imx_clk_composite("usdhc0",  periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xdc);
+	clks[IMX7ULP_CLK_USDHC1]	= imx_clk_composite("usdhc1",  periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xe0);
+	clks[IMX7ULP_CLK_WDG1]		= imx_clk_composite("wdg1",    periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true,  true, base + 0xf4);
+	clks[IMX7ULP_CLK_WDG2]		= imx_clk_composite("sdg2",    periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true,  true, base + 0x10c);
+
+	imx_check_clk_hws(clks, clk_data->num);
+
+	of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+}
+CLK_OF_DECLARE(imx7ulp_clk_pcc2, "fsl,imx7ulp-pcc2", imx7ulp_clk_pcc2_init);
+
+static void __init imx7ulp_clk_pcc3_init(struct device_node *np)
+{
+	struct clk_hw_onecell_data *clk_data;
+	struct clk_hw **clks;
+	void __iomem *base;
+
+	clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
+			   IMX7ULP_CLK_SCG1_END, GFP_KERNEL);
+	if (!clk_data)
+		return;
+
+	clk_data->num = IMX7ULP_CLK_PCC3_END;
+	clks = clk_data->hws;
+
+	/* PCC3 */
+	base = of_iomap(np, 0);
+	WARN_ON(!base);
+
+	clks[IMX7ULP_CLK_LPTPM6]	= imx_clk_composite("lptpm6",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x84);
+	clks[IMX7ULP_CLK_LPTPM7]	= imx_clk_composite("lptpm7",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x88);
+
+	clks[IMX7ULP_CLK_MMDC]		= clk_hw_register_gate(NULL, "mmdc", "nic1_clk", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+							       base + 0xac, 30, 0, &imx_ccm_lock);
+	clks[IMX7ULP_CLK_LPI2C6]	= imx_clk_composite("lpi2c6",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x90);
+	clks[IMX7ULP_CLK_LPI2C7]	= imx_clk_composite("lpi2c7",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
+	clks[IMX7ULP_CLK_LPUART6]	= imx_clk_composite("lpuart6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
+	clks[IMX7ULP_CLK_LPUART7]	= imx_clk_composite("lpuart7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
+	clks[IMX7ULP_CLK_DSI]		= imx_clk_composite("dsi",     periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true,  true, base + 0xa4);
+	clks[IMX7ULP_CLK_LCDIF]		= imx_clk_composite("lcdif",   periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xa8);
+
+	clks[IMX7ULP_CLK_VIU]		= imx_clk_hw_gate("viu",   "nic1_clk",	   base + 0xa0, 30);
+	clks[IMX7ULP_CLK_PCTLC]		= imx_clk_hw_gate("pctlc", "nic1_bus_clk", base + 0xb8, 30);
+	clks[IMX7ULP_CLK_PCTLD]		= imx_clk_hw_gate("pctld", "nic1_bus_clk", base + 0xbc, 30);
+	clks[IMX7ULP_CLK_PCTLE]		= imx_clk_hw_gate("pctle", "nic1_bus_clk", base + 0xc0, 30);
+	clks[IMX7ULP_CLK_PCTLF]		= imx_clk_hw_gate("pctlf", "nic1_bus_clk", base + 0xc4, 30);
+
+	clks[IMX7ULP_CLK_GPU3D]		= imx_clk_composite("gpu3d",   periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x140);
+	clks[IMX7ULP_CLK_GPU2D]		= imx_clk_composite("gpu2d",   periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x144);
+
+	imx_check_clk_hws(clks, clk_data->num);
+
+	of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+}
+CLK_OF_DECLARE(imx7ulp_clk_pcc3, "fsl,imx7ulp-pcc3", imx7ulp_clk_pcc3_init);
-- 
2.7.4


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

* RE: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
  2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
                   ` (8 preceding siblings ...)
  2018-10-21 13:11 ` [PATCH RESEND V4 9/9] clk: imx: add imx7ulp clk driver A.s. Dong
@ 2018-10-21 13:15 ` A.s. Dong
  2018-11-06 15:30   ` A.s. Dong
  9 siblings, 1 reply; 19+ messages in thread
From: A.s. Dong @ 2018-10-21 13:15 UTC (permalink / raw)
  To: sboyd
  Cc: linux-kernel, linux-arm-kernel, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, linux-clk

Hi Stephen,

In case you might miss to queue this series into your review list, so I resend this series again.
This series actually has been pending for a couple of months without comments.
Hopefully you could help find some time to handle it when you're free.

Thanks

Regards
Dong Aisheng

> -----Original Message-----
> From: A.s. Dong
> Sent: Sunday, October 21, 2018 9:11 PM
> To: linux-clk@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> sboyd@kernel.org; mturquette@baylibre.com; shawnguo@kernel.org; Anson
> Huang <anson.huang@nxp.com>; Jacky Bai <ping.bai@nxp.com>; dl-linux-imx
> <linux-imx@nxp.com>; A.s. Dong <aisheng.dong@nxp.com>
> Subject: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
> 
> This is a rebased version of below patch series against latest clk tree.
> [PATCH RESEND V3 0/9] clk: add imx7ulp clk support
> https://lkml.org/lkml/2018/3/16/310
> 
> It only updates the license to SPDX format as well as a minor fix of pllv4.
> 
> This patch series intends to add imx7ulp clk support.
> 
> i.MX7ULP Clock functions are under joint control of the System Clock
> Generation (SCG) modules, Peripheral Clock Control (PCC) modules, and Core
> Mode Controller (CMC)1 blocks
> 
> The clocking scheme provides clear separation between M4 domain and A7
> domain. Except for a few clock sources shared between two domains, such as
> the System Oscillator clock, the Slow IRC (SIRC), and and the Fast IRC clock
> (FIRCLK), clock sources and clock management are separated and contained
> within each domain.
> 
> M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
> A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.
> 
> Note: this series only adds A7 clock domain support as M4 clock domain will
> be handled by M4 seperately.
> 
> Change Log:
> v3->v4:
>  * update after changing scg and pcc into separete nodes according to
>    Rob's suggestion
> v2->v3:
>  * Patch 1 changed on: 1) split normal and gate ops 2) fix the possible racy
>    Others no changes.
> 
> v1->v2:
>  * add enable/disable for the type of CLK_DIVIDER_ZERO_GATE dividers
>  * use clk_hw apis to register clocks
>  * use of_clk_add_hw_provider
>  * split the clocks register process into two parts: early part for possible
>    timers clocks registered by CLK_OF_DECLARE_DRIVER and the later part for
>    the left normal peripheral clocks registered by a platform driver.
> 
> Dong Aisheng (9):
>   clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support
>   clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support
>   clk: imx: add pllv4 support
>   clk: imx: add pfdv2 support
>   clk: imx: add composite clk support
>   dt-bindings: clock: add imx7ulp clock binding doc
>   clk: imx: make mux parent strings const
>   clk: imx: implement new clk_hw based APIs
>   clk: imx: add imx7ulp clk driver
> 
>  .../devicetree/bindings/clock/imx7ulp-clock.txt    |  87 +++++++++
>  drivers/clk/clk-divider.c                          | 152
> +++++++++++++++
>  drivers/clk/clk-fractional-divider.c               |  10 +
>  drivers/clk/imx/Makefile                           |   6 +-
>  drivers/clk/imx/clk-busy.c                         |   2 +-
>  drivers/clk/imx/clk-composite.c                    |  85 +++++++++
>  drivers/clk/imx/clk-fixup-mux.c                    |   2 +-
>  drivers/clk/imx/clk-imx7ulp.c                      | 209
> +++++++++++++++++++++
>  drivers/clk/imx/clk-pfdv2.c                        | 201
> ++++++++++++++++++++
>  drivers/clk/imx/clk-pllv4.c                        | 182
> ++++++++++++++++++
>  drivers/clk/imx/clk.c                              |  22 +++
>  drivers/clk/imx/clk.h                              |  92 ++++++++-
>  include/dt-bindings/clock/imx7ulp-clock.h          | 109 +++++++++++
>  include/linux/clk-provider.h                       |  17 ++
>  14 files changed, 1166 insertions(+), 10 deletions(-)  create mode 100644
> Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
>  create mode 100644 drivers/clk/imx/clk-composite.c  create mode 100644
> drivers/clk/imx/clk-imx7ulp.c  create mode 100644
> drivers/clk/imx/clk-pfdv2.c  create mode 100644 drivers/clk/imx/clk-pllv4.c
> create mode 100644 include/dt-bindings/clock/imx7ulp-clock.h
> 
> --
> 2.7.4


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

* Re: [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc
  2018-10-21 13:11 ` [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc A.s. Dong
@ 2018-10-22 22:16   ` Rob Herring
  2018-10-23  2:09     ` A.s. Dong
  0 siblings, 1 reply; 19+ messages in thread
From: Rob Herring @ 2018-10-22 22:16 UTC (permalink / raw)
  To: A.s. Dong
  Cc: linux-clk, linux-kernel, linux-arm-kernel, sboyd, mturquette,
	shawnguo, Anson Huang, Jacky Bai, dl-linux-imx, Mark Rutland,
	Stephen Boyd, devicetree

On Sun, Oct 21, 2018 at 01:11:09PM +0000, A.s. Dong wrote:
> i.MX7ULP Clock functions are under joint control of the System
> Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
> modules, and Core Mode Controller (CMC)1 blocks
> 
> Note IMX7ULP has two clock domains: M4 and A7. This binding doc
> is only for A7 clock domain.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: devicetree@vger.kernel.org
> Cc: Shawn Guo <shawnguo@kernel.org>
> Cc: Anson Huang <Anson.Huang@nxp.com>
> Cc: Bai Ping <ping.bai@nxp.com>
> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
> 
> ---
> ChangeLog:
> v3->v4:
>  * make scg, pcc separate nodes according to Rob's suggestion
> v2->v3:
>  * no changes
> v1->v2: no changes
> ---
>  .../devicetree/bindings/clock/imx7ulp-clock.txt    |  87 ++++++++++++++++
>  include/dt-bindings/clock/imx7ulp-clock.h          | 109 +++++++++++++++++++++
>  2 files changed, 196 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
>  create mode 100644 include/dt-bindings/clock/imx7ulp-clock.h
> 
> diff --git a/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt b/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> new file mode 100644
> index 0000000..2239383
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> @@ -0,0 +1,87 @@
> +* Clock bindings for Freescale i.MX7ULP
> +
> +i.MX7ULP Clock functions are under joint control of the System
> +Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
> +modules, and Core Mode Controller (CMC)1 blocks
> +
> +The clocking scheme provides clear separation between M4 domain
> +and A7 domain. Except for a few clock sources shared between two
> +domains, such as the System Oscillator clock, the Slow IRC (SIRC),
> +and and the Fast IRC clock (FIRCLK), clock sources and clock
> +management are separated and contained within each domain.
> +
> +M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
> +A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.

Where's the CMC1 binding?

> +
> +Note: this binding doc is only for A7 clock domain.
> +
> +System Clock Generation (SCG) modules:
> +---------------------------------------------------------------------
> +The System Clock Generation (SCG) is responsible for clock generation
> +and distribution across this device. Functions performed by the SCG
> +include: clock reference selection, generation of clock used to derive
> +processor, system, peripheral bus and external memory interface clocks,
> +source selection for peripheral clocks and control of power saving
> +clock gating mode.
> +
> +Required properties:
> +
> +- compatible:	Should be "fsl,imx7ulp-scg1".
> +- reg : 	Should contain registers location and length.
> +- #clock-cells:	Should be <1>.
> +- clocks:	Should contain the fixed input clocks.
> +- clock-name:   Should contain the following clock names:"rosc", "sosc",

clock-names

> +		"sirc", "firc", "upll", "mpll".
> +
> +Peripheral Clock Control (PCC) modules:
> +---------------------------------------------------------------------
> +The Peripheral Clock Control (PCC) is responsible for clock selection,
> +optional division and clock gating mode for peripherals in their
> +respected power domain
> +
> +Required properties:
> +- compatible:	Should be "fsl,imx7ulp-pcc2" or "fsl,imx7ulp-pcc3".

Please format one per line.

There's some register layout difference between PCC2 and PCC3?

> +- reg : 	Should contain registers location and length.
> +- #clock-cells:	Should be <1>.

No input clocks to list?

> +
> +The clock consumer should specify the desired clock by having the clock
> +ID in its "clocks" phandle cell.
> +See include/dt-bindings/clock/imx7ulp-clock.h
> +for the full list of i.MX7ULP clock IDs of each module.
> +
> +Examples:
> +
> +#include <dt-bindings/clock/imx7ulp-clock.h>
> +
> +scg1: scg1@403e0000 {
> +	compatible = "fsl,imx7ulp-scg1;
> +	reg = <0x403e0000 0x10000>;
> +	clocks = <&rosc>, <&sosc>, <&sirc>,
> +		 <&firc>, <&upll>, <&mpll>;
> +	clock-names = "rosc", "sosc", "sirc",
> +		      "firc", "upll", "mpll";
> +	#clock-cells = <1>;
> +};
> +
> +pcc2: pcc2@403f0000 {
> +	compatible = "fsl,imx7ulp-pcc2";
> +	reg = <0x403f0000 0x10000>;
> +	#clock-cells = <1>;
> +};
> +
> +pcc3: pcc3@40b30000 {
> +	compatible = "fsl,imx7ulp-pcc3";
> +	reg = <0x40b30000 0x10000>;
> +	#clock-cells = <1>;
> +};
> +
> +usdhc1: usdhc@40380000 {
> +	compatible = "fsl,imx7ulp-usdhc";
> +	reg = <0x40380000 0x10000>;
> +	interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
> +	clocks = <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>,
> +		 <&scg1 IMX7ULP_CLK_NIC1_DIV>,
> +		 <&pcc2 IMX7ULP_CLK_USDHC1>;
> +	clock-names ="ipg", "ahb", "per";
> +	bus-width = <4>;
> +};
> diff --git a/include/dt-bindings/clock/imx7ulp-clock.h b/include/dt-bindings/clock/imx7ulp-clock.h
> new file mode 100644
> index 0000000..008c5ee
> --- /dev/null
> +++ b/include/dt-bindings/clock/imx7ulp-clock.h
> @@ -0,0 +1,109 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> + * Copyright 2017~2018 NXP
> + *
> + */
> +
> +#ifndef __DT_BINDINGS_CLOCK_IMX7ULP_H
> +#define __DT_BINDINGS_CLOCK_IMX7ULP_H
> +
> +/* SCG1 */
> +
> +#define IMX7ULP_CLK_DUMMY		0
> +#define IMX7ULP_CLK_ROSC		1
> +#define IMX7ULP_CLK_SOSC		2
> +#define IMX7ULP_CLK_FIRC		3
> +#define IMX7ULP_CLK_SPLL_PRE_SEL	4
> +#define IMX7ULP_CLK_SPLL_PRE_DIV	5
> +#define IMX7ULP_CLK_SPLL		6
> +#define IMX7ULP_CLK_SPLL_POST_DIV1	7
> +#define IMX7ULP_CLK_SPLL_POST_DIV2	8
> +#define IMX7ULP_CLK_SPLL_PFD0		9
> +#define IMX7ULP_CLK_SPLL_PFD1		10
> +#define IMX7ULP_CLK_SPLL_PFD2		11
> +#define IMX7ULP_CLK_SPLL_PFD3		12
> +#define IMX7ULP_CLK_SPLL_PFD_SEL	13
> +#define IMX7ULP_CLK_SPLL_SEL		14
> +#define IMX7ULP_CLK_APLL_PRE_SEL	15
> +#define IMX7ULP_CLK_APLL_PRE_DIV	16
> +#define IMX7ULP_CLK_APLL		17
> +#define IMX7ULP_CLK_APLL_POST_DIV1	18
> +#define IMX7ULP_CLK_APLL_POST_DIV2	19
> +#define IMX7ULP_CLK_APLL_PFD0		20
> +#define IMX7ULP_CLK_APLL_PFD1		21
> +#define IMX7ULP_CLK_APLL_PFD2		22
> +#define IMX7ULP_CLK_APLL_PFD3		23
> +#define IMX7ULP_CLK_APLL_PFD_SEL	24
> +#define IMX7ULP_CLK_APLL_SEL		25
> +#define IMX7ULP_CLK_UPLL		26
> +#define IMX7ULP_CLK_SYS_SEL		27
> +#define IMX7ULP_CLK_CORE_DIV		28
> +#define IMX7ULP_CLK_BUS_DIV		29
> +#define IMX7ULP_CLK_PLAT_DIV		30
> +#define IMX7ULP_CLK_DDR_SEL		31
> +#define IMX7ULP_CLK_DDR_DIV		32
> +#define IMX7ULP_CLK_NIC_SEL		33
> +#define IMX7ULP_CLK_NIC0_DIV		34
> +#define IMX7ULP_CLK_GPU_DIV		35
> +#define IMX7ULP_CLK_NIC1_DIV		36
> +#define IMX7ULP_CLK_NIC1_BUS_DIV	37
> +#define IMX7ULP_CLK_NIC1_EXT_DIV	38
> +#define IMX7ULP_CLK_MIPI_PLL		39
> +#define IMX7ULP_CLK_SIRC		40
> +#define IMX7ULP_CLK_SOSC_BUS_CLK	41
> +#define IMX7ULP_CLK_FIRC_BUS_CLK	42
> +#define IMX7ULP_CLK_SPLL_BUS_CLK	43
> +
> +#define IMX7ULP_CLK_SCG1_END		44
> +
> +/* PCC2 */
> +#define IMX7ULP_CLK_DMA1		0
> +#define IMX7ULP_CLK_RGPIO2P1		1
> +#define IMX7ULP_CLK_FLEXBUS		2
> +#define IMX7ULP_CLK_SEMA42_1		3
> +#define IMX7ULP_CLK_DMA_MUX1		4
> +#define IMX7ULP_CLK_SNVS		5
> +#define IMX7ULP_CLK_CAAM		6
> +#define IMX7ULP_CLK_LPTPM4		7
> +#define IMX7ULP_CLK_LPTPM5		8
> +#define IMX7ULP_CLK_LPIT1		9
> +#define IMX7ULP_CLK_LPSPI2		10
> +#define IMX7ULP_CLK_LPSPI3		11
> +#define IMX7ULP_CLK_LPI2C4		12
> +#define IMX7ULP_CLK_LPI2C5		13
> +#define IMX7ULP_CLK_LPUART4		14
> +#define IMX7ULP_CLK_LPUART5		15
> +#define IMX7ULP_CLK_FLEXIO1		16
> +#define IMX7ULP_CLK_USB0		17
> +#define IMX7ULP_CLK_USB1		18
> +#define IMX7ULP_CLK_USB_PHY		19
> +#define IMX7ULP_CLK_USB_PL301		20
> +#define IMX7ULP_CLK_USDHC0		21
> +#define IMX7ULP_CLK_USDHC1		22
> +#define IMX7ULP_CLK_WDG1		23
> +#define IMX7ULP_CLK_WDG2		24
> +
> +#define IMX7ULP_CLK_PCC2_END		25
> +
> +/* PCC3 */
> +#define IMX7ULP_CLK_LPTPM6		0
> +#define IMX7ULP_CLK_LPTPM7		1
> +#define IMX7ULP_CLK_LPI2C6		2
> +#define IMX7ULP_CLK_LPI2C7		3
> +#define IMX7ULP_CLK_LPUART6		4
> +#define IMX7ULP_CLK_LPUART7		5
> +#define IMX7ULP_CLK_VIU			6
> +#define IMX7ULP_CLK_DSI			7
> +#define IMX7ULP_CLK_LCDIF		8
> +#define IMX7ULP_CLK_MMDC		9
> +#define IMX7ULP_CLK_PCTLC		10
> +#define IMX7ULP_CLK_PCTLD		11
> +#define IMX7ULP_CLK_PCTLE		12
> +#define IMX7ULP_CLK_PCTLF		13
> +#define IMX7ULP_CLK_GPU3D		14
> +#define IMX7ULP_CLK_GPU2D		15
> +
> +#define IMX7ULP_CLK_PCC3_END		16
> +
> +#endif /* __DT_BINDINGS_CLOCK_IMX7ULP_H */
> -- 
> 2.7.4
> 

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

* RE: [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc
  2018-10-22 22:16   ` Rob Herring
@ 2018-10-23  2:09     ` A.s. Dong
  0 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-10-23  2:09 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-clk, linux-kernel, linux-arm-kernel, sboyd, mturquette,
	shawnguo, Anson Huang, Jacky Bai, dl-linux-imx, Mark Rutland,
	Stephen Boyd, devicetree

> -----Original Message-----
> From: Rob Herring [mailto:robh@kernel.org]
> Sent: Tuesday, October 23, 2018 6:17 AM
[...]
> 
> On Sun, Oct 21, 2018 at 01:11:09PM +0000, A.s. Dong wrote:
> > i.MX7ULP Clock functions are under joint control of the System Clock
> > Generation (SCG) modules, Peripheral Clock Control (PCC) modules, and
> > Core Mode Controller (CMC)1 blocks
> >
> > Note IMX7ULP has two clock domains: M4 and A7. This binding doc is
> > only for A7 clock domain.
> >
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > Cc: Michael Turquette <mturquette@baylibre.com>
> > Cc: devicetree@vger.kernel.org
> > Cc: Shawn Guo <shawnguo@kernel.org>
> > Cc: Anson Huang <Anson.Huang@nxp.com>
> > Cc: Bai Ping <ping.bai@nxp.com>
> > Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
> >
> > ---
> > ChangeLog:
> > v3->v4:
> >  * make scg, pcc separate nodes according to Rob's suggestion
> > v2->v3:
> >  * no changes
> > v1->v2: no changes
> > ---
> >  .../devicetree/bindings/clock/imx7ulp-clock.txt    |  87
> ++++++++++++++++
> >  include/dt-bindings/clock/imx7ulp-clock.h          | 109
> +++++++++++++++++++++
> >  2 files changed, 196 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> >  create mode 100644 include/dt-bindings/clock/imx7ulp-clock.h
> >
> > diff --git a/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> > b/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> > new file mode 100644
> > index 0000000..2239383
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> > @@ -0,0 +1,87 @@
> > +* Clock bindings for Freescale i.MX7ULP
> > +
> > +i.MX7ULP Clock functions are under joint control of the System Clock
> > +Generation (SCG) modules, Peripheral Clock Control (PCC) modules, and
> > +Core Mode Controller (CMC)1 blocks
> > +
> > +The clocking scheme provides clear separation between M4 domain and
> > +A7 domain. Except for a few clock sources shared between two domains,
> > +such as the System Oscillator clock, the Slow IRC (SIRC), and and the
> > +Fast IRC clock (FIRCLK), clock sources and clock management are
> > +separated and contained within each domain.
> > +
> > +M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
> > +A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.
> 
> Where's the CMC1 binding?
> 

CMC module is more related to low power control, so did not put in
clock series. But yes, it's missed in early series, will add it in next Arch
patch series.

> > +
> > +Note: this binding doc is only for A7 clock domain.
> > +
> > +System Clock Generation (SCG) modules:
> > +---------------------------------------------------------------------
> > +The System Clock Generation (SCG) is responsible for clock generation
> > +and distribution across this device. Functions performed by the SCG
> > +include: clock reference selection, generation of clock used to
> > +derive processor, system, peripheral bus and external memory
> > +interface clocks, source selection for peripheral clocks and control
> > +of power saving clock gating mode.
> > +
> > +Required properties:
> > +
> > +- compatible:	Should be "fsl,imx7ulp-scg1".
> > +- reg : 	Should contain registers location and length.
> > +- #clock-cells:	Should be <1>.
> > +- clocks:	Should contain the fixed input clocks.
> > +- clock-name:   Should contain the following clock names:"rosc", "sosc",
> 
> clock-names
>

Got it
 
> > +		"sirc", "firc", "upll", "mpll".
> > +
> > +Peripheral Clock Control (PCC) modules:
> > +---------------------------------------------------------------------
> > +The Peripheral Clock Control (PCC) is responsible for clock
> > +selection, optional division and clock gating mode for peripherals in
> > +their respected power domain
> > +
> > +Required properties:
> > +- compatible:	Should be "fsl,imx7ulp-pcc2" or "fsl,imx7ulp-pcc3".
> 
> Please format one per line.
> 

Got it

> There's some register layout difference between PCC2 and PCC3?
>

Yes, they're for different clocks and the layout may be slightly different.
For example, some clocks support rate setting (with dividers) while
some of them not.
 
> > +- reg : 	Should contain registers location and length.
> > +- #clock-cells:	Should be <1>.
> 
> No input clocks to list?
> 

Sorry for missing them, should add it.

Appreciate for the review.

Regards
Dong Aisheng

> > +
> > +The clock consumer should specify the desired clock by having the
> > +clock ID in its "clocks" phandle cell.
> > +See include/dt-bindings/clock/imx7ulp-clock.h
> > +for the full list of i.MX7ULP clock IDs of each module.
> > +
> > +Examples:
> > +
> > +#include <dt-bindings/clock/imx7ulp-clock.h>
> > +
> > +scg1: scg1@403e0000 {
> > +	compatible = "fsl,imx7ulp-scg1;
> > +	reg = <0x403e0000 0x10000>;
> > +	clocks = <&rosc>, <&sosc>, <&sirc>,
> > +		 <&firc>, <&upll>, <&mpll>;
> > +	clock-names = "rosc", "sosc", "sirc",
> > +		      "firc", "upll", "mpll";
> > +	#clock-cells = <1>;
> > +};
> > +
> > +pcc2: pcc2@403f0000 {
> > +	compatible = "fsl,imx7ulp-pcc2";
> > +	reg = <0x403f0000 0x10000>;
> > +	#clock-cells = <1>;
> > +};
> > +
> > +pcc3: pcc3@40b30000 {
> > +	compatible = "fsl,imx7ulp-pcc3";
> > +	reg = <0x40b30000 0x10000>;
> > +	#clock-cells = <1>;
> > +};
> > +
> > +usdhc1: usdhc@40380000 {
> > +	compatible = "fsl,imx7ulp-usdhc";
> > +	reg = <0x40380000 0x10000>;
> > +	interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
> > +	clocks = <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>,
> > +		 <&scg1 IMX7ULP_CLK_NIC1_DIV>,
> > +		 <&pcc2 IMX7ULP_CLK_USDHC1>;
> > +	clock-names ="ipg", "ahb", "per";
> > +	bus-width = <4>;
> > +};
> > diff --git a/include/dt-bindings/clock/imx7ulp-clock.h
> > b/include/dt-bindings/clock/imx7ulp-clock.h
> > new file mode 100644
> > index 0000000..008c5ee
> > --- /dev/null
> > +++ b/include/dt-bindings/clock/imx7ulp-clock.h
> > @@ -0,0 +1,109 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> > + * Copyright 2017~2018 NXP
> > + *
> > + */
> > +
> > +#ifndef __DT_BINDINGS_CLOCK_IMX7ULP_H #define
> > +__DT_BINDINGS_CLOCK_IMX7ULP_H
> > +
> > +/* SCG1 */
> > +
> > +#define IMX7ULP_CLK_DUMMY		0
> > +#define IMX7ULP_CLK_ROSC		1
> > +#define IMX7ULP_CLK_SOSC		2
> > +#define IMX7ULP_CLK_FIRC		3
> > +#define IMX7ULP_CLK_SPLL_PRE_SEL	4
> > +#define IMX7ULP_CLK_SPLL_PRE_DIV	5
> > +#define IMX7ULP_CLK_SPLL		6
> > +#define IMX7ULP_CLK_SPLL_POST_DIV1	7
> > +#define IMX7ULP_CLK_SPLL_POST_DIV2	8
> > +#define IMX7ULP_CLK_SPLL_PFD0		9
> > +#define IMX7ULP_CLK_SPLL_PFD1		10
> > +#define IMX7ULP_CLK_SPLL_PFD2		11
> > +#define IMX7ULP_CLK_SPLL_PFD3		12
> > +#define IMX7ULP_CLK_SPLL_PFD_SEL	13
> > +#define IMX7ULP_CLK_SPLL_SEL		14
> > +#define IMX7ULP_CLK_APLL_PRE_SEL	15
> > +#define IMX7ULP_CLK_APLL_PRE_DIV	16
> > +#define IMX7ULP_CLK_APLL		17
> > +#define IMX7ULP_CLK_APLL_POST_DIV1	18
> > +#define IMX7ULP_CLK_APLL_POST_DIV2	19
> > +#define IMX7ULP_CLK_APLL_PFD0		20
> > +#define IMX7ULP_CLK_APLL_PFD1		21
> > +#define IMX7ULP_CLK_APLL_PFD2		22
> > +#define IMX7ULP_CLK_APLL_PFD3		23
> > +#define IMX7ULP_CLK_APLL_PFD_SEL	24
> > +#define IMX7ULP_CLK_APLL_SEL		25
> > +#define IMX7ULP_CLK_UPLL		26
> > +#define IMX7ULP_CLK_SYS_SEL		27
> > +#define IMX7ULP_CLK_CORE_DIV		28
> > +#define IMX7ULP_CLK_BUS_DIV		29
> > +#define IMX7ULP_CLK_PLAT_DIV		30
> > +#define IMX7ULP_CLK_DDR_SEL		31
> > +#define IMX7ULP_CLK_DDR_DIV		32
> > +#define IMX7ULP_CLK_NIC_SEL		33
> > +#define IMX7ULP_CLK_NIC0_DIV		34
> > +#define IMX7ULP_CLK_GPU_DIV		35
> > +#define IMX7ULP_CLK_NIC1_DIV		36
> > +#define IMX7ULP_CLK_NIC1_BUS_DIV	37
> > +#define IMX7ULP_CLK_NIC1_EXT_DIV	38
> > +#define IMX7ULP_CLK_MIPI_PLL		39
> > +#define IMX7ULP_CLK_SIRC		40
> > +#define IMX7ULP_CLK_SOSC_BUS_CLK	41
> > +#define IMX7ULP_CLK_FIRC_BUS_CLK	42
> > +#define IMX7ULP_CLK_SPLL_BUS_CLK	43
> > +
> > +#define IMX7ULP_CLK_SCG1_END		44
> > +
> > +/* PCC2 */
> > +#define IMX7ULP_CLK_DMA1		0
> > +#define IMX7ULP_CLK_RGPIO2P1		1
> > +#define IMX7ULP_CLK_FLEXBUS		2
> > +#define IMX7ULP_CLK_SEMA42_1		3
> > +#define IMX7ULP_CLK_DMA_MUX1		4
> > +#define IMX7ULP_CLK_SNVS		5
> > +#define IMX7ULP_CLK_CAAM		6
> > +#define IMX7ULP_CLK_LPTPM4		7
> > +#define IMX7ULP_CLK_LPTPM5		8
> > +#define IMX7ULP_CLK_LPIT1		9
> > +#define IMX7ULP_CLK_LPSPI2		10
> > +#define IMX7ULP_CLK_LPSPI3		11
> > +#define IMX7ULP_CLK_LPI2C4		12
> > +#define IMX7ULP_CLK_LPI2C5		13
> > +#define IMX7ULP_CLK_LPUART4		14
> > +#define IMX7ULP_CLK_LPUART5		15
> > +#define IMX7ULP_CLK_FLEXIO1		16
> > +#define IMX7ULP_CLK_USB0		17
> > +#define IMX7ULP_CLK_USB1		18
> > +#define IMX7ULP_CLK_USB_PHY		19
> > +#define IMX7ULP_CLK_USB_PL301		20
> > +#define IMX7ULP_CLK_USDHC0		21
> > +#define IMX7ULP_CLK_USDHC1		22
> > +#define IMX7ULP_CLK_WDG1		23
> > +#define IMX7ULP_CLK_WDG2		24
> > +
> > +#define IMX7ULP_CLK_PCC2_END		25
> > +
> > +/* PCC3 */
> > +#define IMX7ULP_CLK_LPTPM6		0
> > +#define IMX7ULP_CLK_LPTPM7		1
> > +#define IMX7ULP_CLK_LPI2C6		2
> > +#define IMX7ULP_CLK_LPI2C7		3
> > +#define IMX7ULP_CLK_LPUART6		4
> > +#define IMX7ULP_CLK_LPUART7		5
> > +#define IMX7ULP_CLK_VIU			6
> > +#define IMX7ULP_CLK_DSI			7
> > +#define IMX7ULP_CLK_LCDIF		8
> > +#define IMX7ULP_CLK_MMDC		9
> > +#define IMX7ULP_CLK_PCTLC		10
> > +#define IMX7ULP_CLK_PCTLD		11
> > +#define IMX7ULP_CLK_PCTLE		12
> > +#define IMX7ULP_CLK_PCTLF		13
> > +#define IMX7ULP_CLK_GPU3D		14
> > +#define IMX7ULP_CLK_GPU2D		15
> > +
> > +#define IMX7ULP_CLK_PCC3_END		16
> > +
> > +#endif /* __DT_BINDINGS_CLOCK_IMX7ULP_H */
> > --
> > 2.7.4
> >

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

* Re: [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support
  2018-10-21 13:10 ` [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE " A.s. Dong
@ 2018-11-05  0:59   ` Michael Turquette
  2018-11-13  2:16     ` A.s. Dong
  0 siblings, 1 reply; 19+ messages in thread
From: Michael Turquette @ 2018-11-05  0:59 UTC (permalink / raw)
  To: A.s. Dong, linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, shawnguo, Anson Huang,
	Jacky Bai, dl-linux-imx, A.s. Dong, Stephen Boyd

Hi Dong,

Quoting A.s. Dong (2018-10-21 06:10:48)
> For dividers with zero indicating clock is disabled, instead of giving a
> warning each time like "clkx: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not
> set" in exist code, we'd like to introduce enable/disable function for it.
> e.g.
> 000b - Clock disabled
> 001b - Divide by 1
> 010b - Divide by 2
> ...

I feel bad to NAK this patch after it's been on the list for so long,
but this implementation really should belong in your hardware specific
clock provider driver.

This patch expands clk-divider to also be a gate, which is a
non-starter.  Basic clock types were meant to remain basic. I'm already
imagining how this precedent would cause other submissions: "why should
I use composite clock when we can just add new clk_ops to the basic
clock types!" :-(

Also the implementation becomes cleaner when you don't have to make it
coexist with clk-divider.c. You can drop the flags and just implement a
machine specific clock type that combines gates and dividers.

Best regards,
Mike

> 
> Set rate when the clk is disabled will cache the rate request and only
> when the clk is enabled will the driver actually program the hardware to
> have the requested divider value. Similarly, when the clk is disabled we'll
> write a 0 there, but when the clk is enabled we'll restore whatever rate
> (divider) was chosen last.
> 
> It does mean that recalc rate will be sort of odd, because when the clk is
> off it will return 0, and when the clk is on it will return the right rate.
> So to make things work, we'll need to return the cached rate in recalc rate
> when the clk is off and read the hardware when the clk is on.
> 
> NOTE for the default off divider, the recalc rate will still return 0 as
> there's still no proper preset rate. Enable such divider will give user
> a reminder error message.
> 
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Shawn Guo <shawnguo@kernel.org>
> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
> 
> ---
> ChangeLog:
> v3->v4:
>  * no changes
> v2->v3:
>  * split normal and gate ops
>  * fix the possible racy
> v1->v2:
>  * add enable/disable for the type of CLK_DIVIDER_ZERO_GATE dividers
> ---
>  drivers/clk/clk-divider.c    | 152 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/clk-provider.h |   9 +++
>  2 files changed, 161 insertions(+)
> 
> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index b6234a5..b3566fd 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
> @@ -122,6 +122,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
>  
>         div = _get_div(table, val, flags, width);
>         if (!div) {
> +               if (flags & CLK_DIVIDER_ZERO_GATE)
> +                       return 0;
> +
>                 WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
>                         "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
>                         clk_hw_get_name(hw));
> @@ -145,6 +148,34 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
>                                    divider->flags, divider->width);
>  }
>  
> +static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
> +                                                 unsigned long parent_rate)
> +{
> +       struct clk_divider *divider = to_clk_divider(hw);
> +       unsigned long flags = 0;
> +       unsigned int val;
> +
> +       if (divider->lock)
> +               spin_lock_irqsave(divider->lock, flags);
> +       else
> +               __acquire(divider->lock);
> +
> +       if (!clk_hw_is_enabled(hw)) {
> +               val = divider->cached_val;
> +       } else {
> +               val = clk_readl(divider->reg) >> divider->shift;
> +               val &= clk_div_mask(divider->width);
> +       }
> +
> +       if (divider->lock)
> +               spin_unlock_irqrestore(divider->lock, flags);
> +       else
> +               __release(divider->lock);
> +
> +       return divider_recalc_rate(hw, parent_rate, val, divider->table,
> +                                  divider->flags, divider->width);
> +}
> +
>  static bool _is_valid_table_div(const struct clk_div_table *table,
>                                                          unsigned int div)
>  {
> @@ -437,6 +468,108 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
>         return 0;
>  }
>  
> +static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
> +                               unsigned long parent_rate)
> +{
> +       struct clk_divider *divider = to_clk_divider(hw);
> +       unsigned long flags = 0;
> +       int value;
> +       u32 val;
> +
> +       value = divider_get_val(rate, parent_rate, divider->table,
> +                               divider->width, divider->flags);
> +       if (value < 0)
> +               return value;
> +
> +       if (divider->lock)
> +               spin_lock_irqsave(divider->lock, flags);
> +       else
> +               __acquire(divider->lock);
> +
> +       if (clk_hw_is_enabled(hw)) {
> +               if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
> +                       val = clk_div_mask(divider->width) << (divider->shift + 16);
> +               } else {
> +                       val = clk_readl(divider->reg);
> +                       val &= ~(clk_div_mask(divider->width) << divider->shift);
> +               }
> +               val |= (u32)value << divider->shift;
> +               clk_writel(val, divider->reg);
> +       } else {
> +               divider->cached_val = value;
> +       }
> +
> +       if (divider->lock)
> +               spin_unlock_irqrestore(divider->lock, flags);
> +       else
> +               __release(divider->lock);
> +
> +       return 0;
> +}
> +
> +static int clk_divider_enable(struct clk_hw *hw)
> +{
> +       struct clk_divider *divider = to_clk_divider(hw);
> +       unsigned long flags = 0;
> +       u32 val;
> +
> +       if (!divider->cached_val) {
> +               pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
> +               return -EINVAL;
> +       }
> +
> +       if (divider->lock)
> +               spin_lock_irqsave(divider->lock, flags);
> +       else
> +               __acquire(divider->lock);
> +
> +       /* restore div val */
> +       val = clk_readl(divider->reg);
> +       val |= divider->cached_val << divider->shift;
> +       clk_writel(val, divider->reg);
> +
> +       if (divider->lock)
> +               spin_unlock_irqrestore(divider->lock, flags);
> +       else
> +               __release(divider->lock);
> +
> +       return 0;
> +}
> +
> +static void clk_divider_disable(struct clk_hw *hw)
> +{
> +       struct clk_divider *divider = to_clk_divider(hw);
> +       unsigned long flags = 0;
> +       u32 val;
> +
> +       if (divider->lock)
> +               spin_lock_irqsave(divider->lock, flags);
> +       else
> +               __acquire(divider->lock);
> +
> +       /* store the current div val */
> +       val = clk_readl(divider->reg) >> divider->shift;
> +       val &= clk_div_mask(divider->width);
> +       divider->cached_val = val;
> +       clk_writel(0, divider->reg);
> +
> +       if (divider->lock)
> +               spin_unlock_irqrestore(divider->lock, flags);
> +       else
> +               __release(divider->lock);
> +}
> +
> +static int clk_divider_is_enabled(struct clk_hw *hw)
> +{
> +       struct clk_divider *divider = to_clk_divider(hw);
> +       u32 val;
> +
> +       val = clk_readl(divider->reg) >> divider->shift;
> +       val &= clk_div_mask(divider->width);
> +
> +       return val ? 1 : 0;
> +}
> +
>  const struct clk_ops clk_divider_ops = {
>         .recalc_rate = clk_divider_recalc_rate,
>         .round_rate = clk_divider_round_rate,
> @@ -444,6 +577,16 @@ const struct clk_ops clk_divider_ops = {
>  };
>  EXPORT_SYMBOL_GPL(clk_divider_ops);
>  
> +const struct clk_ops clk_divider_gate_ops = {
> +       .recalc_rate = clk_divider_gate_recalc_rate,
> +       .round_rate = clk_divider_round_rate,
> +       .set_rate = clk_divider_gate_set_rate,
> +       .enable = clk_divider_enable,
> +       .disable = clk_divider_disable,
> +       .is_enabled = clk_divider_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(clk_divider_gate_ops);
> +
>  const struct clk_ops clk_divider_ro_ops = {
>         .recalc_rate = clk_divider_recalc_rate,
>         .round_rate = clk_divider_round_rate,
> @@ -459,6 +602,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
>         struct clk_divider *div;
>         struct clk_hw *hw;
>         struct clk_init_data init;
> +       u32 val;
>         int ret;
>  
>         if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
> @@ -476,6 +620,8 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
>         init.name = name;
>         if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
>                 init.ops = &clk_divider_ro_ops;
> +       else if (clk_divider_flags & CLK_DIVIDER_ZERO_GATE)
> +               init.ops = &clk_divider_gate_ops;
>         else
>                 init.ops = &clk_divider_ops;
>         init.flags = flags | CLK_IS_BASIC;
> @@ -491,6 +637,12 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
>         div->hw.init = &init;
>         div->table = table;
>  
> +       if (div->flags & CLK_DIVIDER_ZERO_GATE) {
> +               val = clk_readl(reg) >> shift;
> +               val &= clk_div_mask(width);
> +               div->cached_val = val;
> +       }
> +
>         /* register the clock */
>         hw = &div->hw;
>         ret = clk_hw_register(dev, hw);
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 08b1aa7..08f135a 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -387,6 +387,7 @@ struct clk_div_table {
>   * @shift:     shift to the divider bit field
>   * @width:     width of the divider bit field
>   * @table:     array of value/divider pairs, last entry should have div = 0
> + * @cached_val: cached div hw value used for CLK_DIVIDER_ZERO_GATE
>   * @lock:      register lock
>   *
>   * Clock with an adjustable divider affecting its output frequency.  Implements
> @@ -415,6 +416,12 @@ struct clk_div_table {
>   * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like CLK_DIVIDER_ONE_BASED
>   *     except when the value read from the register is zero, the divisor is
>   *     2^width of the field.
> + * CLK_DIVIDER_ZERO_GATE - For dividers which are like CLK_DIVIDER_ONE_BASED
> + *     when the value read from the register is zero, it means the divisor
> + *     is gated. For this case, the cached_val will be used to store the
> + *     intermediate div for the normal rate operation, like set_rate/get_rate/
> + *     recalc_rate. When the divider is ungated, the driver will actually
> + *     program the hardware to have the requested divider value.
>   */
>  struct clk_divider {
>         struct clk_hw   hw;
> @@ -423,6 +430,7 @@ struct clk_divider {
>         u8              width;
>         u8              flags;
>         const struct clk_div_table      *table;
> +       u32             cached_val;
>         spinlock_t      *lock;
>  };
>  
> @@ -436,6 +444,7 @@ struct clk_divider {
>  #define CLK_DIVIDER_ROUND_CLOSEST      BIT(4)
>  #define CLK_DIVIDER_READ_ONLY          BIT(5)
>  #define CLK_DIVIDER_MAX_AT_ZERO                BIT(6)
> +#define CLK_DIVIDER_ZERO_GATE          BIT(7)
>  
>  extern const struct clk_ops clk_divider_ops;
>  extern const struct clk_ops clk_divider_ro_ops;
> -- 
> 2.7.4
> 

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

* RE: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
  2018-10-21 13:15 ` [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
@ 2018-11-06 15:30   ` A.s. Dong
  2018-11-06 15:34     ` Leonard Crestez
  0 siblings, 1 reply; 19+ messages in thread
From: A.s. Dong @ 2018-11-06 15:30 UTC (permalink / raw)
  To: sboyd
  Cc: linux-kernel, linux-arm-kernel, mturquette, shawnguo,
	Anson Huang, Jacky Bai, dl-linux-imx, linux-clk

Gently Ping...

> -----Original Message-----
> From: A.s. Dong
> Sent: Sunday, October 21, 2018 9:15 PM
> To: sboyd@kernel.org
> Cc: linux-kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> mturquette@baylibre.com; shawnguo@kernel.org; Anson Huang
> <anson.huang@nxp.com>; Jacky Bai <ping.bai@nxp.com>; dl-linux-imx
> <linux-imx@nxp.com>; linux-clk@vger.kernel.org
> Subject: RE: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
> 
> Hi Stephen,
> 
> In case you might miss to queue this series into your review list, so I resend this
> series again.
> This series actually has been pending for a couple of months without
> comments.
> Hopefully you could help find some time to handle it when you're free.
> 
> Thanks
> 
> Regards
> Dong Aisheng
> 
> > -----Original Message-----
> > From: A.s. Dong
> > Sent: Sunday, October 21, 2018 9:11 PM
> > To: linux-clk@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org;
> > linux-arm-kernel@lists.infradead.org;
> > sboyd@kernel.org; mturquette@baylibre.com; shawnguo@kernel.org;
> Anson
> > Huang <anson.huang@nxp.com>; Jacky Bai <ping.bai@nxp.com>;
> > dl-linux-imx <linux-imx@nxp.com>; A.s. Dong <aisheng.dong@nxp.com>
> > Subject: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
> >
> > This is a rebased version of below patch series against latest clk tree.
> > [PATCH RESEND V3 0/9] clk: add imx7ulp clk support
> > https://lkml.org/lkml/2018/3/16/310
> >
> > It only updates the license to SPDX format as well as a minor fix of pllv4.
> >
> > This patch series intends to add imx7ulp clk support.
> >
> > i.MX7ULP Clock functions are under joint control of the System Clock
> > Generation (SCG) modules, Peripheral Clock Control (PCC) modules, and
> > Core Mode Controller (CMC)1 blocks
> >
> > The clocking scheme provides clear separation between M4 domain and A7
> > domain. Except for a few clock sources shared between two domains,
> > such as the System Oscillator clock, the Slow IRC (SIRC), and and the
> > Fast IRC clock (FIRCLK), clock sources and clock management are
> > separated and contained within each domain.
> >
> > M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
> > A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.
> >
> > Note: this series only adds A7 clock domain support as M4 clock domain
> > will be handled by M4 seperately.
> >
> > Change Log:
> > v3->v4:
> >  * update after changing scg and pcc into separete nodes according to
> >    Rob's suggestion
> > v2->v3:
> >  * Patch 1 changed on: 1) split normal and gate ops 2) fix the possible racy
> >    Others no changes.
> >
> > v1->v2:
> >  * add enable/disable for the type of CLK_DIVIDER_ZERO_GATE dividers
> >  * use clk_hw apis to register clocks
> >  * use of_clk_add_hw_provider
> >  * split the clocks register process into two parts: early part for possible
> >    timers clocks registered by CLK_OF_DECLARE_DRIVER and the later part
> for
> >    the left normal peripheral clocks registered by a platform driver.
> >
> > Dong Aisheng (9):
> >   clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support
> >   clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support
> >   clk: imx: add pllv4 support
> >   clk: imx: add pfdv2 support
> >   clk: imx: add composite clk support
> >   dt-bindings: clock: add imx7ulp clock binding doc
> >   clk: imx: make mux parent strings const
> >   clk: imx: implement new clk_hw based APIs
> >   clk: imx: add imx7ulp clk driver
> >
> >  .../devicetree/bindings/clock/imx7ulp-clock.txt    |  87 +++++++++
> >  drivers/clk/clk-divider.c                          | 152
> > +++++++++++++++
> >  drivers/clk/clk-fractional-divider.c               |  10 +
> >  drivers/clk/imx/Makefile                           |   6 +-
> >  drivers/clk/imx/clk-busy.c                         |   2 +-
> >  drivers/clk/imx/clk-composite.c                    |  85 +++++++++
> >  drivers/clk/imx/clk-fixup-mux.c                    |   2 +-
> >  drivers/clk/imx/clk-imx7ulp.c                      | 209
> > +++++++++++++++++++++
> >  drivers/clk/imx/clk-pfdv2.c                        | 201
> > ++++++++++++++++++++
> >  drivers/clk/imx/clk-pllv4.c                        | 182
> > ++++++++++++++++++
> >  drivers/clk/imx/clk.c                              |  22 +++
> >  drivers/clk/imx/clk.h                              |  92 ++++++++-
> >  include/dt-bindings/clock/imx7ulp-clock.h          | 109 +++++++++++
> >  include/linux/clk-provider.h                       |  17 ++
> >  14 files changed, 1166 insertions(+), 10 deletions(-)  create mode
> > 100644 Documentation/devicetree/bindings/clock/imx7ulp-clock.txt
> >  create mode 100644 drivers/clk/imx/clk-composite.c  create mode
> > 100644 drivers/clk/imx/clk-imx7ulp.c  create mode 100644
> > drivers/clk/imx/clk-pfdv2.c  create mode 100644
> > drivers/clk/imx/clk-pllv4.c create mode 100644
> > include/dt-bindings/clock/imx7ulp-clock.h
> >
> > --
> > 2.7.4


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

* Re: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
  2018-11-06 15:30   ` A.s. Dong
@ 2018-11-06 15:34     ` Leonard Crestez
  2018-11-07  3:00       ` A.s. Dong
  0 siblings, 1 reply; 19+ messages in thread
From: Leonard Crestez @ 2018-11-06 15:34 UTC (permalink / raw)
  To: sboyd, A.s. Dong
  Cc: dl-linux-imx, linux-kernel, mturquette, Jacky Bai, Abel Vesa,
	shawnguo, linux-arm-kernel, Anson Huang, linux-clk

On Tue, 2018-11-06 at 15:30 +0000, A.s. Dong wrote:
> Gently Ping...

> drivers/clk/imx/clk-composite.c                    |  85 +++++++++

During review for 8m clocks a separate but different composite clk was
added. In order to avoid confusion that was named "clk-composite-8m.c", 
it would make sense to move the 7ulp code to clk-composite-7ulp.c.

--
Regards,
Leonard

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

* RE: [PATCH RESEND V4 0/9] clk: add imx7ulp clk support
  2018-11-06 15:34     ` Leonard Crestez
@ 2018-11-07  3:00       ` A.s. Dong
  0 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-11-07  3:00 UTC (permalink / raw)
  To: Leonard Crestez, sboyd
  Cc: dl-linux-imx, linux-kernel, mturquette, Jacky Bai, Abel Vesa,
	shawnguo, linux-arm-kernel, Anson Huang, linux-clk

> -----Original Message-----
> From: Leonard Crestez
> Sent: Tuesday, November 6, 2018 11:34 PM
[...]
> 
> On Tue, 2018-11-06 at 15:30 +0000, A.s. Dong wrote:
> > Gently Ping...
> 
> > drivers/clk/imx/clk-composite.c                    |  85 +++++++++
> 
> During review for 8m clocks a separate but different composite clk was added.
> In order to avoid confusion that was named "clk-composite-8m.c", it would
> make sense to move the 7ulp code to clk-composite-7ulp.c.
> 

I guess we can do it, thanks.

Regards
Dong Aisheng

> --
> Regards,
> Leonard

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

* RE: [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE clk support
  2018-11-05  0:59   ` Michael Turquette
@ 2018-11-13  2:16     ` A.s. Dong
  0 siblings, 0 replies; 19+ messages in thread
From: A.s. Dong @ 2018-11-13  2:16 UTC (permalink / raw)
  To: Michael Turquette, linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, shawnguo, Anson Huang,
	Jacky Bai, dl-linux-imx, Stephen Boyd

Hi Michael,

> -----Original Message-----
> From: Michael Turquette [mailto:mturquette@baylibre.com]
[...]
> Hi Dong,
> 
> Quoting A.s. Dong (2018-10-21 06:10:48)
> > For dividers with zero indicating clock is disabled, instead of giving
> > a warning each time like "clkx: Zero divisor and
> > CLK_DIVIDER_ALLOW_ZERO not set" in exist code, we'd like to introduce
> enable/disable function for it.
> > e.g.
> > 000b - Clock disabled
> > 001b - Divide by 1
> > 010b - Divide by 2
> > ...
> 
> I feel bad to NAK this patch after it's been on the list for so long, 

Never mind, I feel better than no response about it. :-)

> but this
> implementation really should belong in your hardware specific clock provider
> driver.
> 

Got your point.

> This patch expands clk-divider to also be a gate, which is a non-starter.  Basic
> clock types were meant to remain basic. I'm already imagining how this
> precedent would cause other submissions: "why should I use composite clock
> when we can just add new clk_ops to the basic clock types!" :-(
> 
> Also the implementation becomes cleaner when you don't have to make it
> coexist with clk-divider.c. You can drop the flags and just implement a machine
> specific clock type that combines gates and dividers.

Sound good to me. The original purpose to put it in framework is in order to
save possible duplicated codes for a similar SoC as the implementation actually
is HW independent. But I think we could start from putting it in machine code first.

Thanks for the suggestion.
Will update soon and resend.

Regards
Dong Aisheng
> 
> Best regards,
> Mike
> 
> >
> > Set rate when the clk is disabled will cache the rate request and only
> > when the clk is enabled will the driver actually program the hardware
> > to have the requested divider value. Similarly, when the clk is
> > disabled we'll write a 0 there, but when the clk is enabled we'll
> > restore whatever rate
> > (divider) was chosen last.
> >
> > It does mean that recalc rate will be sort of odd, because when the
> > clk is off it will return 0, and when the clk is on it will return the right rate.
> > So to make things work, we'll need to return the cached rate in recalc
> > rate when the clk is off and read the hardware when the clk is on.
> >
> > NOTE for the default off divider, the recalc rate will still return 0
> > as there's still no proper preset rate. Enable such divider will give
> > user a reminder error message.
> >
> > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > Cc: Michael Turquette <mturquette@baylibre.com>
> > Cc: Shawn Guo <shawnguo@kernel.org>
> > Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
> >
> > ---
> > ChangeLog:
> > v3->v4:
> >  * no changes
> > v2->v3:
> >  * split normal and gate ops
> >  * fix the possible racy
> > v1->v2:
> >  * add enable/disable for the type of CLK_DIVIDER_ZERO_GATE dividers
> > ---
> >  drivers/clk/clk-divider.c    | 152
> +++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/clk-provider.h |   9 +++
> >  2 files changed, 161 insertions(+)
> >
> > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> > index b6234a5..b3566fd 100644
> > --- a/drivers/clk/clk-divider.c
> > +++ b/drivers/clk/clk-divider.c
> > @@ -122,6 +122,9 @@ unsigned long divider_recalc_rate(struct clk_hw
> > *hw, unsigned long parent_rate,
> >
> >         div = _get_div(table, val, flags, width);
> >         if (!div) {
> > +               if (flags & CLK_DIVIDER_ZERO_GATE)
> > +                       return 0;
> > +
> >                 WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
> >                         "%s: Zero divisor and
> CLK_DIVIDER_ALLOW_ZERO not set\n",
> >                         clk_hw_get_name(hw)); @@ -145,6 +148,34
> @@
> > static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
> >                                    divider->flags, divider->width);  }
> >
> > +static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
> > +                                                 unsigned long
> > +parent_rate) {
> > +       struct clk_divider *divider = to_clk_divider(hw);
> > +       unsigned long flags = 0;
> > +       unsigned int val;
> > +
> > +       if (divider->lock)
> > +               spin_lock_irqsave(divider->lock, flags);
> > +       else
> > +               __acquire(divider->lock);
> > +
> > +       if (!clk_hw_is_enabled(hw)) {
> > +               val = divider->cached_val;
> > +       } else {
> > +               val = clk_readl(divider->reg) >> divider->shift;
> > +               val &= clk_div_mask(divider->width);
> > +       }
> > +
> > +       if (divider->lock)
> > +               spin_unlock_irqrestore(divider->lock, flags);
> > +       else
> > +               __release(divider->lock);
> > +
> > +       return divider_recalc_rate(hw, parent_rate, val, divider->table,
> > +                                  divider->flags, divider->width); }
> > +
> >  static bool _is_valid_table_div(const struct clk_div_table *table,
> >                                                          unsigned
> int
> > div)  { @@ -437,6 +468,108 @@ static int clk_divider_set_rate(struct
> > clk_hw *hw, unsigned long rate,
> >         return 0;
> >  }
> >
> > +static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
> > +                               unsigned long parent_rate) {
> > +       struct clk_divider *divider = to_clk_divider(hw);
> > +       unsigned long flags = 0;
> > +       int value;
> > +       u32 val;
> > +
> > +       value = divider_get_val(rate, parent_rate, divider->table,
> > +                               divider->width, divider->flags);
> > +       if (value < 0)
> > +               return value;
> > +
> > +       if (divider->lock)
> > +               spin_lock_irqsave(divider->lock, flags);
> > +       else
> > +               __acquire(divider->lock);
> > +
> > +       if (clk_hw_is_enabled(hw)) {
> > +               if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
> > +                       val = clk_div_mask(divider->width) <<
> (divider->shift + 16);
> > +               } else {
> > +                       val = clk_readl(divider->reg);
> > +                       val &= ~(clk_div_mask(divider->width) <<
> divider->shift);
> > +               }
> > +               val |= (u32)value << divider->shift;
> > +               clk_writel(val, divider->reg);
> > +       } else {
> > +               divider->cached_val = value;
> > +       }
> > +
> > +       if (divider->lock)
> > +               spin_unlock_irqrestore(divider->lock, flags);
> > +       else
> > +               __release(divider->lock);
> > +
> > +       return 0;
> > +}
> > +
> > +static int clk_divider_enable(struct clk_hw *hw) {
> > +       struct clk_divider *divider = to_clk_divider(hw);
> > +       unsigned long flags = 0;
> > +       u32 val;
> > +
> > +       if (!divider->cached_val) {
> > +               pr_err("%s: no valid preset rate\n",
> clk_hw_get_name(hw));
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (divider->lock)
> > +               spin_lock_irqsave(divider->lock, flags);
> > +       else
> > +               __acquire(divider->lock);
> > +
> > +       /* restore div val */
> > +       val = clk_readl(divider->reg);
> > +       val |= divider->cached_val << divider->shift;
> > +       clk_writel(val, divider->reg);
> > +
> > +       if (divider->lock)
> > +               spin_unlock_irqrestore(divider->lock, flags);
> > +       else
> > +               __release(divider->lock);
> > +
> > +       return 0;
> > +}
> > +
> > +static void clk_divider_disable(struct clk_hw *hw) {
> > +       struct clk_divider *divider = to_clk_divider(hw);
> > +       unsigned long flags = 0;
> > +       u32 val;
> > +
> > +       if (divider->lock)
> > +               spin_lock_irqsave(divider->lock, flags);
> > +       else
> > +               __acquire(divider->lock);
> > +
> > +       /* store the current div val */
> > +       val = clk_readl(divider->reg) >> divider->shift;
> > +       val &= clk_div_mask(divider->width);
> > +       divider->cached_val = val;
> > +       clk_writel(0, divider->reg);
> > +
> > +       if (divider->lock)
> > +               spin_unlock_irqrestore(divider->lock, flags);
> > +       else
> > +               __release(divider->lock); }
> > +
> > +static int clk_divider_is_enabled(struct clk_hw *hw) {
> > +       struct clk_divider *divider = to_clk_divider(hw);
> > +       u32 val;
> > +
> > +       val = clk_readl(divider->reg) >> divider->shift;
> > +       val &= clk_div_mask(divider->width);
> > +
> > +       return val ? 1 : 0;
> > +}
> > +
> >  const struct clk_ops clk_divider_ops = {
> >         .recalc_rate = clk_divider_recalc_rate,
> >         .round_rate = clk_divider_round_rate, @@ -444,6 +577,16 @@
> > const struct clk_ops clk_divider_ops = {  };
> > EXPORT_SYMBOL_GPL(clk_divider_ops);
> >
> > +const struct clk_ops clk_divider_gate_ops = {
> > +       .recalc_rate = clk_divider_gate_recalc_rate,
> > +       .round_rate = clk_divider_round_rate,
> > +       .set_rate = clk_divider_gate_set_rate,
> > +       .enable = clk_divider_enable,
> > +       .disable = clk_divider_disable,
> > +       .is_enabled = clk_divider_is_enabled, };
> > +EXPORT_SYMBOL_GPL(clk_divider_gate_ops);
> > +
> >  const struct clk_ops clk_divider_ro_ops = {
> >         .recalc_rate = clk_divider_recalc_rate,
> >         .round_rate = clk_divider_round_rate, @@ -459,6 +602,7 @@
> > static struct clk_hw *_register_divider(struct device *dev, const char *name,
> >         struct clk_divider *div;
> >         struct clk_hw *hw;
> >         struct clk_init_data init;
> > +       u32 val;
> >         int ret;
> >
> >         if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { @@ -476,6
> > +620,8 @@ static struct clk_hw *_register_divider(struct device *dev, const
> char *name,
> >         init.name = name;
> >         if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
> >                 init.ops = &clk_divider_ro_ops;
> > +       else if (clk_divider_flags & CLK_DIVIDER_ZERO_GATE)
> > +               init.ops = &clk_divider_gate_ops;
> >         else
> >                 init.ops = &clk_divider_ops;
> >         init.flags = flags | CLK_IS_BASIC; @@ -491,6 +637,12 @@ static
> > struct clk_hw *_register_divider(struct device *dev, const char *name,
> >         div->hw.init = &init;
> >         div->table = table;
> >
> > +       if (div->flags & CLK_DIVIDER_ZERO_GATE) {
> > +               val = clk_readl(reg) >> shift;
> > +               val &= clk_div_mask(width);
> > +               div->cached_val = val;
> > +       }
> > +
> >         /* register the clock */
> >         hw = &div->hw;
> >         ret = clk_hw_register(dev, hw); diff --git
> > a/include/linux/clk-provider.h b/include/linux/clk-provider.h index
> > 08b1aa7..08f135a 100644
> > --- a/include/linux/clk-provider.h
> > +++ b/include/linux/clk-provider.h
> > @@ -387,6 +387,7 @@ struct clk_div_table {
> >   * @shift:     shift to the divider bit field
> >   * @width:     width of the divider bit field
> >   * @table:     array of value/divider pairs, last entry should have div = 0
> > + * @cached_val: cached div hw value used for CLK_DIVIDER_ZERO_GATE
> >   * @lock:      register lock
> >   *
> >   * Clock with an adjustable divider affecting its output frequency.
> > Implements @@ -415,6 +416,12 @@ struct clk_div_table {
> >   * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like
> CLK_DIVIDER_ONE_BASED
> >   *     except when the value read from the register is zero, the divisor is
> >   *     2^width of the field.
> > + * CLK_DIVIDER_ZERO_GATE - For dividers which are like
> CLK_DIVIDER_ONE_BASED
> > + *     when the value read from the register is zero, it means the divisor
> > + *     is gated. For this case, the cached_val will be used to store the
> > + *     intermediate div for the normal rate operation, like
> set_rate/get_rate/
> > + *     recalc_rate. When the divider is ungated, the driver will actually
> > + *     program the hardware to have the requested divider value.
> >   */
> >  struct clk_divider {
> >         struct clk_hw   hw;
> > @@ -423,6 +430,7 @@ struct clk_divider {
> >         u8              width;
> >         u8              flags;
> >         const struct clk_div_table      *table;
> > +       u32             cached_val;
> >         spinlock_t      *lock;
> >  };
> >
> > @@ -436,6 +444,7 @@ struct clk_divider {
> >  #define CLK_DIVIDER_ROUND_CLOSEST      BIT(4)
> >  #define CLK_DIVIDER_READ_ONLY          BIT(5)
> >  #define CLK_DIVIDER_MAX_AT_ZERO                BIT(6)
> > +#define CLK_DIVIDER_ZERO_GATE          BIT(7)
> >
> >  extern const struct clk_ops clk_divider_ops;  extern const struct
> > clk_ops clk_divider_ro_ops;
> > --
> > 2.7.4
> >

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

* [PATCH RESEND V4 9/9] clk: imx: add imx7ulp clk driver
  2018-05-25  7:51 Dong Aisheng
@ 2018-05-25  7:51 ` Dong Aisheng
  0 siblings, 0 replies; 19+ messages in thread
From: Dong Aisheng @ 2018-05-25  7:51 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, linux-arm-kernel, sboyd, mturquette, shawnguo,
	Anson.Huang, ping.bai, linux-imx, Dong Aisheng, Stephen Boyd

i.MX7ULP Clock functions are under joint control of the System
Clock Generation (SCG) modules, Peripheral Clock Control (PCC)
modules, and Core Mode Controller (CMC)1 blocks

The clocking scheme provides clear separation between M4 domain
and A7 domain. Except for a few clock sources shared between two
domains, such as the System Oscillator clock, the Slow IRC (SIRC),
and and the Fast IRC clock (FIRCLK), clock sources and clock
management are separated and contained within each domain.

M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules.
A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules.

This driver only adds clock support in A7 domain.

Note that most clocks required to be operated when gated, e.g. pll,
pfd, pcc. And more special cases that scs/ddr/nic mux selecting
different clock source requires that clock to be enabled first,
then we need set CLK_OPS_PARENT_ENABLE flag for them properly.

Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Anson Huang <Anson.Huang@nxp.com>
Cc: Bai Ping <ping.bai@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>

---
ChangeLog:
v2->v3:
 * no changes
v1->v2:
 * use of_clk_add_hw_provider instead
 * split the clocks register process into two parts: early part for possible
   timers clocks registered by CLK_OF_DECLARE_DRIVER and the later part for
   the left normal peripheral clocks registered by a platform driver.
---
 drivers/clk/imx/Makefile      |   1 +
 drivers/clk/imx/clk-imx7ulp.c | 227 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 228 insertions(+)
 create mode 100644 drivers/clk/imx/clk-imx7ulp.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index f4da12c..983c0a5 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -29,4 +29,5 @@ obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o
 obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o
 obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o
 obj-$(CONFIG_SOC_IMX7D)  += clk-imx7d.o
+obj-$(CONFIG_SOC_IMX7ULP) += clk-imx7ulp.o
 obj-$(CONFIG_SOC_VF610)  += clk-vf610.o
diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c
new file mode 100644
index 0000000..bbeaa0f
--- /dev/null
+++ b/drivers/clk/imx/clk-imx7ulp.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ *
+ * Author: Dong Aisheng <aisheng.dong@nxp.com>
+ *
+ */
+
+#include <dt-bindings/clock/imx7ulp-clock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+static const char * const pll_pre_sels[]	= { "sosc", "firc", };
+static const char * const spll_pfd_sels[]	= { "spll_pfd0", "spll_pfd1", "spll_pfd2", "spll_pfd3", };
+static const char * const spll_sels[]		= { "spll", "spll_pfd_sel", };
+static const char * const apll_pfd_sels[]	= { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", };
+static const char * const apll_sels[]		= { "apll", "apll_pfd_sel", };
+static const char * const scs_sels[]		= { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "upll", };
+static const char * const ddr_sels[]		= { "apll_pfd_sel", "upll", };
+static const char * const nic_sels[]		= { "firc", "ddr_clk", };
+static const char * const periph_plat_sels[]	= { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", };
+static const char * const periph_bus_sels[]	= { "dummy", "sosc_bus_clk", "mpll", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", };
+
+static struct clk_hw_onecell_data *clk_data;
+
+static void __init imx7ulp_clocks_early_init(struct device_node *scg_node)
+{
+	struct device_node *np = scg_node;
+	void __iomem *base;
+	struct clk_hw **clks;
+
+	clk_data = kzalloc(sizeof(*clk_data) +
+			   sizeof(*clk_data->hws) * IMX7ULP_CLK_END,
+			   GFP_KERNEL);
+	if (!clk_data)
+		return;
+
+	clk_data->num = IMX7ULP_CLK_END;
+	clks = clk_data->hws;
+
+	clks[IMX7ULP_CLK_DUMMY]		= imx_clk_hw_fixed("dummy", 0);
+
+	clks[IMX7ULP_CLK_ROSC]		= imx_obtain_fixed_clk_hw(np, "rosc");
+	clks[IMX7ULP_CLK_SOSC]		= imx_obtain_fixed_clk_hw(np, "sosc");
+	clks[IMX7ULP_CLK_SIRC]		= imx_obtain_fixed_clk_hw(np, "sirc");
+	clks[IMX7ULP_CLK_FIRC]		= imx_obtain_fixed_clk_hw(np, "firc");
+	clks[IMX7ULP_CLK_MIPI_PLL]	= imx_obtain_fixed_clk_hw(np, "mpll");
+	clks[IMX7ULP_CLK_UPLL]		= imx_obtain_fixed_clk_hw(np, "upll");
+
+	/* SCG1 */
+	base = of_iomap(np, 0);
+	WARN_ON(!base);
+
+	/* NOTE: xPLL config can't be changed when xPLL is enabled */
+	clks[IMX7ULP_CLK_APLL_PRE_SEL]	= imx_clk_hw_mux_flags("apll_pre_sel", base + 0x508, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_SPLL_PRE_SEL]	= imx_clk_hw_mux_flags("spll_pre_sel", base + 0x608, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
+
+	/*							   name		    parent_name	   reg			shift	width	flags */
+	clks[IMX7ULP_CLK_APLL_PRE_DIV]	= imx_clk_hw_divider_flags("apll_pre_div", "apll_pre_sel", base + 0x508,	8,	3,	CLK_SET_RATE_GATE);
+	clks[IMX7ULP_CLK_SPLL_PRE_DIV]	= imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608,	8,	3,	CLK_SET_RATE_GATE);
+
+	/*						name	 parent_name	 base */
+	clks[IMX7ULP_CLK_APLL]		= imx_clk_pllv4("apll",  "apll_pre_div", base + 0x500);
+	clks[IMX7ULP_CLK_SPLL]		= imx_clk_pllv4("spll",  "spll_pre_div", base + 0x600);
+
+	/* APLL PFDs */
+	clks[IMX7ULP_CLK_APLL_PFD0]	= imx_clk_pfdv2("apll_pfd0", "apll", base + 0x50c, 0);
+	clks[IMX7ULP_CLK_APLL_PFD1]	= imx_clk_pfdv2("apll_pfd1", "apll", base + 0x50c, 1);
+	clks[IMX7ULP_CLK_APLL_PFD2]	= imx_clk_pfdv2("apll_pfd2", "apll", base + 0x50c, 2);
+	clks[IMX7ULP_CLK_APLL_PFD3]	= imx_clk_pfdv2("apll_pfd3", "apll", base + 0x50c, 3);
+
+	/* SPLL PFDs */
+	clks[IMX7ULP_CLK_SPLL_PFD0]	= imx_clk_pfdv2("spll_pfd0", "spll", base + 0x60C, 0);
+	clks[IMX7ULP_CLK_SPLL_PFD1]	= imx_clk_pfdv2("spll_pfd1", "spll", base + 0x60C, 1);
+	clks[IMX7ULP_CLK_SPLL_PFD2]	= imx_clk_pfdv2("spll_pfd2", "spll", base + 0x60C, 2);
+	clks[IMX7ULP_CLK_SPLL_PFD3]	= imx_clk_pfdv2("spll_pfd3", "spll", base + 0x60C, 3);
+
+	/* PLL Mux */
+	clks[IMX7ULP_CLK_APLL_PFD_SEL]	= imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_SPLL_PFD_SEL]	= imx_clk_hw_mux_flags("spll_pfd_sel", base + 0x608, 14, 2, spll_pfd_sels, ARRAY_SIZE(spll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_APLL_SEL]	= imx_clk_hw_mux_flags("apll_sel", base + 0x508, 1, 1, apll_sels, ARRAY_SIZE(apll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+	clks[IMX7ULP_CLK_SPLL_SEL]	= imx_clk_hw_mux_flags("spll_sel", base + 0x608, 1, 1, spll_sels, ARRAY_SIZE(spll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
+
+	clks[IMX7ULP_CLK_SPLL_BUS_CLK]	= clk_hw_register_divider(NULL, "spll_bus_clk", "spll_sel", CLK_SET_RATE_GATE, base + 0x604, 8, 3, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+
+	/* scs/ddr/nic select different clock source requires that clock to be enabled first */
+	clks[IMX7ULP_CLK_SYS_SEL]	= imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels));
+	clks[IMX7ULP_CLK_NIC_SEL]	= imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels));
+	clks[IMX7ULP_CLK_DDR_SEL]	= imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 1, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
+
+	clks[IMX7ULP_CLK_CORE_DIV]	= imx_clk_hw_divider_flags("divcore",	"scs_sel",  base + 0x14, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+	clks[IMX7ULP_CLK_DDR_DIV]	= clk_hw_register_divider(NULL, "ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3,
+								  CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+
+	clks[IMX7ULP_CLK_NIC0_DIV]	= imx_clk_hw_divider_flags("nic0_clk",		"nic_sel",  base + 0x40, 24, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+	clks[IMX7ULP_CLK_NIC1_DIV]	= imx_clk_hw_divider_flags("nic1_clk",		"nic0_clk", base + 0x40, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+	clks[IMX7ULP_CLK_NIC1_BUS_DIV]	= imx_clk_hw_divider_flags("nic1_bus_clk",	"nic1_clk", base + 0x40, 4,  4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+	clks[IMX7ULP_CLK_SOSC_BUS_CLK]	= clk_hw_register_divider(NULL, "sosc_bus_clk", "sosc", 0, base + 0x104, 8, 3,
+								  CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+	clks[IMX7ULP_CLK_FIRC_BUS_CLK]	= clk_hw_register_divider(NULL, "firc_bus_clk", "firc", 0, base + 0x304, 8, 3,
+								  CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ZERO_GATE, &imx_ccm_lock);
+
+	/* PCC2 */
+	base = of_iomap(np, 1);
+	WARN_ON(!base);
+
+	clks[IMX7ULP_CLK_LPTPM4]	= imx_clk_composite("lptpm4",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
+	clks[IMX7ULP_CLK_LPTPM5]	= imx_clk_composite("lptpm5",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
+	clks[IMX7ULP_CLK_LPIT1]		= imx_clk_composite("lpit1",   periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
+
+	/* PCC3 */
+	base = of_iomap(np, 2);
+	WARN_ON(!base);
+
+	clks[IMX7ULP_CLK_LPTPM6]	= imx_clk_composite("lptpm6",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x84);
+	clks[IMX7ULP_CLK_LPTPM7]	= imx_clk_composite("lptpm7",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x88);
+
+	clks[IMX7ULP_CLK_MMDC]		= clk_hw_register_gate(NULL, "mmdc", "nic1_clk", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+							       base + 0xac, 30, 0, &imx_ccm_lock);
+
+	imx_check_clk_hws(clks, clk_data->num);
+
+	of_clk_add_hw_provider(scg_node, of_clk_hw_onecell_get, clk_data);
+}
+CLK_OF_DECLARE_DRIVER(imx7ulp, "fsl,imx7ulp-clock", imx7ulp_clocks_early_init);
+
+static const struct of_device_id imx7ulp_clk_dt_ids[] = {
+	{ .compatible = "fsl,imx7ulp-clock" },
+	{ /* sentinel */ }
+};
+
+static int imx7ulp_clk_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct clk_hw **clks = clk_data->hws;
+	struct resource *res;
+	void __iomem *base;
+
+	/* PCC2 */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scg1");
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clks[IMX7ULP_CLK_GPU_DIV]	= imx_clk_hw_divider("gpu_clk", "nic0_clk", base + 0x40, 20, 4);
+
+	/* PCC2 */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcc2");
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clks[IMX7ULP_CLK_DMA1]		= imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30);
+	clks[IMX7ULP_CLK_RGPIO2P1]	= imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30);
+	clks[IMX7ULP_CLK_DMA_MUX1]	= imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30);
+	clks[IMX7ULP_CLK_SNVS]		= imx_clk_hw_gate("snvs", "nic1_bus_clk", base + 0x8c, 30);
+	clks[IMX7ULP_CLK_CAAM]		= imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30);
+	clks[IMX7ULP_CLK_LPSPI2]	= imx_clk_composite("lpspi2",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa4);
+	clks[IMX7ULP_CLK_LPSPI3]	= imx_clk_composite("lpspi3",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa8);
+	clks[IMX7ULP_CLK_LPI2C4]	= imx_clk_composite("lpi2c4",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xac);
+	clks[IMX7ULP_CLK_LPI2C5]	= imx_clk_composite("lpi2c5",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb0);
+	clks[IMX7ULP_CLK_LPUART4]	= imx_clk_composite("lpuart4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb4);
+	clks[IMX7ULP_CLK_LPUART5]	= imx_clk_composite("lpuart5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb8);
+	clks[IMX7ULP_CLK_FLEXIO1]	= imx_clk_composite("flexio1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xc4);
+	clks[IMX7ULP_CLK_USB0]		= imx_clk_composite("usb0",    periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xcc);
+	clks[IMX7ULP_CLK_USB1]		= imx_clk_composite("usb1",    periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xd0);
+	clks[IMX7ULP_CLK_USB_PHY]	= imx_clk_hw_gate("usb_phy", "nic1_bus_clk", base + 0xD4, 30);
+	clks[IMX7ULP_CLK_USDHC0]	= imx_clk_composite("usdhc0",  periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xdc);
+	clks[IMX7ULP_CLK_USDHC1]	= imx_clk_composite("usdhc1",  periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xe0);
+	clks[IMX7ULP_CLK_WDG1]		= imx_clk_composite("wdg1",    periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true,  true, base + 0xf4);
+	clks[IMX7ULP_CLK_WDG2]		= imx_clk_composite("sdg2",    periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true,  true, base + 0x10c);
+
+	/* PCC3 */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcc3");
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clks[IMX7ULP_CLK_LPI2C6]	= imx_clk_composite("lpi2c6",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x90);
+	clks[IMX7ULP_CLK_LPI2C7]	= imx_clk_composite("lpi2c7",  periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
+	clks[IMX7ULP_CLK_LPUART6]	= imx_clk_composite("lpuart6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
+	clks[IMX7ULP_CLK_LPUART7]	= imx_clk_composite("lpuart7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
+	clks[IMX7ULP_CLK_DSI]		= imx_clk_composite("dsi",     periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true,  true, base + 0xa4);
+	clks[IMX7ULP_CLK_LCDIF]		= imx_clk_composite("lcdif",   periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true,  true, base + 0xa8);
+
+	clks[IMX7ULP_CLK_VIU]		= imx_clk_hw_gate("viu",   "nic1_clk",	   base + 0xa0, 30);
+	clks[IMX7ULP_CLK_PCTLC]		= imx_clk_hw_gate("pctlc", "nic1_bus_clk", base + 0xb8, 30);
+	clks[IMX7ULP_CLK_PCTLD]		= imx_clk_hw_gate("pctld", "nic1_bus_clk", base + 0xbc, 30);
+	clks[IMX7ULP_CLK_PCTLE]		= imx_clk_hw_gate("pctle", "nic1_bus_clk", base + 0xc0, 30);
+	clks[IMX7ULP_CLK_PCTLF]		= imx_clk_hw_gate("pctlf", "nic1_bus_clk", base + 0xc4, 30);
+
+	clks[IMX7ULP_CLK_GPU3D]		= imx_clk_composite("gpu3d",   periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x140);
+	clks[IMX7ULP_CLK_GPU2D]		= imx_clk_composite("gpu2d",   periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x144);
+
+	imx_check_clk_hws(clks, clk_data->num);
+
+	of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+
+	pr_debug("i.MX7ULP clock tree init done.\n");
+
+	return 0;
+}
+
+static struct platform_driver imx7ulp_clk_driver = {
+	.driver = {
+		.name = "imx7ulp-clock",
+		.of_match_table = imx7ulp_clk_dt_ids,
+	},
+	.probe = imx7ulp_clk_probe,
+};
+
+static int __init imx7ulp_clk_init(void)
+{
+	return platform_driver_register(&imx7ulp_clk_driver);
+}
+core_initcall(imx7ulp_clk_init);
-- 
2.7.4

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

end of thread, other threads:[~2018-11-13  2:16 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-21 13:10 [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
2018-10-21 13:10 ` [PATCH RESEND V4 1/9] clk: clk-divider: add CLK_DIVIDER_ZERO_GATE " A.s. Dong
2018-11-05  0:59   ` Michael Turquette
2018-11-13  2:16     ` A.s. Dong
2018-10-21 13:10 ` [PATCH RESEND V4 2/9] clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support A.s. Dong
2018-10-21 13:10 ` [PATCH RESEND V4 3/9] clk: imx: add pllv4 support A.s. Dong
2018-10-21 13:11 ` [PATCH RESEND V4 4/9] clk: imx: add pfdv2 support A.s. Dong
2018-10-21 13:11 ` [PATCH RESEND V4 5/9] clk: imx: add composite clk support A.s. Dong
2018-10-21 13:11 ` [PATCH RESEND V4 6/9] dt-bindings: clock: add imx7ulp clock binding doc A.s. Dong
2018-10-22 22:16   ` Rob Herring
2018-10-23  2:09     ` A.s. Dong
2018-10-21 13:11 ` [PATCH RESEND V4 7/9] clk: imx: make mux parent strings const A.s. Dong
2018-10-21 13:11 ` [PATCH RESEND V4 8/9] clk: imx: implement new clk_hw based APIs A.s. Dong
2018-10-21 13:11 ` [PATCH RESEND V4 9/9] clk: imx: add imx7ulp clk driver A.s. Dong
2018-10-21 13:15 ` [PATCH RESEND V4 0/9] clk: add imx7ulp clk support A.s. Dong
2018-11-06 15:30   ` A.s. Dong
2018-11-06 15:34     ` Leonard Crestez
2018-11-07  3:00       ` A.s. Dong
  -- strict thread matches above, loose matches on Subject: below --
2018-05-25  7:51 Dong Aisheng
2018-05-25  7:51 ` [PATCH RESEND V4 9/9] clk: imx: add imx7ulp clk driver Dong Aisheng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).