From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CE07A33EAEC for ; Sun, 10 May 2026 21:50:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778449826; cv=none; b=Ori4fCcKjNCp9waGf34BFNAD6iWWguY8gZFbC5MOY+YshTAsYBW4hYKO3Uu5l0PdU0HSfYoEEnRbVbb5qYemFXa9aQrHv7JipVI5/iKPhxj4ATy9kBniAowZdZXZsRACKSQA/Xk6zgNvKLxQgqHgDXdlmqsPEImRJD32pvviokk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778449826; c=relaxed/simple; bh=pxOX5J9a6t3qJy8x/9h97yQyy0Kh8VtqmvNPfJ6QVj8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BywKAZw6+R6aRVT3/+XprEAFdD9DrLxK/jlAckzvXRE1K10tkyfmW/r7dZzQB/bFqbb7YxQ+wad88L3FJAhQLhUqAFEQFoQ5YZa9RqCfbovap67g8det33vXAYiq9BJI8OFe6A33MTqAyKqG8NEb04PsMAC8D8ZZstLfjZamwYE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HOTV1Cao; arc=none smtp.client-ip=209.85.221.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HOTV1Cao" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-43d7e23defbso2033391f8f.0 for ; Sun, 10 May 2026 14:50:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778449823; x=1779054623; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LRkOYhMC8sbVplv828DowOEo6OjLCAbHbvcg1bLlquU=; b=HOTV1CaocnKdA0yU3II0m6jgjejEBM8hAInoR2XI9fc2J2C0W1Mhp9MCIzgxxraA6d Xs11XgaIg+i0U9eLV4e0DY83xQw/SUccdk6mBj9Qhwk8mP5qf0FfdDbweIwp6cOp5qgC WRxN+5VO7WpaCjzdFWsENjcy33HVb8DpBagBYQSOKbd24QZZ1idlfVU0THq7WUNilRTB 0L6ipE1Sl8h1Pmia1LzWkjI/cIBOr4/O1cNv1OUAkUYj/fui6UVG8kBFsseocRoVVYbc 5b8D/iqiU6GfC3O3Tv2YNJf3M0ylFAIGtYOcAiT5xdF8n103lkd3dLywHwAmyAi2r/qr lSQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778449823; x=1779054623; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LRkOYhMC8sbVplv828DowOEo6OjLCAbHbvcg1bLlquU=; b=WHWtiucSNZkwSL20bW/HJfbTw8keGkkoTYCRBnhK35HsTwNhT51Khh7Nc9NI7ij0CE kh9zyn6Sid4sZMVJostClGZ1MTNBTR2AVAcMMLJdDdUVFi4p9KMyJAN6/Xn/VrT+FtbE svqrfA7L3GL6bFQID1pDeYSQGOQQqqjh2CTZeoWTzKvTrdgneWD7Lg+8lSGWKaqKkg8B Wr49s7WlAyExtRcE+O98mypHzaYlt5F6Q8hQWQsO/iKYBw9QzpxI9rxvaWkoYs+bmc+Y VAIKBVcGlWkQndxMQAK0lVl4odrbtoFUUL4CDqVhNFHo/jsJG27HEtsi4wk55WGPdBHy MTrg== X-Forwarded-Encrypted: i=1; AFNElJ+wy4OiJ3zJo+FZBZQ+mERH4ulbO+IRwk864q5GvKIpVcIGfjx5/9Fqfi3Lp2v8ozSrXwnysGuyaeY5@vger.kernel.org X-Gm-Message-State: AOJu0YwycBvy+uru2/JYZ2jQM3//np4Hn+waEjD1fr378m47tn6d0Heu Abg+aK4IOV2FOXU1QRbeOOr1GHvocm1DL8NUvOKAtlcI+RqOEcnkmwDm X-Gm-Gg: Acq92OEhB6fs0lGGRO+gobPcWvxc1ERhxRDoPbFEkudS53DmGGZU6w3dk/HGIk9IvqR 8lcxOvGUAzJ4d/6a/5+z+esMjrWX58cCww1GZ9D8B5yWafqcm3GcHq/6hunmSZdU5HrTRP6Tm+S WpQe2ovs+9FnJT1H+wXcGRhXxA++swXBf0Juu3tx7ji6f6tuZfkXWA4Mu7S6xEClTcvzwxsP1nD yNEzPMBD4MUd/4FsTiXmt+KkC9BxJjcLlPYazyp/NCz2NVBGZV/rF9szNLPns43xGvHx2f1qtRJ EKI9SoOOyT9s34MM7L81Yq7/Qo54/P1yLbUtOjtcIOS4n0RdvJdLO+o7asTjDOkSZDwbSdoCJxB D8BYexKj0fvWAFQZPtX8a0BJLS5TLawL1iBFs2SBR2VWMWag3Y8AssdlYUvBEf3TbX3j/wM1V4h eVmwJh45+jPV+zRWCX5xb/3dCA4GEG/Ak= X-Received: by 2002:a05:6000:144b:b0:43c:f7e5:817b with SMTP id ffacd0b85a97d-4568ae17224mr10528380f8f.19.1778449822930; Sun, 10 May 2026 14:50:22 -0700 (PDT) Received: from [192.168.0.2] ([197.250.51.203]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4548e6a68ebsm19207317f8f.1.2026.05.10.14.50.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 14:50:22 -0700 (PDT) From: =?utf-8?q?Stefan_D=C3=B6singer?= Date: Mon, 11 May 2026 00:49:52 +0300 Subject: [PATCH RFC v2 3/4] clk: zte: Introduce a driver for zx297520v3 matrix clocks and resets. Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit Message-Id: <20260511-zx29clk-v2-3-29f0edc300f5@gmail.com> References: <20260511-zx29clk-v2-0-29f0edc300f5@gmail.com> In-Reply-To: <20260511-zx29clk-v2-0-29f0edc300f5@gmail.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, =?utf-8?q?Stefan_D=C3=B6singer?= X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=10145; i=stefandoesinger@gmail.com; h=from:subject:message-id; bh=pxOX5J9a6t3qJy8x/9h97yQyy0Kh8VtqmvNPfJ6QVj8=; b=owEBiQJ2/ZANAwAIAT0TvMhUTxoiAcsmYgBqAP2SDir1CuTCLtPRJH1i7/B8EcaSN1veBsq4X UO+zqKSJeGJAk8EAAEIADkWIQRDFvS2qgVbJ5UyXWw9E7zIVE8aIgUCagD9khsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQPRO8yFRPGiIIqBAAg+3hXvr7t48dhJ6fUgn5AKr7wTfesco Lv9d17gQZxbkRN8lX6n+4RVPUW5HoDZx9IBIbwPvTNiXIZDtWI2c8TzjtWyV4A6fjjLkNLdnDjR kIYGa78euQsytsHWWRIP+zBci07ISJSb/TLsETo5z+A7PPhV/lytHMkR1X//8FxzdGQmSweAqA+ kFI/uovpnMngGKlgsesWQ0Tx+y/pkGAFQlDB1SNa4/T15TgPpEBhZJOV0DknC+jC3hZU3kvvEuy ECJopaG56alUsUKwMsz3mVQl81jPTg+vztbYQVASuj2uNOUhRaMJznu10X1w2OOAan7+/sbosSO o+mOoVePqPnQDqWboMS5KmB6AbbnGrUSrLfXwMmfSCahhXgf5EXdjlldXhtoMfLix9tzw0psThZ s/39PLck//DbZAWLhDsm0mZ4OhzulAaI6KV+nzVw4UKGI3dqL475eHWAblyhgNKB7R93IexYJgt v9zbnTOEtJpc8V9oeQfOnQh7dpV/CH5vsOlQD1bm4X84v6atTijVSnI9PwICQgioxG+6+Bu6HOS sxu3m4NOdS2AmjphcLlfmc01SloV21iQIAt/wrkCYDRHxlMiSP9zXTZbISWOPif5i7QvXhIeLLN ccoCDBqu+scetD06BvMeugw/05Acnd4o59y3pdqSuLUqSGLt/aGI= X-Developer-Key: i=stefandoesinger@gmail.com; a=openpgp; fpr=4F9C2C8728019633893EBBB98CB81F9A72BBA155 This controls the CPU, DSP, DDR RAM, ethernet, SDIO controllers and a few more devices. It also contains a number of clock gates to pass clock signals down to the next controller. Signed-off-by: Stefan Dösinger --- drivers/clk/zte/clk-zx297520v3.c | 215 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 1 deletion(-) diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx297520v3.c index aa304dd34b7b..d4b683cb6354 100644 --- a/drivers/clk/zte/clk-zx297520v3.c +++ b/drivers/clk/zte/clk-zx297520v3.c @@ -266,7 +266,7 @@ static int zx297520v3_pll(struct device *dev, void __iomem *base, const char *na /* These are the fractionals of the PLLs I have seen. There should be a better way to * generate them than hardcode the list. */ - static const unsigned int pll_fract[] = {2, 3, 4, 5, 6, 8, 12, 26}; + static const unsigned int pll_fract[] = {2, 3, 4, 5, 6, 8, 12, 16, 26}; unsigned long ref, refdiv, fbdiv, vco, postdiv1, postdiv2, freq; struct clk_hw *hw; @@ -578,6 +578,219 @@ static struct platform_driver clk_zx297520v3_topclk = { }; module_platform_driver(clk_zx297520v3_topclk); +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 zx297520v3_composite matrix_clocks[] = { + /* 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 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. + */ + ZX_CLK_CRIT(CPU, 0x28, 1, 0x24, 1, 2, 0x20, 0, 2, cpu_sel, 0, 0, 0), + /* TODO: 0x54 bit 14 and 0x54 bit 6 are supposed to be card detection clocks. */ + ZX_CLK(SD0, 0x58, 1, 0x54, 12, 13, 0x50, 4, 3, sd0_sel, 0, 0, 0), + ZX_CLK(SD1, 0x58, 0, 0x54, 4, 5, 0x50, 8, 3, sd1_sel, 0, 0, 0), + /* This is some "denali" NAND, not the qspi connected one. */ + ZX_CLK(NAND, 0x58, 4, 0x54, 20, 21, 0x50, 12, 2, nand_sel, 0, 0, 0), + ZX_CLK(SSC, 0x94, 24, 0x84, 1, 2, 0, 0, 0, clk_unknown, 0, 0, 0), + ZX_CLK(EDCP, 0x68, 0, 0x64, 2, 1, 0x50, 16, 2, edcp_sel, 0, 0, 0), + /* PDCFG. Like PMM, either clock bit will allow the device to function. */ + ZX_CLK_CRIT(PDCFG, 0x94, 20, 0x88, 0, 1, 0x50, 16, 2, clk_unknown, 0, 0, 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, 0x16c, 0, 0x168, 0, 1, 0, 0, 0, clk_main, 0, 0, 0), +}; + +static const struct zx297520v3_gate matrix_gates[] = { + /* ZTE's driver has a statemt to the effect of *(matrix->base+0x11C) = 5, with a comment + * suggesting that this sets a 50 mhz clock. The clock code itself lists the parents of + * these clock as 50mhz pll output, but the GMAC driver never enables the clocks. + * + * The clocks below are enabled by the boot loader though, so they are on. And it turns + * out that they are necessary for proper operation of the ethernet hardware. As far as + * I can see trough experimentation, bit 1 affects the PHY whereas 0 and 2 affect the + * MAC chip itself. + * + * Chain the wclk and rmii clk together for now. I haven't found a way to make either + * the mdio node or the phy node enable a clock. According to ethernet-phy.yaml it is + * supposed to be possible, but I can't find code to that effect in of_mdio.c. + */ + {ZX297520V3_GMAC_PCLK, "gmac_pclk", "gpll_d4", 0x110, 0}, + {ZX297520V3_GMAC_RMII, "gmac_rmii", "gpll_d4", 0x110, 1}, + {ZX297520V3_GMAC_RMII, "gmac_wclk", "gmac_rmii", 0x110, 2}, + + /* 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 is at matrix+0x3c. + */ + {ZX297520V3_ZSP_WCLK, "zsp_wclk", "osc26m", 0x3c, 0}, + + /* Mailbox. I haven't found a reset for this. It seems to have a PCLK only - turning it off + * makes the MMIO area read 0x0. It looks like it does not need a WCLK. It generates IRQs + * fine with just bit 2 set. Bits 1 and 3 are 0 by default in this register. + */ + {ZX297520V3_MBOX_PCLK, "mbox_pclk", "osc26m", 0x88, 2}, + + /* DMA Controller. It has a reset and PCLK, but no WCLK. */ + {ZX297520V3_DMA_PCLK, "dma_pclk", "osc26m", 0x94, 3}, + + /* There is another clock controlling some "GSM" IP at 0xF3000000 in 0x88, bit 8. It appears + * to be a PCLK, but I have not found a matching WCLK or reset yet. + */ + + /* 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. + * + * This code is commented out until the next patch because disabling unused clocks without + * an LSP consumer breaks the UART. + */ +#if 0 + {ZX297520V3_LSP_MPLL_D5_WCLK, "lsp_mpll_d5", "mpll_d5", 0x7c, 0}, + {ZX297520V3_LSP_MPLL_D4_WCLK, "lsp_mpll_d4", "mpll_d4", 0x7c, 1}, + {ZX297520V3_LSP_MPLL_D6_WCLK, "lsp_mpll_d6", "mpll_d6", 0x7c, 2}, + {ZX297520V3_LSP_MPLL_D8_WCLK, "lsp_mpll_d8", "mpll_d8", 0x7c, 3}, + {ZX297520V3_LSP_MPLL_D12_WCLK, "lsp_mpll_d12", "mpll_d12", 0x7c, 4}, + {ZX297520V3_LSP_OSC26M_WCLK, "lsp_osc26m", "osc26m", 0x7c, 5}, + {ZX297520V3_LSP_OSC32K_WCLK, "lsp_osc32k", "osc32k", 0x7c, 6}, + {ZX297520V3_LSP_PCLK, "lsp_pclk", "osc26m", 0x7c, 7}, + {ZX297520V3_LSP_TDM_WCLK, "lsp_tdm_wclk", "tdm_mux", 0x7c, 8}, + {ZX297520V3_LSP_DPLL_D4_WCLK, "lsp_dpll_d4", "dpll_d4", 0x7c, 9}, +#endif +}; + +static int zx297520_matrixclk_probe(struct platform_device *pdev) +{ + struct zx29_clk_controller *matrix; + struct device *dev = &pdev->dev; + struct clk_hw *hw; + unsigned int i; + int res; + + dev_info(dev, "Registering zx297520v3 matrix clocks\n"); + + matrix = devm_kzalloc(dev, offsetof(struct zx29_clk_controller, + resets[ZX297520V3_MATRIXRST_END]), GFP_KERNEL); + if (!matrix) + return -ENOMEM; + + matrix->clocks = devm_kzalloc(dev, struct_size(matrix->clocks, hws, + ZX297520V3_MATRIXCLK_END), GFP_KERNEL); + if (!matrix->clocks) + return -ENOMEM; + matrix->clocks->num = ZX297520V3_MATRIXCLK_END; + + matrix->base = devm_platform_ioremap_resource(pdev, 0); + WARN_ON(!matrix->base); + + /* One stray mux: The TDM mux is in matrixclk and it is passed to the LSP controller. In a + * way the link gate (LSP_TDM_WCLK) could be considered a matching gate, but there is no + * reset and no pclk. + */ + hw = devm_clk_hw_register_mux(dev, "tdm_mux", tdm_sel, ARRAY_SIZE(tdm_sel), 0, + matrix->base + 0x50, 24, 2, 0, ®_lock); + + res = zx297520v3_composite(dev, matrix->base, matrix->clocks, matrix->resets, + matrix_clocks, ARRAY_SIZE(matrix_clocks)); + if (res) + return res; + + res = zx297520v3_gate(dev, matrix->base, matrix->clocks, + matrix_gates, ARRAY_SIZE(matrix_gates)); + if (res) + return res; + + for (i = 0; i < ZX297520V3_MATRIXCLK_END; i++) { + if (IS_ERR(matrix->clocks->hws[i])) { + pr_err("zx297520 clk %d: register failed with %ld\n", + i, PTR_ERR(matrix->clocks->hws[i])); + return -ENODEV; + } + } + + res = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, matrix->clocks); + if (res) + return res; + + matrix->resets[ZX297520V3_DMA_RESET].reg = matrix->base + 0x70; + matrix->resets[ZX297520V3_DMA_RESET].mask = BIT(0) | BIT(1); + matrix->resets[ZX297520V3_GMAC_RESET].reg = matrix->base + 0x114; + matrix->resets[ZX297520V3_GMAC_RESET].mask = BIT(0) | BIT(1); + + matrix->rcdev.owner = THIS_MODULE; + matrix->rcdev.nr_resets = ZX297520V3_MATRIXRST_END; + matrix->rcdev.ops = &zx297520v3_rst_ops; + matrix->rcdev.of_node = dev->of_node; + return devm_reset_controller_register(dev, &matrix->rcdev); +} + +static const struct of_device_id of_match_zx297520v3_matrixclk[] = { + { .compatible = "zte,zx297520v3-matrixclk"}, + { } +}; +MODULE_DEVICE_TABLE(of, of_match_zx297520v3_matrixclk); + +static struct platform_driver clk_zx297520v3_matrixclk = { + .probe = zx297520_matrixclk_probe, + .driver = { + .name = "clk-zx297520v3-matrixclk", + .of_match_table = of_match_zx297520v3_matrixclk, + }, +}; +module_platform_driver(clk_zx297520v3_matrixclk); + MODULE_AUTHOR("Stefan Dösinger "); MODULE_DESCRIPTION("ZTE zx297520v3 clock driver"); MODULE_LICENSE("GPL"); -- 2.53.0