* [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; 8+ 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] 8+ 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; 8+ 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 related [flat|nested] 8+ 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-25 19:36 ` sashiko-bot 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, 2 replies; 8+ 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 related [flat|nested] 8+ 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-25 19:36 ` sashiko-bot 2026-10-26 10:16 ` kernel test robot 1 sibling, 0 replies; 8+ messages in thread From: sashiko-bot @ 2026-10-25 19:36 UTC (permalink / raw) To: H. Nikolaus Schaller; +Cc: linux-clk, devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider: - [High] clk: ingenic: uninitialized m and n in x1600_pll_calc_m_n_od() - [High] clk: ingenic: x1600: overlapping mux and div bits for X1600_CLK_CIMMCLK - [High] clk: ingenic: x1600: missing .gate initializer for X1600_CLK_RTC - [High] clk: ingenic: x1600: duplicate CLKGR0 gate bit for SADC and UART0 - [High] clk: ingenic: broken mdiv handling in ingenic_clk_calc_div() and set_rate() - [High] clk: ingenic: x1600-cgu requires undocumented clk12m external DT clock - [Medium] clk: ingenic: off-by-one od_max check in ingenic_pll_recalc_rate_od() - [Medium] clk: ingenic: uninitialized d_reg and signed shift in ingenic_clk_set_rate() - [Medium] clk: ingenic: x1600: missing -1 parent sentinels for X1600_CLK_OTGPHY - [Medium] clk: ingenic: x1600: missing cgu->lock in OTG PHY enable/disable callbacks -- commit 3e8079f374773cb8ca4b149b680046e60d28f341 Author: Paul Boddie <paul@boddie.org.uk> clk: ingenic: Add Ingenic X1600 CGU clock driver support This commit adds Clock Generation Unit (CGU) driver support for the Ingenic X1600 SoC, including custom OTG PHY and PLL rate calculation handlers. It also extends the common Ingenic CGU core to handle secondary post-VCO PLL dividers (OD1) and fractional I2S M/N/D divider fields. > 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 > @@ -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); [Severity: Medium] In ingenic_pll_recalc_rate_od(), does moving od++ before WARN_ON_ONCE(od == od_max) cause an off-by-one regression in the bounds check when od_encoding is used? Previously in ingenic_pll_recalc_rate(), the check against pll_info->od_max was performed on the 0-based loop index before incrementing od: for (od = 0; od < pll_info->od_max; od++) if (pll_info->od_encoding[od] == od_enc) break; if (pll_info->od_max == 0) BUG_ON(pll_info->od_bits != 0); else BUG_ON(od == pll_info->od_max); od++; With od++ now happening inside the if (od_encoding) branch before the check, matching the highest valid entry at od_encoding[od_max - 1] increments od to od_max and triggers a false WARN_ON_ONCE(od == od_max). Conversely, if od_enc is not found in od_encoding, the loop finishes with od == od_max, od++ makes it od_max + 1, and the warning does not fire. [ ... ] > @@ -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; [Severity: High] How does the div < 2 constraint here in ingenic_clk_set_rate() interact with ingenic_clk_calc_div() and the hardware N field encoding? In ingenic_clk_calc_div(), div is clamped to a minimum of clk_info->div.div (which is 1 for X1600_CLK_I2S0 and X1600_CLK_I2S1) without checking clk_info->mdiv.reg: div = clamp_t(unsigned int, div, clk_info->div.div, clk_info->div.div << clk_info->div.bits); When a rate >= parent_rate is requested, ingenic_clk_determine_rate() -> ingenic_clk_calc_div() calculates div == 1 and reports parent_rate as a valid rounded rate, but the subsequent call to ingenic_clk_set_rate() fails with -EINVAL because div < 2. Additionally, just before the if (clk_info->mdiv.reg) block, ingenic_clk_set_rate() calculates: hw_div = ((div / clk_info->div.div) - 1); and writes hw_div (which is div - 1) into the N field at clk_info->div.shift, while writing a raw 1 into the M field at clk_info->mdiv.shift. Because the I2S fractional divider uses unoffset M and N values (f_out = f_in * M / N with N >= 2 * M), doesn't subtracting 1 from div program N = div - 1 instead of N = div (and write N = 1, M = 1 when div == 2, violating N >= 2 * M), while ingenic_clk_recalc_rate() adds 1 back when reading N and ignores mdiv? > + > + 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); [Severity: Medium] Can d_reg be used uninitialized here in ingenic_clk_set_rate(), and does the explicit_n_bit shift overflow a signed 32-bit int? d_reg is only initialized when both clk_info->mdiv.reg and clk_info->nddiv.reg are non-zero, whereas writel(d_reg, cgu->base + clk_info->nddiv.reg) only checks clk_info->nddiv.reg, which triggers a -Wmaybe-uninitialized compiler warning when compiling cgu.c. Also, in x1600_cgu_clocks[], X1600_CLK_I2S0 and X1600_CLK_I2S1 set explicit_n_bit to 31: .nddiv = { CGU_REG_I2S0CDR1, 31, 30 }, Should the bit operations on explicit_d_bit and explicit_n_bit use BIT() instead of shifting a signed int 1 << 31? > 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 [ ... ] > +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); > +} [Severity: Medium] Should x1600_otg_phy_enable() and x1600_otg_phy_disable() acquire cgu->lock around the read-modify-write of CGU_REG_OPCR? X1600_CLK_RTC also uses CGU_REG_OPCR (.mux = { CGU_REG_OPCR, 2, 1 }), and ingenic_clk_set_parent() modifies CGU_REG_OPCR under the clock framework prepare_lock and cgu->lock, while clk_enable() and clk_disable() invoke x1600_otg_phy_enable() and x1600_otg_phy_disable() under enable_lock without holding cgu->lock. Can concurrent OTG PHY enable/disable and RTC reparenting race on CGU_REG_OPCR? [ ... ] > +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; > +} [Severity: High] What happens in x1600_pll_calc_m_n_od() if the loop finishes without finding a matching od_combined? m and n are declared without initialization and are only assigned inside the if ((od * od1 == od_combined) && od <= od_max && od1 <= od1_max) block. Since od_max and od1_max are 7, the maximum valid od * od1 product is 49. If a consumer requests a rate > fvco_max (where od_combined_min > od_combined_max), a rate <= ~12.24 MHz (where od_combined_min >= 50), or a rate where [od_combined_min, od_combined_max] contains no integer divisible by int_sqrt(od_combined) with both factors <= 7 (such as 43..48), the loop exits without initializing m and n, and leaves od and od1 with the last tested values (which can exceed od_max / od1_max). When x1600_pll_calc_m_n_od() returns to ingenic_pll_calc() and ingenic_pll_set_rate(): return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, n * od * od1); can uninitialized m and n cause a division-by-zero panic if n is 0, or cause ingenic_pll_set_rate() to write uninitialized m and n and out-of-range od and od1 values into the PLL control register? Also, in the od_combined_min calculation: unsigned int od_combined_min = (fvco_min + rate) / rate; when fvco_min is an exact multiple of rate (for example, rate == 600 MHz), this computes (fvco_min / rate) + 1 instead of ceil(fvco_min / rate). Should this use DIV_ROUND_UP(fvco_min, rate) (after guarding against rate == 0)? > + > +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 }, [Severity: High] Will registering X1600_CLK_12M as CGU_CLK_EXT cause x1600_cgu_init() -> ingenic_cgu_register_clocks() -> ingenic_register_clock() to fail with -ENODEV on Device Trees that follow the ingenic,cgu.yaml binding? In ingenic_register_clock(), every CGU_CLK_EXT clock is required to be present in the CGU Device Tree node: if (clk_info->type == CGU_CLK_EXT) { clk = of_clk_get_by_name(cgu->np, clk_info->name); if (IS_ERR(clk)) { pr_err("%s: no external clock '%s' provided\n", __func__, clk_info->name); err = -ENODEV; goto out; } However, Documentation/devicetree/bindings/clock/ingenic,cgu.yaml only allows two input clocks in clock-names ("ext" and "rtc"/"osc32k") and does not define "clk12m". [ ... ] > + [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 }, > + }, [Severity: High] In x1600_cgu_clocks[X1600_CLK_CIMMCLK], do the .mux and .div definitions overlap at bit 30 of CGU_REG_CIMCDR? .mux = { CGU_REG_CIMCDR, 30, 2 } occupies bits 31:30 of CGU_REG_CIMCDR, while .div = { CGU_REG_CIMCDR, 0, 1, 8, 30, 29, 28 } sets ce_bit = 30, busy_bit = 29, and stop_bit = 28. When ingenic_clk_set_rate() sets BIT(clk_info->div.ce_bit), won't it overwrite bit 30 of the parent mux field in CGU_REG_CIMCDR instead of setting CE_CIM (bit 29) and polling BUSY_CIM (bit 28)? > + > + [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}, > + }, [Severity: High] X1600_CLK_RTC specifies CGU_CLK_MUX | CGU_CLK_GATE, but .gate is not initialized here. Because .gate is zero-initialized to { .reg = 0, .bit = 0 }, won't enabling or disabling rtc_ercs (including during clk_disable_unused() or ingenic_clk_set_parent()) call ingenic_cgu_gate_set() on register 0x00 (CGU_REG_CLOCKCONTROL) bit 0, modifying the LSB of the X1600_CLK_CPU divider? > + > + /* Custom (SoC-specific) OTG PHY */ > + > + [X1600_CLK_OTGPHY] = { > + "otg_phy", CGU_CLK_CUSTOM, > + .parents = { X1600_CLK_12M }, > + .custom = { &x1600_otg_phy_ops }, > + }, [Severity: Medium] Should .parents for X1600_CLK_OTGPHY initialize the remaining slots to -1, i.e. { X1600_CLK_12M, -1, -1, -1 }? In ingenic_register_clock(), clocks with CGU_CLK_CUSTOM iterate over all ARRAY_SIZE(clk_info->parents) (4) entries and register every entry that is not -1: if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { clk_init.num_parents = 0; if (caps & CGU_CLK_MUX) num_possible = 1 << clk_info->mux.bits; else num_possible = ARRAY_SIZE(clk_info->parents); for (i = 0; i < num_possible; i++) { if (clk_info->parents[i] == -1) continue; With .parents = { X1600_CLK_12M }, the remaining three entries are zero-initialized to 0 (X1600_CLK_EXCLK, "ext"), registering otg_phy with four parents ("clk12m", "ext", "ext", "ext") instead of one. [ ... ] > + [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 }, > + }, [Severity: High] Both X1600_CLK_SADC and X1600_CLK_UART0 use .gate = { CGU_REG_CLKGR0, 14 }. Should X1600_CLK_SADC use bit 13 of CGU_REG_CLKGR0 instead, so that gating unused sadc (for instance in clk_disable_unused()) does not gate uart0? -- Sashiko AI review · https://sashiko.dev/#/patchset/cover.1792955907.git.hns@goldelico.com?part=2 ^ permalink raw reply [flat|nested] 8+ 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-25 19:36 ` sashiko-bot @ 2026-10-26 10:16 ` kernel test robot 1 sibling, 0 replies; 8+ 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] 8+ 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; 8+ 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 related [flat|nested] 8+ 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; 8+ 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] 8+ 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; 8+ 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 related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-26 10:16 UTC | newest] Thread overview: 8+ 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-25 19:36 ` sashiko-bot 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