* [PATCH 0/4] clk / clocksource: Add Ingenic X1600 SoC support
@ 2026-10-25 19:18 H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 1/4] dt-bindings: clock: Add Ingenic X1600 CGU bindings H. Nikolaus Schaller
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: H. Nikolaus Schaller @ 2026-10-25 19:18 UTC (permalink / raw)
To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Paul Cercueil
Cc: linux-clk, devicetree, linux-kernel, linux-mips, letux-kernel,
kernel, H. Nikolaus Schaller
PATCH V1 2026-10-25 20:18:26:
This patch series introduces hardware support for the Clock Generation Unit
(CGU) and the Timer Clock Unit (TCU) found in the Ingenic X1600 SoC.
To achieve this cleanly, the common Ingenic clock and timer infrastructure
is modernized and parameterized.
As a side effect of making the core structures flexible enough to handle
the architectural layout of the X1600, it became straightforward to fully
integrate support for the legacy JZ4730 SoC later.
The series is structured as follows:
Patch 1: Adds the device tree bindings and clock definitions for the X1600,
incorporating the specific I2S handling, routing, and RX/TX gates.
Patch 2: Implements the Ingenic X1600 CGU clock driver. It updates the common
CGU core framework to support secondary post-VCO dividers (OD1),
and bypasses the lack of compound divider support via explicit
mdiv/nddiv handling for the I2S clocks.
Patch 3&4: Adapt the existing JZ4760 and X1000 CGU drivers to match the expanded
signature of the calc_m_n_od() callback from Patch 2, preventing
compilation breakages.
The changes have been successfully tested on CI20 (JZ4780), LX16 (X1600).
And on LX20 (X2000), LX26 (X2600) boards (with additional, specific patches).
H. Nikolaus Schaller (3):
dt-bindings: clock: Add Ingenic X1600 CGU bindings
clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework
changes
clk: ingenic: x1000-cgu: Adapt calc_m_n_od() to core framework changes
Paul Boddie (1):
clk: ingenic: Add Ingenic X1600 CGU clock driver support
.../bindings/clock/ingenic,cgu.yaml | 2 +
drivers/clk/ingenic/Kconfig | 10 +
drivers/clk/ingenic/Makefile | 1 +
drivers/clk/ingenic/cgu.c | 150 ++++--
drivers/clk/ingenic/cgu.h | 42 +-
drivers/clk/ingenic/jz4760-cgu.c | 3 +-
drivers/clk/ingenic/x1000-cgu.c | 3 +-
drivers/clk/ingenic/x1600-cgu.c | 476 ++++++++++++++++++
include/dt-bindings/clock/ingenic,x1600-cgu.h | 64 +++
9 files changed, 716 insertions(+), 35 deletions(-)
create mode 100644 drivers/clk/ingenic/x1600-cgu.c
create mode 100644 include/dt-bindings/clock/ingenic,x1600-cgu.h
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/4] dt-bindings: clock: Add Ingenic X1600 CGU bindings
2026-10-25 19:18 [PATCH 0/4] clk / clocksource: Add Ingenic X1600 SoC support H. Nikolaus Schaller
@ 2026-10-25 19:18 ` H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support H. Nikolaus Schaller
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: H. Nikolaus Schaller @ 2026-10-25 19:18 UTC (permalink / raw)
To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Paul Cercueil
Cc: linux-clk, devicetree, linux-kernel, linux-mips, letux-kernel,
kernel, H. Nikolaus Schaller, Paul Boddie
Add the device tree bindings and the clock ID definitions for the
Clock Generation Unit (CGU) of the Ingenic X1600 SoC.
The clock layout features specific I2S handling. To properly support
the I2S controllers on the X1600 without relying on complex compound
dividers that are not yet natively supported by the drivers, split
the structures to explicitly describe the additional I2S divider
fields. This design accounts for the current driver limitations
regarding division encoding while matching the hardware's internal
gating architecture.
Furthermore, introduce dedicated I2S0 RX and TX clock definitions to
allow independent and precise routing for I2S stream usage.
Co-developed-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: Paul Boddie <paul@boddie.org.uk>
Co-developed-by: H. Nikolaus Schaller <hns@goldelico.com>
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
---
.../bindings/clock/ingenic,cgu.yaml | 2 +
include/dt-bindings/clock/ingenic,x1600-cgu.h | 64 +++++++++++++++++++
2 files changed, 66 insertions(+)
create mode 100644 include/dt-bindings/clock/ingenic,x1600-cgu.h
diff --git a/Documentation/devicetree/bindings/clock/ingenic,cgu.yaml b/Documentation/devicetree/bindings/clock/ingenic,cgu.yaml
index 509df06b9c9d8..c622131c5c49b 100644
--- a/Documentation/devicetree/bindings/clock/ingenic,cgu.yaml
+++ b/Documentation/devicetree/bindings/clock/ingenic,cgu.yaml
@@ -28,6 +28,7 @@ select:
- ingenic,jz4770-cgu
- ingenic,jz4780-cgu
- ingenic,x1000-cgu
+ - ingenic,x1600-cgu
- ingenic,x1830-cgu
required:
- compatible
@@ -58,6 +59,7 @@ properties:
- ingenic,jz4770-cgu
- ingenic,jz4780-cgu
- ingenic,x1000-cgu
+ - ingenic,x1600-cgu
- ingenic,x1830-cgu
- const: simple-mfd
minItems: 1
diff --git a/include/dt-bindings/clock/ingenic,x1600-cgu.h b/include/dt-bindings/clock/ingenic,x1600-cgu.h
new file mode 100644
index 0000000000000..a0001a63a6b54
--- /dev/null
+++ b/include/dt-bindings/clock/ingenic,x1600-cgu.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * This header provides clock numbers for the ingenic,x1600-cgu DT binding.
+ *
+ * They are roughly ordered as:
+ * - external clocks
+ * - PLLs
+ * - muxes/dividers in the order they appear in the x1600 programmers manual
+ * - gates in order of their bit in the CLKGR* registers
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_X1600_CGU_H__
+#define __DT_BINDINGS_CLOCK_X1600_CGU_H__
+
+/*
+ * beware of unwritten rules:
+ * - list must start with 0
+ * - list must not have "holes"
+ * - all clock parents must have lower number than a clock itself so that
+ * parents are initialized before
+ */
+
+#define X1600_CLK_EXCLK 0
+#define X1600_CLK_RTCLK 1
+#define X1600_CLK_12M 2
+#define X1600_CLK_APLL 3
+#define X1600_CLK_EPLL 4
+#define X1600_CLK_MPLL 5
+#define X1600_CLK_SCLKA 6
+#define X1600_CLK_CPUMUX 7
+#define X1600_CLK_CPU 8
+#define X1600_CLK_L2CACHE 9
+#define X1600_CLK_AHB0 10
+#define X1600_CLK_AHB2PMUX 11
+#define X1600_CLK_AHB2 12
+#define X1600_CLK_PCLK 13
+#define X1600_CLK_DDR 14
+#define X1600_CLK_I2S0 15
+#define X1600_CLK_I2S1 16
+#define X1600_CLK_LCDPIXCLK 17
+#define X1600_CLK_MAC 18
+#define X1600_CLK_MSC0 19
+#define X1600_CLK_MSC1 20
+#define X1600_CLK_SSI0 21
+#define X1600_CLK_CIMMCLK 22
+#define X1600_CLK_EXCLK_DIV512 23
+#define X1600_CLK_RTC 24
+#define X1600_CLK_OTGPHY 25
+#define X1600_CLK_NEMC 26
+#define X1600_CLK_OTG0 27
+#define X1600_CLK_SMB0 28
+#define X1600_CLK_SMB1 29
+#define X1600_CLK_AIC 30
+#define X1600_CLK_SADC 31
+#define X1600_CLK_UART0 32
+#define X1600_CLK_UART1 33
+#define X1600_CLK_UART2 34
+#define X1600_CLK_UART3 35
+#define X1600_CLK_PDMA 36
+#define X1600_CLK_AES 37
+#define X1600_CLK_I2S0_RX 38
+#define X1600_CLK_I2S0_TX 39
+
+#endif /* __DT_BINDINGS_CLOCK_X1600_CGU_H__ */
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support
2026-10-25 19:18 [PATCH 0/4] clk / clocksource: Add Ingenic X1600 SoC support H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 1/4] dt-bindings: clock: Add Ingenic X1600 CGU bindings H. Nikolaus Schaller
@ 2026-10-25 19:18 ` H. Nikolaus Schaller
2026-10-26 10:16 ` kernel test robot
2026-10-25 19:18 ` [PATCH 3/4] clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework changes H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 4/4] clk: ingenic: x1000-cgu: " H. Nikolaus Schaller
3 siblings, 1 reply; 7+ messages in thread
From: H. Nikolaus Schaller @ 2026-10-25 19:18 UTC (permalink / raw)
To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Paul Cercueil
Cc: linux-clk, devicetree, linux-kernel, linux-mips, letux-kernel,
kernel, Paul Boddie, H. Nikolaus Schaller
From: Paul Boddie <paul@boddie.org.uk>
Introduce clock driver support for the Clock Generation Unit (CGU)
found in the Ingenic X1600 SoC.
To support the X1600 platform, update the common Ingenic CGU core
framework to accommodate a secondary post-VCO divider (OD1) within
the PLL configuration registers. Additionally, prevent a NULL pointer
dereference in the rate determination path by properly supplying the
secondary divider tracking during PLL calculation calls.
Implement custom handling for the I2S clocks on the X1600. Since
complex compound hardware dividers are not yet natively supported by the
core framework, explicit M-divider (mdiv) and N/D-divider (nddiv)
structures are introduced to describe these specialized fields. This
avoids a compilation failure and enables a stable, though limited,
division encoding suitable for current needs.
Furthermore, this patch:
- Fixes the gating of the I2S0 and I2S1 clocks, matching the control
bit behavior which functions as a clock enable flag.
- Introduces the custom OTG PHY clock operations and initialisation
routines utilizing the hardware's OPCR registers.
- Registers the independent I2S0 RX and TX gate clocks employed by the
X1600 peripheral block.
Tested successfully on the LX16 (X1600) boards and CI20 (JZ4780) to
not interefere.
Checkpatch complains about not 4 lines of help paragraph but the
style is the same as for the other machines. It also recommends
using 'unsigned int' for the new struct ingenic_cgu_mdiv_info
and struct ingenic_cgu_nddiv_info but this follows the style 'unsigned'
of all other structs in cgu.h. Finally it complains about the empty
line between struct ingenic_cgu_clk_info x1600_cgu_clocks[] = {
and the comment. But I think it is more readable this way.
Co-developed-by: H. Nikolaus Schaller <hns@goldelico.com>
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
Co-developed-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: Paul Boddie <paul@boddie.org.uk>
---
drivers/clk/ingenic/Kconfig | 10 +
drivers/clk/ingenic/Makefile | 1 +
drivers/clk/ingenic/cgu.c | 150 +++++++---
drivers/clk/ingenic/cgu.h | 42 ++-
drivers/clk/ingenic/x1600-cgu.c | 476 ++++++++++++++++++++++++++++++++
5 files changed, 646 insertions(+), 33 deletions(-)
create mode 100644 drivers/clk/ingenic/x1600-cgu.c
diff --git a/drivers/clk/ingenic/Kconfig b/drivers/clk/ingenic/Kconfig
index f80ac4f2992b4..9b8b00c9e6d13 100644
--- a/drivers/clk/ingenic/Kconfig
+++ b/drivers/clk/ingenic/Kconfig
@@ -75,6 +75,16 @@ config INGENIC_CGU_X1000
If building for a X1000 SoC, you want to say Y here.
+config INGENIC_CGU_X1600
+ bool "Ingenic X1600 CGU driver"
+ default MACH_X1600
+ select INGENIC_CGU_COMMON
+ help
+ Support the clocks provided by the CGU hardware on Ingenic X1600
+ and compatible SoCs.
+
+ If building for a X1600 SoC, you want to say Y here.
+
config INGENIC_CGU_X1830
bool "Ingenic X1830 CGU driver"
default MACH_X1830
diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile
index 81d8e23c26365..7e93e0893abe2 100644
--- a/drivers/clk/ingenic/Makefile
+++ b/drivers/clk/ingenic/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_INGENIC_CGU_JZ4760) += jz4760-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o
obj-$(CONFIG_INGENIC_CGU_X1000) += x1000-cgu.o
+obj-$(CONFIG_INGENIC_CGU_X1600) += x1600-cgu.o
obj-$(CONFIG_INGENIC_CGU_X1830) += x1830-cgu.o
obj-$(CONFIG_INGENIC_TCU_CLK) += tcu.o
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c
index 41e4c69131bd6..b4ef8ddf043a7 100644
--- a/drivers/clk/ingenic/cgu.c
+++ b/drivers/clk/ingenic/cgu.c
@@ -4,6 +4,7 @@
*
* Copyright (c) 2013-2015 Imagination Technologies
* Author: Paul Burton <paul.burton@mips.com>
+ * Copyright (c) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
*/
#include <linux/bitops.h>
@@ -76,6 +77,40 @@ ingenic_cgu_gate_set(struct ingenic_cgu *cgu,
* PLL operations
*/
+static unsigned
+ingenic_pll_recalc_rate_od(u32 ctl, u8 od_shift, u8 od_bits, u8 od_max,
+ const s8 *od_encoding)
+{
+ unsigned int od, od_enc = 0;
+
+ if (od_bits > 0) {
+ od_enc = ctl >> od_shift;
+ od_enc &= GENMASK(od_bits - 1, 0);
+ }
+
+ /*
+ * Use the encoding table if indicated. Otherwise, use the value
+ * directly, interpreting an encoded value of zero as a divider
+ * value of one.
+ */
+ if (od_encoding) {
+ for (od = 0; od < od_max; od++)
+ if (od_encoding[od] == od_enc)
+ break;
+ od++;
+ } else {
+ od = od_enc ? od_enc : 1;
+ }
+
+ /* if od_max = 0, od_bits should be 0 and od is fixed to 1. */
+ if (od_max == 0)
+ WARN_ON_ONCE(od_bits != 0);
+ else
+ WARN_ON_ONCE(od == od_max);
+
+ return od;
+}
+
static unsigned long
ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
@@ -83,7 +118,7 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_pll_info *pll_info;
- unsigned m, n, od, od_enc = 0;
+ unsigned int m, n, od, od1;
bool bypass;
u32 ctl;
@@ -97,71 +132,82 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0);
n += pll_info->n_offset;
- if (pll_info->od_bits > 0) {
- od_enc = ctl >> pll_info->od_shift;
- od_enc &= GENMASK(pll_info->od_bits - 1, 0);
- }
-
if (pll_info->bypass_bit >= 0) {
- ctl = readl(cgu->base + pll_info->bypass_reg);
+ u32 ctl2 = readl(cgu->base + pll_info->bypass_reg);
- bypass = !!(ctl & BIT(pll_info->bypass_bit));
+ bypass = !!(ctl2 & BIT(pll_info->bypass_bit));
if (bypass)
return parent_rate;
}
- for (od = 0; od < pll_info->od_max; od++)
- if (pll_info->od_encoding[od] == od_enc)
- break;
+ od = ingenic_pll_recalc_rate_od(ctl, pll_info->od_shift, pll_info->od_bits,
+ pll_info->od_max, pll_info->od_encoding);
- /* if od_max = 0, od_bits should be 0 and od is fixed to 1. */
- if (pll_info->od_max == 0)
- BUG_ON(pll_info->od_bits != 0);
- else
- BUG_ON(od == pll_info->od_max);
- od++;
+ od1 = ingenic_pll_recalc_rate_od(ctl, pll_info->od1_shift, pll_info->od1_bits,
+ pll_info->od1_max, pll_info->od_encoding);
return div_u64((u64)parent_rate * m * pll_info->rate_multiplier,
- n * od);
+ n * od * od1);
}
static void
ingenic_pll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
- unsigned int *pm, unsigned int *pn, unsigned int *pod)
+ unsigned int *pm, unsigned int *pn, unsigned int *pod,
+ unsigned int *pod1)
{
- unsigned int m, n, od = 1;
+ unsigned int m, n, od = 1, od1 = 1;
/*
- * The frequency after the input divider must be between 10 and 50 MHz.
+ * The frequency after the input divider must be within the range
+ * defined in the programming manual as FREF:
+ *
+ * JZ4740: 1 MHz - 15 MHz
+ * JZ4780: 183 kHz - 1.5 GHz
+ * X1000: 10 MHz - 50 MHz
+ * X1600: 1 MHz - 800 MHz
+ * X2000: 5 MHz - 200 MHz
+ *
* The highest divider yields the best resolution.
*/
n = parent_rate / (10 * MHZ);
n = min_t(unsigned int, n, 1 << pll_info->n_bits);
n = max_t(unsigned int, n, pll_info->n_offset);
- m = (rate / MHZ) * od * n / (parent_rate / MHZ);
+ /*
+ * The frequency after the VCO stage (parent * m / n) must be in the
+ * range defined in the programming manual as FVCO:
+ *
+ * JZ4740: 100 MHz - 500 MHz
+ * JZ4780: 300 MHz - 1.5 GHz
+ * X1000: 300 MHz - 600 MHz (low-band), 500 MHz - 1 GHz (high-band)
+ * X1600: 600 MHz - 2.4 GHz
+ * X2000: 2.4 GHz - 4.8 GHz
+ */
+ m = (rate / MHZ) * od * od1 * n / (parent_rate / MHZ);
m = min_t(unsigned int, m, 1 << pll_info->m_bits);
m = max_t(unsigned int, m, pll_info->m_offset);
*pm = m;
*pn = n;
*pod = od;
+ *pod1 = od1;
}
static unsigned long
ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
unsigned long rate, unsigned long parent_rate,
- unsigned int *pm, unsigned int *pn, unsigned int *pod)
+ unsigned int *pm, unsigned int *pn, unsigned int *pod,
+ unsigned int *pod1)
{
const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
- unsigned int m, n, od;
+ unsigned int m, n, od, od1 = 1;
if (pll_info->calc_m_n_od)
- (*pll_info->calc_m_n_od)(pll_info, rate, parent_rate, &m, &n, &od);
+ (*pll_info->calc_m_n_od)(pll_info, rate, parent_rate, &m, &n, &od, &od1);
else
- ingenic_pll_calc_m_n_od(pll_info, rate, parent_rate, &m, &n, &od);
+ ingenic_pll_calc_m_n_od(pll_info, rate, parent_rate, &m, &n, &od, &od1);
if (pm)
*pm = m;
@@ -169,9 +215,11 @@ ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
*pn = n;
if (pod)
*pod = od;
+ if (pod1)
+ *pod1 = od1;
return div_u64((u64)parent_rate * m * pll_info->rate_multiplier,
- n * od);
+ n * od * od1);
}
static int ingenic_pll_determine_rate(struct clk_hw *hw,
@@ -181,7 +229,7 @@ static int ingenic_pll_determine_rate(struct clk_hw *hw,
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
req->rate = ingenic_pll_calc(clk_info, req->rate, req->best_parent_rate,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
return 0;
}
@@ -208,12 +256,12 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
unsigned long rate, flags;
- unsigned int m, n, od;
+ unsigned int m, n, od, od1;
int ret = 0;
u32 ctl;
rate = ingenic_pll_calc(clk_info, req_rate, parent_rate,
- &m, &n, &od);
+ &m, &n, &od, &od1);
if (rate != req_rate)
pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n",
clk_info->name, req_rate, rate);
@@ -229,7 +277,18 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
if (pll_info->od_bits > 0) {
ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
- ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
+ if (pll_info->od_encoding)
+ ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
+ else
+ ctl |= (od ? od : 1) << pll_info->od_shift;
+ }
+
+ if (pll_info->od1_bits > 0) {
+ ctl &= ~(GENMASK(pll_info->od1_bits - 1, 0) << pll_info->od1_shift);
+ if (pll_info->od_encoding)
+ ctl |= pll_info->od_encoding[od1 - 1] << pll_info->od1_shift;
+ else
+ ctl |= (od1 ? od1 : 1) << pll_info->od1_shift;
}
writel(ctl, cgu->base + pll_info->reg);
@@ -531,7 +590,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
struct ingenic_cgu *cgu = ingenic_clk->cgu;
unsigned long rate, flags;
unsigned int hw_div, div;
- u32 reg, mask;
+ u32 reg, mask, d_reg;
int ret = 0;
if (clk_info->type & CGU_CLK_DIV) {
@@ -554,6 +613,30 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
reg &= ~(mask << clk_info->div.shift);
reg |= hw_div << clk_info->div.shift;
+ /*
+ * NOTE: Special treatment for the I2S dividers in the X1600.
+ * Set the multiplier to 1, imposing an N >= M*2 constraint,
+ * also setting D to "automatic" calculation. This permits the
+ * treatment of these dividers as normal dividers, although
+ * they are configured more similarly to PLLs.
+ */
+ if (clk_info->mdiv.reg) {
+ if (div < 2) {
+ spin_unlock_irqrestore(&cgu->lock, flags);
+ return -EINVAL;
+ }
+
+ mask = GENMASK(clk_info->mdiv.bits - 1, 0);
+ reg &= ~(mask << clk_info->mdiv.shift);
+ reg |= 1 << clk_info->mdiv.shift;
+
+ if (clk_info->nddiv.reg) {
+ d_reg = readl(cgu->base + clk_info->nddiv.reg);
+ d_reg &= ~(1 << clk_info->nddiv.explicit_d_bit);
+ d_reg |= 1 << clk_info->nddiv.explicit_n_bit;
+ }
+ }
+
/* clear the stop bit */
if (clk_info->div.stop_bit != -1)
reg &= ~BIT(clk_info->div.stop_bit);
@@ -565,6 +648,9 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
/* update the hardware */
writel(reg, cgu->base + clk_info->div.reg);
+ if (clk_info->nddiv.reg)
+ writel(d_reg, cgu->base + clk_info->nddiv.reg);
+
/* wait for the change to take effect */
if (clk_info->div.busy_bit != -1)
ret = ingenic_clk_check_stable(cgu, clk_info);
diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h
index 0d417d69dab71..d5bdd7084b201 100644
--- a/drivers/clk/ingenic/cgu.h
+++ b/drivers/clk/ingenic/cgu.h
@@ -36,6 +36,12 @@
* @od_bits: the size of the post-VCO divider field in bits, or 0 if no
* OD field exists (then the OD is fixed to 1)
* @od_max: the maximum post-VCO divider value
+ * @od1_shift: the number of bits to shift the secondary post-VCO divider
+ * value by (ie. the index of the lowest bit of the post-VCO
+ * divider value in the PLL's control register)
+ * @od1_bits: the size of the secondary post-VCO divider field in bits, or 0
+ * if no OD1 field exists (then the OD1 is fixed to 1)
+ * @od1_max: the maximum secondary post-VCO divider value
* @od_encoding: a pointer to an array mapping post-VCO divider values to
* their encoded values in the PLL control register, or -1 for
* unsupported values
@@ -56,13 +62,15 @@ struct ingenic_cgu_pll_info {
u8 m_shift, m_bits, m_offset;
u8 n_shift, n_bits, n_offset;
u8 od_shift, od_bits, od_max;
+ u8 od1_shift, od1_bits, od1_max;
unsigned bypass_reg;
s8 bypass_bit;
s8 enable_bit;
s8 stable_bit;
void (*calc_m_n_od)(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
- unsigned int *m, unsigned int *n, unsigned int *od);
+ unsigned int *m, unsigned int *n, unsigned int *od,
+ unsigned int *od1);
void (*set_rate_hook)(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate);
};
@@ -109,6 +117,36 @@ struct ingenic_cgu_div_info {
const u8 *div_table;
};
+/**
+ * struct ingenic_cgu_mdiv_info - information about a divider M parameter
+ * @reg: offset of the divider control register within the CGU
+ * @shift: number of bits to left shift the M divide value by (ie. the index
+ * of the lowest bit of the divide value within its control
+ * register)
+ * @div: number to divide the M divider value by (i.e. if the
+ * effective divider value is the value written to the register
+ * multiplied by some constant)
+ * @bits: the size of the M divide value in bits
+ */
+struct ingenic_cgu_mdiv_info {
+ unsigned reg;
+ u8 shift;
+ u8 div;
+ u8 bits;
+};
+
+/**
+ * struct ingenic_cgu_nddiv_info - information about divider N and D parameters
+ * @reg: offset of the D divider control register within the CGU
+ * @explicit_n_bit: the bit providing the explicit N divider flag
+ * @explicit_d_bit: the bit providing the explicit D divider flag
+ */
+struct ingenic_cgu_nddiv_info {
+ unsigned reg;
+ u8 explicit_n_bit;
+ u8 explicit_d_bit;
+};
+
/**
* struct ingenic_cgu_fixdiv_info - information about a fixed divider
* @div: the divider applied to the parent clock
@@ -180,6 +218,8 @@ struct ingenic_cgu_clk_info {
struct ingenic_cgu_gate_info gate;
struct ingenic_cgu_mux_info mux;
struct ingenic_cgu_div_info div;
+ struct ingenic_cgu_mdiv_info mdiv;
+ struct ingenic_cgu_nddiv_info nddiv;
struct ingenic_cgu_fixdiv_info fixdiv;
};
diff --git a/drivers/clk/ingenic/x1600-cgu.c b/drivers/clk/ingenic/x1600-cgu.c
new file mode 100644
index 0000000000000..627203f5cabe8
--- /dev/null
+++ b/drivers/clk/ingenic/x1600-cgu.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ingenic X1600 SoC CGU driver
+ *
+ * Copyright (c) 2013-2015 Imagination Technologies
+ * Author: Paul Burton <paul.burton@mips.com>
+ * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+ * Copyright (c) 2023, 2024 Paul Boddie <paul@boddie.org.uk>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/rational.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/ingenic,x1600-cgu.h>
+
+#include "cgu.h"
+#include "pm.h"
+
+/* CGU register offsets */
+#define CGU_REG_CLOCKCONTROL 0x00
+#define CGU_REG_LCR 0x04
+#define CGU_REG_APLL 0x10
+#define CGU_REG_MPLL 0x14
+#define CGU_REG_EPLL 0x18
+#define CGU_REG_CLKGR0 0x20
+#define CGU_REG_OPCR 0x24
+#define CGU_REG_CLKGR1 0x28
+#define CGU_REG_DDRCDR 0x2c
+#define CGU_REG_CPSPR 0x34
+#define CGU_REG_CPSPPR 0x38
+#define CGU_REG_USBPCR 0x3c
+#define CGU_REG_USBRDT 0x40
+#define CGU_REG_USBVBFIL 0x44
+#define CGU_REG_USBPCR1 0x48
+#define CGU_REG_MACCDR 0x54
+#define CGU_REG_SSICDR 0x5c
+#define CGU_REG_I2S0CDR 0x60
+#define CGU_REG_LPCDR 0x64
+#define CGU_REG_MSC0CDR 0x68
+#define CGU_REG_PWMCDR 0x6c
+#define CGU_REG_I2S0CDR1 0x70
+#define CGU_REG_SFCCDR 0x74
+#define CGU_REG_CIMCDR 0x78
+#define CGU_REG_I2S1CDR 0x7c
+#define CGU_REG_I2S1CDR1 0x80
+#define CGU_REG_APLLFRAC 0x84
+#define CGU_REG_MPLLFRAC 0x88
+#define CGU_REG_EPLLFRAC 0x8c
+#define CGU_REG_CAN0CDR 0xa0
+#define CGU_REG_MSC1CDR 0xa4
+#define CGU_REG_CAN1CDR 0xa8
+#define CGU_REG_CDBUSCDR 0xac
+#define CGU_REG_CMP_INTR 0xb0
+#define CGU_REG_CMP_INTRE 0xb4
+#define CGU_REG_CMP_SFTINT 0xbc
+#define CGU_REG_DRCG 0xd0
+#define CGU_REG_CLOCKSTATUS 0xd4
+#define CGU_REG_MPHY0C 0xe4
+
+/* bits within the OPCR register */
+#define OPCR_SPENDN0 BIT(7)
+#define OPCR_GATEUSBPHYCLK BIT(23)
+
+static struct ingenic_cgu *cgu;
+
+static int x1600_otg_phy_enable(struct clk_hw *hw)
+{
+ void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
+
+ writel((readl(reg_opcr) & ~OPCR_GATEUSBPHYCLK) | OPCR_SPENDN0, reg_opcr);
+
+ return 0;
+}
+
+static void x1600_otg_phy_disable(struct clk_hw *hw)
+{
+ void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
+
+ writel((readl(reg_opcr) & ~OPCR_SPENDN0) | OPCR_GATEUSBPHYCLK, reg_opcr);
+}
+
+static int x1600_otg_phy_is_enabled(struct clk_hw *hw)
+{
+ void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
+
+ return (readl(reg_opcr) & (OPCR_SPENDN0 | OPCR_GATEUSBPHYCLK)) == OPCR_SPENDN0;
+}
+
+static u8 x1600_otg_phy_get_parent(struct clk_hw * __maybe_unused hw)
+{
+ return 0;
+}
+
+static const struct clk_ops x1600_otg_phy_ops = {
+ .enable = x1600_otg_phy_enable,
+ .disable = x1600_otg_phy_disable,
+ .is_enabled = x1600_otg_phy_is_enabled,
+ .get_parent = x1600_otg_phy_get_parent,
+};
+
+static void
+x1600_pll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
+ unsigned long rate, unsigned long parent_rate,
+ unsigned int *pm, unsigned int *pn, unsigned int *pod,
+ unsigned int *pod1)
+{
+ const unsigned int od_max = pll_info->od_max - 1;
+ const unsigned int od1_max = pll_info->od1_max - 1;
+ const unsigned int m_max = GENMASK(pll_info->m_bits - 1, 0);
+ const unsigned int n_max = GENMASK(pll_info->n_bits - 1, 0);
+ unsigned long m, n;
+ unsigned int od = 1, od1 = 1;
+ const unsigned long fvco_min = 600000000, fvco_max = 2400000000;
+ unsigned long fvco;
+
+ /*
+ * Combined output divider range:
+ *
+ * ceil(fvco_min / rate) - fvco_max / rate
+ */
+ unsigned int od_combined_min = (fvco_min + rate) / rate;
+ unsigned int od_combined_max = fvco_max / rate;
+ unsigned int od_combined;
+
+ for (od_combined = od_combined_min; od_combined <= od_combined_max; od_combined++) {
+ od = int_sqrt(od_combined);
+ od1 = od_combined / od;
+
+ /* od == floor(sqrt(od_combined)); od1 >= od */
+ if ((od * od1 == od_combined) && od <= od_max && od1 <= od1_max) {
+ fvco = rate * od_combined;
+ rational_best_approximation(fvco, parent_rate, m_max, n_max, &m, &n);
+ break;
+ }
+ }
+
+ *pm = m;
+ *pn = n;
+ *pod = od;
+ *pod1 = od1;
+}
+
+static const struct ingenic_cgu_clk_info x1600_cgu_clocks[] = {
+
+ /* External clocks */
+
+ [X1600_CLK_EXCLK] = { "ext", CGU_CLK_EXT },
+ [X1600_CLK_RTCLK] = { "rtc", CGU_CLK_EXT },
+ [X1600_CLK_12M] = { "clk12m", CGU_CLK_EXT },
+
+ /* PLLs */
+
+#define DEF_PLL(name) { \
+ .reg = CGU_REG_ ## name, \
+ .rate_multiplier = 1, \
+ .m_shift = 20, \
+ .m_bits = 12, \
+ .m_offset = 0, \
+ .n_shift = 14, \
+ .n_bits = 6, \
+ .n_offset = 0, \
+ .od_shift = 8, \
+ .od_bits = 3, \
+ .od_max = 8, \
+ .od1_shift = 11, \
+ .od1_bits = 3, \
+ .od1_max = 8, \
+ .od_encoding = 0, \
+ .stable_bit = 2, \
+ .enable_bit = 0, \
+ .bypass_bit = -1, \
+ .calc_m_n_od = x1600_pll_calc_m_n_od, \
+}
+
+ [X1600_CLK_APLL] = {
+ "apll", CGU_CLK_PLL,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .pll = DEF_PLL(APLL),
+ },
+
+ [X1600_CLK_MPLL] = {
+ "mpll", CGU_CLK_PLL,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .pll = DEF_PLL(MPLL),
+ },
+
+ [X1600_CLK_EPLL] = {
+ "epll", CGU_CLK_PLL,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .pll = DEF_PLL(EPLL),
+ },
+
+#undef DEF_PLL
+
+ /* Muxes & dividers */
+
+ [X1600_CLK_SCLKA] = {
+ "sclk_a", CGU_CLK_MUX,
+ .parents = { -1, X1600_CLK_EXCLK, X1600_CLK_APLL, -1 },
+ .mux = { CGU_REG_CLOCKCONTROL, 30, 2 },
+ },
+
+ [X1600_CLK_CPUMUX] = {
+ "cpumux", CGU_CLK_MUX,
+ .parents = { -1, X1600_CLK_SCLKA, X1600_CLK_MPLL, -1 },
+ .mux = { CGU_REG_CLOCKCONTROL, 28, 2 },
+ },
+
+ [X1600_CLK_CPU] = {
+ "cpu", CGU_CLK_DIV,
+ /*
+ * Disabling the CPU clock or any parent clocks will hang the
+ * system; mark it critical.
+ */
+ .flags = CLK_IS_CRITICAL,
+ .parents = { X1600_CLK_CPUMUX, -1, -1, -1 },
+ .div = { CGU_REG_CLOCKCONTROL, 0, 1, 4, 22, -1, -1 },
+ },
+
+ [X1600_CLK_L2CACHE] = {
+ "l2cache", CGU_CLK_DIV,
+ /*
+ * The L2 cache clock is critical if caches are enabled and
+ * disabling it or any parent clocks will hang the system.
+ */
+ .flags = CLK_IS_CRITICAL,
+ .parents = { X1600_CLK_CPUMUX, -1, -1, -1 },
+ .div = { CGU_REG_CLOCKCONTROL, 4, 1, 4, -1, -1, -1 },
+ },
+
+ [X1600_CLK_AHB0] = {
+ "ahb0", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { -1, X1600_CLK_SCLKA, X1600_CLK_MPLL, -1 },
+ .mux = { CGU_REG_CLOCKCONTROL, 26, 2 },
+ .div = { CGU_REG_CLOCKCONTROL, 8, 1, 4, 21, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 29 },
+ },
+
+ [X1600_CLK_AHB2PMUX] = {
+ "ahb2_apb_mux", CGU_CLK_MUX,
+ .parents = { -1, X1600_CLK_SCLKA, X1600_CLK_MPLL, -1 },
+ .mux = { CGU_REG_CLOCKCONTROL, 24, 2 },
+ },
+
+ [X1600_CLK_AHB2] = {
+ "ahb2", CGU_CLK_DIV,
+ .parents = { X1600_CLK_AHB2PMUX, -1, -1, -1 },
+ .div = { CGU_REG_CLOCKCONTROL, 12, 1, 4, 20, -1, -1 },
+ },
+
+ [X1600_CLK_PCLK] = {
+ "pclk", CGU_CLK_DIV,
+ .parents = { X1600_CLK_AHB2PMUX, -1, -1, -1 },
+ .div = { CGU_REG_CLOCKCONTROL, 16, 1, 4, 20, -1, -1 },
+ },
+
+ [X1600_CLK_DDR] = {
+ "ddr", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ /*
+ * Disabling DDR clock or its parents will render DRAM
+ * inaccessible; mark it critical.
+ */
+ .flags = CLK_IS_CRITICAL,
+ .parents = { -1, X1600_CLK_SCLKA, X1600_CLK_MPLL, -1 },
+ .mux = { CGU_REG_DDRCDR, 30, 2 },
+ .div = { CGU_REG_DDRCDR, 0, 1, 4, 29, 28, 27 },
+ .gate = { CGU_REG_CLKGR0, 31 },
+ },
+
+ [X1600_CLK_I2S0] = {
+ "i2s0", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_EPLL, -1, -1 },
+ .mux = { CGU_REG_I2S0CDR, 30, 1 },
+ .div = { CGU_REG_I2S0CDR, 20, 1, 9, -1, -1, -1 },
+ .mdiv = { CGU_REG_I2S0CDR, 0, 1, 20 },
+ .nddiv = { CGU_REG_I2S0CDR1, 31, 30 },
+ .gate = { CGU_REG_I2S0CDR, 29, true },
+ },
+
+ [X1600_CLK_I2S1] = {
+ "i2s1", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_EPLL, -1, -1 },
+ .mux = { CGU_REG_I2S1CDR, 30, 1 },
+ .div = { CGU_REG_I2S1CDR, 20, 1, 9, -1, -1, -1 },
+ .mdiv = { CGU_REG_I2S1CDR, 0, 1, 20 },
+ .nddiv = { CGU_REG_I2S1CDR1, 31, 30 },
+ .gate = { CGU_REG_I2S1CDR, 29, true },
+ },
+
+ [X1600_CLK_LCDPIXCLK] = {
+ "lcd0pixclk", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_MPLL,
+ X1600_CLK_EPLL, -1 },
+ .mux = { CGU_REG_LPCDR, 30, 2 },
+ .div = { CGU_REG_LPCDR, 0, 1, 8, 28, 27, 26 },
+ .gate = { CGU_REG_CLKGR0, 23 },
+ },
+
+ [X1600_CLK_MAC] = {
+ "mac", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_MPLL,
+ X1600_CLK_EPLL, -1 },
+ .mux = { CGU_REG_MACCDR, 30, 2 },
+ .div = { CGU_REG_MACCDR, 0, 1, 8, 28, 27, 26 },
+ .gate = { CGU_REG_CLKGR1, 23 },
+ },
+
+ [X1600_CLK_MSC0] = {
+ "msc0", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_MPLL,
+ X1600_CLK_EPLL, -1 },
+ .mux = { CGU_REG_MSC0CDR, 30, 2 },
+ .div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 },
+ .gate = { CGU_REG_CLKGR0, 4 },
+ },
+
+ [X1600_CLK_MSC1] = {
+ "msc1", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_MPLL,
+ X1600_CLK_EPLL, -1 },
+ .mux = { CGU_REG_MSC1CDR, 30, 2 },
+ .div = { CGU_REG_MSC1CDR, 0, 2, 8, 29, 28, 27 },
+ .gate = { CGU_REG_CLKGR0, 5 },
+ },
+
+ [X1600_CLK_SSI0] = {
+ "ssi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_MPLL,
+ X1600_CLK_EPLL, -1 },
+ .mux = { CGU_REG_SSICDR, 30, 2 },
+ .div = { CGU_REG_SSICDR, 0, 1, 8, 29, 28, 27 },
+ .gate = { CGU_REG_CLKGR0, 19 },
+ },
+
+ [X1600_CLK_CIMMCLK] = {
+ "cim_mclk", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { X1600_CLK_SCLKA, X1600_CLK_MPLL,
+ X1600_CLK_EPLL, -1 },
+ .mux = { CGU_REG_CIMCDR, 30, 2 },
+ .div = { CGU_REG_CIMCDR, 0, 1, 8, 30, 29, 28 },
+ .gate = { CGU_REG_CLKGR0, 22 },
+ },
+
+ [X1600_CLK_EXCLK_DIV512] = {
+ "exclk_div512", CGU_CLK_FIXDIV,
+ .parents = { X1600_CLK_EXCLK },
+ .fixdiv = { 512 },
+ },
+
+ [X1600_CLK_RTC] = {
+ "rtc_ercs", CGU_CLK_MUX | CGU_CLK_GATE,
+ .parents = { X1600_CLK_EXCLK_DIV512, X1600_CLK_RTCLK },
+ .mux = { CGU_REG_OPCR, 2, 1},
+ },
+
+ /* Custom (SoC-specific) OTG PHY */
+
+ [X1600_CLK_OTGPHY] = {
+ "otg_phy", CGU_CLK_CUSTOM,
+ .parents = { X1600_CLK_12M },
+ .custom = { &x1600_otg_phy_ops },
+ },
+
+ /* Gate-only clocks */
+
+ [X1600_CLK_NEMC] = {
+ "nemc", CGU_CLK_GATE,
+ .parents = { X1600_CLK_AHB2, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 0 },
+ },
+
+ [X1600_CLK_OTG0] = {
+ "otg0", CGU_CLK_GATE,
+ .parents = { X1600_CLK_AHB2, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 3 },
+ },
+
+ [X1600_CLK_SMB0] = {
+ "smb0", CGU_CLK_GATE,
+ .parents = { X1600_CLK_PCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 7 },
+ },
+
+ [X1600_CLK_SMB1] = {
+ "smb1", CGU_CLK_GATE,
+ .parents = { X1600_CLK_PCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 8 },
+ },
+
+ [X1600_CLK_AIC] = {
+ "aic", CGU_CLK_GATE,
+ .parents = { X1600_CLK_AHB2, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 11 },
+ },
+
+ [X1600_CLK_I2S0_RX] = {
+ "i2s0_rx", CGU_CLK_GATE,
+ .parents = { X1600_CLK_I2S0, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR1, 8 },
+ },
+
+ [X1600_CLK_I2S0_TX] = {
+ "i2s0_tx", CGU_CLK_GATE,
+ .parents = { X1600_CLK_I2S1, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR1, 9 },
+ },
+
+ [X1600_CLK_SADC] = {
+ "sadc", CGU_CLK_GATE,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 14 },
+ },
+
+ [X1600_CLK_UART0] = {
+ "uart0", CGU_CLK_GATE,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 14 },
+ },
+
+ [X1600_CLK_UART1] = {
+ "uart1", CGU_CLK_GATE,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 15 },
+ },
+
+ [X1600_CLK_UART2] = {
+ "uart2", CGU_CLK_GATE,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 16 },
+ },
+
+ [X1600_CLK_UART3] = {
+ "uart3", CGU_CLK_GATE,
+ .parents = { X1600_CLK_EXCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR1, 16 },
+ },
+
+ [X1600_CLK_PDMA] = {
+ "pdma", CGU_CLK_GATE,
+ .parents = { X1600_CLK_AHB2, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR0, 21 },
+ },
+
+ [X1600_CLK_AES] = {
+ "aes", CGU_CLK_GATE,
+ .parents = { X1600_CLK_AHB2, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR1, 24 },
+ },
+
+};
+
+static void __init x1600_cgu_init(struct device_node *np)
+{
+ int retval;
+
+ cgu = ingenic_cgu_new(x1600_cgu_clocks,
+ ARRAY_SIZE(x1600_cgu_clocks), np);
+ if (!cgu) {
+ pr_err("%s: failed to initialise CGU\n", __func__);
+ return;
+ }
+
+ retval = ingenic_cgu_register_clocks(cgu);
+ if (retval) {
+ pr_err("%s: failed to register CGU Clocks\n", __func__);
+ return;
+ }
+
+ ingenic_cgu_register_syscore(cgu);
+}
+
+CLK_OF_DECLARE_DRIVER(x1600_cgu, "ingenic,x1600-cgu", x1600_cgu_init);
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/4] clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework changes
2026-10-25 19:18 [PATCH 0/4] clk / clocksource: Add Ingenic X1600 SoC support H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 1/4] dt-bindings: clock: Add Ingenic X1600 CGU bindings H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support H. Nikolaus Schaller
@ 2026-10-25 19:18 ` H. Nikolaus Schaller
2026-10-25 22:54 ` Paul Cercueil
2026-10-25 19:18 ` [PATCH 4/4] clk: ingenic: x1000-cgu: " H. Nikolaus Schaller
3 siblings, 1 reply; 7+ messages in thread
From: H. Nikolaus Schaller @ 2026-10-25 19:18 UTC (permalink / raw)
To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Paul Cercueil
Cc: linux-clk, devicetree, linux-kernel, linux-mips, letux-kernel,
kernel, H. Nikolaus Schaller
The common Ingenic CGU core framework was updated to support a
secondary post-VCO divider (OD1), which extended the signature of the
calc_m_n_od() callback.
Update the JZ4760 CGU driver implementation to accept the new pod1
argument, preventing compilation errors. The parameter remains unused
on this specific SoC architecture.
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
---
drivers/clk/ingenic/jz4760-cgu.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/ingenic/jz4760-cgu.c b/drivers/clk/ingenic/jz4760-cgu.c
index 372fe4b079927..52cc5ce150048 100644
--- a/drivers/clk/ingenic/jz4760-cgu.c
+++ b/drivers/clk/ingenic/jz4760-cgu.c
@@ -56,7 +56,8 @@ static const u8 jz4760_cgu_pll_half_div_table[] = {
static void
jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
- unsigned int *pm, unsigned int *pn, unsigned int *pod)
+ unsigned int *pm, unsigned int *pn, unsigned int *pod,
+ unsigned int *pod1)
{
unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1;
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 4/4] clk: ingenic: x1000-cgu: Adapt calc_m_n_od() to core framework changes
2026-10-25 19:18 [PATCH 0/4] clk / clocksource: Add Ingenic X1600 SoC support H. Nikolaus Schaller
` (2 preceding siblings ...)
2026-10-25 19:18 ` [PATCH 3/4] clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework changes H. Nikolaus Schaller
@ 2026-10-25 19:18 ` H. Nikolaus Schaller
3 siblings, 0 replies; 7+ messages in thread
From: H. Nikolaus Schaller @ 2026-10-25 19:18 UTC (permalink / raw)
To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Paul Cercueil
Cc: linux-clk, devicetree, linux-kernel, linux-mips, letux-kernel,
kernel, H. Nikolaus Schaller
The common Ingenic CGU core framework was updated to support a
secondary post-VCO divider (OD1), which extended the signature of the
calc_m_n_od() callback.
Update the X1000 CGU driver implementation to accept the new pod1
argument, preventing compilation errors. The parameter remains unused
on this specific SoC architecture.
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
---
drivers/clk/ingenic/x1000-cgu.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/ingenic/x1000-cgu.c b/drivers/clk/ingenic/x1000-cgu.c
index d89bdfb7c219a..cb82d8e6470b6 100644
--- a/drivers/clk/ingenic/x1000-cgu.c
+++ b/drivers/clk/ingenic/x1000-cgu.c
@@ -173,7 +173,8 @@ static const struct clk_ops x1000_otg_phy_ops = {
static void
x1000_i2spll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
- unsigned int *pm, unsigned int *pn, unsigned int *pod)
+ unsigned int *pm, unsigned int *pn, unsigned int *pod,
+ unsigned int *pod1)
{
const unsigned long m_max = GENMASK(pll_info->m_bits - 1, 0);
const unsigned long n_max = GENMASK(pll_info->n_bits - 1, 0);
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 3/4] clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework changes
2026-10-25 19:18 ` [PATCH 3/4] clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework changes H. Nikolaus Schaller
@ 2026-10-25 22:54 ` Paul Cercueil
0 siblings, 0 replies; 7+ messages in thread
From: Paul Cercueil @ 2026-10-25 22:54 UTC (permalink / raw)
To: H. Nikolaus Schaller, Stephen Boyd, Brian Masney, Jerome Brunet,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-clk, devicetree, linux-kernel, linux-mips, letux-kernel, kernel
Hi Nikolaus,
Le dimanche 25 octobre 2026 à 20:18 +0100, H. Nikolaus Schaller a
écrit :
> The common Ingenic CGU core framework was updated to support a
> secondary post-VCO divider (OD1), which extended the signature of the
> calc_m_n_od() callback.
>
> Update the JZ4760 CGU driver implementation to accept the new pod1
> argument, preventing compilation errors. The parameter remains unused
> on this specific SoC architecture.
>
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
> ---
> drivers/clk/ingenic/jz4760-cgu.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/clk/ingenic/jz4760-cgu.c
> b/drivers/clk/ingenic/jz4760-cgu.c
> index 372fe4b079927..52cc5ce150048 100644
> --- a/drivers/clk/ingenic/jz4760-cgu.c
> +++ b/drivers/clk/ingenic/jz4760-cgu.c
> @@ -56,7 +56,8 @@ static const u8 jz4760_cgu_pll_half_div_table[] = {
> static void
> jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
> unsigned long rate, unsigned long
> parent_rate,
> - unsigned int *pm, unsigned int *pn, unsigned
> int *pod)
> + unsigned int *pm, unsigned int *pn, unsigned
> int *pod,
> + unsigned int *pod1)
> {
> unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1;
>
Well you can't update a callback prototype in patch 2/4 then update the
implementations in 3/4, the kernel has to be buildable after each
patch.
Cheers,
-Paul
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support
2026-10-25 19:18 ` [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support H. Nikolaus Schaller
@ 2026-10-26 10:16 ` kernel test robot
0 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2026-10-26 10:16 UTC (permalink / raw)
To: H. Nikolaus Schaller, Stephen Boyd, Brian Masney, Jerome Brunet,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Cercueil
Cc: llvm, oe-kbuild-all, linux-clk, devicetree, linux-kernel,
linux-mips, letux-kernel, kernel, Paul Boddie,
H. Nikolaus Schaller
Hi Nikolaus,
kernel test robot noticed the following build warnings:
[auto build test WARNING on clk/clk-next]
[also build test WARNING on robh/for-next linus/master v7.3 next-20261023]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/H-Nikolaus-Schaller/dt-bindings-clock-Add-Ingenic-X1600-CGU-bindings/20261025-201824
base: https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git clk-next
patch link: https://lore.kernel.org/r/50983be55e467076e83e3ffc37b30fc4e5648e15.1792955907.git.hns%40goldelico.com
patch subject: [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20261026/202610261821.QhaEUvEU-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project 186e70f4e6a1521991d2340d3d49b23b1beff11f)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261026/202610261821.QhaEUvEU-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202610261821.QhaEUvEU-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/clk/ingenic/x1600-cgu.c:129:38: warning: variable 'm' is used uninitialized whenever 'for' loop exits because its condition is false [-Wsometimes-uninitialized]
129 | for (od_combined = od_combined_min; od_combined <= od_combined_max; od_combined++) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/ingenic/x1600-cgu.c:141:8: note: uninitialized use occurs here
141 | *pm = m;
| ^
drivers/clk/ingenic/x1600-cgu.c:129:38: note: remove the condition if it is always true
129 | for (od_combined = od_combined_min; od_combined <= od_combined_max; od_combined++) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/ingenic/x1600-cgu.c:115:17: note: initialize the variable 'm' to silence this warning
115 | unsigned long m, n;
| ^
| = 0
>> drivers/clk/ingenic/x1600-cgu.c:129:38: warning: variable 'n' is used uninitialized whenever 'for' loop exits because its condition is false [-Wsometimes-uninitialized]
129 | for (od_combined = od_combined_min; od_combined <= od_combined_max; od_combined++) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/ingenic/x1600-cgu.c:142:8: note: uninitialized use occurs here
142 | *pn = n;
| ^
drivers/clk/ingenic/x1600-cgu.c:129:38: note: remove the condition if it is always true
129 | for (od_combined = od_combined_min; od_combined <= od_combined_max; od_combined++) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/ingenic/x1600-cgu.c:115:20: note: initialize the variable 'n' to silence this warning
115 | unsigned long m, n;
| ^
| = 0
2 warnings generated.
vim +129 drivers/clk/ingenic/x1600-cgu.c
104
105 static void
106 x1600_pll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
107 unsigned long rate, unsigned long parent_rate,
108 unsigned int *pm, unsigned int *pn, unsigned int *pod,
109 unsigned int *pod1)
110 {
111 const unsigned int od_max = pll_info->od_max - 1;
112 const unsigned int od1_max = pll_info->od1_max - 1;
113 const unsigned int m_max = GENMASK(pll_info->m_bits - 1, 0);
114 const unsigned int n_max = GENMASK(pll_info->n_bits - 1, 0);
115 unsigned long m, n;
116 unsigned int od = 1, od1 = 1;
117 const unsigned long fvco_min = 600000000, fvco_max = 2400000000;
118 unsigned long fvco;
119
120 /*
121 * Combined output divider range:
122 *
123 * ceil(fvco_min / rate) - fvco_max / rate
124 */
125 unsigned int od_combined_min = (fvco_min + rate) / rate;
126 unsigned int od_combined_max = fvco_max / rate;
127 unsigned int od_combined;
128
> 129 for (od_combined = od_combined_min; od_combined <= od_combined_max; od_combined++) {
130 od = int_sqrt(od_combined);
131 od1 = od_combined / od;
132
133 /* od == floor(sqrt(od_combined)); od1 >= od */
134 if ((od * od1 == od_combined) && od <= od_max && od1 <= od1_max) {
135 fvco = rate * od_combined;
136 rational_best_approximation(fvco, parent_rate, m_max, n_max, &m, &n);
137 break;
138 }
139 }
140
141 *pm = m;
142 *pn = n;
143 *pod = od;
144 *pod1 = od1;
145 }
146
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-26 10:16 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-25 19:18 [PATCH 0/4] clk / clocksource: Add Ingenic X1600 SoC support H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 1/4] dt-bindings: clock: Add Ingenic X1600 CGU bindings H. Nikolaus Schaller
2026-10-25 19:18 ` [PATCH 2/4] clk: ingenic: Add Ingenic X1600 CGU clock driver support H. Nikolaus Schaller
2026-10-26 10:16 ` kernel test robot
2026-10-25 19:18 ` [PATCH 3/4] clk: ingenic: jz4760-cgu: Adapt calc_m_n_od() to core framework changes H. Nikolaus Schaller
2026-10-25 22:54 ` Paul Cercueil
2026-10-25 19:18 ` [PATCH 4/4] clk: ingenic: x1000-cgu: " H. Nikolaus Schaller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®