From nobody Sat Jun 13 04:48:47 2026 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) (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 86DE7320CD3 for ; Sun, 10 May 2026 19:01:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439685; cv=none; b=ZSahi3AAPUjLOkdxoS87onOaj9q36+mxh9CfWhl60dfzWmDFcu5afJNZdmxFHPiHpCZGGK2/1MSuckhK0RthzdoNw7ij+P8/DP76EUwDe5/dSviWU6kmHhjeB9yPeTVj/02c+0HvrU5jo4sqbSkV0i7rKtAU//PNWRabTE+z6rs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439685; c=relaxed/simple; bh=9PBytlOdxzrPJbIqiajYqPTsWWSCL3zrgDIhFWyyBXI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=eczchD1QKmzA5Lt+PdfQPUh+qARv6a6w2aR0JsTPnuiflBW0b7TCrZ7qirKZnRGuwd0bIU3zuekLbZ315mjcoMLCGG11edBPRf0LRQJ1tshcXp6LlGaagE1RuNZQvsk+78zQcD8V86/yH4MuB1BljCUr0uw683PVloUlUFdxg+k= 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=lkcaavAD; arc=none smtp.client-ip=209.85.128.50 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="lkcaavAD" Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-488b0e1b870so60811245e9.2 for ; Sun, 10 May 2026 12:01:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778439682; x=1779044482; 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=1Y4bpMXA9dLIyws/xAA80YyQ4eVqEzh8/1BvF+CshSE=; b=lkcaavADtg0GmI/8oaSRGQgX5zMrlnPX+F0xyL1pacysYRwpjFKHoU0zZq2bT3+REc vev1gyB+2kepSVKUnwu5QrNNsYSNeW9kP5cH/wlapU7UdavMh59NoB3LQ1ej4JmSPsuR eYG3fnKRJlOXLpDgEjctCtwYtW7Cz0FuimzBVCvTiY/kWH1WoaipDPmnREUZXRXLeCxv UqkOHjLAmVY9Qkpjijb4TvmVHW1tL/Ibn7kGApFkngfjhyC8biu1zH19F4ggBi/bQE34 OwSAUa3PJiJbqQ6tpKCTb2CA5do6fB8Hq3FhER9m4C5YZxUz7delU4hMmNN7OGUzUUsH 2Tkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778439682; x=1779044482; 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=1Y4bpMXA9dLIyws/xAA80YyQ4eVqEzh8/1BvF+CshSE=; b=CXO90IxqEnXrnxcTd9GfFZS+R/xptoylr7S4Hr0/iw87K9yR4nC0UNt/fulVpM1t9c EQ2FKhO90zRuL21f28LDC4MR5NkfFs5/zDyYqsaCynAUcdxtsxdhW6CVCqPIj08s4CsC Kwp/Tsv+X/oX9FvYRNcpXnLPUGh7bIwr03cyCGI/LMQCbU6yhOzRx/+P2xQ63VMJ75Sm 4QzskXBx81TEDyBCFkpiK0wAXYXoQdFRfBNzWJnBWJ7xzsiRDfoUZT4Pt/yvXUPkp6Ha jQwIF88XbCVeWeN8oik2stjFyb7bQ/5D4uOlYhwRI0NM+UmsQX2qUJxLeGUWkGJMr47K 6PTQ== X-Forwarded-Encrypted: i=1; AFNElJ8/P+HNe41GCTULWl7WWWDYSB7tr0TBquUdSTsm1TqmElmO1br9gs3wNgzgf1PtsmQWZPGaVlTRy8bZh+Y=@vger.kernel.org X-Gm-Message-State: AOJu0YyvgwKt5n+iEFUjk173tG8jVnW5XaEMRt84Nop0eQknGeYPf0rG nX2bcWJB3DEf97XflgJwzJuh9WxzgmmtNZgLxknEofG8vu0ResLCgJ3l X-Gm-Gg: Acq92OGB85a7wWRUncuBVc9q5F9IW6HCEFHDFNYNnDgPv8lUm/QSFi7LhSPwEBMSuhj LVRkc725sO3z/1ysBksj2OXyKKxYr9X1oS5/fkiV9gUn2JvK+fRILFoc6nU09bN8WFN2tBSr/A/ kvHh0mwDXQHmtfeAymLCuX+MMVlopiFd9iYB3m6v1/zfs3sujsi1ZsM6lGg7VIJsq7QGJSlDxSQ 81M0ymroumMNEU6CSLVpEFyGvqrINtGo2wQgBvSvUwP3VOu6Dep7OC6nv6x3N/52gf1sEQufhMo JTUJVsCCEf4uwJxIo5f8Wi6oAXsko2aKSrqfpdgAWaYAo8bOHTPmyqFPa6G5VBsRgah8klDEwR+ T3TGEjpDxyDR3+KAFtwtVrWlB7jZFpNyat6IBduYkXVJNO7TTgsZosT+3A7dSevCYA3ZRJxTrBq yLeb1CvK1fZ6ORac7b7j21iy09IBd7DvzxCg== X-Received: by 2002:a05:600c:8b06:b0:485:4388:3492 with SMTP id 5b1f17b1804b1-48e51e223ccmr357669875e9.11.1778439681476; Sun, 10 May 2026 12:01:21 -0700 (PDT) Received: from [192.168.0.2] ([197.250.51.203]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e701e9585sm132877475e9.5.2026.05.10.12.01.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 12:01:20 -0700 (PDT) From: =?utf-8?q?Stefan_D=C3=B6singer?= Date: Sun, 10 May 2026 22:01:00 +0300 Subject: [PATCH RFC 1/4] dt-bindings: clk: zte: Add zx297520v3 clock and reset bindings. Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260510-zx29clk-v1-1-e1bacfffe967@gmail.com> References: <20260510-zx29clk-v1-0-e1bacfffe967@gmail.com> In-Reply-To: <20260510-zx29clk-v1-0-e1bacfffe967@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=15475; i=stefandoesinger@gmail.com; h=from:subject:message-id; bh=9PBytlOdxzrPJbIqiajYqPTsWWSCL3zrgDIhFWyyBXI=; b=owEBiQJ2/ZANAwAIAT0TvMhUTxoiAcsmYgBqANX5V03cs0gJ0Oc/d8zFavnicMOpSIpXjDQnx LMcB4+MLv2JAk8EAAEIADkWIQRDFvS2qgVbJ5UyXWw9E7zIVE8aIgUCagDV+RsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQPRO8yFRPGiJSqRAAs+RumGyi9fbbK4DOPc2GaHd1aytK+Jh mFxTUvmuynn91svibBpLBKA2fAXsE8BkMkXSkySHiIz/VdgOUZp0bnBYq0BqhXpHLJhv5tMw4GN Z6w9+vGDW3fO4UogQx7mRzaBe9rLwcEEKgfJf+/WBs/mi6vZ5F7wd8qrbreU0UXkvCZHlWlna5x NUrDskLqaXIVgaULns/EsSLm9h8Dw8idBv0Q8OWqd/ACK+KErb9whwZ/uNLjz0a+KNs6ZVw1lsK qVbtNq8KIis/wuAmTBL/pSnEFP42BzAnsKndFjN3k7aOWBvVsQUWqwlH6WwbRJXtllc+mbOlQ2s wJgGUGkeqmRnPjhWpjqksnrUXS86yKcJ13KzYTtd25Rlf0MxNdw1o6ZuXSTwijZtMuI70wjmS1A 2sylctqm2bU2pjBOLq83RiWsgfpZHBLeWsOzSLpl8dZiPSxhtHwQsSVjWfkzvQBsqtfoa3DkY4x ZvF/xubsa0uGE9QJyEagw15jO3/NoQdEg0g5ABAh2zJ2bi5oTpmAJ0IhYsHK32aI7o1Io4A37vN 5cIMd3tUoyOyLnBtskpjovd7mOXOGBg5u5xwrhclnlM9p4dxI+M7xlXoBn6AvrwXZeydvu52zBJ XrnTljyKnyaHj9bM+yhMtEOKL3MEPxKVIHA9RqHzv/DR33/GfA48= X-Developer-Key: i=stefandoesinger@gmail.com; a=openpgp; fpr=4F9C2C8728019633893EBBB98CB81F9A72BBA155 These SoCs have 3 clock and reset controllers. The "top" controller - all names follow ZTE's naming - controls core devices like the AHB bus, most timers and the Cortex M0 that brings up the board. The register layout is fairly chaotic. Some patterns can be found, but nothing that holds true for all devices it controls. Generally every device has two clocks (one work clock, and one that connects it to the bus, I call it PCLK), two reset bits (I don't know what the difference is - sometimes asserting one is enough to reset the device, sometimes both need to be asserted) and one mux. Some devices, like the GPIO controller, only have reset bits and no clocks. The top clock controller is fed by a 26mhz external oscillator and has 4 PLLs to generate other clock rates. ZTE's kernel does not manipulate the PLLs at all and relies on BROM and the boot loader to set them up. The bitfields in the control registers are somewhat documented in a Lauterback TRACE32 debug file in the kernel sources though. At the moment, my driver extracts clock rates from the PLLs, but cannot change them. A proper PLL clk is on my TODO list before I remove the [RFC] tag from the submission. It will be necessary for the LTE hardware with replacement boot loaders because BROM does not set up the LTE-related PLL. The "matrix" controller controls the main Cortex A53 CPU, the LTE ZSP, SDIO and a few others. It is even more chaotic than the "top" controller. The "LSP" controller - I suspect it stands for "low speed peripherals" - is very regular. One 32 bit register for 2 clock gates, two resets, one mux (1-3 bit) and in some cases a 4 bit divider. Not all clocks will have an explicit user in the end. I am defining a lot of them simply to shut them off. The boot loader sets up a few of the proprietary timers, which will send regular IRQs (although the kernel of course doesn't need to listen to them). I don't plan to add a driver for the proprietary timer as I see no use for them - the ARM arch timer works just fine. I will add a driver for the very similar proprietary watchdog though. The top and matrix list is not exhaustive. There are other bits that are enabled, but I couldn't deduce what they are controlling by trial and error. Some of them seem to do nothing. Others cause an instant hang of the board when disabled. I isolated a few (SRAM PCLK, arm arch timer clock) where I don't see a reason to manipulate them. It is quite likely that a handful more clocks will be added in the future, but not a large number. Signed-off-by: Stefan D=C3=B6singer --- .../bindings/clock/zte,zx297520v3-clk.yaml | 173 +++++++++++++++++= +++ include/dt-bindings/clock/zte,zx297520v3-clk.h | 179 +++++++++++++++++= ++++ 2 files changed, 352 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/zte,zx297520v3-clk.yam= l b/Documentation/devicetree/bindings/clock/zte,zx297520v3-clk.yaml new file mode 100644 index 000000000000..3b7084a18a97 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/zte,zx297520v3-clk.yaml @@ -0,0 +1,173 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/zte,zx297520v3-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ZTE zx297520v3 SoC clock and reset controller + +maintainers: + - Stefan D=C3=B6singer + +description: | + The zx297520v3's clock controller consists of 3 controllers, which gener= ate + clocks for internal SoC devices. In addition to clocks it also has reset + controls for most, but not all, devices. + + While there is a certain hierarchy among the controllers ("top" controll= s core + parts like the boot-up Cortex M0, "matrix" controls the main CPU and LTE= DSP, + "lsp" controls peripherals"), in practise all 3 are required to reasonab= ly + operate the SoC. + + The top controller has two inputs: a 26 MHz and a 32 KHz external oscill= ator. + They need to be provided as input clocks. The matrix controller controll= s 10 + clock lines that get fed into the LSP controller. The LSP device node ne= eds + to list these input clocks. + + The matrix controller consumes clocks generated by PLLs in the top + controller, but there are no controls in the top controller to sever this + link. The interface between these controllers is not expressed in the de= vice + tree, but the matrix controller cannot work without the clock handles + registered by the top controller. + + All available clocks are defined as preprocessor macros in + 'dt-bindings/clock/zte,zx297520v3-clk.h' header. + +properties: + compatible: + enum: + - zte,zx297520v3-topclk + - zte,zx297520v3-matrixclk + - zte,zx297520v3-lspclk + + clocks: + minItems: 2 + maxItems: 10 + + clock-names: + minItems: 2 + maxItems: 10 + + "#clock-cells": + const: 1 + + "#reset-cells": + const: 1 + + reg: + maxItems: 1 + +allOf: + - if: + properties: + compatible: + contains: + const: zte,zx297520v3-topclk + then: + properties: + clocks: + items: + - description: External reference clock (26 MHz) + - description: External reference clock (32 KHz) + clock-names: + items: + - const: osc26m + - const: osc32k + required: + - clocks + - clock-names + + - if: + properties: + compatible: + contains: + const: zte,zx297520v3-lspclk + then: + properties: + clocks: + items: + - description: Main PLL divided by 5 output from matrixclk (12= 4.8 MHz) + - description: Main PLL divided by 4 output from matrixclk (15= 6 MHz) + - description: Main PLL divided by 6 output from matrixclk (10= 4 MHz) + - description: Main PLL divided by 8 output from matrixclk (78= MHz) + - description: Main PLL divided by 12 output from matrixclk (5= 2 MHz) + - description: Main oscillator output from matrixclk (26 MHz) + - description: Timer oscillator output from matrixclk (32 KHz) + - description: LSP pclk output from matrixclk (26 MHz) + - description: TDM wclk mux output from matrixclk + - description: DPLL divided by 4 output from matrixclk (122.88= MHz) + clock-names: + items: + - const: mpll_d5 + - const: mpll_d4 + - const: mpll_d6 + - const: mpll_d8 + - const: mpll_d12 + - const: osc26m + - const: osc32k + - const: pclk + - const: tdm_wclk + - const: dpll_d4 + required: + - clocks + - clock-names + +additionalProperties: false + +required: + - compatible + - '#clock-cells' + - reg + - '#reset-cells' + +examples: + - | + #include + + osc26m: osc26m { + compatible =3D "fixed-clock"; + clock-output-names =3D "osc26m"; + #clock-cells =3D <0>; + }; + + osc32k: osc32k { + compatible =3D "fixed-clock"; + clock-output-names =3D "osc32k"; + #clock-cells =3D <0>; + }; + + topclk: topclk@13b000 { + compatible =3D "zte,zx297520v3-topclk"; + reg =3D <0x0013b000 0x400>; + #clock-cells =3D <1>; + #reset-cells =3D <1>; + clocks =3D <&osc26m>, <&osc32k>; + clock-names =3D "osc26m", "osc32k"; + }; + + matrixclk: matrixclk@1306000 { + compatible =3D "zte,zx297520v3-matrixclk"; + reg =3D <0x01306000 0x400>; + #clock-cells =3D <1>; + #reset-cells =3D <1>; + }; + + lspclk: lspclk@1400000 { + compatible =3D "zte,zx297520v3-lspclk"; + reg =3D <0x01400000 0x100>; + #clock-cells =3D <1>; + #reset-cells =3D <1>; + + clocks =3D <&matrixclk ZX297520V3_LSP_MPLL_D5_WCLK>, + <&matrixclk ZX297520V3_LSP_MPLL_D4_WCLK>, + <&matrixclk ZX297520V3_LSP_MPLL_D6_WCLK>, + <&matrixclk ZX297520V3_LSP_MPLL_D8_WCLK>, + <&matrixclk ZX297520V3_LSP_MPLL_D12_WCLK>, + <&matrixclk ZX297520V3_LSP_OSC26M_WCLK>, + <&matrixclk ZX297520V3_LSP_OSC32K_WCLK>, + <&matrixclk ZX297520V3_LSP_PCLK>, + <&matrixclk ZX297520V3_LSP_TDM_WCLK>, + <&matrixclk ZX297520V3_LSP_DPLL_D4_WCLK>; + clock-names =3D "mpll_d5", "mpll_d4", "mpll_d6", "mpll_d8", "mpll_= d12", + "osc26m", "osc32k", "pclk", "tdm_wclk", "dpll_d4"; + }; diff --git a/include/dt-bindings/clock/zte,zx297520v3-clk.h b/include/dt-bi= ndings/clock/zte,zx297520v3-clk.h new file mode 100644 index 000000000000..322b53be8b12 --- /dev/null +++ b/include/dt-bindings/clock/zte,zx297520v3-clk.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) Stefan D=C3=B6singer. + */ + +#ifndef __DT_BINDINGS_CLOCK_ZX297520V3_H +#define __DT_BINDINGS_CLOCK_ZX297520V3_H + +#define ZX297520V3_AHB_WCLK 0 +#define ZX297520V3_AHB_PCLK 1 +#define ZX297520V3_PMM_WCLK 2 +#define ZX297520V3_PMM_PCLK 3 +#define ZX297520V3_USB_24M 4 +#define ZX297520V3_USB_AHB 5 +#define ZX297520V3_TIMER_T08_WCLK 6 +#define ZX297520V3_TIMER_T08_PCLK 7 +#define ZX297520V3_TIMER_T09_WCLK 8 +#define ZX297520V3_TIMER_T09_PCLK 9 +#define ZX297520V3_TIMER_T12_WCLK 10 +#define ZX297520V3_TIMER_T12_PCLK 11 +#define ZX297520V3_TIMER_T13_WCLK 12 +#define ZX297520V3_TIMER_T13_PCLK 13 +#define ZX297520V3_TIMER_T14_WCLK 14 +#define ZX297520V3_TIMER_T14_PCLK 15 +#define ZX297520V3_TIMER_T15_WCLK 16 +#define ZX297520V3_TIMER_T15_PCLK 17 +#define ZX297520V3_TIMER_T16_WCLK 18 +#define ZX297520V3_TIMER_T16_PCLK 19 +#define ZX297520V3_TIMER_T17_WCLK 20 +#define ZX297520V3_TIMER_T17_PCLK 21 +#define ZX297520V3_WDT_T18_WCLK 22 +#define ZX297520V3_WDT_T18_PCLK 23 +#define ZX297520V3_UART0_WCLK 24 +#define ZX297520V3_UART0_PCLK 25 +#define ZX297520V3_I2C0_WCLK 26 +#define ZX297520V3_I2C0_PCLK 27 +#define ZX297520V3_RTC_WCLK 28 +#define ZX297520V3_RTC_PCLK 29 +#define ZX297520V3_LPM_GSM_WCLK 30 +#define ZX297520V3_LPM_GSM_PCLK 31 +#define ZX297520V3_LPM_LTE_WCLK 32 +#define ZX297520V3_LPM_LTE_PCLK 33 +#define ZX297520V3_LPM_TD_WCLK 34 +#define ZX297520V3_LPM_TD_PCLK 35 +#define ZX297520V3_LPM_W_WCLK 36 +#define ZX297520V3_LPM_W_PCLK 37 +#define ZX297520V3_USIM1_WCLK 38 +#define ZX297520V3_USIM1_PCLK 39 +#define ZX297520V3_M0_WCLK 40 +#define ZX297520V3_TOPCLK_END 41 + +#define ZX297520V3_AHB_RESET 0 +#define ZX297520V3_TIMER_T08_RESET 1 +#define ZX297520V3_TIMER_T09_RESET 2 +#define ZX297520V3_TIMER_T12_RESET 3 +#define ZX297520V3_TIMER_T13_RESET 4 +#define ZX297520V3_TIMER_T14_RESET 5 +#define ZX297520V3_TIMER_T15_RESET 6 +#define ZX297520V3_TIMER_T16_RESET 7 +#define ZX297520V3_TIMER_T17_RESET 8 +#define ZX297520V3_WDT_T18_RESET 9 +#define ZX297520V3_UART0_RESET 10 +#define ZX297520V3_I2C0_RESET 11 +#define ZX297520V3_RTC_RESET 12 +#define ZX297520V3_USIM1_RESET 13 +#define ZX297520V3_PMM_RESET 14 +#define ZX297520V3_GPIO8_RESET 15 +#define ZX297520V3_GPIO_RESET 16 +#define ZX297520V3_ZSP_RESET 17 +#define ZX297520V3_USB_RESET 18 +#define ZX297520V3_TOPRST_END 19 + +#define ZX297520V3_CPU_WCLK 0 +#define ZX297520V3_CPU_PCLK 1 +#define ZX297520V3_SD0_WCLK 2 +#define ZX297520V3_SD0_PCLK 3 +#define ZX297520V3_SD1_WCLK 4 +#define ZX297520V3_SD1_PCLK 5 +#define ZX297520V3_SD1_CDET 6 +#define ZX297520V3_NAND_WCLK 7 +#define ZX297520V3_NAND_PCLK 8 +#define ZX297520V3_SSC_WCLK 9 +#define ZX297520V3_SSC_PCLK 10 +#define ZX297520V3_EDCP_WCLK 11 +#define ZX297520V3_EDCP_PCLK 12 +#define ZX297520V3_EDCP_SYNCAXI 13 +#define ZX297520V3_VOU_WCLK 14 +#define ZX297520V3_VOU_PCLK 15 +#define ZX297520V3_PDCFG_WCLK 16 +#define ZX297520V3_PDCFG_PCLK 17 +#define ZX297520V3_GMAC_WCLK 18 +#define ZX297520V3_GMAC_RMII 19 +#define ZX297520V3_GMAC_PCLK 20 +#define ZX297520V3_ZSP_WCLK 21 +#define ZX297520V3_MBOX_PCLK 22 +#define ZX297520V3_DMA_PCLK 23 +#define ZX297520V3_LSP_MPLL_D5_WCLK 24 +#define ZX297520V3_LSP_MPLL_D4_WCLK 25 +#define ZX297520V3_LSP_MPLL_D6_WCLK 26 +#define ZX297520V3_LSP_MPLL_D8_WCLK 27 +#define ZX297520V3_LSP_MPLL_D12_WCLK 28 +#define ZX297520V3_LSP_OSC26M_WCLK 29 +#define ZX297520V3_LSP_OSC32K_WCLK 30 +#define ZX297520V3_LSP_PCLK 31 +#define ZX297520V3_LSP_TDM_WCLK 32 +#define ZX297520V3_LSP_DPLL_D4_WCLK 33 +#define ZX297520V3_MATRIXCLK_END 34 + +#define ZX297520V3_CPU_RESET 0 +#define ZX297520V3_SD0_RESET 1 +#define ZX297520V3_SD1_RESET 2 +#define ZX297520V3_NAND_RESET 3 +#define ZX297520V3_SSC_RESET 4 +#define ZX297520V3_EDCP_RESET 5 +#define ZX297520V3_VOU_RESET 6 +#define ZX297520V3_PDCFG_RESET 7 +#define ZX297520V3_GMAC_RESET 8 +#define ZX297520V3_DMA_RESET 9 +#define ZX297520V3_MATRIXRST_END 10 + +#define ZX297520V3_TIMER_L1_WCLK 0 +#define ZX297520V3_TIMER_L1_PCLK 1 +#define ZX297520V3_WDT_L2_WCLK 2 +#define ZX297520V3_WDT_L2_PCLK 3 +#define ZX297520V3_WDT_L3_WCLK 4 +#define ZX297520V3_WDT_L3_PCLK 5 +#define ZX297520V3_I2C1_WCLK 6 +#define ZX297520V3_I2C1_PCLK 7 +#define ZX297520V3_I2S0_WCLK 8 +#define ZX297520V3_I2S0_PCLK 9 +#define ZX297520V3_I2S1_WCLK 10 +#define ZX297520V3_I2S1_PCLK 11 +#define ZX297520V3_QSPI_WCLK 12 +#define ZX297520V3_QSPI_PCLK 13 +#define ZX297520V3_UART1_WCLK 14 +#define ZX297520V3_UART1_PCLK 15 +#define ZX297520V3_I2C2_WCLK 16 +#define ZX297520V3_I2C2_PCLK 17 +#define ZX297520V3_SPI0_WCLK 18 +#define ZX297520V3_SPI0_PCLK 19 +#define ZX297520V3_TIMER_LB_WCLK 20 +#define ZX297520V3_TIMER_LB_PCLK 21 +#define ZX297520V3_TIMER_LC_WCLK 22 +#define ZX297520V3_TIMER_LC_PCLK 23 +#define ZX297520V3_UART2_WCLK 24 +#define ZX297520V3_UART2_PCLK 25 +#define ZX297520V3_WDT_LE_WCLK 26 +#define ZX297520V3_WDT_LE_PCLK 27 +#define ZX297520V3_TIMER_LF_WCLK 28 +#define ZX297520V3_TIMER_LF_PCLK 29 +#define ZX297520V3_SPI1_WCLK 30 +#define ZX297520V3_SPI1_PCLK 31 +#define ZX297520V3_TIMER_L11_WCLK 32 +#define ZX297520V3_TIMER_L11_PCLK 33 +#define ZX297520V3_TDM_WCLK 34 +#define ZX297520V3_TDM_PCLK 35 +#define ZX297520V3_LSPCLK_END 36 + +#define ZX297520V3_TIMER_L1_RESET 0 +#define ZX297520V3_WDT_L2_RESET 1 +#define ZX297520V3_WDT_L3_RESET 2 +#define ZX297520V3_I2C1_RESET 3 +#define ZX297520V3_I2S0_RESET 4 +#define ZX297520V3_I2S1_RESET 5 +#define ZX297520V3_QSPI_RESET 6 +#define ZX297520V3_UART1_RESET 7 +#define ZX297520V3_I2C2_RESET 8 +#define ZX297520V3_SPI0_RESET 9 +#define ZX297520V3_TIMER_LB_RESET 10 +#define ZX297520V3_TIMER_LC_RESET 11 +#define ZX297520V3_UART2_RESET 12 +#define ZX297520V3_WDT_LE_RESET 13 +#define ZX297520V3_TIMER_LF_RESET 14 +#define ZX297520V3_SPI1_RESET 15 +#define ZX297520V3_TIMER_L11_RESET 16 +#define ZX297520V3_TDM_RESET 17 +#define ZX297520V3_LSPRST_END 18 + +#endif /* __DT_BINDINGS_CLOCK_ZX297520V3_H */ --=20 2.53.0 From nobody Sat Jun 13 04:48:47 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (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 9830F31327D for ; Sun, 10 May 2026 19:01:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439690; cv=none; b=QHzHsFCibhjjAla0gzgMO2T7OgtVgTdy1LFY7CKeuxJXSEm68YhIn/n6sWpiy3CaNlH03II+YWTkAnsodSw6cTlRfVyznkc9baNfDNFy5z9pCsl2kTcqjk+aNBsv9m5v48vngH07m9ux7yX/ne+msM3aE6sgvl+S8pGtwNbExhU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439690; c=relaxed/simple; bh=rPVoVPDaS3h1k6kP6NFFjGExx4MKEzhdrZSmglv4YyQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=noWmA0sLIns8kcMroiYz41OHCcgZuJdpXPC3aX4c6etUJJUXkxFlBHGyhZpsobqGzHk+hG6SlNzveLJ01Ekr/3/lL6gxKRA3cBe46nQq8sQv5f5gJXP5enjB56jaWDPBncGB8/kBekoRhvx+rzrrHQvhRI1zi1X1c8WeHu00jJk= 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=EPG/v7Zc; arc=none smtp.client-ip=209.85.128.51 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="EPG/v7Zc" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4891f625344so34591845e9.0 for ; Sun, 10 May 2026 12:01:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778439686; x=1779044486; 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=y/c53daTmcjY7XWjCYDMdK/Rxigv7taFUcsu1M5+5bU=; b=EPG/v7ZcKGp9+BmFCcBSz5viWP0MGAkC1VUAB4bLelAgGGNs7nGAgxXZcL04wwYueD VioCl9qGEx8y9JBv8+btytcEruVvKyQ3o4p9poBXTavJZ/EarBvjCTbHd+n8iX1lwWzP E8IeMYAdUSTJFJs2xSPaGYZdViihp6WKz0/NlAXwNx0yArptKuymKxjjgkoGisxCHC+d 7w3rLNPe+GzgIXZ3ngCFCkbMqcB6uBjQz3tT9m5g3zie5vxxDDSXcuNyLU8tMMWbJTjJ k/YTCOZt0Gd8KCPdmrrJ8LKKfC9lgx9Q+J14csHGPNYkZ/Iydzn55wI/rI2cyFx13lEb sbpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778439686; x=1779044486; 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=y/c53daTmcjY7XWjCYDMdK/Rxigv7taFUcsu1M5+5bU=; b=M2Nc4lEagncmIWGjZy+h7VMCautSqyVDlkxxFfOqfWTJSGP26r8iUMqXBHQXIMV+8K QOTCvQEFQCv5ze+CKONAb0wjenplfFVBE7ynQwaW3O/5yru8hFLPIm39r06F9O6GtQf+ 02BvoGhDxFv5a6MuianIVB611vqk9Z90ZHU+mLTy5yxZu2fF/gMjtF/XCpW84fbTTMGW o047AptUACEHTN2vFz1EP69+oqtoDjYsycK13Rye6hiz/SYCiFONcom0AWMWpEcpQRe2 MtipZ3jtSmvECCinHaprdQvUL+vjxBwhT2f8N4bmKAjGrIDzHYkQAnV8YJR480c3OAN4 5cvw== X-Forwarded-Encrypted: i=1; AFNElJ9coE88lWa7jEAp4vSLZRujDjKU0cgjtWQt0fBNbbhS/e79q+hqnDB7GVVfK+KBW5eyKkjIn1L3DUPr6ss=@vger.kernel.org X-Gm-Message-State: AOJu0YwCOXteLVEkkfgYUQrkS27KnyeALI4kOnnUxxHTt6a+uIlffd7U 4TnBzaT7IZtw0aG+t4GtJ1XshIM+PTix0SckKQjqHM5SQ65ysNA7AQWD X-Gm-Gg: Acq92OFHeAyxlE4WSPeNPDL+Utgu/hHmNAPu2aMYlcgh4wkQgBX80qOX5UsG6LBDHeW 4lfJWAj/B5X6wi53sM3bvl+q4thUK9fNw/CrJY419gBZcNmBznkfA/XQnt1KYHAAg7s/htdz/jB N927hvESa5L8068hyCLbshNlOjoFjFi04/PcOlbj43VKNC6KbO5mims4OCSvAKRCGeqqilXMF18 YYlI3RJxwalIb0+PE7hIXJsC1+fPnskkMSKeXwUPG5OHTflrNCnPj765pUjmicSla73gnNwuPFW Km0k/2zHaZBgcunZ8FcsIhn4rKPfKmV3XJvggeNqlMWHeCO4DmFLH0Wa5UysJAa5F82+wNCJr6v wTBsadZQOM1PvsoW7wssa+mycF6947wTPPAYSnjmA/F+D1GHUpl3zU7qSdYX6r8OfctzTP2W24A soyJurFG+FZmI0IpJkqG6pngLYd0l2GmEB/UmLpj5zkORO X-Received: by 2002:a05:600c:15cc:b0:48e:635a:18c3 with SMTP id 5b1f17b1804b1-48e635a19afmr113112605e9.4.1778439685505; Sun, 10 May 2026 12:01:25 -0700 (PDT) Received: from [192.168.0.2] ([197.250.51.203]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e701e9585sm132877475e9.5.2026.05.10.12.01.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 12:01:24 -0700 (PDT) From: =?utf-8?q?Stefan_D=C3=B6singer?= Date: Sun, 10 May 2026 22:01:01 +0300 Subject: [PATCH RFC 2/4] clk: zte: Introduce a driver for zx297520v3 top clocks and resets. Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260510-zx29clk-v1-2-e1bacfffe967@gmail.com> References: <20260510-zx29clk-v1-0-e1bacfffe967@gmail.com> In-Reply-To: <20260510-zx29clk-v1-0-e1bacfffe967@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=24952; i=stefandoesinger@gmail.com; h=from:subject:message-id; bh=rPVoVPDaS3h1k6kP6NFFjGExx4MKEzhdrZSmglv4YyQ=; b=owEBiQJ2/ZANAwAIAT0TvMhUTxoiAcsmYgBqANX5+M3jwAhLsjf54UrA5QBnsvk2aLgEhpYRb mekmnArAayJAk8EAAEIADkWIQRDFvS2qgVbJ5UyXWw9E7zIVE8aIgUCagDV+RsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQPRO8yFRPGiKmtQ//ZKhHIwYwypcoesbkA4/OhSZ801r50up dyCnDXGnS2SbvxvFa/2EDs6hSoQbHxLMbBGx4uqJHhl6HsbHe24BMyVF5cpfrkYrSey20zrfvap pBs0Km+0AN8vVisAZxVh0ZE16EqJlh+lqrOQI5YL8LXePRDFFtPI1SjojvuqLJ7S9ZItn+pT6wf sKIRHl8CiBO7+Y0modEgG3Y6kiSD2epY1AMgJr4HUeAk0geiLzFZHvC7FsNw9uruXufzXMWJZXR yQme7OnzHt2KwFBnQNIL8miPDPc0zQKi3Wod8U8xPmwdgvt4YUJqJU01MitZzx4iIL290SE+S12 uVPtIQ8QKXnFjP07o8KX+VZgTj8w8/lO97bv2exZY+YOwqmI1tyBMDW5MGnEznjcSf8NMs7u+JG lLk4z3lcAjqz/SJehTGx1Omw6W+LigHAyLaSS7huELPy505f0fFeTbfaid2uFGritiOXS5jHeIX 2M5e7M708mZwTA4BzGb07NyH7+25rGMc/0MCz5pcGRQ7sIO6S1Kl3s/alclUMsdOoe5A3t0M3pF w5OGHtG2V+Lw/1m2g9fYBb9ByxZ0Cg/KOCftskXl+HSrmZCKhtc0uxECebMShegJUAN9wMmwQHX Z3Y6N5WhbYA3M5iBlRqMdVTj1UxonrILSyxc+H4/p0Hy1KXEqqX0= X-Developer-Key: i=stefandoesinger@gmail.com; a=openpgp; fpr=4F9C2C8728019633893EBBB98CB81F9A72BBA155 This register space controls core devices: PLLs, the AHB bus, a lot of timers, the USB controller, the Cortex M0 processor that boots the board and a few other devices. For some reason the LTE coprocessor is also partially controlled by it. The main application processor and DDR memory are not found here though. The register to reboot the board is also found here. Signed-off-by: Stefan D=C3=B6singer --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/zte/Kconfig | 18 ++ drivers/clk/zte/Makefile | 2 + drivers/clk/zte/clk-zx297520v3.c | 584 +++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 606 insertions(+) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3d803b4cf5c1..971ea6daa2b6 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -539,6 +539,7 @@ source "drivers/clk/visconti/Kconfig" source "drivers/clk/x86/Kconfig" source "drivers/clk/xilinx/Kconfig" source "drivers/clk/zynqmp/Kconfig" +source "drivers/clk/zte/Kconfig" =20 # Kunit test cases config CLK_KUNIT_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f7bce3951a30..c164a3de2b14 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -165,5 +165,6 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_X86) +=3D x86/ endif obj-y +=3D xilinx/ +obj-$(CONFIG_ARCH_ZTE) +=3D zte/ obj-$(CONFIG_ARCH_ZYNQ) +=3D zynq/ obj-$(CONFIG_COMMON_CLK_ZYNQMP) +=3D zynqmp/ diff --git a/drivers/clk/zte/Kconfig b/drivers/clk/zte/Kconfig new file mode 100644 index 000000000000..e7acd28832cd --- /dev/null +++ b/drivers/clk/zte/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# ZTE Clock Drivers +# +menu "Clock driver for ZTE SoC" + depends on ARCH_ZTE || COMPILE_TEST + +config COMMON_CLK_ZX297520V3 + tristate "Clock driver for ZTE zx297520v3" + default SOC_ZX297520V3 + help + This driver supports ZTE zx297520v3 basic clocks. + + Enable this if you want to build a kernel that is able to run on + boards based on this SoC. You can safely enable multiple clock + drivers. The one(s) matching the device tree will be used. + +endmenu diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile new file mode 100644 index 000000000000..3751ebcba0b0 --- /dev/null +++ b/drivers/clk/zte/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_COMMON_CLK_ZX297520V3) +=3D clk-zx297520v3.o diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx29752= 0v3.c new file mode 100644 index 000000000000..f73f5c006641 --- /dev/null +++ b/drivers/clk/zte/clk-zx297520v3.c @@ -0,0 +1,584 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Stefan D=C3=B6singer + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(reg_lock); + +struct zx29_reset_reg { + void __iomem *reg; + u32 mask; +}; + +struct zx29_clk_controller { + void __iomem *base; + struct clk_hw_onecell_data *clocks; + struct reset_controller_dev rcdev; + struct zx29_reset_reg resets[]; +}; + +static int zx297520v3_rst_assert(struct reset_controller_dev *rcdev, unsig= ned long id) +{ + struct zx29_clk_controller *data =3D container_of(rcdev, struct zx29_clk_= controller, rcdev); + u32 val; + + val =3D readl(data->resets[id].reg); + val &=3D ~data->resets[id].mask; + writel(val, data->resets[id].reg); + + return 0; +} + +static int zx297520v3_rst_deassert(struct reset_controller_dev *rcdev, uns= igned long id) +{ + struct zx29_clk_controller *data =3D container_of(rcdev, struct zx29_clk_= controller, rcdev); + u32 val; + + val =3D readl(data->resets[id].reg); + val |=3D data->resets[id].mask; + writel(val, data->resets[id].reg); + + return 0; +} + +static int zx297520v3_rst_reset(struct reset_controller_dev *rcdev, unsign= ed long id) +{ + int ret; + + ret =3D zx297520v3_rst_assert(rcdev, id); + if (ret) + return ret; + + usleep_range(100, 100 * 2); + + return zx297520v3_rst_deassert(rcdev, id); +} + +static int zx297520v3_rst_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct zx29_clk_controller *data =3D container_of(rcdev, struct zx29_clk_= controller, rcdev); + u32 val; + + val =3D readl(data->resets[id].reg); + + return (val & data->resets[id].mask) =3D=3D data->resets[id].mask; +} + +const struct reset_control_ops zx297520v3_rst_ops =3D { + .assert =3D zx297520v3_rst_assert, + .deassert =3D zx297520v3_rst_deassert, + .reset =3D zx297520v3_rst_reset, + .status =3D zx297520v3_rst_status, +}; + +/* Used for gates where we don't know the parent input(s). Assume general = bus clock. */ +static const char * const clk_unknown[] =3D { + "osc26m", +}; + +/* Used for gates where we know it is using the 26 mhz main clock. */ +static const char * const clk_main[] =3D { + "osc26m", +}; + +/* Top and matrix clocks are chaotic - I haven't found a consistent patter= n behind their register + * and bit locations. Generally there are two gates (pclk, wclk), one mux,= one reset and sometimes + * one divider, but exceptions apply. For some devices there is only a res= et and some general + * (parent) clocks need setup. This structure plus macro handles the somew= hat regular parts. + * + * There are some patterns that can be observed. + * mux 0x3c, div 0x48, gate 0x54 + * mux 0x40, div 0x4c, gate 0x5c + * mux 0x44, div 0x50, gate 0x60 + * + * For a 0 - 0xc - 0x18 pattern. Muxes from 0x3c to 0x44, dividers from 0x= 48 to 0x50, gates 0x54 to + * 0x60. The pattern is broken for timer t17 though. + * + * Gates have 4 bits per clock - bit 0 for wclk, bit 1 for pclk, bit 2 for= something the ZTE kernel + * calls "gate" (the bits we use here are called "en"), which I don't know= what it does, and bit 3 + * seems unused. E.g. offset 0x54 accepts all bits in 0xF77F7F7F - suggest= ing RTC, I2C0 have an + * extra gate bit. + */ +struct zx297520v3_composite { + u32 reset_id, wclk_id, pclk_id; + const char *name; + u32 reset_reg, reset_shift; + u32 gate_reg, wclk_gate_shift, pclk_gate_shift; + const char *pclk_parent; + u32 mux_reg, mux_shift, mux_size; + const char * const *mux_sel; + u32 mux_sel_count; + u32 div_reg, div_shift, div_size; + u32 flags; +}; + +struct zx297520v3_gate { + u32 id; + const char *name, *parent; + u32 reg, shift; +}; + +#define _ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_s= hift, pclk_parent,\ + mux_reg, mux_shift, mux_size, mux_sel,\ + div_reg, div_shift, div_size, flags) \ + {ZX297520V3_##name##_RESET, ZX297520V3_##name##_WCLK, ZX297520V3_##name#= #_PCLK,\ + #name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_shift, pclk_p= arent,\ + mux_reg, mux_shift, mux_size, mux_sel, ARRAY_SIZE(mux_sel),\ + div_reg, div_shift, div_size, flags} + +#define ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_sh= ift,\ + mux_reg, mux_shift, mux_size, mux_sel,\ + div_reg, div_shift, div_size) \ + _ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_s= hift, "osc26m",\ + mux_reg, mux_shift, mux_size, mux_sel,\ + div_reg, div_shift, div_size, 0) + +#define ZX_CLK_CRIT(name, reset_reg, reset_shift, gate_reg, wclk_shift, pc= lk_shift,\ + mux_reg, mux_shift, mux_size, mux_sel,\ + div_reg, div_shift, div_size) \ + _ZX_CLK(name, reset_reg, reset_shift, gate_reg, wclk_shift, pclk_s= hift, "osc26m",\ + mux_reg, mux_shift, mux_size, mux_sel,\ + div_reg, div_shift, div_size, CLK_IS_CRITICAL) + +/* The default mpll settings multiply the 26 MHz reference clock times 24.= A mux selection of 26 MHz + * could mean using the 26 MHz oscillator directly, or passing it through = the PLL and divide by 24. + * + * If a UART is set to mpl_d6 (default 104 MHz), changing the mpll multipl= iers does affect UART + * timing as it should. This does not happen when the UART is set to 26 MH= z input or timers that + * read 26 MHz input. This suggests 26 MHz clocks use the reference clock = directly. + */ +static const char * const ahb_sel[] =3D { + "osc26m", + "mpll_d6", /* 104 mhz */ + "mpll_d8", /* 78 mhz */ + "mpll_d8", /* 78 mhz */ +}; + +static const char * const timer_top_sel[] =3D { + "osc32k", + "osc26m", +}; + +static const char * const uart_top_sel[] =3D { + "osc26m", + "mpll_d6", /* 104 mhz */ +}; + +static const char * const m0_sel[] =3D { + "osc26m", + "mpll_d6", /* 104 mhz */ + "mpll_d8", /* 78 mhz */ + "osc32k", /* Yes, tested. It is SLLLLOOOOOWWW. */ +}; + +static const struct zx297520v3_composite top_clocks[] =3D { + /* (NAME, RESET, GATE, MUX, D= IV ), */ + + /* AHB: Don't turn this one off. The clock mux works and impact can be te= sted e.g. with + * iperf speed testing of the USB network connection. Values 2 and 3 give= the same speed. + */ + ZX_CLK_CRIT(AHB, 0x70, 0, 0x54, 12, 13, 0x3c, 4, 2, ahb_sel, 0= , 0, 0), + + /* Pinmux (AON, TOP, IOCFG but not PDCFG). Critical as well until we have= a driver that + * consumes it. I don't think we'll realistically shut this off ever. + * + * Setting either bit 0 or 1 in register 0x58 makes the device work. + */ + ZX_CLK_CRIT(PMM, 0x74, 0, 0x58, 0, 1, 0x00, 0, 0, clk_unknown, 0= , 0, 0), + + /* Timers. We don't use any of them, just shut them off. The timers are n= amed and sorted + * by the IO address of the main timer controls. Some of the controls are= documented in + * ZTE's kernel. Some I found by trial and error. + * + * Timer T17 is used by the ZSP firmware. The rproc driver will enable th= em as needed. + */ + ZX_CLK(TIMER_T08, 0x78, 4, 0x5c, 8, 9, 0x40, 1, 1, timer_top_sel, 0= x4c, 8, 4), + ZX_CLK(TIMER_T09, 0x78, 2, 0x5c, 4, 5, 0x40, 0, 1, timer_top_sel, 0= x4c, 0, 4), + ZX_CLK(TIMER_T12, 0x74, 6, 0x54, 4, 5, 0x3c, 0, 1, timer_top_sel, 0= x48, 0, 4), + ZX_CLK(TIMER_T13, 0x7c, 0, 0x60, 0, 1, 0x44, 0, 1, timer_top_sel, 0= x50, 0, 4), + ZX_CLK(TIMER_T14, 0x7c, 2, 0x60, 4, 5, 0x44, 1, 1, timer_top_sel, 0= x50, 4, 4), + ZX_CLK(TIMER_T15, 0x74, 10, 0x54, 20, 21, 0x3c, 3, 1, timer_top_sel, 0= x48, 4, 4), + ZX_CLK(TIMER_T16, 0x7c, 4, 0x60, 8, 9, 0x44, 2, 1, timer_top_sel, 0= x50, 8, 4), + ZX_CLK(TIMER_T17, 0x12c, 0, 0x128, 0, 1, 0x120, 0, 1, timer_top_sel, 0= x124, 0, 4), + + /* This watchdog is set up by the bootloader and in normal operation the = m0 firmware will + * feed the dog. The m0 firmware in turn wants to be fed in its own way. = Since we normally + * don't run any m0 firmware we shut it off by default and expose it to u= serspace via the + * watchdog driver. + */ + ZX_CLK(WDT_T18, 0x74, 12, 0x54, 24, 25, 0x3c, 6, 1, timer_top_sel, 0= x48, 8, 4), + + ZX_CLK(I2C0, 0x74, 8, 0x54, 8, 9, 0x3c, 1, 1, uart_top_sel, 0= , 0, 0), + ZX_CLK(UART0, 0x78, 6, 0x5c, 12, 13, 0x40, 2, 1, uart_top_sel, 0= , 0, 0), + + /* How does this RTC work? I don't know, the ZTE kernel does not talk to = it. It has an + * external RTC connected to I2C0. + */ + ZX_CLK(RTC, 0x74, 4, 0x54, 0, 1, 0x00, 0, 0, timer_top_sel, 0= , 0, 0), + + /* This doesn't see to be talking to the physical SIM card. I can turn it= off on the ZTE + * firmware without breaking LTE, and the "uicc" IRQ count keeps climbing= . I think this is + * a eSim-like chip that can be provisioned with data at runtime, but I h= ave no idea how to + * do it. + */ + ZX_CLK(USIM1, 0x74, 14, 0x54, 28, 29, 0x00, 0, 0, clk_main, 0= x48, 12, 1), + + /* (NAME, RESET, GATE, MUX, D= IV ), */ +}; + +/* Stand-alone topclk gates. */ +static const struct zx297520v3_gate top_gates[] =3D { + {ZX297520V3_USB_24M, "usb_24m", "mpll_d26", 0x6c, 3}, + {ZX297520V3_USB_AHB, "usb_ahb", "AHB_wclk", 0x6c, 4}, + /* LTE: gate only as far as I can see. I looked for resets and did not fi= nd any. There may + * be mux/div, but without understanding the behavior of this hardware it= is impossible to + * tell. They are sorted by physical MMIO address of the devices, which h= appens to be the + * inverse order of the bits. + * + * I don't know what "LPM", "TD" and "W" mean. I copied them from ZTE's n= ames. + */ + {ZX297520V3_LPM_GSM_WCLK, "LPM_GSM_wclk", clk_unknown[0], 0x58, 10}, + {ZX297520V3_LPM_GSM_PCLK, "LPM_GSM_pclk", clk_unknown[0], 0x58, 11}, + {ZX297520V3_LPM_LTE_WCLK, "LPM_LTE_wclk", clk_unknown[0], 0x58, 8}, + {ZX297520V3_LPM_LTE_PCLK, "LPM_LTE_pclk", clk_unknown[0], 0x58, 9}, + {ZX297520V3_LPM_TD_WCLK, "LPM_TD_wclk", clk_unknown[0], 0x58, 6}, + {ZX297520V3_LPM_TD_PCLK, "LPM_TD_pclk", clk_unknown[0], 0x58, 7}, + {ZX297520V3_LPM_W_WCLK, "LPM_W_wclk", clk_unknown[0], 0x58, 4}, + {ZX297520V3_LPM_W_PCLK, "LPM_W_pclk", clk_unknown[0], 0x58, 5}, + /* There are PCLKs for BROM/SRAM2 in 0x54, bit 16 and SRAM1 in 0x54, bit = 18. Turning them + * off locks up the Cortex M0 coproc. Not added to the kernel until a way= is found to + * recover the Cortex M0 or evidence of power savings. + */ +}; + +static int zx297520v3_pll(struct device *dev, void __iomem *base, const ch= ar *name, + struct clk *parent) +{ + /* These are the fractionals of the PLLs I have seen. There should be a b= etter way to + * generate them than hardcode the list. + */ + static const unsigned int pll_fract[] =3D {2, 3, 4, 5, 6, 8, 12, 26}; + + unsigned long ref, refdiv, fbdiv, vco, postdiv1, postdiv2, freq; + struct clk_hw *hw; + char plldiv[16]; + unsigned int i; + u32 val; + + /* PLLs are configured by the boot rom, we only read their settings to kn= ow how the rate + * of the derived clocks. ZTE's sources explain the PLL register contents= only in a .cmm + * file (A Lauterback TRACE32 script). When calculating the frequencies w= ith the default + * PLL configuration the results match the fixed rate clocks from their c= lock driver. + * + * The 26mhz and 32khz clocks can be easily observed with the timers. The= 104mhz output + * can be observed through the UART. All others can only be indirectly ob= served by e.g. + * comparing the CPU speed at 26mhz and 624mhz. + * + * The contents of the PLL registers is as follows: + * + * Bit 31: PLL Locked + * Bit 30: PLL disable bit (0 =3D PLL enabled, 1 =3D PLL disabled) + * Bits 29:25: Unknown. Could be a parent mux + * Bits 24:18: refdiv + * Bits 17:6: fbdiv + * Bits 5:3: post vco divider 1 + * Bits 2:0: post vco divider 2 + * + * There is a second register following immediately afterwards that is su= pposed to have + * another value that gets added to fbdiv, but it doesn't seem to make a = difference in my + * testing and it is always 0 in the preconfigured values. + */ + ref =3D clk_get_rate(parent); + val =3D readl(base); + + refdiv =3D (val & GENMASK(24, 18)) >> 18; + fbdiv =3D (val & GENMASK(17, 6)) >> 6; + postdiv1 =3D (val & GENMASK(5, 3)) >> 3; + postdiv2 =3D (val & GENMASK(2, 0)); + dev_dbg(dev, "%s: reference clock %lu HZ, PLL setting 0x%08x\n", name, re= f, val); + + if (!refdiv || !postdiv1 || !postdiv2) + return -EINVAL; + + vco =3D (ref / refdiv) * fbdiv; + freq =3D vco / postdiv1 / postdiv2; + dev_dbg(dev, "%s: %lu MHZ\n", name, freq / 1000000); + + hw =3D devm_clk_hw_register_fixed_factor(dev, name, __clk_get_name(parent= ), 0, fbdiv, + refdiv * postdiv1 * postdiv2); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + for (i =3D 0; i < ARRAY_SIZE(pll_fract); ++i) { + sprintf(plldiv, "%s_d%u", name, pll_fract[i]); + hw =3D devm_clk_hw_register_fixed_factor(dev, plldiv, name, 0, 1, pll_fr= act[i]); + if (IS_ERR(hw)) + return PTR_ERR(hw); + dev_dbg(dev, "%s: %lu hz\n", clk_hw_get_name(hw), clk_hw_get_rate(hw)); + } + + return 0; +} + +static int zx297520v3_composite(struct device *dev, void __iomem *base, + struct clk_hw_onecell_data *clocks, struct zx29_reset_reg *resets, + const struct zx297520v3_composite *input, size_t size) +{ + char pclk_name[32], wclk_name[32], mux_name[32], div_name[32]; + const char *wclk_parent, *div_parent; + struct clk_hw *hw; + size_t copy_res; + unsigned int i; + + for (i =3D 0; i < size; ++i) { + strscpy(wclk_name, input[i].name, ARRAY_SIZE(wclk_name)); + strcat(wclk_name, "_wclk"); + strscpy(pclk_name, input[i].name, ARRAY_SIZE(pclk_name)); + strcat(pclk_name, "_pclk"); + strscpy(mux_name, input[i].name, ARRAY_SIZE(mux_name)); + strcat(mux_name, "_mux"); + strscpy(div_name, input[i].name, ARRAY_SIZE(div_name)); + strcat(div_name, "_div"); + + resets[input[i].reset_id].reg =3D base + input[i].reset_reg; + resets[input[i].reset_id].mask =3D BIT(input[i].reset_shift) + + BIT(input[i].reset_shift + 1); + + if (input[i].div_size) + wclk_parent =3D div_name; + else if (input[i].mux_size) + wclk_parent =3D mux_name; + else + wclk_parent =3D input[i].mux_sel[0]; + + hw =3D devm_clk_hw_register_gate(dev, pclk_name, input[i].pclk_parent, i= nput[i].flags, + base + input[i].gate_reg, input[i].pclk_gate_shift, + 0, ®_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + clocks->hws[input[i].pclk_id] =3D hw; + + if (input[i].mux_size) { + hw =3D devm_clk_hw_register_mux(dev, mux_name, input[i].mux_sel, + input[i].mux_sel_count, 0, + base + input[i].mux_reg, + input[i].mux_shift, input[i].mux_size, 0, + ®_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + div_parent =3D mux_name; + } else { + div_parent =3D input[i].mux_sel[0]; + } + + hw =3D devm_clk_hw_register_gate(dev, wclk_name, wclk_parent, + input[i].flags | CLK_SET_RATE_PARENT, + base + input[i].gate_reg, input[i].wclk_gate_shift, + 0, ®_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + clocks->hws[input[i].wclk_id] =3D hw; + + if (!input[i].div_size) + continue; + + hw =3D devm_clk_hw_register_divider(dev, div_name, div_parent, CLK_SET_R= ATE_PARENT, + base + input[i].div_reg, input[i].div_shift, + input[i].div_size, 0, ®_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + } + + return 0; +} + +static int zx297520v3_gate(struct device *dev, void __iomem *base, + struct clk_hw_onecell_data *clocks, + const struct zx297520v3_gate *input, size_t size) +{ + struct clk_hw *hw; + unsigned int i; + + for (i =3D 0; i < size; ++i) { + hw =3D devm_clk_hw_register_gate(dev, input[i].name, input[i].parent, + CLK_SET_RATE_PARENT, base + input[i].reg, + input[i].shift, 0, ®_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + clocks->hws[input[i].id] =3D hw; + } + + return 0; +} + +static int zx_restart_handle(struct sys_off_data *data) +{ + struct zx29_clk_controller *top =3D data->cb_data; + + writel(1, top->base); + mdelay(1000); + + pr_emerg("Unable to restart system\n"); + return NOTIFY_DONE; +} + +static int zx297520_topclk_probe(struct platform_device *pdev) +{ + struct zx29_clk_controller *top; + struct device *dev =3D &pdev->dev; + struct clk_hw *hw; + struct clk *clk; + int res; + + dev_info(dev, "Registering zx297520v3 top clocks and resets\n"); + top =3D devm_kzalloc(dev, offsetof(struct zx29_clk_controller, + resets[ZX297520V3_TOPRST_END]), GFP_KERNEL); + if (!top) + return -ENOMEM; + + top->clocks =3D devm_kzalloc(dev, struct_size(top->clocks, hws, + ZX297520V3_TOPCLK_END), GFP_KERNEL); + if (!top->clocks) + return -ENOMEM; + top->clocks->num =3D ZX297520V3_TOPCLK_END; + + top->base =3D devm_platform_ioremap_resource(pdev, 0); + WARN_ON(!top->base); + + /* Offset 0x0 is the global board reset + * Offset 0x4 gives some static boot information - raw NAND or SPI NAND + */ + + clk =3D devm_clk_get_prepared(dev, "osc32k"); + if (IS_ERR(clk)) { + dev_err(dev, "32 KHz input clock not found 1\n"); + return PTR_ERR(clk); + } + + clk =3D devm_clk_get_prepared(dev, "osc26m"); + if (IS_ERR(clk)) { + dev_err(dev, "26 MHz input clock not found\n"); + return PTR_ERR(clk); + } + + /* Default setting: 0x48040c11. 624/312/156... */ + res =3D zx297520v3_pll(dev, top->base + 0x8, "mpll", clk); + if (res) + return res; + + /* There is a PLL at 0x10 called "upll" in ZTE's code, but I don't see an= y documented + * consumers. Default setting 0x48347811. 480/240/160 MHz. + */ + + /* Default value 0x4834902d. Feeds dpll. 46.08 MHz */ + res =3D zx297520v3_pll(dev, top->base + 0x100, "unknownpll", clk); + if (res) + return res; + + /* The documentation says 491.52 MHz and measurement with the LSP TDM dev= ice supports this. + * The default value is 0x480C2011. To get to 491.52 with these settings = it needs a 23.04 + * MHz reference clock, which matches unknownpll_d2. If unknownpll is dis= abled, dpll loses + * its lock. + * + * The proprietary LTE driver or coproc enables and disables it. TDM and = I2S can use it. + * + * FIXME: Isn't there a nicer way to get the struct clk for unknownpll_d2= ? I don't want to + * return all generated clocks from zx297520v3_pll or store them in the c= ontroller because + * I need one of them here. I could always pass the parent by name though. + */ + res =3D zx297520v3_pll(dev, top->base + 0x18, "dpll", __clk_lookup("unkno= wnpll_d2")); + if (res) + return res; + res =3D zx297520v3_pll(dev, top->base + 0x110, "gpll", clk); + if (res) + return res; + + res =3D zx297520v3_composite(dev, top->base, top->clocks, top->resets, + top_clocks, ARRAY_SIZE(top_clocks)); + if (res) + return res; + + res =3D zx297520v3_gate(dev, top->base, top->clocks, top_gates, ARRAY_SIZ= E(top_gates)); + if (res) + return res; + + /* The Cortex M0 coprocessor. It is responsible for booting the board and= runs some power + * management helper code on the stock firmware, but isn't critical. We c= an run custom code + * on it but currently do not. These bits control the speed and the valu= es are mentioned in + * ZTE's uboot. It isn't clear to me if this is directly responsible for = the m0 clock, or + * if it is the input to another clock. I also haven't found a gate that = shuts the m0 off + * and allows restarting. There don't seem to be resets either. + * + * Also note the comment about SRAM1 and SRAM2 PCLKs. They can be turned = off to crash the + * M0 by feeding it garbage instructions. + */ + hw =3D devm_clk_hw_register_mux(dev, "m0_wclk", m0_sel, ARRAY_SIZE(m0_sel= ), + 0, top->base + 0x38, 0, 2, 0, ®_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + top->clocks->hws[ZX297520V3_M0_WCLK] =3D hw; + + of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, top->clocks); + + res =3D devm_register_restart_handler(dev, zx_restart_handle, top); + if (res) + dev_err(dev, "can't register restart handler (res=3D%d)\n", res); + + /* Stray reset bits follow. + * + * I haven't found any clocks for GPIO. It probably wouldn't make much + * sense anyway. Only one bit per controller. + */ + top->resets[ZX297520V3_GPIO8_RESET].reg =3D top->base + 0x74; + top->resets[ZX297520V3_GPIO8_RESET].mask =3D BIT(2); + top->resets[ZX297520V3_GPIO_RESET].reg =3D top->base + 0x74; + top->resets[ZX297520V3_GPIO_RESET].mask =3D BIT(3); + + /* USB reset. This is slightly special because it needs to wait for a rea= dy bit after + * deasserting. + * + * FIXME: Actually implement this waiting. + */ + top->resets[ZX297520V3_USB_RESET].reg =3D top->base + 0x80; + top->resets[ZX297520V3_USB_RESET].mask =3D BIT(3) | BIT(4) | BIT(5); + + /* This bit is set by ZTE's cpko.ko blob, it looks like a reset bit for t= he LTE DSP + * coprocessor. Clocks for it are in matrixclk. + */ + top->resets[ZX297520V3_ZSP_RESET].reg =3D top->base + 0x13c; + top->resets[ZX297520V3_ZSP_RESET].mask =3D BIT(0); + + top->rcdev.owner =3D THIS_MODULE; + top->rcdev.nr_resets =3D ZX297520V3_TOPRST_END; + top->rcdev.ops =3D &zx297520v3_rst_ops; + top->rcdev.of_node =3D dev->of_node; + return devm_reset_controller_register(dev, &top->rcdev); +} + +static const struct of_device_id of_match_zx297520v3_topclk[] =3D { + { .compatible =3D "zte,zx297520v3-topclk"}, + { } +}; +MODULE_DEVICE_TABLE(of, of_match_zx297520v3_topclk); + +static struct platform_driver clk_zx297520v3_topclk =3D { + .probe =3D zx297520_topclk_probe, + .driver =3D { + .name =3D "clk-zx297520v3-topclk", + .of_match_table =3D of_match_zx297520v3_topclk, + }, +}; +module_platform_driver(clk_zx297520v3_topclk); + +MODULE_AUTHOR("Stefan D=C3=B6singer "); +MODULE_DESCRIPTION("ZTE zx297520v3 clock driver"); +MODULE_LICENSE("GPL"); --=20 2.53.0 From nobody Sat Jun 13 04:48:47 2026 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (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 C65EC3164BA for ; Sun, 10 May 2026 19:01:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439692; cv=none; b=MsqbvArdOwpUVGnqSafB+RG3fWhYUtphe+16nS2I7pZZYZyI14S7PGtq4e/38xY1AbryaS7xfKuLs9KTzEyDdPEpOGXW7REUD4Xg+92fdsQy81IBry/ysSWxoePxzwq/EGJsj9XPlNCNUuiESkivMjfTewfVveS1NgWso1waaQM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439692; c=relaxed/simple; bh=1Lapw7R95aof3AnNhcgqE/OG+v6IP0W2dYz8kOJXyhM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=awqsOc8lBYV5RpL+X1fJxfSxxO0SBncryo/SZiILQWiSmh+mSlGExAcM8yJaABvt99L/BnHNJ/GOijbiOYbQ2GLacAP79m+KRJsSuRGTv/OqnNYw1xToY9YUWJF71CkbaxS3Yq/t+7EH9lm3prCRvK4w6q8/saPKJls4gUMWYhU= 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=IUMf98vF; arc=none smtp.client-ip=209.85.128.48 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="IUMf98vF" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-48d102471a4so35283875e9.2 for ; Sun, 10 May 2026 12:01:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778439689; x=1779044489; 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=BpF6AlXvDXugl5vI24HTS1gp9sy7JRRVKw/m0MMOaTo=; b=IUMf98vFja11MkXioCripXgnPSqYTMpaGnNkhR8ejFBc+mw4lFzPDK76sbevxJDb68 /0F63D8/wg7wrLoEM0IUPCpg88ZAEiLhcLhYPCynvKcW4i/qxsWyB2AILDjmzRCtMdSE 6nqSEFc70KmmB5fJy0gVxEl5GML2LTsN7EcsHFd+jqNiF8KcI3FSSfoP0SfeQxCTRhI9 gFywOIZAqjMXXBZ5X0pZ7A1Cl5/4f+y3Q2WgsJgNYeZ7VtJ0NGx486Anr4gknL9qdjAJ h8tcQUuXiGjfhLPbBFeNVmDswTVCNKPxDXR9AfB3MgGzxorMQ5oOJIip9ypomxoivERd at+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778439689; x=1779044489; 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=BpF6AlXvDXugl5vI24HTS1gp9sy7JRRVKw/m0MMOaTo=; b=ThaFVHRAzGy5BayGvjqhS+FRV2yu9KPMPyka85Nu86slJWK/Alu3AuoccJZfDWhks9 1G8sKfGZBRj6QPD2IotuLRED6LgGK32juYFCEJGxdl7ZUmeY2wNaY4C54R62pHIlyG9I 64nJ5iRFJZsMmaz0cTUlxmbu/62C/ZbuJvMiEtbf61ZzhbD32N0s1I4EG7cLptUZoxMS Cyi2LP+ASM9o7Axo0YBFtFM4tilXR/vvj/W/G6KU6Uz8kH8tVs7KwTL0JVx9xaOSJNEY aU0ayt3s9IJTgpyE/AsmtBxoYNzU0igMCGLsJkQltlNL6cENoFHG0zmpDWGuD/Mn0bcS ddeg== X-Forwarded-Encrypted: i=1; AFNElJ/4DTDnvArjWNl493fM+vVJS0U/zcgQBztvqaFsWG78lYCIRfo9fadSQLsiAlc59IiAZgCyRVqX10cKS1o=@vger.kernel.org X-Gm-Message-State: AOJu0Ywz4ygAcreRA126Ddh4sfUa74ZyI3p97Q1JgK9OeFyR2r9qbagX f3vlPecdY4lOjuZNzUyzRqNrGGHQzx9oG1Vj13ZgqYeCru2T4rDFIsLx X-Gm-Gg: Acq92OEre/Grr4TShpHplGnMSiiiUxzzPdrL6LcGVJbuDE5UE7ICeMrNlnPK9g5MYvT q8ct/lawsfXAioRZci/7F2VqEinoKLalpsRmQ1TjmrV7blVeKlPBNf9WJ5OcKOELI+bsspBMdl+ eUJ1jFv18v/kfONpSyC1K0l2vqjvo5O1QaXzzFoQOZfRO3rCvdHHLCQFvPdCTxHMtKvbKwIl5mp 7syEiZVVZ4z56Ml+5RzmqMUlSeZvbDT3HWW87lon2BIL9xtiosNPTl0t1b6+chvP+z4zq+UGvJ4 i4aFPVLFKJMdif9rvCioNcBInrwTeQ/2kVv8KXRRqvhusueHhG5eAm1Nf/fOa8sJvg5Wr6nBmWv MSqpfemlfYxfPcs0tO9qh1OpUtzrHGabThN31tnhMJbj4gKfxHvBZ/94hnsMo/BmioUFM/WYJi/ 9lEidVvMMdDqO4z6/8wZ/icnrsvtmRu+MWKw== X-Received: by 2002:a05:600c:a47:b0:48a:75b9:5e07 with SMTP id 5b1f17b1804b1-48e6767e04fmr184708415e9.11.1778439688962; Sun, 10 May 2026 12:01:28 -0700 (PDT) Received: from [192.168.0.2] ([197.250.51.203]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e701e9585sm132877475e9.5.2026.05.10.12.01.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 12:01:28 -0700 (PDT) From: =?utf-8?q?Stefan_D=C3=B6singer?= Date: Sun, 10 May 2026 22:01:02 +0300 Subject: [PATCH RFC 3/4] clk: zte: Introduce a driver for zx297520v3 matrix clocks and resets. Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260510-zx29clk-v1-3-e1bacfffe967@gmail.com> References: <20260510-zx29clk-v1-0-e1bacfffe967@gmail.com> In-Reply-To: <20260510-zx29clk-v1-0-e1bacfffe967@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=1Lapw7R95aof3AnNhcgqE/OG+v6IP0W2dYz8kOJXyhM=; b=owEBiQJ2/ZANAwAIAT0TvMhUTxoiAcsmYgBqANX68B87qJ1QaGHEXgTCDFuyoRoepcJSmmOBJ Zo2/CqnJMCJAk8EAAEIADkWIQRDFvS2qgVbJ5UyXWw9E7zIVE8aIgUCagDV+hsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQPRO8yFRPGiIfLw/+NtogShmAVlIaQVRTx4c8z14eqCXBGCU /hSMPiHVAgdA8EBPXjfBQO/MT3pGHYJ46RsRIUgocofkbP6ECc8iw0rKIRpeViSsdYHCWnJU6X5 MDXHKrtoSMQeJefxIxB9MSxlJoLvcJaGD4wLwOD49IlwSoRgPH215fSDMqBHLJh/Cp8cb0Lrp0N B9jl1AekWK2gTYFNHRDM8VGbrNmsOFpEqK+pJrY6guh15/6Zd/o/SjfrC/U3V+kCOwXPMbOUrFb dUwDcVQv+6o8eHYWhE2WOE3zjm07+o+6wmwWxyVhnh/XcuZTm+bt6HorIibq/bTWRZERojZm0NI bVp50ea0HUtadul6TzsB8941asji1NgQ6RiGgCGI6wmqmEdUfuB1DTEKV4SsEweYeZspdmES2eL 1iCsQqFu0VmVkUaGuwGRS+ytu+z6hUr6tJpreykRxE23oOfFI34IwKrilIvTxD86RV6HKwF8Ii8 viZrf6zJuL8WXgH+fxogTT3UUpv+5zfFqP83CnCjbRsTIyzFI1LhgvOdGmKRQeKogQnBQ4BcbC2 4e1qoMXn3p7b6zClKmfTkqVHyQ63rpu4KjlnAd5mHblNYvk6n7HbqxmTuZRtWp4lVOYyDI8M+j0 AbtlruLkhZ6pJGmuX3qRsAWzIJNzLoM+N4NMLbQ8VhMOS3lQ7IEw= 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=C3=B6singer --- 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-zx29752= 0v3.c index f73f5c006641..0c06add433ee 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 __io= mem *base, const char *na /* These are the fractionals of the PLLs I have seen. There should be a b= etter way to * generate them than hardcode the list. */ - static const unsigned int pll_fract[] =3D {2, 3, 4, 5, 6, 8, 12, 26}; + static const unsigned int pll_fract[] =3D {2, 3, 4, 5, 6, 8, 12, 16, 26}; =20 unsigned long ref, refdiv, fbdiv, vco, postdiv1, postdiv2, freq; struct clk_hw *hw; @@ -579,6 +579,219 @@ static struct platform_driver clk_zx297520v3_topclk = =3D { }; module_platform_driver(clk_zx297520v3_topclk); =20 +static const char * const cpu_sel[] =3D { + "osc26m", + "mpll", /* 624 MHz */ + "mpll_d2", /* 312 MHz */ + "mpll_d4", /* 156 MHz */ +}; + +static const char * const sd0_sel[] =3D { + "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[] =3D { + "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[] =3D { + "mpll_d4", /* 156 MHz */ + "osc26m", +}; + +static const char * const edcp_sel[] =3D { + "osc26m", + "mpll_d4", /* 156 MHz */ + "mpll_d5", /* 124.8 MHz */ + "mpll_d6", /* 104 MHz */ +}; + +static const char * const tdm_sel[] =3D { + "osc26m", + "dpll_d4", /* 122.88 MHz */ + "mpll_d6", /* 104 MHz */ +}; + +static const struct zx297520v3_composite matrix_clocks[] =3D { + /* Both 0x24 and 0x28 bits 1 and 2 stop the CPU. There is also a bit in t= opclk+0x138, which + * ZTE's uboot calls "A53 reset", which also stops the CPU. I can't reall= y tell the + * difference between matrix+28 and top+138. The clock can be disabled an= d enabled from the + * Cortex M0 and it will nicely stop and restart the A53, retaining all s= tate. + * + * 0x50, bits 0-3 have the DDR clock. A lot of DDR gates and resets are i= n 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 clo= cks. */ + 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 turn= ing 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[] =3D { + /* ZTE's driver has a statemt to the effect of *(matrix->base+0x11C) =3D = 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 hardw= are. 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-ph= y.yaml it is + * supposed to be possible, but I can't find code to that effect in of_md= io.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 ha= ve 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 onl= y - turning it off + * makes the MMIO area read 0x0. It looks like it does not need a WCLK. I= t generates IRQs + * fine with just bit 2 set. Bits 1 and 3 are 0 by default in this regist= er. + */ + {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 qsp= i 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 can= not directly + * observe the qspi mux frequency, so the clock rates depend on ZTE's qsp= i 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 sel= ect the dpll_d4 + * clock. + * + * This code is commented out until the next patch because disabling unus= ed 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 =3D &pdev->dev; + struct clk_hw *hw; + unsigned int i; + int res; + + dev_info(dev, "Registering zx297520v3 matrix clocks\n"); + + matrix =3D devm_kzalloc(dev, offsetof(struct zx29_clk_controller, + resets[ZX297520V3_MATRIXRST_END]), GFP_KERNEL); + if (!matrix) + return -ENOMEM; + + matrix->clocks =3D devm_kzalloc(dev, struct_size(matrix->clocks, hws, + ZX297520V3_MATRIXCLK_END), GFP_KERNEL); + if (!matrix->clocks) + return -ENOMEM; + matrix->clocks->num =3D ZX297520V3_MATRIXCLK_END; + + matrix->base =3D 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 =3D devm_clk_hw_register_mux(dev, "tdm_mux", tdm_sel, ARRAY_SIZE(tdm_s= el), 0, + matrix->base + 0x50, 24, 2, 0, ®_lock); + + res =3D zx297520v3_composite(dev, matrix->base, matrix->clocks, matrix->r= esets, + matrix_clocks, ARRAY_SIZE(matrix_clocks)); + if (res) + return res; + + res =3D zx297520v3_gate(dev, matrix->base, matrix->clocks, + matrix_gates, ARRAY_SIZE(matrix_gates)); + if (res) + return res; + + for (i =3D 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 =3D of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, matri= x->clocks); + if (res) + return res; + + matrix->resets[ZX297520V3_DMA_RESET].reg =3D matrix->base + 0x70; + matrix->resets[ZX297520V3_DMA_RESET].mask =3D BIT(0) | BIT(1); + matrix->resets[ZX297520V3_GMAC_RESET].reg =3D matrix->base + 0x114; + matrix->resets[ZX297520V3_GMAC_RESET].mask =3D BIT(0) | BIT(1); + + matrix->rcdev.owner =3D THIS_MODULE; + matrix->rcdev.nr_resets =3D ZX297520V3_MATRIXRST_END; + matrix->rcdev.ops =3D &zx297520v3_rst_ops; + matrix->rcdev.of_node =3D dev->of_node; + return devm_reset_controller_register(dev, &matrix->rcdev); +} + +static const struct of_device_id of_match_zx297520v3_matrixclk[] =3D { + { .compatible =3D "zte,zx297520v3-matrixclk"}, + { } +}; +MODULE_DEVICE_TABLE(of, of_match_zx297520v3_matrixclk); + +static struct platform_driver clk_zx297520v3_matrixclk =3D { + .probe =3D zx297520_matrixclk_probe, + .driver =3D { + .name =3D "clk-zx297520v3-matrixclk", + .of_match_table =3D of_match_zx297520v3_matrixclk, + }, +}; +module_platform_driver(clk_zx297520v3_matrixclk); + MODULE_AUTHOR("Stefan D=C3=B6singer "); MODULE_DESCRIPTION("ZTE zx297520v3 clock driver"); MODULE_LICENSE("GPL"); --=20 2.53.0 From nobody Sat Jun 13 04:48:47 2026 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) (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 7E1F3322768 for ; Sun, 10 May 2026 19:01:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439696; cv=none; b=FNCB520jnZ5J85gepN7zCmYVpUmm4ialWrSgqPm5m188ShWT/a+3mY+e25xTxvUslFCfqLkk3IiuWl2L87EVsnTNSdfAe1LcyemsLB3uSBVIckRSAvuUxnO4cFcDkVETNhkzhsrO4lCsGxiCxwLhFlzPqEEENb5mSR76JYZCYT4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778439696; c=relaxed/simple; bh=lT0PY5Ih14lC9dDe3Ae8vidP2UICe0uc4N1nVYtyQR0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ID6zjZPx6oDFQM1mQoyYWKYf+SWRQTJDMvUjCMVdUbGrEKLWR6eHWf/P3e2hyZuAwomIhYX3agnahGS7sSHhaQB6nvGrokdTvv9bUi3PHSBYTmseofjbICm3f1nwSnU32BPvQf3qgYwu7k6fRqriieHN+2mGrIf4XEWxtqIIDAc= 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=DHH50+wM; arc=none smtp.client-ip=209.85.128.41 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="DHH50+wM" Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-4891e5b9c1fso34035305e9.2 for ; Sun, 10 May 2026 12:01:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778439693; x=1779044493; 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=60aiFMdJ5EF0YuV44E9gs+Z4pT0nbdBQxVcQwSAiePU=; b=DHH50+wMOWiuj+hZOcFvIQd7oR/N9Im8ry+tqkT88h9i4JZTm8HXl8+s9+S6Z6K18s 5MRYDmpXUnuoALv6/hU5KAQnFHdIOV5UuN84lPYatvdNl0r923ffR8/GQO1RH5IizOwp NH3WrsMmNzU3A0xfNvUVEMDNuOadRouppzYthcOVgmI2p6B1KcmhChaTKiRGFWNCF4od Qj0Y4RA3FnlSqGRrrxIJBjWJ/smzJ6JzL8Muwu7zrCrCpmREg//6GsvpTaE+jrIOChci PPWt6cAZcZ/JdYw+A3DGvZQykccBmyXqYS2aJkulr1448dYRQtBB3IELhuu9HKe96fNU R+kQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778439693; x=1779044493; 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=60aiFMdJ5EF0YuV44E9gs+Z4pT0nbdBQxVcQwSAiePU=; b=LRrogMOvgwcfuqtcsyjGndx5P06v8xY2CoNfE0BrOluDvmtkB/pSosMcxPMZwxabh2 NGqvalmgvbVTsNe7YClDhdIXDdy30GhJKjFtBAfXBRpfJ51uvGWqD9MVAzfCw4xEBEkw RUj8+0WH1nV52x3NSLMNLn/XJVIHfuynUP5iloUP0pPitNsS+6Ai8j8TvrvulByxdUcJ fXIGBEfTNZEk0YO16hd95KQD/g3rDey2hY2ETAHnKvWuOWzSrJ6V34chmKSUKX8swYJO 0SzJq/Gf/nFikPt4OQsifdRKezo0b15g6IlHXVFE++zH6aYBzK1cd2VMlSIKbOugWQfl P5MA== X-Forwarded-Encrypted: i=1; AFNElJ8IMDhN7BOkT9/S9Za1Rz6RRFd/Pp5IRA7nCKbesWS0o+jQFeuE0sJbI0JjbyVmT1YGwLb98px9nwe0//Y=@vger.kernel.org X-Gm-Message-State: AOJu0YwKFSjjt39I8mhrCzyUA3ptnKaBQPfTLePi30nANs5hTsLNvzbP UHtKwTnVmuF2NWQSobNhHEQYLbTAtedo3aY3x5g+t723sIEz2331pDZ3 X-Gm-Gg: Acq92OFkgGsduELox2RdZKyHlbLEoSOttBfiNw+8XKmDmSmtWa+Pi8NdfprltNJEc7m CKk/QKX7aeVvqURvXqwcRzygVIT4673ctogG/wyIFjsBPlQ3RFpcSgtOdxWt7coJPIMLMqbTGAp oxMfpVsY9iYRGm/1lNbGoMRQvjDJ4AVOMvlvQmye4+LWI7Vg3zYeefBgREzqJTOZz/IYAmzeKTD oz2pfzJx4O4KoTEbyh8LXiVtrdqumCA4oNwiKL8MWe4SuFPXEufaa5ElHlrbpycsuo2YJdKHG+w Sm1V/8nP7jF34O+9Dq4Aq+CAdC6MdaSWQlqty9h0uQ8Pjkz+9gIDTOqtpxJpZkl2zPKdrCwXEsB eAwURIrAIoXgvoMyhv9Z0yB1oViAFiYpOyyWvPiJdAfa1O1ygKUXhYkj3o3mjF7A7xVFRooT1n4 dm8wi4C//7nU3XZIZJS3TTgQGTdMBXkqIKMA== X-Received: by 2002:a05:600c:45c6:b0:48a:93f8:dd02 with SMTP id 5b1f17b1804b1-48e521e6090mr332212165e9.14.1778439692806; Sun, 10 May 2026 12:01:32 -0700 (PDT) Received: from [192.168.0.2] ([197.250.51.203]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e701e9585sm132877475e9.5.2026.05.10.12.01.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 12:01:31 -0700 (PDT) From: =?utf-8?q?Stefan_D=C3=B6singer?= Date: Sun, 10 May 2026 22:01:03 +0300 Subject: [PATCH RFC 4/4] clk: zte: Introduce a driver for zx297520v3 LSP clocks and resets. Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260510-zx29clk-v1-4-e1bacfffe967@gmail.com> References: <20260510-zx29clk-v1-0-e1bacfffe967@gmail.com> In-Reply-To: <20260510-zx29clk-v1-0-e1bacfffe967@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=7176; i=stefandoesinger@gmail.com; h=from:subject:message-id; bh=lT0PY5Ih14lC9dDe3Ae8vidP2UICe0uc4N1nVYtyQR0=; b=owEBiQJ2/ZANAwAIAT0TvMhUTxoiAcsmYgBqANX6KYEJuO+6GM3YtADEJWSukHdj2xJi9mUzX Nm+gxNizbWJAk8EAAEIADkWIQRDFvS2qgVbJ5UyXWw9E7zIVE8aIgUCagDV+hsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQPRO8yFRPGiLGKRAAlF8qgbBoYBwUAKhOl3BIKIT6G9WGh50 9kTI/pwGC+KlmgvE/ovevGLOdlV7vG2hD0VCtxrPInVEO9d8ZgXvkgQ3U6Xe9U0aHEiWk92sE7l 5k/oQCpw9pB6XKlmmmFDFWoXwjo0HwjtJ99JDpEIc6FAo59XdD6PAul3ftFUxm/m4VcORllzt0T XegVmC2WRnwYDxTUORx/zUdWq5NxnzodpjMi0BWcqOhgYTOQPIq1o0eiLuq8/dLczzymwKeAY7g WSoTEBmxI6ztxHa8vaHI4X4vpu3OcesZ4n8XH8qC5wAUftaaVsV9zC+MITotUYSvelAno7JPSaf qrpwxpQv+Ag989LjXcBu0t6rbMRTYF0wrV0Z6pzGBAOCE7zNd+yM92JyhY7ABGfjyECePEHNLKH rlUGICfrSs4SZxraxdjYrLG+mExSE05bM1mK3hsmkqXZGrxfNu840Tf6+LUgcmM8i5j9a0JIasr PaFFpyLMGbKbWpnx0GodduaVFM70YV8hYBxTxIpqJVFoEMhTDRBHVq4CokujPM0MzKTAQp0GDuf HeVixbtwAxsznfhuD2rk3g1XOKvSgEAfADWuAIQfaMedLC7/uYr4QdRE63BTv6FwDsRSVVrUWAD J5TCm7fGR3oUfkYcmmeA1bLAEXqznTlPqP84P3Xd6sZCz7UCJd/s= X-Developer-Key: i=stefandoesinger@gmail.com; a=openpgp; fpr=4F9C2C8728019633893EBBB98CB81F9A72BBA155 "LSP" is ZTE's term for this part of the SoC, I suspect it stands for "low speed peripherals". The main UART is here, together with the flash controller and more surplus proprietary timers. It also has two more I2C controllers that supposedly connect to a battery charger, SPI for displays and I2S for analog telephones. The boards I have don't have any of these components though. Signed-off-by: Stefan D=C3=B6singer --- drivers/clk/zte/clk-zx297520v3.c | 183 +++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 183 insertions(+) diff --git a/drivers/clk/zte/clk-zx297520v3.c b/drivers/clk/zte/clk-zx29752= 0v3.c index 0c06add433ee..e69be686c1b1 100644 --- a/drivers/clk/zte/clk-zx297520v3.c +++ b/drivers/clk/zte/clk-zx297520v3.c @@ -792,6 +792,189 @@ static struct platform_driver clk_zx297520v3_matrixcl= k =3D { }; module_platform_driver(clk_zx297520v3_matrixclk); =20 +/* LSP clock entries have a common pattern: Bit 0 for PCLK, Bit 1 for WCLK= . Bit 4 (and sometimes + * more) for WCLK mux. + * + * Bit 8 and 9 are reset bits. I don't know the difference between the two= , but they both + * need to be set to deassert the reset. + * + * Bits 12-16 can be a divisor, but not all clocks have it. Some clocks ha= ve a divisor in 16-20. + * + * The ID given in this table is the first register in the device's MMIO s= pace. ZTE's drivers + * usually call this a version register, but it looks more like a device i= dentifier. + * + * It looks like the registers map to devices like this: + * + * Timer reg function div dev offset(lsp + xxxx) ID + * 0x0: Read-only, probably device identifier 0x00752100 + * 0x4: timer_l1 Y 0x1000 0x02020000 + * 0x8: watchdog_l2 Y 0x2000 0x02020000 + * 0xc: watchdog_l3 Y 0x3000 0x02020000 + * 0x10: i2c1 N 0x4000 0x01020000 + * 0x14: i2s0 Yh 0x5000 0x01030000 + * 0x18: always 0 N - + * 0x1c: i2s1 Yh 0x6000 0x01030000 + * 0x20: always 0 N - + * 0x24: qspi N 0x7000 0x01040000 + * 0x28: uart1 N 0x8000 0x01060000 + * 0x2c: i2c2 N 0x9000 0x01020000 + * 0x30: spi0 Y 0xa000 0x01040000 + * 0x34: timer_lb Y 0xb000 0x02020000 + * 0x38: timer_lc Y 0xc000 0x02020000 + * 0x3c: uart2 N 0xd000 0x01060000 + * 0x40: watchdog_le Y 0xe000 0x02020000 + * 0x44: timer_lf Y 0xf000 0x02020000 + * 0x48: spi1 Y 0x10000 0x01040000 + * 0x4c: timer_l11 Y 0x11000 0x02020000 + * 0x50: tdm Y 0x12000 0x01040000 + * + * Registers 0x58, 0x5c, 0x60, 0x64, 0x68 seem to contain more controls fo= r i2s and tdm. + */ + +static const char * const timer_lsp_sel[] =3D { + "lsp_osc32k", + "lsp_osc26m", +}; + +static const char * const uart_lsp_sel[] =3D { + "lsp_osc26m", + "lsp_mpll_d6", +}; + +static const char * const i2s_lsp_sel[] =3D { + "lsp_osc26m", + "lsp_dpll_d4", + "lsp_mpll_d6", + /* Unknown */ +}; + +static const char * const tdm_lsp_sel[] =3D { + "lsp_tdm_wclk", +}; + +static const char * const spi_lsp_sel[] =3D { + "lsp_osc26m", + "lsp_mpll_d4", + "lsp_mpll_d6", + /* Unknown */ +}; + +static const char * const qspi_lsp_sel[] =3D { + "lsp_osc26m", + "lsp_mpll_d4", + "lsp_mpll_d5", + "lsp_mpll_d6", + "lsp_mpll_d8", + "lsp_mpll_d12", + "lsp_osc26m", + "lsp_osc26m", +}; + +#define LSP_CLOCK(offset, name, mux, div_shift, div_size) {\ + ZX297520V3_##name##_RESET, ZX297520V3_##name##_WCLK, ZX297520V3_##name##= _PCLK,\ + #name, offset, 8, offset, 0, 1, "lsp_pclk", offset, 4, 4, mux, ARRAY_SIZ= E(mux),\ + offset, div_shift, div_size, 0} + +static const struct zx297520v3_composite lsp_clocks[] =3D { + LSP_CLOCK(0x4, TIMER_L1, timer_lsp_sel, 0, 0), + LSP_CLOCK(0x8, WDT_L2, timer_lsp_sel, 0, 0), + LSP_CLOCK(0xc, WDT_L3, timer_lsp_sel, 0, 0), + LSP_CLOCK(0x10, I2C1, uart_lsp_sel, 0, 0), + LSP_CLOCK(0x14, I2S0, i2s_lsp_sel, 16, 4), + LSP_CLOCK(0x1c, I2S1, i2s_lsp_sel, 16, 4), + LSP_CLOCK(0x24, QSPI, qspi_lsp_sel, 0, 0), + LSP_CLOCK(0x28, UART1, uart_lsp_sel, 0, 0), + LSP_CLOCK(0x2C, I2C2, uart_lsp_sel, 0, 0), + LSP_CLOCK(0x30, SPI0, spi_lsp_sel, 12, 4), + LSP_CLOCK(0x34, TIMER_LB, timer_lsp_sel, 12, 4), + LSP_CLOCK(0x38, TIMER_LC, timer_lsp_sel, 12, 4), + LSP_CLOCK(0x3c, UART2, uart_lsp_sel, 0, 0), + LSP_CLOCK(0x40, WDT_LE, timer_lsp_sel, 12, 4), + LSP_CLOCK(0x44, TIMER_LF, timer_lsp_sel, 12, 4), + LSP_CLOCK(0x48, SPI1, spi_lsp_sel, 12, 4), + LSP_CLOCK(0x4c, TIMER_L11, timer_lsp_sel, 12, 4), + LSP_CLOCK(0x50, TDM, tdm_lsp_sel, 16, 4), +}; + +#undef LSP_CLOCK + +static int zx297520_lspclk_probe(struct platform_device *pdev) +{ + static const char * const *parent_names[] =3D { "mpll_d5", "mpll_d4", "mp= ll_d6", "mpll_d8", + "mpll_d12", "osc26m", "osc32k", "pclk" }; + + struct zx29_clk_controller *lsp; + struct device *dev =3D &pdev->dev; + struct clk *parent; + unsigned int i; + int res; + + dev_info(dev, "Registering zx297520v3 LSP clocks and resets\n"); + + lsp =3D devm_kzalloc(dev, offsetof(struct zx29_clk_controller, + resets[ZX297520V3_LSPRST_END]), GFP_KERNEL); + if (!lsp) + return -ENOMEM; + + lsp->clocks =3D devm_kzalloc(dev, struct_size(lsp->clocks, hws, + ZX297520V3_LSPCLK_END), GFP_KERNEL); + if (!lsp->clocks) + return -ENOMEM; + lsp->clocks->num =3D ZX297520V3_LSPCLK_END; + + lsp->base =3D devm_platform_ioremap_resource(pdev, 0); + WARN_ON(!lsp->base); + + /* TODO: Technically we can disable the pclk if all LSP devices are shut = down, but that + * needs custom clk ops to tiptoe around a disabled LSP pclk before attem= pting to access + * the actual clock. In normal operation it is unlikely that all LSP devi= ces are shut down + * simultaneously though as UART and NAND are located here. + */ + parent =3D devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(parent)) { + dev_err(dev, "failed to find lsp pclk\n"); + return PTR_ERR(parent); + } + + for (i =3D 0; i < ARRAY_SIZE(parent_names); ++i) { + parent =3D devm_clk_get(dev, parent_names[i]); + if (IS_ERR(parent)) { + dev_err(dev, "failed to find lsp %s clock\n", parent_names[i]); + return PTR_ERR(parent); + } + } + + res =3D zx297520v3_composite(dev, lsp->base, lsp->clocks, lsp->resets, + lsp_clocks, ARRAY_SIZE(lsp_clocks)); + if (res) + return res; + + res =3D of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, lsp->= clocks); + if (res) + return res; + + lsp->rcdev.owner =3D THIS_MODULE; + lsp->rcdev.nr_resets =3D ZX297520V3_LSPRST_END; + lsp->rcdev.ops =3D &zx297520v3_rst_ops; + lsp->rcdev.of_node =3D dev->of_node; + return devm_reset_controller_register(dev, &lsp->rcdev); +} + +static const struct of_device_id of_match_zx297520v3_lspclk[] =3D { + { .compatible =3D "zte,zx297520v3-lspclk"}, + { } +}; +MODULE_DEVICE_TABLE(of, of_match_zx297520v3_lspclk); + +static struct platform_driver zx297520v3_lspclk =3D { + .probe =3D zx297520_lspclk_probe, + .driver =3D { + .name =3D "clk-zx297520v3-lspclk", + .of_match_table =3D of_match_zx297520v3_lspclk, + }, +}; +module_platform_driver(zx297520v3_lspclk); + MODULE_AUTHOR("Stefan D=C3=B6singer "); MODULE_DESCRIPTION("ZTE zx297520v3 clock driver"); MODULE_LICENSE("GPL"); --=20 2.53.0