clk: zte: Introduce a driver for zx297520v3 matrix clocks

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.

Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
Signed-off-by: Brian Masney <bmasney@redhat.com>
This commit is contained in:
Stefan Dösinger 2026-09-27 23:13:38 +03:00 committed by Brian Masney
parent 913e9c3b02
commit 0d852cd2ae

View File

@ -812,6 +812,528 @@ static const struct zx_clk_data zx297520v3_topclk_data = {
.num_exports = ARRAY_SIZE(zx297520v3_top_exports),
};
enum matrix_clock_ids {
ZX_CLK_MATRIX_OSC26M_D4,
ZX_CLK_MATRIX_MPLL_D2_PREGATE,
ZX_CLK_MATRIX_MPLL_D4_PREGATE,
ZX_CLK_MATRIX_MPLL_D6_PREGATE,
ZX_CLK_MATRIX_MPLL_D8_PREGATE,
ZX_CLK_MATRIX_MPLL_D12_PREGATE,
ZX_CLK_MATRIX_MPLL_D16_PREGATE,
ZX_CLK_MATRIX_GPLL_D2_D2_PREGATE,
ZX_CLK_MATRIX_GPLL_D2_D4_PREGATE,
ZX_CLK_MATRIX_DPLL_D2_PREGATE,
ZX_CLK_MATRIX_DPLL_D4_PREGATE,
ZX_CLK_MATRIX_MPLL_D16,
ZX_CLK_MATRIX_MPLL_D12,
ZX_CLK_MATRIX_MPLL_D8,
ZX_CLK_MATRIX_MPLL_D6,
ZX_CLK_MATRIX_MPLL_D4,
ZX_CLK_MATRIX_MPLL_D2,
ZX_CLK_MATRIX_DPLL_D4,
ZX_CLK_MATRIX_DPLL_D2,
ZX_CLK_MATRIX_GPLL_D2_D4,
ZX_CLK_MATRIX_GPLL_D2_D2,
ZX_CLK_CPU_MUX,
ZX_CLK_ZSP_MUX,
ZX_CLK_DDR_CTRL_MUX,
ZX_CLK_SD0_MUX,
ZX_CLK_SD1_MUX,
ZX_CLK_NAND_MUX,
ZX_CLK_EDCP_MUX,
ZX_CLK_TDM_MUX,
ZX_CLK_AXI_MUX,
ZX_CLK_SYS_TIMER_WCLK,
ZX_CLK_CPU_WCLK,
ZX_CLK_CPU_PCLK,
ZX_CLK_ZSP_WCLK,
ZX_CLK_ZSP_PCLK,
ZX_CLK_DDR_CTRL_PCLK,
ZX_CLK_DDR_CTRL_WCLK,
ZX_CLK_SD0_PCLK,
ZX_CLK_SD0_WCLK,
ZX_CLK_SD0_CDET,
ZX_CLK_SD1_PCLK,
ZX_CLK_SD1_WCLK,
ZX_CLK_SD1_CDET,
ZX_CLK_NAND_WCLK,
ZX_CLK_NAND_PCLK,
ZX_CLK_LTE_MODEM_WCLK,
ZX_CLK_LTE_MODEM_PCLK,
ZX_CLK_EDCP_PCLK,
ZX_CLK_EDCP_WCLK,
ZX_CLK_RFFE_WCLK,
ZX_CLK_SSC_WCLK,
ZX_CLK_SSC_PCLK,
ZX_CLK_PDCFG_WCLK,
ZX_CLK_PDCFG_PCLK,
ZX_CLK_MBOX_PCLK,
ZX_CLK_SRAM0_PCLK,
ZX_CLK_GSM_CFG_PCLK,
ZX_CLK_GMAC_WCLK,
ZX_CLK_GMAC_PCLK,
ZX_CLK_GMAC_AHB,
ZX_CLK_AXI_WCLK,
ZX_CLK_DMA_PCLK,
ZX_CLK_VOU_WCLK,
ZX_CLK_VOU_PCLK,
ZX_CLK_TOLSP_MPLL_D5_WCLK,
ZX_CLK_TOLSP_MPLL_D4_WCLK,
ZX_CLK_TOLSP_MPLL_D6_WCLK,
ZX_CLK_TOLSP_MPLL_D8_WCLK,
ZX_CLK_TOLSP_MPLL_D12_WCLK,
ZX_CLK_TOLSP_OSC26M_WCLK,
ZX_CLK_TOLSP_OSC32K_WCLK,
ZX_CLK_TOLSP_PCLK,
ZX_CLK_TOLSP_TDM_WCLK,
ZX_CLK_TOLSP_DPLL_D4_WCLK,
};
/*
* For devices which have a working driver the work clock can be figured out by gating off registers
* in topcrm+0x140. This is used for devices where I can't interpret the register contents yet.
*/
static const struct zx_parent_desc matrix_unk = PARENT_FW("wclk-osc26m");
static const struct zx_parent_desc cpu_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_FW("mpll"), /* 624 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D2), /* 312 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
};
/*
* Figuring these values out is a bit tricky. The best liveness check of the ZSP is reading its TCM
* at 0x81000000. If it is clocked up and out of reset reading it will succeed. Otherwise, reading
* will stall. Value 1 depends on topcrm+0x140, bit 13, but not any matrixcrm+0x118 gate, so it is
* likely dpll without a div. Mux value 2 depends on 0x118 bit 5, so mpll-d2. Mux value 3 depends on
* 0x118 bit 4, so mpll-d4. Mux value 0 in turn depends on topcrm+0x140, bit 24 (osc26m), so it
* follows the usual pattern that mux 0 is osc26m.
*
* If the firmware is running and fed with initialization data it will start writing status messages
* on UART1. An interruption of these messages or a failure to respond to rpmsg is not proof that
* the ZSP's clock has been disabled. The firmware's workings depend on regular timer and LTE device
* IRQs, so one of these devices might have been stopped instead.
*/
static const struct zx_parent_desc zsp_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_FW("dpll"), /* 491.52 MHz, boot default */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D2), /* 312 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
};
/*
* We can't realistically change DDR speed while running an OS out of DDR (it reads garbage for a
* short while on transition), but we need to know if we have to keep gpll alive. 32 MB devices use
* 200 MHz, while 64/128 MB ones use 156 MHz.
*/
static const struct zx_parent_desc ddr_ctrl_sel[] = {
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
PARENT_FW("gpll"), /* 200 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D6), /* 104 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D8), /* 78 MHz */
};
static const struct zx_parent_desc sd0_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
PARENT_FW("gpll-d2"), /* 100 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D8), /* 78 MHz */
PARENT_ID(ZX_CLK_MATRIX_GPLL_D2_D2), /* 50 MHz */
PARENT_ID(ZX_CLK_MATRIX_GPLL_D2_D4), /* 25 MHz */
};
static const struct zx_parent_desc sd1_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_FW("gpll-d2"), /* 100 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D8), /* 78 MHz */
PARENT_ID(ZX_CLK_MATRIX_GPLL_D2_D2), /* 50 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D16), /* 39 MHz */
PARENT_ID(ZX_CLK_MATRIX_GPLL_D2_D4), /* 25 MHz */
};
/*
* ZTE's kernel puts the high frequency first, but this is against the usual convention on this SoC.
* I don't have any device that has a raw NAND controller though.
*/
static const struct zx_parent_desc nand_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
};
static const struct zx_parent_desc edcp_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
PARENT_FW("mpll-d5"), /* 124.8 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D6), /* 104 MHz */
};
/*
* The 122.88 MHz selection of value 1 is given in ZTE's kernel and matches up to observation: Print
* the TDM counter at regular intervals and look at its increase at values 0/1/2. It increases by
* about 310*26 for value 0, 310*122.88 for value 1 and 310*104 for value 2.
*
* Mux value 1 is gated off by matrix+0x118 bit 8. Values 2 and 3 are both gated off by 0x118 bit 3,
* suggesting that they select the same parent.
*/
static const struct zx_parent_desc tdm_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_ID(ZX_CLK_MATRIX_DPLL_D4), /* 122.88 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D6), /* 104 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D6),
};
/*
* ZTE's firmware uses 0x128 as the PHY's vote and names 0x120 as "PS" and 0x124 as "AP" vote.
* However, their cpufreq driver ultimately uses the "PS" define and thus writes to 0x120, which can
* be observed in the running system.
*/
#define ZX297520V3_AXI_AP_VOTE 0x120
static const struct zx_parent_desc axi_sel[] = {
PARENT_FW("wclk-osc26m"),
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4), /* 156 MHz */
PARENT_FW("mpll-d5"), /* 124.8 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D6), /* 104 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D8), /* 78 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D12), /* 52 MHz */
PARENT_ID(ZX_CLK_MATRIX_MPLL_D16), /* 39 MHz */
PARENT_ID(ZX_CLK_MATRIX_OSC26M_D4), /* 6.5 MHz */
};
static const struct zx_clock zx297520v3_matrix_clocks[] = {
[ZX_CLK_MATRIX_OSC26M_D4] = FIXED_DIV("matrix-osc26m-d4", PARENT_FW("wclk-osc26m"), 4),
[ZX_CLK_MATRIX_MPLL_D2_PREGATE] = FIXED_DIV("matrix-mpll-d2-pregate", PARENT_FW("mpll"), 2),
[ZX_CLK_MATRIX_MPLL_D4_PREGATE] = FIXED_DIV("matrix-mpll-d4-pregate", PARENT_FW("mpll"), 4),
/* div 5: provided as a separate line from topcrm */
[ZX_CLK_MATRIX_MPLL_D6_PREGATE] = FIXED_DIV("matrix-mpll-d6-pregate", PARENT_FW("mpll"), 6),
[ZX_CLK_MATRIX_MPLL_D8_PREGATE] = FIXED_DIV("matrix-mpll-d8-pregate", PARENT_FW("mpll"), 8),
[ZX_CLK_MATRIX_MPLL_D12_PREGATE] = FIXED_DIV("matrix-mpll-d12-pregate", PARENT_FW("mpll"),
12),
[ZX_CLK_MATRIX_MPLL_D16_PREGATE] = FIXED_DIV("matrix-mpll-d16-pregate", PARENT_FW("mpll"),
16),
[ZX_CLK_MATRIX_GPLL_D2_D2_PREGATE] = FIXED_DIV("matrix-gpll-d2-d2-pregate",
PARENT_FW("gpll-d2"), 2),
[ZX_CLK_MATRIX_GPLL_D2_D4_PREGATE] = FIXED_DIV("matrix-gpll-d2-d4-pregate",
PARENT_FW("gpll-d2"), 4),
[ZX_CLK_MATRIX_DPLL_D2_PREGATE] = FIXED_DIV("matrix-dpll-d2-pregate", PARENT_FW("dpll"), 2),
[ZX_CLK_MATRIX_DPLL_D4_PREGATE] = FIXED_DIV("matrix-dpll-d4-pregate", PARENT_FW("dpll"), 4),
/*
* Matrix distribution gates: Every matrix-generated subdivision of the input PLLs
* has a gate in 0x118. The original inputs from topcrm (mpll 624, mpll 124.8, gpll 200,
* gpll 100, dpll 491.52) do not.
*
* Bit 8 is consumed by TDM and the frequency dpll/4 holds up to observation. Bit 9 is
* consumed by an unknown device and the frequency is guessed from the pattern that lower
* bits gate lower frequencies.
*/
[ZX_CLK_MATRIX_MPLL_D16] = GATE("matrix-mpll-d16",
PARENT_ID(ZX_CLK_MATRIX_MPLL_D16_PREGATE), 0x118, 0,
ZX297520V3_AXI_CANDIDATE),
[ZX_CLK_MATRIX_MPLL_D12] = GATE("matrix-mpll-d12",
PARENT_ID(ZX_CLK_MATRIX_MPLL_D12_PREGATE), 0x118, 1,
ZX297520V3_AXI_CANDIDATE),
[ZX_CLK_MATRIX_MPLL_D8] = GATE("matrix-mpll-d8",
PARENT_ID(ZX_CLK_MATRIX_MPLL_D8_PREGATE), 0x118, 2,
ZX297520V3_AXI_CANDIDATE),
[ZX_CLK_MATRIX_MPLL_D6] = GATE("matrix-mpll-d6",
PARENT_ID(ZX_CLK_MATRIX_MPLL_D6_PREGATE), 0x118, 3,
ZX297520V3_AXI_CANDIDATE),
[ZX_CLK_MATRIX_MPLL_D4] = GATE("matrix-mpll-d4",
PARENT_ID(ZX_CLK_MATRIX_MPLL_D4_PREGATE), 0x118, 4,
ZX297520V3_AXI_CANDIDATE),
[ZX_CLK_MATRIX_MPLL_D2] = GATE("matrix-mpll-d2",
PARENT_ID(ZX_CLK_MATRIX_MPLL_D2_PREGATE), 0x118, 5, 0),
[ZX_CLK_MATRIX_DPLL_D4] = GATE("matrix-dpll-d4",
PARENT_ID(ZX_CLK_MATRIX_DPLL_D4_PREGATE), 0x118, 8, 0),
[ZX_CLK_MATRIX_DPLL_D2] = GATE("matrix-dpll-d2",
PARENT_ID(ZX_CLK_MATRIX_DPLL_D2_PREGATE), 0x118, 9, 0),
[ZX_CLK_MATRIX_GPLL_D2_D4] = GATE("matrix-gpll-d2-d4",
PARENT_ID(ZX_CLK_MATRIX_GPLL_D2_D4_PREGATE),
0x118, 12, 0),
[ZX_CLK_MATRIX_GPLL_D2_D2] = GATE("matrix-gpll-d2-d2",
PARENT_ID(ZX_CLK_MATRIX_GPLL_D2_D2_PREGATE),
0x118, 13, 0),
[ZX_CLK_CPU_MUX] = MUX("cpu-mux", cpu_sel, 0x20, 0, 2),
[ZX_CLK_ZSP_MUX] = MUX("zsp-mux", zsp_sel, 0x30, 0, 2),
[ZX_CLK_DDR_CTRL_MUX] = MUX("ddr-ctrl-mux", ddr_ctrl_sel, 0x50, 0, 2),
[ZX_CLK_SD0_MUX] = MUX("sd0-mux", sd0_sel, 0x50, 4, 3),
[ZX_CLK_SD1_MUX] = MUX("sd1-mux", sd1_sel, 0x50, 8, 3),
[ZX_CLK_NAND_MUX] = MUX("nand-mux", nand_sel, 0x50, 12, 2),
[ZX_CLK_EDCP_MUX] = MUX("edcp-mux", edcp_sel, 0x50, 16, 2),
[ZX_CLK_TDM_MUX] = MUX("tdm-mux", tdm_sel, 0x50, 24, 2),
[ZX_CLK_AXI_MUX] = MUX("axi-mux", axi_sel, ZX297520V3_AXI_AP_VOTE, 0, 3),
/*
* This bit cuts off the clock signal to the ARM architected timer, which the kernel uses
* as its main timer. It isn't critical per se - there are plenty of proprietary timers
* available that could be used - but the arm arch timer binding does not accept a clock, so
* this CCF driver won't know if the timer is in use.
*
* This clock is fed by the osc26m gate in topcrm+0x140 - this critical clock here protects
* its parent.
*
* Registers 0x148, 0x14c, 0x150, 0x154 allow setting and clearing the timer counter. To
* set the timer, load a value into 0x148 (high 32 bits) and 0x14c (low 32 bits), then
* toggle 0x150 to 1. 0x150 will automatically reset to 0. 0x154 sets the timer to some
* "clear" value, but this value isn't 0.
*/
[ZX_CLK_SYS_TIMER_WCLK] = GATE("sys-timer-wclk", PARENT_FW("wclk-osc26m"), 0x144, 1,
CLK_IS_CRITICAL),
/*
* Both 0x24 and 0x28 bits 1 and 2 stop the CPU. There is also a bit in topcrm+0x138, which
* ZTE's uboot calls "A53 reset", which also stops the CPU. I can't really tell the
* difference between matrix+0x28 and top+0x138. The clock (matrix+0x24) can be disabled
* and enabled from the Cortex M0 and it will nicely stop and restart the A53, retaining
* all state.
*/
[ZX_CLK_CPU_WCLK] = GATE("cpu-wclk", PARENT_ID(ZX_CLK_CPU_MUX), 0x24, 1,
CLK_IS_CRITICAL),
[ZX_CLK_CPU_PCLK] = GATE("cpu-pclk", clk_main[0], 0x24, 2, CLK_IS_CRITICAL),
/*
* There are a lot more controls in matrix+0x100. 13-16 appear to be 4 different AXI
* channels for different priorities. Bit 19 appears to be the DDR PHY wclk.
*
* The important task is to keep gpll enabled if the bootloader selected a gpll-based rate
* for RAM.
*/
[ZX_CLK_DDR_CTRL_PCLK] = GATE("ddr-ctrl-pclk", clk_main[0], 0x100, 17, CLK_IS_CRITICAL),
[ZX_CLK_DDR_CTRL_WCLK] = GATE("ddr-ctrl-wclk", PARENT_ID(ZX_CLK_DDR_CTRL_MUX), 0x100, 18,
CLK_IS_CRITICAL),
[ZX_CLK_ZSP_PCLK] = GATE("zsp-pclk", clk_main[0], 0x34, 0, 0),
[ZX_CLK_ZSP_WCLK] = GATE("zsp-wclk", PARENT_ID(ZX_CLK_ZSP_MUX), 0x34, 1, 0),
/* Both SDIO controllers depend on the ahb-pclk for register access. */
[ZX_CLK_SD0_PCLK] = GATE("sd0-pclk", PARENT_FW("ahb"), 0x54, 12, 0),
[ZX_CLK_SD0_WCLK] = GATE("sd0-wclk", PARENT_ID(ZX_CLK_SD0_MUX), 0x54, 13, 0),
[ZX_CLK_SD0_CDET] = GATE("sd0-cdet", PARENT_FW("osc32k"), 0x54, 14, 0),
[ZX_CLK_SD1_PCLK] = GATE("sd1-pclk", PARENT_FW("ahb"), 0x54, 4, 0),
[ZX_CLK_SD1_WCLK] = GATE("sd1-wclk", PARENT_ID(ZX_CLK_SD1_MUX), 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.
*/
[ZX_CLK_SD1_CDET] = GATE("sd1-cdet", PARENT_FW("osc32k"), 0x54, 6, 0),
/* This is some "denali" NAND, not the qspi connected one */
[ZX_CLK_NAND_WCLK] = GATE("nand-wclk", PARENT_ID(ZX_CLK_NAND_MUX), 0x54, 20, 0),
[ZX_CLK_NAND_PCLK] = GATE("nand-pclk", clk_main[0], 0x54, 21, 0),
/*
* There is a set of gates for an unknown device at matrix+0x60. It is some LTE related
* device and the pclk gates off mmio areas 0xf8000000 and 0xfc000000. These areas are only
* accessible to M0 and ZSP, not the AP.
*
* If it is fed an incorrect clock, e.g. because dpll is not locked and outputs 26 MHz, it
* will spam IRQs at SPI 68. This is likely a distress notification that some
* internal PLL did not lock or similar. ZTE's kernel has two defines for SPI 68:
* "LTE_TPU_INT_PS_INT" and "VOU_OSD_INT". The latter is unlikely because VOU related clocks
* sit in matrix+0x168.
*
* The address space in 0xfc000000 appears to contain a number of different devices, so I
* have named the clock LTE_MODEM_{W/P}CLK. It is not known how this mmio area and clock
* relates to SPI 68 or the ABB clock in topcrm+0x144. Because it is not accessible to the
* AP at all, I don't think we need to care beyond turning on the clocks before booting the
* ZSP.
*
* Bit 9 in register 0x118 turns the IRQs off, so it is the immediate wclk parent. It isn't
* clear what divisor of dpll it is using though. Following the pattern that lower
* frequencies are gated by lower bits, it would indicate dpll/2 or dpll/3.
*/
[ZX_CLK_LTE_MODEM_WCLK] = GATE("lte-modem-wclk", PARENT_ID(ZX_CLK_MATRIX_DPLL_D2),
0x60, 0, 0),
[ZX_CLK_LTE_MODEM_PCLK] = GATE("lte-modem-pclk", clk_main[0],
0x60, 2, 0),
[ZX_CLK_EDCP_PCLK] = GATE("edcp-pclk", clk_main[0], 0x64, 1, 0),
[ZX_CLK_EDCP_WCLK] = GATE("edcp-wclk", PARENT_ID(ZX_CLK_EDCP_MUX), 0x64, 2, 0),
/*
* This seems to be another SPI-Like device. cpko.ko uses it to send calibration blobs to
* the LTE radio hardware. After initialization the ZSP firmware takes over control of this
* device.
*
* Testing reveals the parent as matrix's 104 MHz mpll fraction.
*/
[ZX_CLK_RFFE_WCLK] = GATE("rffe-wclk", PARENT_ID(ZX_CLK_MATRIX_MPLL_D6), 0x84, 0, 0),
[ZX_CLK_SSC_WCLK] = GATE("ssc-wclk", PARENT_ID(ZX_CLK_MATRIX_MPLL_D6), 0x84, 1, 0),
[ZX_CLK_SSC_PCLK] = GATE("ssc-pclk", clk_main[0], 0x84, 2, 0),
/*
* PDCFG. Like PMM, either clock bit will allow the device to function. Probably there is
* no wclk line at all and the two bits are just an artifact of generally having two bits
* per device.
*/
[ZX_CLK_PDCFG_WCLK] = GATE("pdcfg-wclk", matrix_unk, 0x88, 0, CLK_IS_CRITICAL),
[ZX_CLK_PDCFG_PCLK] = GATE("pdcfg-pclk", clk_main[0], 0x88, 1, CLK_IS_CRITICAL),
[ZX_CLK_MBOX_PCLK] = GATE("mbox-pclk", clk_main[0], 0x88, 2, 0),
[ZX_CLK_SRAM0_PCLK] = GATE("sram0-pclk", clk_main[0], 0x88, 4, 0),
[ZX_CLK_GSM_CFG_PCLK] = GATE("gsm-cfg-pclk", clk_main[0], 0x88, 8, 0),
/*
* ZTE's driver has a statement 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+0x110 and lists the parents of these clocks as 50 MHz gpll output, but
* the downstream ZTE GMAC driver never enables the clocks. It turns out ZTE's code is
* highly misleading.
*
* The GMAC's work clock is definitely not any gpll output because it keeps working fine
* with gpll disabled. Gating off matrix-osc26m breaks GMAC, so it must be its 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.
*
* The gmac-ahb clock is a child of topcrm's ahb-pclk and is required for DMA transfers in
* and out of GMAC. Without it, GMAC still receives ethernet frames, but can't drain them
* to memory and will eventually set the RX FIFO overflow flag.
*/
[ZX_CLK_GMAC_WCLK] = GATE("gmac-wclk", PARENT_FW("wclk-osc26m"), 0x110, 0, 0),
[ZX_CLK_GMAC_PCLK] = GATE("gmac-pclk", clk_main[0], 0x110, 1, 0),
[ZX_CLK_GMAC_AHB] = GATE("gmac-ahb", PARENT_FW("ahb"), 0x110, 2, 0),
/*
* Is there an AXI bus gate? The symptom of cutting off the AXI mux selection in top+0x140
* is that matrixcrm becomes unreadable from m0 and A53 hangs. Inside matrix itself only 3
* bits fit that bill: 0x8c bits 5, 6, 7. It seems a bit self-defeating to have a clock
* gate that shuts off access to itself though. I expect a clock gate for the bus
* somewhere, and the mux exists, so exposing one AXI clock in the bindings is the correct
* thing to do. It also serves to tell the kernel to keep the mux's parent enabled.
*
* Register 0x8c has 12 settable bits (0xfff). Ultimately it doesn't matter much which
* do-not-remove bit we don't remove. Other bits in this register behave like gates (e.g
* bits 11:8 cut off USB temporarily), so I think we are looking in the right place.
*
* If an explanation for the remaining bits surfaces and they are further gates and/or
* resets, add them to the bindings.
*/
[ZX_CLK_AXI_WCLK] = GATE("axi-wclk", PARENT_ID(ZX_CLK_AXI_MUX), 0x8c, 5,
CLK_IS_CRITICAL),
[ZX_CLK_DMA_PCLK] = GATE("dma-pclk", clk_main[0], 0x94, 3, 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.
*/
[ZX_CLK_VOU_WCLK] = GATE("vou-wclk", matrix_unk, 0x168, 0, 0),
[ZX_CLK_VOU_PCLK] = GATE("vou-pclk", clk_main[0], 0x168, 1, 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 26 MHz and 32 kHz 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 matrixcrm and gets passed down. I2S has a mux in LSP, which can select the dpll-d4
* clock.
*/
[ZX_CLK_TOLSP_MPLL_D5_WCLK] = GATE("lsp-mpll-d5", PARENT_FW("mpll-d5"), 0x7c, 0, 0),
[ZX_CLK_TOLSP_MPLL_D4_WCLK] = GATE("lsp-mpll-d4", PARENT_ID(ZX_CLK_MATRIX_MPLL_D4),
0x7c, 1, 0),
[ZX_CLK_TOLSP_MPLL_D6_WCLK] = GATE("lsp-mpll-d6", PARENT_ID(ZX_CLK_MATRIX_MPLL_D6),
0x7c, 2, 0),
[ZX_CLK_TOLSP_MPLL_D8_WCLK] = GATE("lsp-mpll-d8", PARENT_ID(ZX_CLK_MATRIX_MPLL_D8),
0x7c, 3, 0),
[ZX_CLK_TOLSP_MPLL_D12_WCLK] = GATE("lsp-mpll-d12", PARENT_ID(ZX_CLK_MATRIX_MPLL_D12),
0x7c, 4, 0),
[ZX_CLK_TOLSP_OSC26M_WCLK] = GATE("lsp-osc26m", PARENT_FW("wclk-osc26m"), 0x7c, 5, 0),
[ZX_CLK_TOLSP_OSC32K_WCLK] = GATE("lsp-osc32k", PARENT_FW("osc32k"), 0x7c, 6, 0),
[ZX_CLK_TOLSP_PCLK] = GATE("lsp-pclk", clk_main[0], 0x7c, 7, 0),
[ZX_CLK_TOLSP_TDM_WCLK] = GATE("lsp-tdm-wclk", PARENT_ID(ZX_CLK_TDM_MUX), 0x7c, 8, 0),
[ZX_CLK_TOLSP_DPLL_D4_WCLK] = GATE("lsp-dpll-d4", PARENT_ID(ZX_CLK_MATRIX_DPLL_D4),
0x7c, 9, 0),
};
static int zx297520v3_matrix_init(struct regmap *map)
{
u32 axi_val;
int res;
/* Read the global AXI speed selection, insert it into our ballot and enable voting */
res = regmap_read(map, 0x0, &axi_val);
if (res)
return res;
axi_val &= 0x7;
res = regmap_write(map, ZX297520V3_AXI_AP_VOTE, axi_val);
if (res)
return res;
return regmap_write(map, 0x12c, 1);
}
static const unsigned int zx297520v3_matrix_exports[] = {
[ZX297520V3_SYS_TIMER_WCLK] = ZX_CLK_SYS_TIMER_WCLK,
[ZX297520V3_CPU_WCLK] = ZX_CLK_CPU_WCLK,
[ZX297520V3_CPU_PCLK] = ZX_CLK_CPU_PCLK,
[ZX297520V3_ZSP_WCLK] = ZX_CLK_ZSP_WCLK,
[ZX297520V3_ZSP_PCLK] = ZX_CLK_ZSP_PCLK,
[ZX297520V3_DDR_CTRL_PCLK] = ZX_CLK_DDR_CTRL_PCLK,
[ZX297520V3_DDR_CTRL_WCLK] = ZX_CLK_DDR_CTRL_WCLK,
[ZX297520V3_EDCP_WCLK] = ZX_CLK_EDCP_WCLK,
[ZX297520V3_EDCP_PCLK] = ZX_CLK_EDCP_PCLK,
[ZX297520V3_SD0_WCLK] = ZX_CLK_SD0_WCLK,
[ZX297520V3_SD0_PCLK] = ZX_CLK_SD0_PCLK,
[ZX297520V3_SD0_CDET] = ZX_CLK_SD0_CDET,
[ZX297520V3_SD1_WCLK] = ZX_CLK_SD1_WCLK,
[ZX297520V3_SD1_PCLK] = ZX_CLK_SD1_PCLK,
[ZX297520V3_SD1_CDET] = ZX_CLK_SD1_CDET,
[ZX297520V3_NAND_WCLK] = ZX_CLK_NAND_WCLK,
[ZX297520V3_NAND_PCLK] = ZX_CLK_NAND_PCLK,
[ZX297520V3_DMA_PCLK] = ZX_CLK_DMA_PCLK,
[ZX297520V3_MBOX_PCLK] = ZX_CLK_MBOX_PCLK,
[ZX297520V3_PDCFG_WCLK] = ZX_CLK_PDCFG_WCLK,
[ZX297520V3_PDCFG_PCLK] = ZX_CLK_PDCFG_PCLK,
[ZX297520V3_RFFE_WCLK] = ZX_CLK_RFFE_WCLK,
[ZX297520V3_SSC_WCLK] = ZX_CLK_SSC_WCLK,
[ZX297520V3_SSC_PCLK] = ZX_CLK_SSC_PCLK,
[ZX297520V3_AXI_WCLK] = ZX_CLK_AXI_WCLK,
[ZX297520V3_GMAC_WCLK] = ZX_CLK_GMAC_WCLK,
[ZX297520V3_GMAC_PCLK] = ZX_CLK_GMAC_PCLK,
[ZX297520V3_GMAC_AHB] = ZX_CLK_GMAC_AHB,
[ZX297520V3_VOU_WCLK] = ZX_CLK_VOU_WCLK,
[ZX297520V3_VOU_PCLK] = ZX_CLK_VOU_PCLK,
[ZX297520V3_LSP_MPLL_D5_WCLK] = ZX_CLK_TOLSP_MPLL_D5_WCLK,
[ZX297520V3_LSP_MPLL_D4_WCLK] = ZX_CLK_TOLSP_MPLL_D4_WCLK,
[ZX297520V3_LSP_MPLL_D6_WCLK] = ZX_CLK_TOLSP_MPLL_D6_WCLK,
[ZX297520V3_LSP_MPLL_D8_WCLK] = ZX_CLK_TOLSP_MPLL_D8_WCLK,
[ZX297520V3_LSP_MPLL_D12_WCLK] = ZX_CLK_TOLSP_MPLL_D12_WCLK,
[ZX297520V3_LSP_OSC26M_WCLK] = ZX_CLK_TOLSP_OSC26M_WCLK,
[ZX297520V3_LSP_OSC32K_WCLK] = ZX_CLK_TOLSP_OSC32K_WCLK,
[ZX297520V3_LSP_PCLK] = ZX_CLK_TOLSP_PCLK,
[ZX297520V3_LSP_TDM_WCLK] = ZX_CLK_TOLSP_TDM_WCLK,
[ZX297520V3_LSP_DPLL_D4_WCLK] = ZX_CLK_TOLSP_DPLL_D4_WCLK,
[ZX297520V3_SRAM0_PCLK] = ZX_CLK_SRAM0_PCLK,
[ZX297520V3_GSM_CFG_PCLK] = ZX_CLK_GSM_CFG_PCLK,
[ZX297520V3_LTE_MODEM_WCLK] = ZX_CLK_LTE_MODEM_WCLK,
[ZX297520V3_LTE_MODEM_PCLK] = ZX_CLK_LTE_MODEM_PCLK,
};
static const struct zx_clk_data zx297520v3_matrixclk_data = {
.init = zx297520v3_matrix_init,
.clocks = zx297520v3_matrix_clocks,
.num_clocks = ARRAY_SIZE(zx297520v3_matrix_clocks),
.exports = zx297520v3_matrix_exports,
.num_exports = ARRAY_SIZE(zx297520v3_matrix_exports),
};
static int clk_zx297520v3_probe(struct platform_device *pdev)
{
const struct platform_device_id *id = platform_get_device_id(pdev);
@ -828,6 +1350,10 @@ static const struct platform_device_id clk_zx297520v3_ids[] = {
.name = "zx297520v3-topclk",
.driver_data = (kernel_ulong_t)&zx297520v3_topclk_data,
},
{
.name = "zx297520v3-matrixclk",
.driver_data = (kernel_ulong_t)&zx297520v3_matrixclk_data,
},
{ }
};
MODULE_DEVICE_TABLE(platform, clk_zx297520v3_ids);