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: 31+ 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-30 8:27 ` Philipp Zabel
2026-06-30 8:53 ` Stefan Dösinger
2026-07-02 9:01 ` Philipp Zabel
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-30 8:45 ` Philipp Zabel
2026-06-28 19:59 ` [PATCH RFC v5 12/12] ARM: dts: zte: Declare zx297520v3 CRM device nodes Stefan Dösinger
2026-06-29 15:49 ` [PATCH RFC v5 00/12] ZTE zx297520v3 clock bindings and driver Conor Dooley
2026-07-01 17:22 ` Stefan Dösinger
2026-07-02 19:05 ` Conor Dooley
2026-07-02 19:26 ` 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.