From: "Stefan Dösinger" <stefandoesinger@gmail.com>
To: Michael Turquette <mturquette@baylibre.com>,
Stephen Boyd <sboyd@kernel.org>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Philipp Zabel <p.zabel@pengutronix.de>,
Brian Masney <bmasney@redhat.com>
Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
"Stefan Dösinger" <stefandoesinger@gmail.com>
Subject: [PATCH RFC v5 09/12] clk: zte: Introduce a driver for zx297520v3 matrix clocks
Date: Sun, 28 Jun 2026 22:59:04 +0300 [thread overview]
Message-ID: <20260628-zx29clk-v5-9-79ff044e4192@gmail.com> (raw)
In-Reply-To: <20260628-zx29clk-v5-0-79ff044e4192@gmail.com>
This clock controller controls high speed devices: CPU, DMA, RAM, SDIO,
Ethernet. The only non-clock, non-reset registers I have spotted here
are hardware spinlocks.
I put the entire set of PLL generated frequencies as consumed clocks in
the binding. Due to lack of a data sheet I can't rule out that the any
of the PLL subdivions are used.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Version 5:
Make it an MFD child device, remove aux device
Fix zx297529 -> zx297520 typos
Fix AHB_wclk -> ahb_wclk. That was a leftover of my old composite structs
Add SRAM0 and GSM_CFG clocks
---
drivers/clk/zte/clk-zx297520v3.c | 186 +++++++++++++++++++++++++++++++++++++++
1 file changed, 186 insertions(+)
diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx297520v3.c
index bb3d64eff909..17e1f741b48c 100644
--- a/drivers/clk/zte/clk-zx297520v3.c
+++ b/drivers/clk/zte/clk-zx297520v3.c
@@ -431,8 +431,194 @@ static struct platform_driver clk_zx297520v3_top = {
},
};
+static const char * const cpu_sel[] = {
+ "osc26m",
+ "mpll", /* 624 MHz */
+ "mpll_d2", /* 312 MHz */
+ "mpll_d4", /* 156 MHz */
+};
+
+static const char * const sd0_sel[] = {
+ "osc26m",
+ "mpll_d4", /* 156 MHz */
+ "gpll_d2", /* 100 MHz */
+ "mpll_d8", /* 78 MHz */
+ "gpll_d4", /* 50 MHz */
+ "gpll_d8", /* 25 MHz */
+};
+
+static const char * const sd1_sel[] = {
+ "osc26m",
+ "gpll_d2", /* 100 MHz */
+ "mpll_d8", /* 78 MHz */
+ "gpll_d4", /* 50 MHz */
+ "mpll_d16", /* 39 MHz */
+ "gpll_d8", /* 25 MHz */
+};
+
+static const char * const nand_sel[] = {
+ "mpll_d4", /* 156 MHz */
+ "osc26m",
+};
+
+static const char * const edcp_sel[] = {
+ "osc26m",
+ "mpll_d4", /* 156 MHz */
+ "mpll_d5", /* 124.8 MHz */
+ "mpll_d6", /* 104 MHz */
+};
+
+static const char * const tdm_sel[] = {
+ "osc26m",
+ "dpll_d4", /* 122.88 MHz */
+ "mpll_d6", /* 104 MHz */
+};
+
+static const struct zx_mux_desc zx297520v3_matrix_muxes[] = {
+ MUX(0, "cpu_mux", cpu_sel, 0x20, 0, 2),
+ MUX(0, "sd0_mux", sd0_sel, 0x50, 4, 3),
+ MUX(0, "sd1_mux", sd1_sel, 0x50, 8, 3),
+ MUX(0, "nand_mux", nand_sel, 0x50, 12, 2),
+ MUX(0, "edcp_mux", edcp_sel, 0x50, 16, 2),
+ MUX(0, "tdm_mux", tdm_sel, 0x50, 24, 2),
+};
+
+static const struct zx_gate_desc zx297520v3_matrix_gates[] = {
+ /* Both 0x24 and 0x28 bits 1 and 2 stop the CPU. There is also a bit in topclk+0x138, which
+ * ZTE's uboot calls "A53 reset", which also stops the CPU. I can't really tell the
+ * difference between matrix+28 and top+138. The clock (maxtrix+0x24) can be disabled and
+ * enabled from the Cortex M0 and it will nicely stop and restart the A53, retaining all
+ * state.
+ *
+ * 0x50, bits 0-3 have the DDR clock. A lot of DDR gates and resets are in 0x100.
+ */
+ GATE(ZX297520V3_CPU_WCLK, "cpu_wclk", "cpu_mux", 0x24, 1,
+ CLK_IS_CRITICAL),
+ GATE(ZX297520V3_CPU_PCLK, "cpu_pclk", clk_main[0], 0x24, 2,
+ CLK_IS_CRITICAL),
+
+ /* ZSP aka LTE DSP clock. I think there is a mux at matrix+0x30, but I have no idea
+ * about the frequencies it selects.
+ */
+ GATE(ZX297520V3_ZSP_WCLK, "zsp_wclk", clk_unknown[0], 0x3c, 0, 0),
+
+ GATE(ZX297520V3_SD0_WCLK, "sd0_wclk", "sd0_mux", 0x54, 12, 0),
+ GATE(ZX297520V3_SD0_PCLK, "sd0_pclk", clk_main[0], 0x54, 13, 0),
+ GATE(ZX297520V3_SD0_CDET, "sd0_cdet", "osc32k", 0x54, 14, 0),
+ GATE(ZX297520V3_SD1_WCLK, "sd1_wclk", "sd1_mux", 0x54, 4, 0),
+ GATE(ZX297520V3_SD1_PCLK, "sd1_pclk", clk_main[0], 0x54, 5, 0),
+ /* I don't know how the cdet clock works. Card detection in the way the dwc,mmc driver uses
+ * it appears broken no matter this clock's setting.
+ */
+ GATE(ZX297520V3_SD1_CDET, "sd1_cdet", "osc32k", 0x54, 6, 0),
+
+ /* This is some "denali" NAND, not the qspi connected one */
+ GATE(ZX297520V3_NAND_WCLK, "nand_wclk", "nand_mux", 0x54, 20, 0),
+ GATE(ZX297520V3_NAND_PCLK, "nand_pclk", clk_main[0], 0x54, 21, 0),
+ GATE(ZX297520V3_SSC_WCLK, "ssc_wclk", clk_unknown[0], 0x84, 1, 0),
+ GATE(ZX297520V3_SSC_PCLK, "ssc_pclk", clk_main[0], 0x84, 2, 0),
+
+ /* Yes, WCLK bit > PCLK bit for EDCP */
+ GATE(ZX297520V3_EDCP_WCLK, "edcp_wclk", "edcp_mux", 0x64, 2, 0),
+ GATE(ZX297520V3_EDCP_PCLK, "edcp_pclk", clk_main[0], 0x64, 1, 0),
+
+ /* There are a lot more VOU related controls in these registers, but turning off the main
+ * clock seems to shut off the entire VOU MMIO range.
+ */
+ GATE(ZX297520V3_VOU_WCLK, "vou_wclk", clk_unknown[0], 0x168, 0, 0),
+ GATE(ZX297520V3_VOU_PCLK, "vou_pclk", clk_main[0], 0x168, 1, 0),
+
+ /* PDCFG. Like PMM, either clock bit will allow the device to function. */
+ GATE(ZX297520V3_PDCFG_WCLK, "pdcfg_wclk", clk_unknown[0], 0x88, 0,
+ CLK_IS_CRITICAL),
+ GATE(ZX297520V3_PDCFG_PCLK, "pdcfg_pclk", clk_main[0], 0x88, 1,
+ CLK_IS_CRITICAL),
+
+ /* ZTE's driver has a statemt to the effect of *(top->base+0x11c) = 5, with a comment
+ * suggesting that this sets a 50 mhz clock. The clock code itself lists gmac clocks in
+ * matrix+110 and lists the parents of these clock as 50mhz gpll output, but the GMAC
+ * driver never enables the clocks. It turns out ZTE's code is highly misleading.
+ *
+ * The GMAC's work clock is definitly not any gpll output because it keeps working fine with
+ * gpll disabled. The MDIO speed is mostly unaffected by mpll speed changes, so it is most
+ * likely not fed by mpll either. All other PLLs can be disabled without breaking GMAC, so
+ * osc26m is the only possible remaining parent.
+ *
+ * The GMAC Gates are left enabled by the boot loader and are required for the GMAC to work.
+ *
+ * As for the 50 MHz comment: See rmiiphy_wclk.
+ */
+ GATE(ZX297520V3_GMAC_WCLK, "gmac_wclk", clk_main[0], 0x110, 0, 0),
+ GATE(ZX297520V3_GMAC_PCLK, "gmac_pclk", clk_main[0], 0x110, 1, 0),
+ GATE(ZX297520V3_GMAC_AHB, "gmac_ahb", "ahb_wclk", 0x110, 2, 0),
+
+ GATE(ZX297520V3_MBOX_PCLK, "mbox_pclk", clk_main[0], 0x88, 2, 0),
+ GATE(ZX297520V3_SRAM0_PCLK, "sram0_pclk", clk_main[0], 0x88, 4, 0),
+ GATE(ZX297520V3_GSM_CFG_PCLK, "gsm_cfg_pclk", clk_main[0], 0x88, 8, 0),
+ GATE(ZX297520V3_DMA_PCLK, "dma_pclk", clk_main[0], 0x94, 3, 0),
+
+ /* LSP uplink clocks. The PCLK is fairly obvious (disabling it shuts off the entire LSP
+ * register area). The WCLK speeds were deduced by setting timers and qspi muxes to a
+ * specific speed and seeing which bit in matrix+0x7c needs to be enabled for the device
+ * to work.
+ *
+ * Due to the timers I am certain about the 26mhz and 32khz clocks. I cannot directly
+ * observe the qspi mux frequency, so the clock rates depend on ZTE's qspi mux selection
+ * being correct.
+ *
+ * Two additional bits are specific to sound components - the mux for the LSP's TDM IP is
+ * in matrixclk and gets passed down. I2S has a mux in LSP, which can select the dpll_d4
+ * clock.
+ */
+ GATE(ZX297520V3_LSP_MPLL_D5_WCLK, "lsp_mpll_d5", "mpll_d5", 0x7c, 0, 0),
+ GATE(ZX297520V3_LSP_MPLL_D4_WCLK, "lsp_mpll_d4", "mpll_d4", 0x7c, 1, 0),
+ GATE(ZX297520V3_LSP_MPLL_D6_WCLK, "lsp_mpll_d6", "mpll_d6", 0x7c, 2, 0),
+ GATE(ZX297520V3_LSP_MPLL_D8_WCLK, "lsp_mpll_d8", "mpll_d8", 0x7c, 3, 0),
+ GATE(ZX297520V3_LSP_MPLL_D12_WCLK, "lsp_mpll_d12", "mpll_d12", 0x7c, 4, 0),
+ GATE(ZX297520V3_LSP_OSC26M_WCLK, "lsp_osc26m", clk_main[0], 0x7c, 5, 0),
+ GATE(ZX297520V3_LSP_OSC32K_WCLK, "lsp_osc32k", "osc32k", 0x7c, 6, 0),
+ GATE(ZX297520V3_LSP_PCLK, "lsp_pclk", clk_main[0], 0x7c, 7, 0),
+ GATE(ZX297520V3_LSP_TDM_WCLK, "lsp_tdm_wclk", "tdm_mux", 0x7c, 8, 0),
+ GATE(ZX297520V3_LSP_DPLL_D4_WCLK, "lsp_dpll_d4", "dpll_d4", 0x7c, 9, 0),
+};
+
+static const char * const zx297520v3_matrix_inputs[] = {
+ "osc26m", "osc32k",
+ "mpll", "mpll_d2", "mpll_d3", "mpll_d4", "mpll_d5", "mpll_d6", "mpll_d8", "mpll_d12",
+ "mpll_d16", "mpll_d26",
+ "upll", "upll_d2", "upll_d3", "upll_d4", "upll_d5", "upll_d6", "upll_d8", "upll_d12",
+ "upll_d16",
+ "dpll", "dpll_d2", "dpll_d3", "dpll_d4", "dpll_d5", "dpll_d6", "dpll_d8", "dpll_d12",
+ "dpll_d16",
+ "gpll", "gpll_d2", "gpll_d3", "gpll_d4", "gpll_d5", "gpll_d6", "gpll_d8", "gpll_d12",
+ "gpll_d16",
+};
+
+static const struct zx_clk_data zx297520v3_matrixclk_data = {
+ .inputs = zx297520v3_matrix_inputs,
+ .num_inputs = ARRAY_SIZE(zx297520v3_matrix_inputs),
+ .muxes = zx297520v3_matrix_muxes,
+ .num_muxes = ARRAY_SIZE(zx297520v3_matrix_muxes),
+ .gates = zx297520v3_matrix_gates,
+ .num_gates = ARRAY_SIZE(zx297520v3_matrix_gates),
+};
+
+static int zx297520v3_matrixclk_probe(struct platform_device *pdev)
+{
+ return zx_clk_common_probe(&pdev->dev, pdev->dev.parent->of_node,
+ &zx297520v3_matrixclk_data);
+}
+
+static struct platform_driver clk_zx297520v3_matrix = {
+ .probe = zx297520v3_matrixclk_probe,
+ .driver = {
+ .name = "zx297520v3-matrixclk",
+ },
+};
+
static struct platform_driver * const clk_zx297520v3_drivers[] = {
&clk_zx297520v3_top,
+ &clk_zx297520v3_matrix,
};
static int __init clk_zx297520v3_init(void)
--
2.53.0
next prev parent reply other threads:[~2026-06-28 19:59 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-28 19:58 [PATCH RFC v5 00/12] ZTE zx297520v3 clock bindings and driver Stefan Dösinger
2026-06-28 19:58 ` [PATCH RFC v5 01/12] dt-bindings: soc: zte: Add zx297520v3 top clock and reset bindings Stefan Dösinger
2026-06-28 20:12 ` sashiko-bot
2026-06-28 19:58 ` [PATCH RFC v5 02/12] dt-bindings: soc: zte: Add zx297520v3 matrix " Stefan Dösinger
2026-06-28 20:09 ` sashiko-bot
2026-06-28 19:58 ` [PATCH RFC v5 03/12] dt-bindings: clk: zte: Add zx297520v3 LSP " Stefan Dösinger
2026-06-28 19:58 ` [PATCH RFC v5 04/12] mfd: zx297520v3: Add a clock and reset MFD driver Stefan Dösinger
2026-06-28 20:10 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 05/12] clk: zte: Add Clock registration infrastructure Stefan Dösinger
2026-06-28 20:10 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 06/12] clk: zte: Add zx PLL support infrastructure Stefan Dösinger
2026-06-28 20:14 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 07/12] clk: zte: Add regmap based clocks Stefan Dösinger
2026-06-28 20:28 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 08/12] clk: zte: Introduce a driver for zx297520v3 top clocks Stefan Dösinger
2026-06-28 20:16 ` sashiko-bot
2026-06-28 19:59 ` Stefan Dösinger [this message]
2026-06-28 20:12 ` [PATCH RFC v5 09/12] clk: zte: Introduce a driver for zx297520v3 matrix clocks sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 10/12] clk: zte: Introduce a driver for zx297520v3 LSP clocks and resets Stefan Dösinger
2026-06-28 20:18 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 11/12] reset: zte: Add a zx297520v3 reset driver Stefan Dösinger
2026-06-28 20:23 ` sashiko-bot
2026-06-28 19:59 ` [PATCH RFC v5 12/12] ARM: dts: zte: Declare zx297520v3 CRM device nodes Stefan Dösinger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260628-zx29clk-v5-9-79ff044e4192@gmail.com \
--to=stefandoesinger@gmail.com \
--cc=bmasney@redhat.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mturquette@baylibre.com \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=sboyd@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox