Devicetree
 help / color / mirror / Atom feed
* [PATCH 11/13] arm64: dts: qcom: sm8750: Add Display CRM device
From: Jagadeesh Kona @ 2026-04-20 17:24 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

From: Maulik Shah <maulik.shah@oss.qualcomm.com>

CESTA Resource Manager (CRM) device provides DRVs for global and local
resource voting. Add CRM device to vote for resources needed for display
subsytem.

Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/sm8750.dtsi | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi
index 18fb52c14acd..5d2c59f37015 100644
--- a/arch/arm64/boot/dts/qcom/sm8750.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi
@@ -3436,6 +3436,22 @@ dispcc: clock-controller@af00000 {
 			#power-domain-cells = <1>;
 		};
 
+		disp_crm: crm@af21000 {
+			compatible = "qcom,sm8750-disp-crm";
+			label = "disp_crm";
+			reg = <0x0 0x0af21000 0x0 0x6000>,
+			      <0x0 0x0af29f00 0x0 0x100>;
+			power-domains = <&rpmhpd RPMHPD_MMCX>;
+			interrupts = <GIC_SPI 703 IRQ_TYPE_EDGE_RISING>,
+				     <GIC_SPI 708 IRQ_TYPE_EDGE_RISING>,
+				     <GIC_SPI 714 IRQ_TYPE_EDGE_RISING>,
+				     <GIC_SPI 68 IRQ_TYPE_EDGE_RISING>,
+				     <GIC_SPI 96 IRQ_TYPE_EDGE_RISING>,
+				     <GIC_SPI 249 IRQ_TYPE_EDGE_RISING>;
+			qcom,sw-drv-ids = <0 1 2 3 4 5>;
+		};
+
+
 		pdc: interrupt-controller@b220000 {
 			compatible = "qcom,sm8750-pdc", "qcom,pdc";
 			reg = <0x0 0x0b220000 0x0 0x10000>, <0x0 0x164400f0 0x0 0x64>;
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH v3 1/2] dt-bindings: rng: mtk-rng: add SMC-based TRNG variants
From: Krzysztof Kozlowski @ 2026-04-20 17:24 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
	Sean Wang, linux-crypto, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <aeY8xF82FB7Plu7W@makrotopia.org>

On 20/04/2026 16:48, Daniel Golle wrote:
> On Mon, Apr 20, 2026 at 04:43:00PM +0200, Krzysztof Kozlowski wrote:
>> On 20/04/2026 16:24, Daniel Golle wrote:
>>> On Mon, Apr 20, 2026 at 04:07:33PM +0200, Krzysztof Kozlowski wrote:
>>>> On Sun, Apr 19, 2026 at 01:05:01PM +0100, Daniel Golle wrote:
>>>>> +    rng {
>>>>> +            compatible = "mediatek,mt7981-rng";
>>>>
>>>> I asked at v1. Reminded at v2. Nothing serious, but repeating myself is
>>>> pointless and kind of waste of time.
>>>
>>> Replying *once* telling what you would actually want, or replying to
>>> me asking back would have helped enormously:
>>> https://patchwork.kernel.org/comment/26880354/
>>>
>>> All I can see is that you concluded "no improvements" without telling
>>> *what it is you would like to see improved*.
>>>
>>
>> Yes, and then you should go to v1 and read the review. There was only
>> single comment in this spot, so trivial to find.
>>
>> AGAIN:
>>
>> Use four spaces for indentation.
> 
> Thank you, that IS helpful.
> 
> I've read the "no improvements" statement as an overall conclusion and
> not even considered it to be specific to any *place* without further
> context.
> 
> Is that the only remaining problem you see in the binding right now?

Yes at least quickly looking.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v5 4/4] Input: charlieplex_keypad: add GPIO charlieplex keypad
From: Hugo Villeneuve @ 2026-04-20 17:19 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: robin, andy, geert, robh, krzk+dt, conor+dt, hvilleneuve,
	mkorpershoek, matthias.bgg, angelogioacchino.delregno, lee,
	alexander.sverdlin, marek.vasut, akurz, devicetree, linux-kernel,
	linux-input, linux-arm-kernel, linux-mediatek
In-Reply-To: <aeZe7XoDbOMqMG_c@google.com>

On Mon, 20 Apr 2026 10:16:02 -0700
Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:

> On Mon, Apr 20, 2026 at 11:01:59AM -0400, Hugo Villeneuve wrote:
> > I tested it on the real hardware and all is good.
> 
> Thank you for testing the changes.
> 
> > 
> > So I imagine that it can still go into 7.1 since it is a new driver
> > and not a modification of an existing one?
> 
> Yes, since this is a new driver I will include it in 7.1 pull request.
> 
> Thanks.

Great, thank you.

-- 
Hugo Villeneuve

^ permalink raw reply

* Re: [PATCH v5 4/4] Input: charlieplex_keypad: add GPIO charlieplex keypad
From: Dmitry Torokhov @ 2026-04-20 17:16 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: robin, andy, geert, robh, krzk+dt, conor+dt, hvilleneuve,
	mkorpershoek, matthias.bgg, angelogioacchino.delregno, lee,
	alexander.sverdlin, marek.vasut, akurz, devicetree, linux-kernel,
	linux-input, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260420110159.29ccb815eb584bca18a407ac@hugovil.com>

On Mon, Apr 20, 2026 at 11:01:59AM -0400, Hugo Villeneuve wrote:
> I tested it on the real hardware and all is good.

Thank you for testing the changes.

> 
> So I imagine that it can still go into 7.1 since it is a new driver
> and not a modification of an existing one?

Yes, since this is a new driver I will include it in 7.1 pull request.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH v6 3/3] dts: s32g: Add GPR syscon region
From: Dan Carpenter @ 2026-04-20 16:45 UTC (permalink / raw)
  To: Jared Kangas
  Cc: Chester Lin, Matthias Brugger, Ghennadi Procopciuc,
	NXP S32 Linux Team, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-arm-kernel, imx,
	devicetree, linux-kernel, linaro-s32, netdev
In-Reply-To: <aeZOcCOgMy2g9wqp@rh-jkangas-kernel>

On Mon, Apr 20, 2026 at 09:04:00AM -0700, Jared Kangas wrote:
> Fixing Dan's address based on mailmap update, sorry for the noise.
> 
> On Fri, Apr 17, 2026 at 02:36:25PM -0700, Jared Kangas wrote:
> > Hi Dan,
> > 
> > On Fri, Jan 30, 2026 at 04:19:52PM +0300, Dan Carpenter wrote:
> > > Add the GPR syscon region for the s32 chipset.
> > > 
> > > Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> > > ---
> > >
> > > [snip]
> > >
> > > diff --git a/arch/arm64/boot/dts/freescale/s32g3.dtsi b/arch/arm64/boot/dts/freescale/s32g3.dtsi
> > > index e314f3c7d61d..be03db737384 100644
> > > --- a/arch/arm64/boot/dts/freescale/s32g3.dtsi
> > > +++ b/arch/arm64/boot/dts/freescale/s32g3.dtsi
> > > @@ -383,6 +383,11 @@ usdhc0-200mhz-grp4 {
> > >  			};
> > >  		};
> > >  
> > > +		gpr: syscon@4007c000 {
> > > +			compatible = "nxp,s32g3-gpr", "syscon";
> > > +			reg = <0x4007c000 0x3000>;
> > > +		};
> > > +
> > >  		ocotp: nvmem@400a4000 {
> > >  			compatible = "nxp,s32g3-ocotp", "nxp,s32g2-ocotp";
> > >  			reg = <0x400a4000 0x400>;
> > > @@ -808,6 +813,7 @@ gmac0: ethernet@4033c000 {
> > >  			compatible = "nxp,s32g2-dwmac";
> > >  			reg = <0x4033c000 0x2000>, /* gmac IP */
> > >  			      <0x4007c004 0x4>;    /* GMAC_0_CTRL_STS */
> > > +			nxp,phy-sel = <&gpr 0x4>;
> > >  			interrupt-parent = <&gic>;
> > >  			interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
> > >  			interrupt-names = "macirq";
> > 
> > I gave this a test on an S32G-VNP-RDB3 and didn't see any issues on the
> > dwmac-s32 side, but this appears to trigger a panic when reading the new
> > debugfs regmap/*/registers file for the syscon node:
> > 
> >     # grep 4007c000 /proc/vmallocinfo
> >     0xffff800083da8000-0xffff800083dac000   16384 ioremap_prot+0x74/0xe0 phys=0x000000004007c000 ioremap
> >     # cat /sys/kernel/debug/regmap/dummy-syscon@0x000000004007c000/registers
> >     Internal error: synchronous external abort: 0000000096000210 [#1]  SMP
> >     [...]
> >     CPU: 0 UID: 0 PID: 4344 Comm: cat Tainted: G   M        E  X   ------  ---  6.12.0+ #226 PREEMPT_RT
> >     Tainted: [M]=MACHINE_CHECK, [E]=UNSIGNED_MODULE, [X]=AUX
> >     [...]
> >     pc : regmap_mmio_read32le+0x44/0xa0
> >     lr : regmap_mmio_read32le+0x44/0xa0
> >     [...]
> >     x23: ffff00080c080000 x22: ffff000802ac4c00 x21: ffff800087b13c9c
> >     x20: ffff800080a46494 x19: ffff800083da810c x18: 0000000000000004
> >     [...]
> >     x5 : ffff800080a46448 x4 : ffff800083da8000 x3 : ffff800080a46494
> >     x2 : ffff800080a47230 x1 : ffff800083da810c x0 : 0000000000000020
> >     Call trace:
> >      regmap_mmio_read32le+0x44/0xa0 (P)
> >      regmap_mmio_read+0x4c/0x80
> >      [...]
> >     Code: 52800400 8b214093 aa1303e1 97f4caf0 (b9400275)
> >     ---[ end trace 0000000000000000 ]---
> >     Kernel panic - not syncing: synchronous external abort: Fatal exception
> > 
> > Running this through decodecode gives:
> > 
> >     All code
> >     ========
> >        0:   52800400        mov     w0, #0x20                       // #32
> >        4:   8b214093        add     x19, x4, w1, uxtw
> >        8:   aa1303e1        mov     x1, x19
> >        c:   97f4caf0        bl      0xffffffffffd32bcc
> >       10:*  b9400275        ldr     w21, [x19]              <-- trapping instruction
> > 
> >     Code starting with the faulting instruction
> >     ===========================================
> >        0:   b9400275        ldr     w21, [x19]
> > 
> > x19's offset from the base address in /proc/vmallocinfo is 0x10c, which
> > points to a bad read at physical address 0x4007c10c; I also confirmed
> > that the preceding memory reads back without issues:

Oh, ugh...  I didn't realize that this wasn't merged.  I don't have a
way to test this any more.  The simplest fix would be to do change the
0x3000 to 0x100.  The GPR63 register is at 0xFC.

		reg = <0x4007c000 0x100>;

That's probably the best fix as well.  The later register areas would
be their own syscons.

regards,
dan carpenter


^ permalink raw reply

* Re: [PATCH v3 3/7] spi: qcom-qspi: Add interconnect support for memory path
From: Mark Brown @ 2026-04-20 16:44 UTC (permalink / raw)
  To: Viken Dadhaniya
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, cros-qcom-dts-watchers, linux-arm-msm, linux-spi,
	devicetree, linux-kernel
In-Reply-To: <20260420-spi-nor-v3-3-7de325a29010@oss.qualcomm.com>

[-- Attachment #1: Type: text/plain, Size: 784 bytes --]

On Mon, Apr 20, 2026 at 11:42:50AM +0530, Viken Dadhaniya wrote:
> The QSPI controller has two interconnect paths:
> 1. qspi-config: CPU to QSPI controller for register access
> 2. qspi-memory: QSPI controller to memory for DMA operations

> @@ -729,6 +737,11 @@ static int qcom_qspi_probe(struct platform_device *pdev)
>  		return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi),
>  				     "Failed to get cpu path\n");
>  
> +	ctrl->icc_path_mem = devm_of_icc_get(dev, "qspi-memory");
> +	if (IS_ERR(ctrl->icc_path_mem))
> +		return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_mem),
> +				     "Failed to get memory path\n");
> +

Does this handle missing qspi-memory properly?  It's not conditional on
the compatible so it'll run even for existing devices.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v2 1/5] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver
From: Sakari Ailus @ 2026-04-20 16:38 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Svyatoslav Ryhel, Lee Jones, Pavel Machek, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Mauro Carvalho Chehab,
	linux-leds, devicetree, linux-kernel, linux-media
In-Reply-To: <20260420-affection-ferocious-e28cd29f360a@spud>

Hi Conor,

On Mon, Apr 20, 2026 at 05:20:28PM +0100, Conor Dooley wrote:
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - ti,lm3559
> > +      - ti,lm3560
> 
> What differentiates these devices to the point that fallback compatibles
> are not suitable?

Good question.

It seems the currents are different albeit the register values themselves
are the same. The driver doesn't seem to handle that properly right now, so
it's a driver bug.

I'd keep the compatibles as-is as the current limit applied should be as
specified in DT.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply

* Re: [PATCH v2 1/5] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver
From: Svyatoslav Ryhel @ 2026-04-20 16:35 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab, linux-leds,
	devicetree, linux-kernel, linux-media
In-Reply-To: <20260420-affection-ferocious-e28cd29f360a@spud>

пн, 20 квіт. 2026 р. о 19:20 Conor Dooley <conor@kernel.org> пише:
>
> On Sun, Apr 19, 2026 at 12:34:08PM +0300, Svyatoslav Ryhel wrote:
> > Document TI LM3560 Synchronous Boost Flash Driver used for camera flash
> > LEDs.
> >
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> >  .../devicetree/bindings/leds/ti,lm3560.yaml   | 131 ++++++++++++++++++
> >  1 file changed, 131 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3560.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/leds/ti,lm3560.yaml b/Documentation/devicetree/bindings/leds/ti,lm3560.yaml
> > new file mode 100644
> > index 000000000000..c6c553ad23f9
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/leds/ti,lm3560.yaml
> > @@ -0,0 +1,131 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/leds/ti,lm3560.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: TI LM3560 Synchronous Boost Flash Driver
> > +
> > +maintainers:
> > +  - Svyatoslav Ryhel <clamor95@gmail.com>
> > +
> > +description:
> > +  The LM3560 is a 2-MHz fixed frequency synchronous boost converter with two
> > +  1000-mA constant current drivers for high-current white LEDs. The dual high-
> > +  side current sources allow for grounded cathode LED operation and can be
> > +  tied together for providing flash currents at up to 2 A through a single LED.
> > +  An adaptive regulation method ensures the current for each LED remains in
> > +  regulation and maximizes efficiency.
> > +
> > +allOf:
> > +  - $ref: /schemas/leds/common.yaml
> > +
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - ti,lm3559
> > +      - ti,lm3560
>
> What differentiates these devices to the point that fallback compatibles
> are not suitable?
>

noted

> Cheers,
> Conor.
>
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  '#address-cells':
> > +    const: 1
> > +
> > +  '#size-cells':
> > +    const: 0
> > +
> > +  enable-gpios:
> > +    description: GPIO connected to the HWEN pin.
> > +    maxItems: 1
> > +
> > +  vin-supply:
> > +    description: Supply connected to the IN line.
> > +
> > +  flash-max-timeout-us:
> > +    minimum: 32000
> > +    maximum: 1024000
> > +    default: 32000
> > +
> > +  ti,peak-current-microamp:
> > +    description:
> > +      The LM3560 features 4 selectable current limits 1.6A, 2.3A, 3A, and 3.6A.
> > +      When the current limit is reached, the LM3560 stops switching for the
> > +      remainder of the switching cycle.
> > +    enum: [16000000, 23000000, 30000000, 36000000]
> > +    default: 16000000
> > +
> > +patternProperties:
> > +  '^led@[01]$':
> > +    description: LED control bank nodes.
> > +    $ref: /schemas/leds/common.yaml#
> > +    unevaluatedProperties: false
> > +
> > +    properties:
> > +      reg:
> > +        description: Control bank selection (0 = bank A, 1 = bank B).
> > +        maximum: 1
> > +
> > +      flash-max-microamp:
> > +        minimum: 62500
> > +        maximum: 1000000
> > +
> > +      led-max-microamp:
> > +        minimum: 31250
> > +        maximum: 250000
> > +
> > +    required:
> > +      - reg
> > +      - flash-max-microamp
> > +      - led-max-microamp
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - '#address-cells'
> > +  - '#size-cells'
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/gpio/gpio.h>
> > +
> > +    i2c {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        led-controller@53 {
> > +            compatible = "ti,lm3560";
> > +            reg = <0x53>;
> > +
> > +            enable-gpios = <&gpio 28 GPIO_ACTIVE_HIGH>;
> > +            vin-supply = <&vdd_3v3_sys>;
> > +
> > +            flash-max-timeout-us = <1024000>;
> > +            ti,peak-current-microamp = <16000000>;
> > +
> > +            #address-cells = <1>;
> > +            #size-cells = <0>;
> > +
> > +            led@0 {
> > +                reg = <0>;
> > +
> > +                label = "white::flash";
> > +                linux,default-trigger = "flash";
> > +
> > +                flash-max-microamp = <562500>;
> > +                led-max-microamp = <156250>;
> > +            };
> > +
> > +            led@1 {
> > +                reg = <1>;
> > +
> > +                label = "yellow::flash";
> > +                linux,default-trigger = "flash";
> > +
> > +                flash-max-microamp = <562500>;
> > +                led-max-microamp = <156250>;
> > +            };
> > +        };
> > +    };
> > --
> > 2.51.0
> >

^ permalink raw reply

* [PATCH v4 3/3] hwrng: mtk - add support for hw access via SMCC
From: Daniel Golle @ 2026-04-20 16:35 UTC (permalink / raw)
  To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
	Sean Wang, Daniel Golle, linux-crypto, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <912fe579eccf577f3064b69d6c945e2c9087cab8.1776702734.git.daniel@makrotopia.org>

Newer versions of ARM TrustedFirmware-A on MediaTek's ARMv8 SoCs no longer
allow accessing the TRNG from outside of the trusted firmware.
Instead, a vendor-defined custom Secure Monitor Call can be used to
acquire random bytes.

Add support for newer SoCs (MT7981, MT7987, MT7988).

As TF-A for the MT7986 may either follow the old or the new
convention, the best bet is to test if firmware blocks direct access
to the hwrng and if so, expect the SMCC interface to be usable.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4: unchanged
v3: unchanged
v2: unchanged

 drivers/char/hw_random/mtk-rng.c | 127 ++++++++++++++++++++++++++-----
 1 file changed, 106 insertions(+), 21 deletions(-)

diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c
index 5808d09d12c45..8f5856b59ad66 100644
--- a/drivers/char/hw_random/mtk-rng.c
+++ b/drivers/char/hw_random/mtk-rng.c
@@ -3,6 +3,7 @@
  * Driver for Mediatek Hardware Random Number Generator
  *
  * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
  */
 #define MTK_RNG_DEV KBUILD_MODNAME
 
@@ -17,6 +18,8 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/arm-smccc.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
 
 /* Runtime PM autosuspend timeout: */
 #define RNG_AUTOSUSPEND_TIMEOUT		100
@@ -30,6 +33,11 @@
 
 #define RNG_DATA			0x08
 
+/* Driver feature flags */
+#define MTK_RNG_SMC			BIT(0)
+
+#define MTK_SIP_KERNEL_GET_RND		MTK_SIP_SMC_CMD(0x550)
+
 #define to_mtk_rng(p)	container_of(p, struct mtk_rng, rng)
 
 struct mtk_rng {
@@ -37,6 +45,7 @@ struct mtk_rng {
 	struct clk *clk;
 	struct hwrng rng;
 	struct device *dev;
+	unsigned long flags;
 };
 
 static int mtk_rng_init(struct hwrng *rng)
@@ -103,6 +112,56 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
 	return retval || !wait ? retval : -EIO;
 }
 
+static int mtk_rng_read_smc(struct hwrng *rng, void *buf, size_t max,
+			    bool wait)
+{
+	struct arm_smccc_res res;
+	int retval = 0;
+
+	while (max >= sizeof(u32)) {
+		arm_smccc_smc(MTK_SIP_KERNEL_GET_RND, 0, 0, 0, 0, 0, 0, 0,
+			      &res);
+		if (res.a0)
+			break;
+
+		*(u32 *)buf = res.a1;
+		retval += sizeof(u32);
+		buf += sizeof(u32);
+		max -= sizeof(u32);
+	}
+
+	return retval || !wait ? retval : -EIO;
+}
+
+static bool mtk_rng_hw_accessible(struct mtk_rng *priv)
+{
+	u32 val;
+	int err;
+
+	err = clk_prepare_enable(priv->clk);
+	if (err)
+		return false;
+
+	val = readl(priv->base + RNG_CTRL);
+	val |= RNG_EN;
+	writel(val, priv->base + RNG_CTRL);
+
+	val = readl(priv->base + RNG_CTRL);
+
+	if (val & RNG_EN) {
+		/* HW is accessible, clean up: disable RNG and clock */
+		writel(val & ~RNG_EN, priv->base + RNG_CTRL);
+		clk_disable_unprepare(priv->clk);
+		return true;
+	}
+
+	/*
+	 * If TF-A blocks direct access, the register reads back as 0.
+	 * Leave the clock enabled as TF-A needs it.
+	 */
+	return false;
+}
+
 static int mtk_rng_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -114,23 +173,42 @@ static int mtk_rng_probe(struct platform_device *pdev)
 
 	priv->dev = &pdev->dev;
 	priv->rng.name = pdev->name;
-#ifndef CONFIG_PM
-	priv->rng.init = mtk_rng_init;
-	priv->rng.cleanup = mtk_rng_cleanup;
-#endif
-	priv->rng.read = mtk_rng_read;
 	priv->rng.quality = 900;
-
-	priv->clk = devm_clk_get(&pdev->dev, "rng");
-	if (IS_ERR(priv->clk)) {
-		ret = PTR_ERR(priv->clk);
-		dev_err(&pdev->dev, "no clock for device: %d\n", ret);
-		return ret;
+	priv->flags = (unsigned long)device_get_match_data(&pdev->dev);
+
+	if (!(priv->flags & MTK_RNG_SMC)) {
+		priv->clk = devm_clk_get(&pdev->dev, "rng");
+		if (IS_ERR(priv->clk)) {
+			ret = PTR_ERR(priv->clk);
+			dev_err(&pdev->dev, "no clock for device: %d\n", ret);
+			return ret;
+		}
+
+		priv->base = devm_platform_ioremap_resource(pdev, 0);
+		if (IS_ERR(priv->base))
+			return PTR_ERR(priv->base);
+
+		if (IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) &&
+		    of_device_is_compatible(pdev->dev.of_node,
+					    "mediatek,mt7986-rng") &&
+		    !mtk_rng_hw_accessible(priv)) {
+			priv->flags |= MTK_RNG_SMC;
+			dev_info(&pdev->dev,
+				 "HW RNG not MMIO accessible, using SMC\n");
+		}
 	}
 
-	priv->base = devm_platform_ioremap_resource(pdev, 0);
-	if (IS_ERR(priv->base))
-		return PTR_ERR(priv->base);
+	if (priv->flags & MTK_RNG_SMC) {
+		if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC))
+			return -ENODEV;
+		priv->rng.read = mtk_rng_read_smc;
+	} else {
+#ifndef CONFIG_PM
+		priv->rng.init = mtk_rng_init;
+		priv->rng.cleanup = mtk_rng_cleanup;
+#endif
+		priv->rng.read = mtk_rng_read;
+	}
 
 	ret = devm_hwrng_register(&pdev->dev, &priv->rng);
 	if (ret) {
@@ -139,12 +217,15 @@ static int mtk_rng_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	dev_set_drvdata(&pdev->dev, priv);
-	pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT);
-	pm_runtime_use_autosuspend(&pdev->dev);
-	ret = devm_pm_runtime_enable(&pdev->dev);
-	if (ret)
-		return ret;
+	if (!(priv->flags & MTK_RNG_SMC)) {
+		dev_set_drvdata(&pdev->dev, priv);
+		pm_runtime_set_autosuspend_delay(&pdev->dev,
+						 RNG_AUTOSUSPEND_TIMEOUT);
+		pm_runtime_use_autosuspend(&pdev->dev);
+		ret = devm_pm_runtime_enable(&pdev->dev);
+		if (ret)
+			return ret;
+	}
 
 	dev_info(&pdev->dev, "registered RNG driver\n");
 
@@ -181,8 +262,11 @@ static const struct dev_pm_ops mtk_rng_pm_ops = {
 #endif	/* CONFIG_PM */
 
 static const struct of_device_id mtk_rng_match[] = {
-	{ .compatible = "mediatek,mt7986-rng" },
 	{ .compatible = "mediatek,mt7623-rng" },
+	{ .compatible = "mediatek,mt7981-rng", .data = (void *)MTK_RNG_SMC },
+	{ .compatible = "mediatek,mt7986-rng" },
+	{ .compatible = "mediatek,mt7987-rng", .data = (void *)MTK_RNG_SMC },
+	{ .compatible = "mediatek,mt7988-rng", .data = (void *)MTK_RNG_SMC },
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_rng_match);
@@ -200,4 +284,5 @@ module_platform_driver(mtk_rng_driver);
 
 MODULE_DESCRIPTION("Mediatek Random Number Generator Driver");
 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
 MODULE_LICENSE("GPL");
-- 
2.53.0

^ permalink raw reply related

* [PATCH v4 2/3] dt-bindings: rng: mtk-rng: add SMC-based TRNG variants
From: Daniel Golle @ 2026-04-20 16:35 UTC (permalink / raw)
  To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
	Sean Wang, Daniel Golle, linux-crypto, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <912fe579eccf577f3064b69d6c945e2c9087cab8.1776702734.git.daniel@makrotopia.org>

Add compatible strings for MediaTek SoCs where the hardware random number
generator is accessed via a vendor-defined Secure Monitor Call (SMC)
rather than direct MMIO register access:

  - mediatek,mt7981-rng
  - mediatek,mt7987-rng
  - mediatek,mt7988-rng

These variants require no reg, clocks, or clock-names properties since
the RNG hardware is managed by ARM Trusted Firmware-A.

Relax the $nodename pattern to also allow 'rng' in addition to the
existing 'rng@...' pattern.

Add a second example showing the minimal SMC variant binding.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4:
 * fix indentation in example

v3:
 * drop not: in compatible conditional
 * add reg/clocks/clock-names: false for mt7981-rng
 * add else: requiring reg/clocks/clock-names for others

v2: express compatibilities with fallback

 .../devicetree/bindings/rng/mtk-rng.yaml      | 32 ++++++++++++++++---
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/rng/mtk-rng.yaml b/Documentation/devicetree/bindings/rng/mtk-rng.yaml
index 8f2f4c32a0cfc..38e67861b88fa 100644
--- a/Documentation/devicetree/bindings/rng/mtk-rng.yaml
+++ b/Documentation/devicetree/bindings/rng/mtk-rng.yaml
@@ -11,12 +11,13 @@ maintainers:
 
 properties:
   $nodename:
-    pattern: "^rng@[0-9a-f]+$"
+    pattern: "^rng(@[0-9a-f]+)?$"
 
   compatible:
     oneOf:
       - enum:
           - mediatek,mt7623-rng
+          - mediatek,mt7981-rng
       - items:
           - enum:
               - mediatek,mt7622-rng
@@ -25,6 +26,11 @@ properties:
               - mediatek,mt8365-rng
               - mediatek,mt8516-rng
           - const: mediatek,mt7623-rng
+      - items:
+          - enum:
+              - mediatek,mt7987-rng
+              - mediatek,mt7988-rng
+          - const: mediatek,mt7981-rng
 
   reg:
     maxItems: 1
@@ -38,9 +44,23 @@ properties:
 
 required:
   - compatible
-  - reg
-  - clocks
-  - clock-names
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: mediatek,mt7981-rng
+    then:
+      properties:
+        reg: false
+        clocks: false
+        clock-names: false
+    else:
+      required:
+        - reg
+        - clocks
+        - clock-names
 
 additionalProperties: false
 
@@ -54,3 +74,7 @@ examples:
         clocks = <&infracfg CLK_INFRA_TRNG>;
         clock-names = "rng";
     };
+  - |
+    rng {
+        compatible = "mediatek,mt7981-rng";
+    };
-- 
2.53.0

^ permalink raw reply related

* [PATCH v4 1/3] dt-bindings: rng: mtk-rng: fix style problems in example
From: Daniel Golle @ 2026-04-20 16:34 UTC (permalink / raw)
  To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
	Sean Wang, Daniel Golle, linux-crypto, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek

Use 4 spaces for each level indentation, remove unused label, and add
missing empty line between header include and body.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4: new patch

 Documentation/devicetree/bindings/rng/mtk-rng.yaml | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/rng/mtk-rng.yaml b/Documentation/devicetree/bindings/rng/mtk-rng.yaml
index 7e8dc62e5d3a6..8f2f4c32a0cfc 100644
--- a/Documentation/devicetree/bindings/rng/mtk-rng.yaml
+++ b/Documentation/devicetree/bindings/rng/mtk-rng.yaml
@@ -47,9 +47,10 @@ additionalProperties: false
 examples:
   - |
     #include <dt-bindings/clock/mt2701-clk.h>
-    rng: rng@1020f000 {
-            compatible = "mediatek,mt7623-rng";
-            reg = <0x1020f000 0x1000>;
-            clocks = <&infracfg CLK_INFRA_TRNG>;
-            clock-names = "rng";
+
+    rng@1020f000 {
+        compatible = "mediatek,mt7623-rng";
+        reg = <0x1020f000 0x1000>;
+        clocks = <&infracfg CLK_INFRA_TRNG>;
+        clock-names = "rng";
     };
-- 
2.53.0

^ permalink raw reply related

* [PATCH 10/13] clk: qcom: dispcc-sm8750: Add support to control MDP clocks using CESTA
From: Jagadeesh Kona @ 2026-04-20 16:29 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

Add support to control the DISPCC MDSS MDP RCG and the associated display
PLL0 using display CESTA hardware on SM8750 platform. If display CRM is
enabled, the clock ops of these clocks will be updated by the common code
before registration to use CRM specific clock ops, allowing these clocks
to be controlled using display CRM (CESTA Resource Manager) hardware.

Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/clk/qcom/dispcc-sm8750.c | 89 +++++++++++++++++++++++++---------------
 1 file changed, 56 insertions(+), 33 deletions(-)

diff --git a/drivers/clk/qcom/dispcc-sm8750.c b/drivers/clk/qcom/dispcc-sm8750.c
index ca09da111a50e811481fd862b54d454de024d1c9..328e43b52192702dbbfd1ed65737520acdd4a649 100644
--- a/drivers/clk/qcom/dispcc-sm8750.c
+++ b/drivers/clk/qcom/dispcc-sm8750.c
@@ -71,6 +71,16 @@ enum {
 	P_SLEEP_CLK,
 };
 
+static struct clk_crm disp_crm = {
+	.max_perf_ol = 10,
+	.regs = {
+		.reg_cfg_rcgr_lut_base = 0xd8,
+		.reg_l_val_lut_base = 0xdc,
+		.vcd_offset = 0x268,
+		.lut_level_offset = 0x28,
+	},
+};
+
 static const struct pll_vco pongo_elu_vco[] = {
 	{ 38400000, 38400000, 0 },
 };
@@ -89,21 +99,22 @@ static struct alpha_pll_config disp_cc_pll0_config = {
 	.user_ctl_hi_val = 0x00000002,
 };
 
+static struct clk_init_data disp_cc_pll0_init = {
+	.name = "disp_cc_pll0",
+	.parent_data = &(const struct clk_parent_data) {
+		.index = DT_BI_TCXO,
+	},
+	.num_parents = 1,
+	.flags = CLK_GET_RATE_NOCACHE,
+	.ops = &clk_alpha_pll_taycan_elu_ops,
+};
+
 static struct clk_alpha_pll disp_cc_pll0 = {
 	.offset = 0x0,
 	.vco_table = taycan_elu_vco,
 	.num_vco = ARRAY_SIZE(taycan_elu_vco),
 	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_ELU],
-	.clkr = {
-		.hw.init = &(const struct clk_init_data) {
-			.name = "disp_cc_pll0",
-			.parent_data = &(const struct clk_parent_data) {
-				.index = DT_BI_TCXO,
-			},
-			.num_parents = 1,
-			.ops = &clk_alpha_pll_taycan_elu_ops,
-		},
-	},
+	.clkr.hw.init = &disp_cc_pll0_init,
 };
 
 static struct alpha_pll_config disp_cc_pll1_config = {
@@ -681,25 +692,25 @@ static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = {
 	{ }
 };
 
-static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = {
-	.cmd_rcgr = 0x8150,
-	.mnd_width = 0,
-	.hid_width = 5,
-	.parent_map = disp_cc_parent_map_9,
-	.freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
-	.clkr.hw.init = &(const struct clk_init_data) {
-		.name = "disp_cc_mdss_mdp_clk_src",
-		.parent_data = disp_cc_parent_data_9,
-		.num_parents = ARRAY_SIZE(disp_cc_parent_data_9),
-		.flags = CLK_SET_RATE_PARENT,
-		/*
-		 * TODO: Downstream does not manage the clock directly, but
-		 * places votes via new hardware block called "cesta".
-		 * It is not clear whether such approach should be taken instead
-		 * of manual control.
-		 */
-		.ops = &clk_rcg2_shared_ops,
+static struct clk_init_data disp_cc_mdss_mdp_clk_src_init = {
+	.name = "disp_cc_mdss_mdp_clk_src",
+	.parent_data = disp_cc_parent_data_9,
+	.num_parents = ARRAY_SIZE(disp_cc_parent_data_9),
+	.flags = CLK_SET_RATE_PARENT,
+	.ops = &clk_rcg2_shared_ops,
+};
+
+static struct clk_rcg2_crm disp_cc_mdss_mdp_clk_src = {
+	.rcg = {
+		.cmd_rcgr = 0x8150,
+		.mnd_width = 0,
+		.hid_width = 5,
+		.parent_map = disp_cc_parent_map_9,
+		.freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
+		.clkr.hw.init = &disp_cc_mdss_mdp_clk_src_init,
 	},
+	.crm_vcd = 1,
+	.crm = &disp_crm,
 };
 
 static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = {
@@ -1562,7 +1573,7 @@ static struct clk_branch disp_cc_mdss_mdp1_clk = {
 		.hw.init = &(const struct clk_init_data) {
 			.name = "disp_cc_mdss_mdp1_clk",
 			.parent_hws = (const struct clk_hw*[]) {
-				&disp_cc_mdss_mdp_clk_src.clkr.hw,
+				&disp_cc_mdss_mdp_clk_src.rcg.clkr.hw,
 			},
 			.num_parents = 1,
 			.flags = CLK_SET_RATE_PARENT,
@@ -1580,7 +1591,7 @@ static struct clk_branch disp_cc_mdss_mdp_clk = {
 		.hw.init = &(const struct clk_init_data) {
 			.name = "disp_cc_mdss_mdp_clk",
 			.parent_hws = (const struct clk_hw*[]) {
-				&disp_cc_mdss_mdp_clk_src.clkr.hw,
+				&disp_cc_mdss_mdp_clk_src.rcg.clkr.hw,
 			},
 			.num_parents = 1,
 			.flags = CLK_SET_RATE_PARENT,
@@ -1598,7 +1609,7 @@ static struct clk_branch disp_cc_mdss_mdp_lut1_clk = {
 		.hw.init = &(const struct clk_init_data) {
 			.name = "disp_cc_mdss_mdp_lut1_clk",
 			.parent_hws = (const struct clk_hw*[]) {
-				&disp_cc_mdss_mdp_clk_src.clkr.hw,
+				&disp_cc_mdss_mdp_clk_src.rcg.clkr.hw,
 			},
 			.num_parents = 1,
 			.flags = CLK_SET_RATE_PARENT,
@@ -1616,7 +1627,7 @@ static struct clk_branch disp_cc_mdss_mdp_lut_clk = {
 		.hw.init = &(const struct clk_init_data) {
 			.name = "disp_cc_mdss_mdp_lut_clk",
 			.parent_hws = (const struct clk_hw*[]) {
-				&disp_cc_mdss_mdp_clk_src.clkr.hw,
+				&disp_cc_mdss_mdp_clk_src.rcg.clkr.hw,
 			},
 			.num_parents = 1,
 			.flags = CLK_SET_RATE_PARENT,
@@ -1844,7 +1855,7 @@ static struct clk_regmap *disp_cc_sm8750_clocks[] = {
 	[DISP_CC_MDSS_ESC1_CLK_SRC] = &disp_cc_mdss_esc1_clk_src.clkr,
 	[DISP_CC_MDSS_MDP1_CLK] = &disp_cc_mdss_mdp1_clk.clkr,
 	[DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr,
-	[DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr,
+	[DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.rcg.clkr,
 	[DISP_CC_MDSS_MDP_LUT1_CLK] = &disp_cc_mdss_mdp_lut1_clk.clkr,
 	[DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr,
 	[DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr,
@@ -1866,6 +1877,11 @@ static struct clk_regmap *disp_cc_sm8750_clocks[] = {
 	[DISP_CC_XO_CLK_SRC] = &disp_cc_xo_clk_src.clkr,
 };
 
+static const struct crm_clk_data disp_cc_sm8750_crm_clks[] = {
+	CRM_CLK_PLL(disp_cc_pll0),
+	CRM_CLK_RCG_CRMB(disp_cc_mdss_mdp_clk_src),
+};
+
 static const struct qcom_reset_map disp_cc_sm8750_resets[] = {
 	[DISP_CC_MDSS_CORE_BCR] = { 0x8000 },
 	[DISP_CC_MDSS_CORE_INT2_BCR] = { 0xa000 },
@@ -1885,6 +1901,12 @@ static const struct regmap_config disp_cc_sm8750_regmap_config = {
 	.fast_io = true,
 };
 
+static struct qcom_cc_driver_data disp_cc_sm8750_driver_data = {
+	.crm = &disp_crm,
+	.crm_clks = disp_cc_sm8750_crm_clks,
+	.num_crm_clks = ARRAY_SIZE(disp_cc_sm8750_crm_clks),
+};
+
 static const struct qcom_cc_desc disp_cc_sm8750_desc = {
 	.config = &disp_cc_sm8750_regmap_config,
 	.clks = disp_cc_sm8750_clocks,
@@ -1893,6 +1915,7 @@ static const struct qcom_cc_desc disp_cc_sm8750_desc = {
 	.num_resets = ARRAY_SIZE(disp_cc_sm8750_resets),
 	.gdscs = disp_cc_sm8750_gdscs,
 	.num_gdscs = ARRAY_SIZE(disp_cc_sm8750_gdscs),
+	.driver_data = &disp_cc_sm8750_driver_data,
 };
 
 static const struct of_device_id disp_cc_sm8750_match_table[] = {

-- 
2.34.1


^ permalink raw reply related

* [PATCH 09/13] clk: qcom: common: Add support to register and control clocks using CRM
From: Jagadeesh Kona @ 2026-04-20 16:29 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

Add support for helper macros to maintain the list of CRM clocks in a
clock controller. Add support for qcom_cc_register_cesta_clks() which takes
this list of CRM clocks and update the ops of these clocks to CRM clock ops
only if CRM is enabled.

Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/clk/qcom/common.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/common.h | 19 +++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 968e19997daf5eebec65315974cd0c41e08e55ec..14dfae80e567852d09a5972858ae14c62e5074ee 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -10,10 +10,13 @@
 #include <linux/platform_device.h>
 #include <linux/clk-provider.h>
 #include <linux/interconnect-clk.h>
+#include <linux/mfd/syscon.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset-controller.h>
 #include <linux/of.h>
 
+#include <soc/qcom/crm.h>
+
 #include "common.h"
 #include "clk-alpha-pll.h"
 #include "clk-branch.h"
@@ -266,6 +269,47 @@ int qcom_cc_register_sleep_clk(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
 
+static void qcom_register_crm_clk(const struct crm_clk_data *data)
+{
+	struct clk_init_data *init = data->init;
+
+	switch (data->flags) {
+	case CRM_PLL:
+		init->ops = &clk_alpha_pll_crm_ops;
+		break;
+	case CRM_RCG_CRMB:
+		init->ops = &clk_rcg2_crmb_ops;
+		break;
+	default:
+		pr_err("Invalid CRM flag for %s\n", init->name);
+		break;
+	}
+}
+
+static int qcom_cc_register_crm_clks(struct device *dev, struct clk_crm *crm,
+				     const struct crm_clk_data *clks, size_t len)
+{
+	int i;
+
+	crm->crm_dev = crm_get(dev);
+	if (IS_ERR(crm->crm_dev))
+		return PTR_ERR(crm->crm_dev);
+
+	if (crm->crm_dev && crm->max_perf_ol) {
+		crm->regmap_crmc = syscon_regmap_lookup_by_phandle(dev->of_node,
+								   "qcom,crmc-regmap");
+		if (IS_ERR(crm->regmap_crmc))
+			return PTR_ERR(crm->regmap_crmc);
+
+		for (i = 0; i < len; i++)
+			qcom_register_crm_clk(&clks[i]);
+
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
 /* Drop 'protected-clocks' from the list of clocks to register */
 static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
 {
@@ -439,6 +483,16 @@ int qcom_cc_really_probe(struct device *dev,
 			goto put_rpm;
 	}
 
+	if (desc->driver_data &&
+	    desc->driver_data->crm) {
+		ret = qcom_cc_register_crm_clks(dev, desc->driver_data->crm,
+						desc->driver_data->crm_clks,
+						desc->driver_data->num_crm_clks);
+		if (ret)
+			dev_err_probe(dev, ret,
+					"Failed to register clocks with CRM, ret: %d\n", ret);
+	}
+
 	cc->rclks = rclks;
 	cc->num_rclks = num_clks;
 
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 9987cec84324a258f3405c7b9093237f52a41b45..d00376f058ce1de7aa82ff760f92826282b1606f 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -35,6 +35,9 @@ struct qcom_cc_driver_data {
 	size_t num_clk_cbcrs;
 	const struct clk_rcg_dfs_data *dfs_rcgs;
 	size_t num_dfs_rcgs;
+	struct clk_crm *crm;
+	const struct crm_clk_data *crm_clks;
+	size_t num_crm_clks;
 	void (*clk_regs_configure)(struct device *dev, struct regmap *regmap);
 };
 
@@ -97,6 +100,22 @@ struct clk_crm {
 	u8 client_idx;
 };
 
+struct crm_clk_data {
+	struct clk_init_data *init;
+#define CRM_PLL		BIT(0)
+#define CRM_RCG_CRMB	BIT(1)
+	u8 flags;
+};
+
+#define CRM_CLK(clk, flag)		\
+{					\
+	.init = &clk##_init,		\
+	.flags = flag,			\
+}
+
+#define CRM_CLK_PLL(clk) CRM_CLK(clk, CRM_PLL)
+#define CRM_CLK_RCG_CRMB(clk) CRM_CLK(clk, CRM_RCG_CRMB)
+
 extern int qcom_find_crm_freq_index(const struct freq_tbl *f, unsigned long rate);
 extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
 					     unsigned long rate);

-- 
2.34.1


^ permalink raw reply related

* [PATCH 08/13] clk: qcom: clk-rcg2: Add support for CRM based RCG ops
From: Jagadeesh Kona @ 2026-04-20 16:29 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

Add clock ops to control RCGs via the CESTA Resource Manager(CRM).
This adds support for clk_rcg2_crmb_ops where the RCG's frequency
table is dynamically populated by reading the LUT entries provided
by CRM. For scaling the RCG, the RCG frequency is converted to BCM
TCS bandwidth vote and sent to CESTA HW using CRM APIs. The CESTA
HW would then scale the RCG to the desired frequency requested via
CRM.

Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/clk/qcom/clk-rcg.h  |  23 ++++-
 drivers/clk/qcom/clk-rcg2.c | 242 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 264 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 4fbdf4880d0308714a2d1dc8c510ec3819206b9e..4872e7f9caa1096bdebca36dfbb439dc790638ef 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -1,5 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved. */
+/*
+ * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
 
 #ifndef __QCOM_CLK_RCG_H__
 #define __QCOM_CLK_RCG_H__
@@ -175,9 +178,26 @@ struct clk_rcg2 {
 	u8			cfg_off;
 	u32			parked_cfg;
 	bool			hw_clk_ctrl;
+
+};
+
+/**
+ * struct clk_rcg2_crm - root clock generator controlled via CRM
+ * (CESTA Resource Manager)
+ *
+ * @rcg: root clock generator
+ * @crm_vcd: Virtual Clock Domain(VCD) index for a CRM controlled RCG
+ * @crm: pointer to CLK CRM instance used for reading frequency LUTs
+ *	 and to communicate with CESTA HW
+ */
+struct clk_rcg2_crm {
+	struct clk_rcg2		rcg;
+	u8			crm_vcd;
+	struct clk_crm		*crm;
 };
 
 #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
+#define to_clk_rcg2_crm(_hw) container_of(to_clk_rcg2(_hw), struct clk_rcg2_crm, rcg)
 
 struct clk_rcg2_gfx3d {
 	u8 div;
@@ -202,6 +222,7 @@ extern const struct clk_ops clk_rcg2_shared_ops;
 extern const struct clk_ops clk_rcg2_shared_floor_ops;
 extern const struct clk_ops clk_rcg2_shared_no_init_park_ops;
 extern const struct clk_ops clk_dp_ops;
+extern const struct clk_ops clk_rcg2_crmb_ops;
 
 struct clk_rcg_dfs_data {
 	struct clk_rcg2 *rcg;
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 6064a0e17d5190e9d228688c04c0d4947876b4e6..668115b94a0f202cf3ac4f68efe3cef1684055be 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #include <linux/kernel.h>
@@ -16,9 +17,12 @@
 #include <linux/math64.h>
 #include <linux/gcd.h>
 #include <linux/minmax.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #include <asm/div64.h>
+#include <soc/qcom/crm.h>
+#include <soc/qcom/tcs.h>
 
 #include "clk-rcg.h"
 #include "common.h"
@@ -59,6 +63,33 @@
 #define SE_PERF_M_DFSR(level)	(0x5c + 0x4 * (level))
 #define SE_PERF_N_DFSR(level)	(0x9c + 0x4 * (level))
 
+/* Cesta configuration*/
+#define MAX_CRM_SW_DRV_STATE	3
+
+/* Address offset for specific VCD */
+#define CRMC_OFFS_VCD(crm, _vcd) \
+	((crm)->regs.vcd_offset * (_vcd))
+
+/* LUT registers address offset for specific vcd and lut entry level */
+#define CRMC_OFFS_LUT(crm, vcd, _level) \
+	(CRMC_OFFS_VCD(crm, vcd) + ((crm)->regs.lut_level_offset * (_level)))
+
+/* CFG_RCGR LUT register address offset for specific vcd and lut entry level */
+#define CRMC_OFFS_CFG_RCGR(crm, vcd, level) \
+	(CRMC_OFFS_LUT(crm, vcd, level) + (crm)->regs.reg_cfg_rcgr_lut_base)
+
+/* L_VAL LUT register address offset for specific vcd and lut entry level */
+#define CRMC_OFFS_L_VAL(crm, vcd, level) \
+	(CRMC_OFFS_LUT(crm, vcd, level) + (crm)->regs.reg_l_val_lut_base)
+
+#define PLL_L_VAL_MASK		GENMASK(7, 0)
+#define PLL_ALPHA_VAL_MASK	GENMASK(31, 16)
+#define PLL_ALPHA_VAL_SHIFT	16
+#define PERF_OL_OFF		0
+#define XO_FREQ			19200000ULL
+#define CALC_PLL_RATE(l, a)	((XO_FREQ * (l)) + ((XO_FREQ * (a)) >> 16))
+#define HZ_TO_MHZ(rate)		(rate / 1000000ULL)
+
 enum freq_policy {
 	FLOOR,
 	CEIL,
@@ -1859,3 +1890,214 @@ const struct clk_ops clk_dp_ops = {
 	.determine_rate = clk_rcg2_dp_determine_rate,
 };
 EXPORT_SYMBOL_GPL(clk_dp_ops);
+
+static int clk_rcg2_crm_populate_freq(struct clk_hw *hw, unsigned int lut_index,
+				      struct freq_tbl *f)
+{
+	u32 mask, cfg_rcgr, src, pll_lval, lval, alpha_val, num_parents, pindex;
+	struct clk_rcg2_crm *rcg_crm = to_clk_rcg2_crm(hw);
+	struct clk_rcg2 *rcg = &rcg_crm->rcg;
+	struct clk_crm *crm = rcg_crm->crm;
+	u32 vcd = rcg_crm->crm_vcd;
+	unsigned long prate = 0;
+	struct clk_hw *parent;
+
+	/* Read CFG_RCGR and PLL LUT entries */
+	regmap_read(crm->regmap_crmc, CRMC_OFFS_CFG_RCGR(crm, vcd, lut_index), &cfg_rcgr);
+	regmap_read(crm->regmap_crmc, CRMC_OFFS_L_VAL(crm, vcd, lut_index), &pll_lval);
+
+	/* Calculate the pre_div and parent source from LUT entry */
+	mask = BIT(rcg->hid_width) - 1;
+	f->pre_div = 1;
+	if (cfg_rcgr & mask)
+		f->pre_div = cfg_rcgr & mask;
+
+	src = cfg_rcgr & CFG_SRC_SEL_MASK;
+	src >>= CFG_SRC_SEL_SHIFT;
+
+	lval = pll_lval & PLL_L_VAL_MASK;
+	alpha_val = (pll_lval & PLL_ALPHA_VAL_MASK) >> PLL_ALPHA_VAL_SHIFT;
+
+	/* Find the matching parent and calculate it's rate for LUT entry */
+	num_parents = clk_hw_get_num_parents(hw);
+	for (pindex = 0; pindex < num_parents; pindex++) {
+		if (src == rcg->parent_map[pindex].cfg) {
+			f->src = rcg->parent_map[pindex].src;
+
+			parent = clk_hw_get_parent_by_index(hw, pindex);
+			if (!parent)
+				return -EINVAL;
+
+			if (!lval)
+				prate = clk_hw_get_rate(parent);
+			else
+				prate = CALC_PLL_RATE(lval, alpha_val);
+
+			break;
+		}
+	}
+
+	if (!prate)
+		return -EINVAL;
+
+	/* Calculate the RCG's rate from parent rate */
+	f->freq = calc_rate(prate, 0, 0, 0, f->pre_div);
+
+	return 0;
+}
+
+static int clk_rcg2_crm_populate_freq_table(struct clk_hw *hw)
+{
+	struct freq_tbl *freq_tbl, *curr_freq_tbl;
+	struct clk_rcg2_crm *rcg_crm = to_clk_rcg2_crm(hw);
+	struct clk_rcg2 *rcg = &rcg_crm->rcg;
+	struct clk_crm *crm = rcg_crm->crm;
+	u32 prev_freq = 0;
+	int i, ret;
+
+	/* Allocate space for 1 extra since table is NULL terminated */
+	freq_tbl = kcalloc(crm->max_perf_ol + 1, sizeof(*freq_tbl), GFP_KERNEL);
+	if (!freq_tbl)
+		return -ENOMEM;
+
+	for (i = 0; i < crm->max_perf_ol; i++) {
+		/* Skip first LUT entry as first entry is used to disable RCG */
+		ret = clk_rcg2_crm_populate_freq(hw, i + 1, freq_tbl + i);
+		if (ret) {
+			kfree(freq_tbl);
+			return ret;
+		}
+
+		curr_freq_tbl = freq_tbl + i;
+
+		/* Two of the same/decreasing frequencies means end of LUT */
+		if (prev_freq >= curr_freq_tbl->freq) {
+			curr_freq_tbl->freq = 0;
+			break;
+		}
+
+		prev_freq = curr_freq_tbl->freq;
+	}
+
+	rcg->freq_tbl = freq_tbl;
+
+	return 0;
+}
+
+static int clk_rcg2_crm_determine_rate(struct clk_hw *hw,
+				       struct clk_rate_request *req)
+{
+	struct device *dev = clk_hw_get_dev(hw);
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+	int ret;
+
+	if (!rcg->freq_tbl) {
+		ret = pm_runtime_get_sync(dev);
+		if (ret < 0)
+			return ret;
+
+		ret = clk_rcg2_crm_populate_freq_table(hw);
+		if (ret) {
+			pm_runtime_put(dev);
+			return ret;
+		}
+
+		pm_runtime_put(dev);
+	}
+
+	return clk_rcg2_determine_rate(hw, req);
+}
+
+static unsigned long
+clk_rcg2_crm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+	if (!rcg->freq_tbl)
+		return clk_rcg2_recalc_rate(hw, parent_rate);
+
+	return rcg->freq_tbl[rcg->parked_cfg].freq;
+}
+
+static int clk_rcg2_crm_init(struct clk_hw *hw)
+{
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+	rcg->freq_tbl = NULL;
+
+	return 0;
+}
+
+static int clk_rcg2_vote_bw(struct clk_hw *hw, unsigned long rate)
+{
+	struct clk_rcg2_crm *rcg_crm = to_clk_rcg2_crm(hw);
+	struct clk_crm *crm = rcg_crm->crm;
+	struct crm_cmd cmd;
+	int ret, i;
+
+	cmd.resource_idx = 0;
+	cmd.wait = true;
+	cmd.data = BCM_TCS_CMD(1, 1, 0, HZ_TO_MHZ(rate));
+
+	for (i = 0; i < MAX_CRM_SW_DRV_STATE; i++) {
+		cmd.pwr_state = i;
+		ret = crm_write_bw_vote(crm->crm_dev, crm->client_idx, &cmd);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int clk_rcg2_crmb_prepare(struct clk_hw *hw)
+{
+	unsigned long rate = clk_hw_get_rate(hw);
+
+	if (!rate)
+		return -EINVAL;
+
+	return clk_rcg2_vote_bw(hw, rate);
+}
+
+static void clk_rcg2_crmb_unprepare(struct clk_hw *hw)
+{
+	clk_rcg2_vote_bw(hw, PERF_OL_OFF);
+}
+
+static int clk_rcg2_crmb_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_rcg2_crm *rcg_crm = to_clk_rcg2_crm(hw);
+	struct clk_rcg2 *rcg = &rcg_crm->rcg;
+	struct clk_crm *crm = rcg_crm->crm;
+	int perf_index;
+
+	perf_index = qcom_find_crm_freq_index(rcg->freq_tbl, rate);
+	if (perf_index <= PERF_OL_OFF || perf_index > crm->max_perf_ol)
+		return -EINVAL;
+
+	/*
+	 * perf_index returned from qcom_find_crm_freq_index() starts from
+	 * index 1 since 0 is treated as OFF by CRM, but RCG's frequency
+	 * table starts from index 0, store parked_cfg aligning to RCG's
+	 * frequency table index.
+	 */
+	rcg->parked_cfg = perf_index - 1;
+
+	if (!clk_hw_is_prepared(hw))
+		return 0;
+
+	return clk_rcg2_vote_bw(hw, rate);
+}
+
+const struct clk_ops clk_rcg2_crmb_ops = {
+	.prepare = clk_rcg2_crmb_prepare,
+	.unprepare = clk_rcg2_crmb_unprepare,
+	.is_enabled = clk_rcg2_is_enabled,
+	.get_parent = clk_rcg2_get_parent,
+	.set_rate = clk_rcg2_crmb_set_rate,
+	.determine_rate = clk_rcg2_crm_determine_rate,
+	.recalc_rate = clk_rcg2_crm_recalc_rate,
+	.init = clk_rcg2_crm_init,
+};
+EXPORT_SYMBOL_GPL(clk_rcg2_crmb_ops);

-- 
2.34.1


^ permalink raw reply related

* [PATCH 07/13] clk: qcom: clk-alpha-pll: Add support to skip PLL configuration
From: Jagadeesh Kona @ 2026-04-20 16:29 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

Some PLLs are already configured as part of CRM(CESTA Resource
manager) initialization. Add support to skip PLL reconfiguration
for such PLLs that are already configured.

Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/clk/qcom/clk-alpha-pll.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 67fc97739d0d4c26aec0bac5d43d1b87d297bc6a..2f4ebf4d3884b92c981dbe0e67245704a88881ad 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -2332,7 +2332,7 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_zonda_ops);
 void clk_lucid_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 				 const struct alpha_pll_config *config)
 {
-	u32 lval = config->l;
+	u32 lval = config->l, regval;
 
 	/*
 	 * If the bootloader left the PLL enabled it's likely that there are
@@ -2343,6 +2343,12 @@ void clk_lucid_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regma
 		return;
 	}
 
+	/* Return early if PLL is already configured */
+	regmap_read(regmap, PLL_L_VAL(pll), &regval);
+	regval &= LUCID_EVO_PLL_L_VAL_MASK;
+	if (regval)
+		return;
+
 	if (config->cal_l)
 		lval |= config->cal_l << LUCID_EVO_PLL_CAL_L_VAL_SHIFT;
 	else

-- 
2.34.1


^ permalink raw reply related

* [PATCH 06/13] clk: qcom: clk-alpha-pll: Add support for CRM based PLL ops
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

Add clock ops for PLLs that are controlled by the CESTA Resource
Manager(CRM). For these PLLs, CRM programs the hardware based on
aggregated votes, so the clock driver does not need to program the
PLL registers directly, but can still model the PLLs and expose
them as parents to RCGs in the common clock framework.

Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/clk/qcom/clk-alpha-pll.c | 7 +++++++
 drivers/clk/qcom/clk-alpha-pll.h | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index f8313f9d0e30ffd5393e3163ebc0e51b2bd96437..67fc97739d0d4c26aec0bac5d43d1b87d297bc6a 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -2,6 +2,7 @@
 /*
  * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
  * Copyright (c) 2021, 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #include <linux/kernel.h>
@@ -2585,6 +2586,12 @@ const struct clk_ops clk_alpha_pll_reset_lucid_evo_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_alpha_pll_reset_lucid_evo_ops);
 
+const struct clk_ops clk_alpha_pll_crm_ops = {
+	.recalc_rate = alpha_pll_lucid_evo_recalc_rate,
+	.determine_rate = clk_alpha_pll_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_crm_ops);
+
 static int alpha_pll_pongo_elu_prepare(struct clk_hw *hw)
 {
 	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 42d3344921457c39deadcca5c43d72ded3b572a3..1ef515708ce19e636eb9a59b126df5048e262d85 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -2,6 +2,7 @@
 /*
  * Copyright (c) 2015, 2018, 2021 The Linux Foundation. All rights reserved.
  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 
@@ -219,6 +220,8 @@ extern const struct clk_ops clk_alpha_pll_rivian_evo_ops;
 extern const struct clk_ops clk_alpha_pll_regera_ops;
 extern const struct clk_ops clk_alpha_pll_slew_ops;
 
+extern const struct clk_ops clk_alpha_pll_crm_ops;
+
 void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 			     const struct alpha_pll_config *config);
 void clk_huayra_2290_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,

-- 
2.34.1


^ permalink raw reply related

* [PATCH 05/13] clk: qcom: common: Add helpers to control clocks using CRM
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

CESTA Resource Manager (CRM) scales clock frequencies based on
performance operating levels. Introduce qcom_find_crm_freq_index()
to convert a requested clock rate into a CRM LUT performance level.

Additionally, add clk_crm and qcom_clk_crm_soc_data structures, which
capture the CRM-specific clock configuration for a clock controller
(register layout, LUT information, CRM device etc). This info is used
to read frequency lookup tables provided by CRM and for sending votes
through CRM to scale the clock frequencies.

Co-developed-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/clk/qcom/common.c | 39 +++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/common.h | 38 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index eec369d2173b5ce24bc1ca860d2ac1bbdce04524..968e19997daf5eebec65315974cd0c41e08e55ec 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
 #include <linux/export.h>
@@ -80,6 +81,44 @@ const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
 }
 EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
 
+/**
+ * qcom_find_crm_freq_index - Map a clock rate to a CRM LUT performance level
+ * @f: pointer to the frequency table
+ * @rate: requested clock rate in Hz
+ *
+ * Convert a requested clock rate into a CRM LUT performance level index.
+ * CRM treats performance level 0 as CLK_OFF, so valid performance levels
+ * start from 1.
+ *
+ * Return: a non-negative performance level index on success, or -EINVAL
+ * if @f is NULL or the frequency table is empty.
+ */
+
+int qcom_find_crm_freq_index(const struct freq_tbl *f, unsigned long rate)
+{
+	int index;
+
+	if (!f || !f->freq)
+		return -EINVAL;
+
+	/* Return 0 for CRM to disable the clocks */
+	if (!rate)
+		return 0;
+
+	/*
+	 * Calculate the perf level for supported frequencies starting
+	 * from 1, since PERF_OL 0 is treated as CLK_OFF by CRM.
+	 */
+	for (index = 1; f->freq; f++, index++) {
+		if (rate <= f->freq)
+			return index;
+	}
+
+	/* Default to highest corner */
+	return index - 1;
+}
+EXPORT_SYMBOL_GPL(qcom_find_crm_freq_index);
+
 int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
 {
 	int i, num_parents = clk_hw_get_num_parents(hw);
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 6f2406f8839e8f432ef34ce32c930cf045f6ae7a..9987cec84324a258f3405c7b9093237f52a41b45 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -1,5 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved. */
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
 
 #ifndef __QCOM_CLK_COMMON_H__
 #define __QCOM_CLK_COMMON_H__
@@ -62,6 +65,39 @@ struct parent_map {
 	u8 cfg;
 };
 
+/**
+ * struct qcom_clk_crm_soc_data - CRM clock register layout for a clock controller
+ *
+ * @reg_cfg_rcgr_lut_base: Base register address for CFG_RCGR LUT values
+ * @reg_l_val_lut_base: Base register address for PLL L and ALPHA LUT values
+ * @vcd_offset: Offset between consecutive VCD blocks
+ * @lut_level_offset: Offset between consecutive LUT levels within a VCD
+ */
+struct qcom_clk_crm_soc_data {
+	u32 reg_cfg_rcgr_lut_base;
+	u32 reg_l_val_lut_base;
+	u32 vcd_offset;
+	u32 lut_level_offset;
+};
+
+/**
+ * struct clk_crm - Clock CRM used to communicate with CRM
+ *
+ * @crm_dev: Pointer to CRM device structure used to communicate with CESTA
+ * @regmap_crmc: regmap for the crmc instance to read frequency look up tables
+ * @regs: CRMC (CRM clock) base register addresses and offsets
+ * @max_perf_ol: Maximum number of performance operating levels supported
+ * @client_idx: SW Client Index
+ */
+struct clk_crm {
+	struct device *crm_dev;
+	struct regmap *regmap_crmc;
+	struct qcom_clk_crm_soc_data regs;
+	u8 max_perf_ol;
+	u8 client_idx;
+};
+
+extern int qcom_find_crm_freq_index(const struct freq_tbl *f, unsigned long rate);
 extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
 					     unsigned long rate);
 extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,

-- 
2.34.1


^ permalink raw reply related

* [PATCH 04/13] soc: qcom: Introduce CESTA resource manager driver
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

From: Maulik Shah <maulik.shah@oss.qualcomm.com>

Add driver for QCOM SoC that have hardware based resource management.

The hardware IP known as CESTA (Client State Aggregation) have different
Resource Manager (CESTA Resource Manager) or CRM.

CRM can be of 4 different types,
  - CRMC is CRM for clocks local to subsystem
  - CRMB is CRM for bandwidth votes local to subsystem
  - CRMB_PT is pass through CRM for bandwidth votes global to subsystem
  - CRMV is CRM for voltage rail / LDO requirements global to subsystem

A set of SW DRVs (Direct Resource Voters) can vote to all/subset of
CRMC/CRMB/CRMB_PT which then takes care of the respective resource voting.
There is no direct voter on CRMV, rather the CRMC votes picks up the
respective voltage from LUTs and scales the voltage accordingly required
for clocks or bandwidth votes.

Some important aspects of the CRM communication -
 - Resource votes can be placed in the form of performance operating
   levels or bandwidth votes for local/global NoCs
 - SW DRVs vote can be of the Active / Sleep / Wake type
 - All the resource votes are in the form of CESTA command
   (resource id, value) pair.

Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 drivers/soc/qcom/Kconfig     |  10 +
 drivers/soc/qcom/Makefile    |   2 +
 drivers/soc/qcom/crm.c       | 996 +++++++++++++++++++++++++++++++++++++++++++
 drivers/soc/qcom/trace-crm.h | 111 +++++
 include/soc/qcom/crm.h       |  72 ++++
 5 files changed, 1191 insertions(+)

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 2caadbbcf8307ff94f5afbdd1481e5e5e291749f..50f6c3cbf12f87ca2bab183e7996dba7312b9f58 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -26,6 +26,16 @@ config QCOM_COMMAND_DB
 	  resource on a RPM-hardened platform must use this database to get
 	  SoC specific identifier and information for the shared resources.
 
+config QCOM_CRM
+	tristate "Qualcomm Technologies, Inc. (QTI) CESTA Resource Manager (CRM) driver"
+	depends on QCOM_RPMH && (ARCH_QCOM || COMPILE_TEST)
+	help
+	  Support for communication with the hardened-CRM blocks in
+	  Qualcomm Technologies, Inc. (QTI) SoCs. CRM provides interface
+	  to vote desired power state of resources local or global to a subsystem.
+	  A set of hardware components aggregate requests for these resources and
+	  help apply the aggregated power state on the resource.
+
 config QCOM_GENI_SE
 	tristate "QCOM GENI Serial Engine Driver"
 	depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index b7f1d2a5736748b8772c090fd24462fa91f321c6..7f465e86c1b8a4c2657870783a22acc4315a7407 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,9 +1,11 @@
 # SPDX-License-Identifier: GPL-2.0
+CFLAGS_crm.o := -I$(src)
 CFLAGS_rpmh-rsc.o := -I$(src)
 CFLAGS_qcom_aoss.o := -I$(src)
 obj-$(CONFIG_QCOM_AOSS_QMP) +=	qcom_aoss.o
 obj-$(CONFIG_QCOM_GENI_SE) +=	qcom-geni-se.o
 obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
+obj-$(CONFIG_QCOM_CRM)	+= crm.o
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
 obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
 obj-$(CONFIG_QCOM_OCMEM)	+= ocmem.o
diff --git a/drivers/soc/qcom/crm.c b/drivers/soc/qcom/crm.c
new file mode 100644
index 0000000000000000000000000000000000000000..9fc4460a65f7f0b4cc262da48f49b769db434082
--- /dev/null
+++ b/drivers/soc/qcom/crm.c
@@ -0,0 +1,996 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <soc/qcom/crm.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace-crm.h"
+
+#undef field_get
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+
+#define MAX_NAME_LENGTH			20
+
+/* Applicable for SW DRVs BW Registers */
+#define PERF_OL_VALUE_BITS		0x7
+
+/* Applicable for SW DRVs BW Registers */
+#define BW_VOTE_VALID			BIT(29)
+/* Applicable only for SW DRVs BW PT Registers */
+#define BW_PT_VOTE_VALID		BIT(29)
+#define BW_PT_VOTE_TRIGGER		BIT(0)
+/* Applicable only for SW DRVs BW Registers */
+#define BW_VOTE_RESP_REQ		BIT(31)
+
+/* Set 1 to Enable IRQ for each VCD */
+#define IRQ_ENABLE_BIT			BIT(0)
+#define IRQ_CLEAR_BIT			BIT(0)
+
+/* SW DRV has ACTIVE, SLEEP and WAKE PWR STATES */
+
+#define MAX_SW_DRV_PWR_STATES		3
+/* Time out for ACTIVE Only PWR STATE completion IRQ */
+#define CRM_TIMEOUT_MS			msecs_to_jiffies(10000)
+
+/*
+ * Here's a high level overview of CESTA - Client State Aggregation
+ *
+ * - CESTA resource manager (CRM) is used for client side resource management.
+ *   These local resources can be clocks or NoCs. A vote can be placed by DRVs
+ *   (Direct Resource Voters) which can be either SW DRV or HW DRV.
+ *
+ * - Current driver supports placing votes via SW DRVs.
+ *
+ * - A resource vote can be placed by voting to VCD (Virtual Clock Domain) or
+ *   Node (ND).
+ *   Vote can be in the form of
+ *   (1) performance operating level votes - Local clocks scaling
+ *   (2) bandwidth votes - Local NoCs scaling (Camera / Display NoC)
+ *   (3) bandwidth pass through votes - Global resource scaling (DDR)
+ *   Not all CRM can vote via all three forms
+ *
+ * - The base crm address is the DRV base of a register space that is used to place votes.
+ *   Each VCD/ND/PT_ND has IRQ Status/Clear/Enable registers.
+ *
+ * - The config crm address is used to find overall configuration of the hardware including
+ *   version, num_perf_ol_vcds, num_nds and num_nds_pt and no. of SW DRVs.
+ *
+ * Here's a picture:
+ *
+ *  +--------------------------------------------------------+
+ *  | CRM                                                    |
+ *  |                                                        |
+ *  | +----------------------------------------------------+ |
+ *  | | SW DRV0                                            | |
+ *  | |                                                    | |
+ *  | | +--------------------------------------------- --+ | |
+ *  | | | PERF OL VCDs                                   | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | | |VCD 0      |VCD 1      |....num_perf_ol_vcds| | | |
+ *  | | | |-----------|--------------------------------| | | |
+ *  | | | |IRQ STATUS |IRQ STATUS |... IRQ_STATUS      | | | |
+ *  | | | |IRQ CLEAR  |IRQ CLEAR  |... IRQ CLEAR       | | | |
+ *  | | | |IRQ ENABLE |IRQ ENABLE |... IRQ ENABLE      | | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | |                                                | | |
+ *  | | +------------------------------------------------+ | |
+ *  | | | BW VOTE Nodes                                  | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | | |Node 0     |Node 1     |....num_nds         | | | |
+ *  | | | |-----------|--------------------------------| | | |
+ *  | | | |IRQ STATUS |IRQ STATUS |... IRQ_STATUS      | | | |
+ *  | | | |IRQ CLEAR  |IRQ CLEAR  |... IRQ CLEAR       | | | |
+ *  | | | |IRQ ENABLE |IRQ ENABLE |... IRQ ENABLE      | | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | |                                                | | |
+ *  | | +------------------------------------------------+ | |
+ *  | | | BW PT VOTE Nodes                               | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | | |PT Node 0  |PT Node 1  |....num_nds_pt      | | | |
+ *  | | | |-----------|--------------------------------| | | |
+ *  | | | |IRQ STATUS                                  | | | |
+ *  | | | |IRQ CLEAR                                   | | | |
+ *  | | | |IRQ ENABLE                                  | | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | |                                              | | | |
+ *  | | +------------------------------------------------+ | |
+ *  | |                       ......                       | |
+ *  | +----------------------------------------------------+ |
+ *  | |                                                    | |
+ *  | +----------------------------------------------------+ |
+ *  | | SW DRV1                                            | |
+ *  | | (same as DRV0)                                     | |
+ *  | +----------------------------------------------------+ |
+ *  |                         ......                         |
+ *  | +----------------------------------------------------+ |
+ *  | |Configuration                                       | |
+ *  | | +------------------------------------------------+ | |
+ *  | | | |VERSION  | CFG 1            |CFG 2          | | | |
+ *  | | | |---------|------------------|---------------| | | |
+ *  | | | |Major    | num_perf_ol_vcds |num_nds        | | | |
+ *  | | | |Minor    | num_nds_pt       |               | | | |
+ *  | | | |         | num_sw_drvs      |               | | | |
+ *  | | | +--------------------------------------------+ | | |
+ *  | | +------------------------------------------------+ | |
+ *  | +----------------------------------------------------+ |
+ *  +--------------------------------------------------------+
+ *
+ */
+
+ /**
+  * enum crm_cfg - Configuration registers space description for CRM
+  *
+  * This enumeration is used for three registers (CRM_VERSION, CRM_CFG_PARAM_1
+  * and CRM_CFG_PARAM_2) which are read only along with their bit meanings.
+  * The CRM_ENABLE indicates if the CRM is setup and enabled by firmware to be
+  * used. These are used during probe to setup DRVs.
+  *
+  * @CRM_VERSION:               Overall CRM version identifier.
+  * @MAJOR_VERSION:             Major version number of the CRM module.
+  * @MINOR_VERSION:             Minor version number of the CRM module.
+  * @CRM_CFG_PARAM_1:           Configuration parameter 1.
+  * @NUM_OF_NODES_PT:           Number of Pass through (PT) nodes in the power topology.
+  * @NUM_VCD_VOTED_BY_BW:       Number of VCD (voltage/current domains) voted by bandwidth.
+  * @NUM_SW_DRVS:               Number of software drivers interacting with CRM.
+  * @NUM_OF_RAILS:              Number of power rails managed by CRM.
+  * @NUM_VCD_VOTED_BY_PERF_OL:  Number of VCDs voted by performance operating levels.
+  * @CRM_CFG_PARAM_2:           Configuration parameter 2.
+  * @NUM_OF_NODES:              Total number of nodes managed by CRM.
+  * @CRM_ENABLE:                Flag to enable or disable CRM functionality.
+  * @CFG_REG_MAX:               Maximum number of configuration registers.
+  *                             Used as a boundary marker for array sizing.
+  */
+enum crm_cfg {
+	CRM_VERSION,
+	MAJOR_VERSION,
+	MINOR_VERSION,
+	CRM_CFG_PARAM_1,
+	NUM_OF_NODES_PT,
+	NUM_VCD_VOTED_BY_BW,
+	NUM_SW_DRVS,
+	NUM_OF_RAILS,
+	NUM_VCD_VOTED_BY_PERF_OL,
+	CRM_CFG_PARAM_2,
+	NUM_OF_NODES,
+	CRM_ENABLE,
+	CFG_REG_MAX,
+};
+
+/**
+ * enum crm_vcd_type - Types of VCD (Voltage/Clock Domain) requests
+ *
+ * This enumeration defines the various types of VCD (Voltage/Clock Domain)
+ * requests that can be made by the CRM (Clock and Resource Manager) DRVs.
+ * These types are used to categorize and handle different performance and
+ * bandwidth-related requests.
+ *
+ * @PERF_OL_VCD:       Votes are placed interms of Performance operating levels.
+ * @BW_VOTE_VCD:       Votes are placed interms of Bandwidth for local resources.
+ * @BW_PT_VOTE_VCD:    Votes are placed interms of Bandwidth for global resources.
+ * @MAX_VCD_TYPE:      Maximum number of VCD types. Not a valid type itself,
+ *                     but used for bounds checking and array sizing.
+ */
+enum crm_vcd_type {
+	PERF_OL_VCD,
+	BW_VOTE_VCD,
+	BW_PT_VOTE_VCD,
+	MAX_VCD_TYPE,
+};
+
+/**
+ * enum crm_client - CRM (Clock and Resource Manager) client register space
+ *
+ * This enumeration defines the various registers used by CRM clients.
+ * They are used for managing driver base addresses, updating power state,
+ * interrupt handling and other CRM-related operations.
+ *
+ * @DRV_BASE:              Base address register for the driver.
+ * @DRV_DISTANCE:          Offset from the base to DRV registers.
+ * @DRV_RESOURCE_DISTANCE: Distance between each VCD or ND resources.
+ *
+ * @PWR_ST0:               Power state register 0 for the driver.
+ * @PWR_ST1:               Power state register 1 for the driver.
+ * @PWR_ST2:               Power state register 2 for the driver.
+ * @PWR_ST3:               Power state register 3 for the driver.
+ * @PWR_ST4:               Power state register 4 for the driver.
+ *
+ * @IRQ_STATUS:            Interrupt status register for a VCD or ND.
+ * @IRQ_CLEAR:             Interrupt clear register for a VCD or ND.
+ * @IRQ_ENABLE:            Interrupt enable register for a VCD or ND.
+ *
+ * @CRMB_PT_TRIGGER:       Trigger register for CRM bandwidth/performance point.
+ *
+ * @CRM_CLIENT_REG_MAX:    Maximum number of CRM client register types. Used for
+ *                         bounds checking and array sizing.
+ */
+
+enum crm_client {
+	DRV_BASE,
+	DRV_DISTANCE,
+	DRV_RESOURCE_DISTANCE,
+	ACTIVE_VOTE,
+	SLEEP_VOTE,
+	WAKE_VOTE,
+	IRQ_STATUS,
+	IRQ_CLEAR,
+	IRQ_ENABLE,
+	CRMB_PT_TRIGGER,
+	CRM_CLIENT_REG_MAX,
+};
+
+/**
+ * enum crm_capability - CRM capability types
+ * @PERF_OL_VOTING_CAPABLE: Indicates support for performance operating level (OL) voting
+ * @BW_VOTING_CAPABLE: Indicates support for bandwidth (BW) voting
+ * @BW_PT_VOTING_CAPABLE: Indicates support for bandwidth power throttle (PT) voting
+ *
+ * This enumeration defines the various capabilities that a CRM (Clock and Resource Manager)
+ * instance may support. These capabilities determine the types of resource management
+ * operations the CRM can perform.
+ */
+enum crm_capability {
+	PERF_OL_VOTING_CAPABLE,
+	BW_VOTING_CAPABLE,
+	BW_PT_VOTING_CAPABLE,
+};
+
+/**
+ * struct crm_desc: CRM register and capability description
+ *
+ * @crm_capability:		Capability if H/W can vote on PERF_OL/BW_VOTE/BW_PT_VOTE via
+ *                              crm_write_perf_ol(), crm_write_bw_vote() and crm_write_bw_pt_vote()
+ *                              APIs.
+ * @cfg_regs:			Configuration registers, memory mapped.
+ * @sw_drv_perf_ol_vcd_regs:	PERF_OL voting registers
+ * @sw_drv_bw_vote_vcd_regs:	BW_VOTE voting registers
+ * @sw_drv_bw_pt_vote_vcd_regs: BW_PT_VOTE voting registers
+ */
+struct crm_desc {
+	u32 crm_capability;
+	u32 *cfg_regs;
+	u32 sw_drv_perf_ol_vcd_regs[CRM_CLIENT_REG_MAX];
+	u32 sw_drv_bw_vote_vcd_regs[CRM_CLIENT_REG_MAX];
+	u32 sw_drv_bw_pt_vote_vcd_regs[CRM_CLIENT_REG_MAX];
+};
+
+/**
+ * struct crm_sw_votes: SW DRV's ACTIVE_VOTEs in progress.
+ * One per VCD.
+ *
+ * @cmd:                The ACTIVE_VOTE being sent to CRM.
+ * @compl:              Wait for completion if the cmd->wait is set.
+ *                      Applicable only for ACTIVE_VOTEs.
+ * @in_progress:        Indicates if the cmd is in flight.
+ * @wait:               Wait queue used to wait for @in_progress to be false.
+ *                      This is needed because HW do not keep a record of new
+ *                      requests issued until current one is completed.
+ */
+struct crm_sw_votes {
+	struct crm_cmd cmd;
+	struct completion compl;
+	bool in_progress;
+	wait_queue_head_t wait;
+};
+
+/**
+ * struct crm_vcd: The Virtual Clock Domain's (VCDs) of the CRM.
+ * One instance per VCD type (PERF_OL/ BW_VOTE/ BW_PT_VOTE).
+ *
+ * @num_pwr_states:     Number of pwr state that DRV VCD can vote for
+ * @num_resources:      Number of VCD resources (for PERF_OL votes) OR
+ *                      Number of Node resoureces (for BW votes)
+ * @offsets:            Register offsets for DRV controller
+ * @sw_votes:           Cache of SW DRV's ACTIVE_VOTEs
+ */
+struct crm_vcd {
+	u32 num_pwr_states;
+	u32 num_resources;
+	u32 *offsets;
+	struct crm_sw_votes *sw_votes;
+};
+
+/**
+ * struct crm_drv: The Direct Resource Voter (DRV) of the
+ * CESTA Resource manager (CRM).
+ *
+ * @name:               Controller name
+ * @base:               Base address of the CRM device
+ * @drv_id:             DRV (Direct Resource Voter) number
+ * @vcd:                VCDs in this DRV.
+ * @lock:               Used to synchronize SW DRV's vote placement. Must
+ *                      be acquired before modifying ACTIVE/SLEEP/WAKE votes
+ * @irq:                Completion IRQ, Received for ACTIVE votes which gets
+ *                      instant applied.
+ * @initialized:        Whether DRV is initialized
+ */
+struct crm_drv {
+	char name[MAX_NAME_LENGTH];
+	void __iomem *base;
+	u32 drv_id;
+	struct crm_vcd vcd[MAX_VCD_TYPE];
+	spinlock_t lock;
+	int irq;
+	bool initialized;
+};
+
+/**
+ * struct crm_drv_top: Our representation of the top CRM device.
+ *
+ * @name:               CRM device name
+ * @crm_enabled:        Indicates if CRM is enabled
+ * @cfg:                Configuration address of the CRM device
+ *                      Includes version and VCDs information
+ * @base:               Base address of the CRM device
+ * @sw_drvs:            Controller for each SW DRV
+ * @num_sw_drvs:        Number of SW DRV controllers in the CRM device
+ * @max_sw_drv:         Max id of SW DRV controller in use
+ * @list:               CRM device added in crm_dev_list.
+ * @desc:               CRM description
+ * @dev:                CRM dev
+ * @pdev:               CRM platform device
+ */
+struct crm_drv_top {
+	char name[MAX_NAME_LENGTH];
+	bool crm_enabled;
+	void __iomem *cfg;
+	void __iomem *base;
+	struct crm_drv *sw_drvs;
+	int num_sw_drvs;
+	int max_sw_drv;
+	struct list_head list;
+	const struct crm_desc *desc;
+	struct device *dev;
+	struct platform_device *pdev;
+};
+
+static inline void __iomem *crm_get_offset(const struct crm_drv *drv, u32 reg, u32 ch,
+					   u32 vcd_type, u32 resource_idx)
+{
+	const struct crm_vcd *vcd = &drv->vcd[vcd_type];
+	u32 offset;
+
+	offset = vcd->offsets[DRV_BASE] + drv->drv_id * vcd->offsets[DRV_DISTANCE];
+	offset += vcd->offsets[reg];
+	offset += resource_idx * vcd->offsets[DRV_RESOURCE_DISTANCE];
+
+	return drv->base + offset;
+}
+
+static struct crm_drv *get_crm_drv(const struct device *dev, u32 drv_id, u32 capability)
+{
+	struct crm_drv_top *crm = NULL;
+	int i;
+
+	if (!dev)
+		goto exit;
+
+	crm = dev_get_drvdata(dev);
+	if (IS_ERR_OR_NULL(crm) || !crm->crm_enabled)
+		goto exit;
+
+	if (!(crm->desc->crm_capability & capability))
+		goto exit;
+
+	for (i = 0; i < crm->num_sw_drvs; i++) {
+		if (drv_id == crm->sw_drvs[i].drv_id)
+			return &crm->sw_drvs[i];
+	}
+
+exit:
+	return ERR_PTR(-ENODEV);
+}
+
+static void crm_vote_completion(struct crm_sw_votes *votes)
+{
+	struct completion *compl = &votes->compl;
+
+	votes->in_progress = false;
+	complete(compl);
+}
+
+/**
+ * crm_vote_complete_irq() - Vote completion interrupt handler for SW DRVs.
+ * @irq: The IRQ number.
+ * @p:   Pointer to "struct crm_drv".
+ *
+ * Called for ACTIVE_VOTE transfers (those are the only ones we enable the
+ * IRQ for) when a transfer is done.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t crm_vote_complete_irq(int irq, void *p)
+{
+	struct crm_drv_top *crm = p;
+	struct crm_drv *drv;
+	struct crm_vcd *vcd;
+	struct crm_sw_votes *votes;
+	unsigned long irq_status;
+	int i, j, k, num_irq;
+	void __iomem *addr;
+
+	for (i = 0; i < crm->num_sw_drvs; i++) {
+		drv = &crm->sw_drvs[i];
+		if (!drv->initialized && drv->irq != irq)
+			continue;
+
+		spin_lock(&drv->lock);
+		for (j = 0; j < MAX_VCD_TYPE; j++) {
+			if (!(crm->desc->crm_capability & BIT(j)))
+				continue;
+			vcd = &drv->vcd[j];
+
+			num_irq = j == BW_PT_VOTE_VCD ? 1 : vcd->num_resources;
+
+			for (k = 0; k < num_irq; k++) {
+				addr = crm_get_offset(drv, IRQ_STATUS, 0, j, k);
+				irq_status = readl_relaxed(addr);
+				if (!irq_status)
+					continue;
+
+				addr = crm_get_offset(drv, IRQ_CLEAR, 0, j, k);
+				writel_relaxed(IRQ_CLEAR_BIT, addr);
+				trace_crm_irq(drv->name, j, k, irq_status);
+
+				votes = &vcd->sw_votes[k];
+				if (WARN_ON_ONCE(!votes->in_progress))
+					continue;
+
+				if (votes->cmd.wait)
+					crm_vote_completion(votes);
+			}
+		}
+		spin_unlock(&drv->lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+/**
+ * crm_send_cmd - Send a command to the CRM hardware
+ * @drv: Pointer to the CRM driver structure
+ * @vcd_type: Type of VCD (PERF_OL_VCD, BW_VOTE_VCD or BW_PT_VOTE_VCD)
+ * @cmd: Pointer to the CRM structure containing command parameters
+ * @pt_trigger: Boolean flag indicating whether this is a pass through command
+ *
+ * This function sends a command to the CRM hardware for the specified VCD type.
+ * It assumes that the command has already been validated
+ * (e.g., using crm_is_invalid_cmd()).
+ *
+ * May sleep.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int crm_send_cmd(struct crm_drv *drv, u32 vcd_type, const struct crm_cmd *cmd,
+			bool pt_trigger)
+{
+	struct crm_vcd *vcd = &drv->vcd[vcd_type];
+	struct completion *compl = NULL;
+	u32 time_left = CRM_TIMEOUT_MS;
+	u32 data = cmd->data, irq_idx;
+	void __iomem *addr;
+
+	spin_lock_irq(&drv->lock);
+
+	/* Note: Set BIT(31) for RESP_REQ */
+	if (vcd_type == BW_VOTE_VCD && cmd->wait)
+		data |= BW_VOTE_RESP_REQ;
+
+	irq_idx = vcd_type == BW_PT_VOTE_VCD ? 0 : cmd->resource_idx;
+
+	switch (cmd->pwr_state) {
+	case CRM_ACTIVE_STATE:
+		/* Wait forever for a previous request to complete */
+		wait_event_lock_irq(vcd->sw_votes[irq_idx].wait,
+				    !vcd->sw_votes[irq_idx].in_progress,
+				    drv->lock);
+
+		compl = &vcd->sw_votes[irq_idx].compl;
+		init_completion(compl);
+		memcpy(&vcd->sw_votes[irq_idx], cmd, sizeof(struct crm_cmd));
+		vcd->sw_votes[irq_idx].in_progress = true;
+		addr = crm_get_offset(drv, ACTIVE_VOTE, 0, vcd_type, cmd->resource_idx);
+		writel_relaxed(data, addr);
+		break;
+	case CRM_SLEEP_STATE:
+		addr = crm_get_offset(drv, SLEEP_VOTE, 0, vcd_type, cmd->resource_idx);
+		writel_relaxed(data, addr);
+		break;
+	case CRM_WAKE_STATE:
+		addr = crm_get_offset(drv, WAKE_VOTE, 0, vcd_type, cmd->resource_idx);
+		writel_relaxed(data, addr);
+		break;
+	}
+
+	/* Set COMMIT to start aggregating votes */
+	if (pt_trigger) {
+		addr = crm_get_offset(drv, CRMB_PT_TRIGGER, 0, vcd_type, 0);
+		writel_relaxed(BW_PT_VOTE_TRIGGER, addr);
+
+		udelay(1);
+
+		addr = crm_get_offset(drv, CRMB_PT_TRIGGER, 0, vcd_type, 0);
+		writel_relaxed(0, addr);
+	}
+
+	spin_unlock_irq(&drv->lock);
+	trace_crm_write_vcd_votes(drv->name, vcd_type, cmd->resource_idx, cmd->pwr_state, data);
+
+	if (compl && cmd->wait) {
+		time_left = wait_for_completion_timeout(compl, CRM_TIMEOUT_MS);
+		WARN_ON(!time_left);
+		/* Unblock new requests for same VCD */
+		wake_up(&vcd->sw_votes[irq_idx].wait);
+	}
+
+	return (time_left > 0) ? 0 : -ETIMEDOUT;
+}
+
+/**
+ * crm_is_invalid_cmd - Validate a CRM command structure
+ * @drv: Pointer to the CRM driver structure
+ * @vcd_type: Type of VCD (Voltage/Clock Domain)
+ * @cmd: Pointer to the CRM command structure
+ *
+ * This function checks whether the given CRM command is valid for the
+ * specified VCD type. It performs the following validations:
+ * - Ensures that the driver and command pointers are not NULL or erroneous.
+ * - Checks that the power state index is within the valid range.
+ * - Checks that the resource index is within the valid range.
+ * - For BW_VOTE_VCD, ensures the BW_VOTE_VALID bit is set in the data field.
+ * - For BW_PT_VOTE_VCD, ensures the BW_PT_VOTE_VALID bit is set in the data field.
+ * - For PERF_OL_VCD, ensures only valid bits are set in the data field.
+ *
+ * Return: true if the command is invalid, false otherwise.
+ */
+static bool crm_is_invalid_cmd(struct crm_drv *drv, u32 vcd_type, const struct crm_cmd *cmd)
+{
+	struct crm_vcd *vcd;
+
+	if (IS_ERR_OR_NULL(drv) || IS_ERR_OR_NULL(cmd))
+		return true;
+
+	vcd = &drv->vcd[vcd_type];
+
+	if (cmd->pwr_state >= vcd->num_pwr_states)
+		return true;
+	else if (cmd->resource_idx >= vcd->num_resources)
+		return true;
+	else if (vcd_type == BW_VOTE_VCD && !(cmd->data & BW_VOTE_VALID))
+		return true;
+	else if (vcd_type == BW_PT_VOTE_VCD && !(cmd->data & BW_PT_VOTE_VALID))
+		return true;
+	else if (vcd_type == PERF_OL_VCD && (cmd->data & ~PERF_OL_VALUE_BITS))
+		return true;
+
+	return false;
+}
+
+/**
+ * crm_write_perf_ol() - Write a perf ol vote for a resource
+ * @dev:       The device making the request, dev->parent must be CRM device
+ * @drv_id:    DRV ID for which the votes are sent
+ * @cmd:       The CRM CMD
+ *
+ * Caches the votes for logging and immediately sends the votes for SW DRVs
+ * if the @cmd have .wait set and is for ACTIVE_VOTE then waits for completion
+ * IRQ before return. for SLEEP_VOTE and WAKE_VOTE no completion IRQ is sent
+ * and they are triggered within HW during idle/awake scenarios.
+ *
+ * Return:
+ * * 0			- Success
+ * * -Error             - Error code
+ */
+int crm_write_perf_ol(const struct device *dev, u32 drv_id, const struct crm_cmd *cmd)
+{
+	struct crm_drv *drv = get_crm_drv(dev, drv_id, PERF_OL_VOTING_CAPABLE);
+	int ret;
+
+	ret = crm_is_invalid_cmd(drv, PERF_OL_VCD, cmd);
+	if (ret)
+		return -EINVAL;
+
+	return crm_send_cmd(drv, PERF_OL_VCD, cmd, false);
+}
+EXPORT_SYMBOL_GPL(crm_write_perf_ol);
+
+/**
+ * crm_write_bw_vote() - Write a bw vote for a resource
+ * @dev:       The device making the request, dev->parent must be CRM device
+ * @drv_id:    DRV ID for which the votes are sent
+ * @cmd:       The CRM CMD
+ *
+ * Caches the votes for logging and immediately sends the votes for SW DRVs
+ * if the @cmd have .wait set and is for ACTIVE_VOTE then waits for completion
+ * IRQ before return. for SLEEP_VOTE and WAKE_VOTE no completion IRQ is sent
+ * and they are triggered within HW during idle/awake scenarios.
+ *
+ * Return:
+ * * 0			- Success
+ * * -Error             - Error code
+ */
+int crm_write_bw_vote(const struct device *dev, u32 drv_id, const struct crm_cmd *cmd)
+{
+	struct crm_drv *drv = get_crm_drv(dev, drv_id, BW_VOTING_CAPABLE);
+	int ret;
+
+	ret = crm_is_invalid_cmd(drv, BW_VOTE_VCD, cmd);
+	if (ret)
+		return -EINVAL;
+
+	return crm_send_cmd(drv, BW_VOTE_VCD, cmd, false);
+}
+EXPORT_SYMBOL_GPL(crm_write_bw_vote);
+
+/**
+ * crm_write_bw_pt_vote() - Write a bw vote for a resource
+ * @dev:       The device making the request, dev->parent must be CRM device
+ * @drv_id:    DRV ID for which the votes are sent
+ * @cmd:       The CRM CMD
+ *
+ * Caches the votes for logging and immediately sends the votes for SW DRVs
+ * if the @cmd have .wait set and is for ACTIVE_VOTE then waits for completion
+ * IRQ before return. For SLEEP_VOTE and WAKE_VOTE no completion IRQ is sent
+ * and they are triggered within HW during idle/awake scenarios.
+ *
+ * Return:
+ * * 0			- Success
+ * * -Error             - Error code
+ */
+int crm_write_bw_pt_vote(const struct device *dev, u32 drv_id, const struct crm_cmd *cmd)
+{
+	struct crm_drv *drv = get_crm_drv(dev, drv_id, BW_PT_VOTING_CAPABLE);
+	int ret;
+
+	ret = crm_is_invalid_cmd(drv, BW_PT_VOTE_VCD, cmd);
+	if (ret)
+		return -EINVAL;
+
+	return crm_send_cmd(drv, BW_PT_VOTE_VCD, cmd, true);
+}
+EXPORT_SYMBOL_GPL(crm_write_bw_pt_vote);
+
+/**
+ * crm_get() - Get a crm handle from a device
+ * @dev: Client device pointer
+ *
+ * Return: Handle to crm device on success, ERR_PTR() on failure
+ */
+struct device *crm_get(const struct device *dev)
+{
+	struct platform_device *pdev;
+	struct crm_drv_top *crm;
+	struct device_node *np;
+
+	if (!dev || !dev->of_node)
+		return ERR_PTR(-EINVAL);
+
+	np = of_parse_phandle(dev->of_node, "qcom,crm", 0);
+	if (!np)
+		return ERR_PTR(-ENODEV);
+
+	pdev = of_find_device_by_node(np);
+	of_node_put(np);
+	if (!pdev)
+		return ERR_PTR(-EINVAL);
+
+	crm = platform_get_drvdata(pdev);
+	if (!crm) {
+		put_device(&pdev->dev);
+		return ERR_PTR(-ENODEV);
+	}
+
+	return crm->dev;
+}
+EXPORT_SYMBOL_GPL(crm_get);
+
+static int crm_probe_get_irqs(struct crm_drv_top *crm)
+{
+	struct crm_drv *drvs = crm->sw_drvs;
+	struct crm_vcd *vcd;
+	int i, j, k, irq, ret;
+	void __iomem *addr;
+
+	/* Only SW DRVs have associated vote completion IRQ */
+	for (i = 0; i < crm->num_sw_drvs; i++) {
+		if (!crm->sw_drvs[i].initialized)
+			continue;
+
+		irq = platform_get_irq(crm->pdev, drvs[i].drv_id);
+		if (irq < 0)
+			return irq;
+
+		ret = devm_request_irq(crm->dev, irq, crm_vote_complete_irq,
+				       IRQF_TRIGGER_RISING, drvs[i].name, crm);
+		if (ret)
+			return ret;
+
+		drvs[i].irq = irq;
+
+		/* Additionally allocate memory for sw_votes */
+		for (j = 0; j < MAX_VCD_TYPE; j++) {
+			if (!(crm->desc->crm_capability & BIT(j)))
+				continue;
+
+			vcd = &drvs[i].vcd[j];
+			vcd->sw_votes = devm_kcalloc(crm->dev, vcd->num_resources,
+						     sizeof(struct crm_sw_votes),
+						     GFP_KERNEL);
+			if (!vcd->sw_votes)
+				return -ENOMEM;
+
+			/* Enable IRQs for all VCDs */
+			for (k = 0; k < vcd->num_resources; k++) {
+				init_waitqueue_head(&vcd->sw_votes[k].wait);
+				addr = crm_get_offset(&drvs[i], IRQ_ENABLE, 0, j, k);
+				writel_relaxed(IRQ_ENABLE_BIT, addr);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int crm_probe_set_vcd_caches(struct crm_drv_top *crm, u32 crm_cfg, u32 crm_cfg_2)
+{
+	struct crm_vcd *vcd;
+	struct crm_drv *drv;
+	u32 num_perf_ol_vcds, num_nds, num_nds_pt;
+	int i, j;
+
+	num_perf_ol_vcds = field_get(crm->desc->cfg_regs[NUM_VCD_VOTED_BY_PERF_OL], crm_cfg);
+	num_nds = field_get(crm->desc->cfg_regs[NUM_OF_NODES], crm_cfg_2);
+	num_nds_pt = field_get(crm->desc->cfg_regs[NUM_OF_NODES_PT], crm_cfg);
+
+	for (i = 0; i < crm->num_sw_drvs; i++) {
+		drv = &crm->sw_drvs[i];
+
+		if (!drv->initialized)
+			continue;
+
+		for (j = 0; j < MAX_VCD_TYPE; j++) {
+			if (!(crm->desc->crm_capability & BIT(j)))
+				continue;
+			vcd = &drv->vcd[j];
+
+			if (j == PERF_OL_VCD) {
+				vcd->offsets = (u32 *)&crm->desc->sw_drv_perf_ol_vcd_regs;
+				vcd->num_resources = num_perf_ol_vcds;
+			} else if (j == BW_VOTE_VCD) {
+				vcd->offsets = (u32 *)&crm->desc->sw_drv_bw_vote_vcd_regs;
+				/* BW_VOTE_VCD can have multiple NDs with which BW can be voted */
+				vcd->num_resources = num_nds;
+			} else {
+				vcd->offsets = (u32 *)&crm->desc->sw_drv_bw_pt_vote_vcd_regs;
+				vcd->num_resources = num_nds_pt;
+			}
+			vcd->num_pwr_states = MAX_SW_DRV_PWR_STATES;
+		}
+	}
+
+	return 0;
+}
+
+static struct crm_drv *crm_probe_get_drvs(struct crm_drv_top *crm, int num_drvs,
+					  const char *prop_name, const char *name)
+{
+	struct device_node *dn = crm->dev->of_node;
+	u32 *drv_ids;
+	int i, id;
+	int ret;
+	struct crm_drv *drvs;
+
+	if (!num_drvs)
+		return ERR_PTR(-EINVAL);
+
+	drvs = devm_kcalloc(crm->dev, num_drvs, sizeof(struct crm_drv), GFP_KERNEL);
+	if (!drvs)
+		return ERR_PTR(-ENOMEM);
+
+	drv_ids = kcalloc(num_drvs, sizeof(u32), GFP_KERNEL);
+	if (!drv_ids)
+		return ERR_PTR(-ENOMEM);
+
+	ret = of_property_read_variable_u32_array(dn, prop_name, drv_ids, 1, num_drvs);
+	if (ret < 0) {
+		kfree(drv_ids);
+		return ERR_PTR(ret);
+	}
+
+	for (i = 0; i < num_drvs; i++) {
+		id = drv_ids[i];
+
+		scnprintf(drvs[i].name, sizeof(drvs[i].name), "%s_%s_%d", crm->name, name, id);
+		spin_lock_init(&drvs[i].lock);
+		drvs[i].drv_id = id;
+		drvs[i].base = crm->base;
+		drvs[i].initialized = true;
+	}
+
+	kfree(drv_ids);
+	return drvs;
+}
+
+static int crm_probe_drvs(struct crm_drv_top *crm, struct device_node *dn)
+{
+	u32 crm_ver, major_ver, minor_ver;
+	u32 crm_cfg, crm_cfg_2;
+
+	crm_ver = readl_relaxed(crm->cfg + crm->desc->cfg_regs[CRM_VERSION]);
+	major_ver = field_get(crm->desc->cfg_regs[MAJOR_VERSION], crm_ver);
+	minor_ver = field_get(crm->desc->cfg_regs[MINOR_VERSION], crm_ver);
+	dev_info(crm->dev, "CRM %s version major=%u minor=%u\n", crm->name, major_ver, minor_ver);
+
+	crm_cfg = readl_relaxed(crm->cfg + crm->desc->cfg_regs[CRM_CFG_PARAM_1]);
+	crm->max_sw_drv = field_get(crm->desc->cfg_regs[NUM_SW_DRVS], crm_cfg);
+
+	crm->num_sw_drvs = of_property_count_u32_elems(dn, "qcom,sw-drv-ids");
+	if (crm->num_sw_drvs < 0)
+		return -EINVAL;
+
+	crm->sw_drvs = crm_probe_get_drvs(crm, crm->num_sw_drvs, "qcom,sw-drv-ids", "sw_drv");
+	if (IS_ERR(crm->sw_drvs))
+		return PTR_ERR(crm->sw_drvs);
+
+	if (crm->num_sw_drvs > crm->max_sw_drv || !crm->num_sw_drvs)
+		return -EINVAL;
+
+	crm_cfg_2 = readl_relaxed(crm->cfg + crm->desc->cfg_regs[CRM_CFG_PARAM_2]);
+
+	return crm_probe_set_vcd_caches(crm, crm_cfg, crm_cfg_2);
+}
+
+static int crm_probe(struct platform_device *pdev)
+{
+	struct device_node *dn = pdev->dev.of_node;
+	struct crm_drv_top *crm;
+	const char *name;
+	int ret;
+
+	crm = devm_kzalloc(&pdev->dev, sizeof(*crm), GFP_KERNEL);
+	if (!crm)
+		return -ENOMEM;
+
+	crm->desc = of_device_get_match_data(&pdev->dev);
+	if (!crm->desc)
+		return -EINVAL;
+
+	name = of_get_property(dn, "label", NULL);
+	if (!name)
+		name = dev_name(&pdev->dev);
+
+	crm->pdev = pdev;
+	crm->dev = &pdev->dev;
+	scnprintf(crm->name, sizeof(crm->name), "%s", name);
+
+	crm->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+	if (IS_ERR(crm->base))
+		return -ENOMEM;
+
+	crm->cfg = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
+	if (IS_ERR(crm->cfg))
+		return -ENOMEM;
+
+	if (pdev->dev.pm_domain) {
+		ret = devm_pm_runtime_enable(&pdev->dev);
+		if (ret)
+			return ret;
+
+		ret = pm_runtime_resume_and_get(&pdev->dev);
+		if (ret)
+			return ret;
+	}
+
+	crm->crm_enabled = readl_relaxed(crm->cfg + crm->desc->cfg_regs[CRM_ENABLE]);
+	if (!crm->crm_enabled) {
+		dev_err(crm->dev, "%s not enabled, exiting\n", crm->name);
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	ret = crm_probe_drvs(crm, dn);
+	if (ret)
+		goto exit;
+
+	ret = crm_probe_get_irqs(crm);
+	if (ret)
+		goto exit;
+
+	dev_set_drvdata(&pdev->dev, crm);
+exit:
+	if (pdev->dev.pm_domain)
+		pm_runtime_put(&pdev->dev);
+
+	return ret;
+}
+
+static u32 crm_v2_cfg[] = {
+	[CRM_VERSION]			= 0x0,
+	[MAJOR_VERSION]			= GENMASK(23, 16),
+	[MINOR_VERSION]			= GENMASK(15, 8),
+	[CRM_CFG_PARAM_1]		= 0x4,
+	[NUM_OF_NODES_PT]		= GENMASK(31, 27),
+	[NUM_VCD_VOTED_BY_BW]		= GENMASK(26, 24),
+	[NUM_SW_DRVS]			= GENMASK(23, 20),
+	[NUM_OF_RAILS]			= GENMASK(15, 12),
+	[NUM_VCD_VOTED_BY_PERF_OL]	= GENMASK(11, 8),
+	[CRM_CFG_PARAM_2]		= 0x8,
+	[NUM_OF_NODES]			= GENMASK(30, 26),
+	[CRM_ENABLE]			= 0xc,
+};
+
+static const struct crm_desc disp_crm_desc_v2 = {
+	.crm_capability = PERF_OL_VOTING_CAPABLE | BW_VOTING_CAPABLE | BW_PT_VOTING_CAPABLE,
+	.cfg_regs = crm_v2_cfg,
+	.sw_drv_perf_ol_vcd_regs = {
+		[DRV_BASE]			 = 0xe0,
+		[DRV_DISTANCE]			 = 0x1000,
+		[DRV_RESOURCE_DISTANCE]		 = 0x20,
+		[ACTIVE_VOTE]			 = 0x0,
+		[SLEEP_VOTE]			 = 0x4,
+		[WAKE_VOTE]			 = 0x8,
+		[IRQ_STATUS]			 = 0x10,
+		[IRQ_CLEAR]			 = 0x14,
+		[IRQ_ENABLE]			 = 0x18,
+	},
+	.sw_drv_bw_vote_vcd_regs = {
+		[DRV_BASE]			 = 0xe0,
+		[DRV_DISTANCE]			 = 0x1000,
+		[DRV_RESOURCE_DISTANCE]		 = 0x10,
+		[ACTIVE_VOTE]			 = 0x20,
+		[SLEEP_VOTE]			 = 0x24,
+		[WAKE_VOTE]			 = 0x28,
+		[IRQ_STATUS]			 = 0x90,
+		[IRQ_CLEAR]			 = 0x94,
+		[IRQ_ENABLE]			 = 0x98,
+	},
+	.sw_drv_bw_pt_vote_vcd_regs = {
+		[DRV_BASE]			 = 0xe0,
+		[DRV_DISTANCE]			 = 0x1000,
+		[DRV_RESOURCE_DISTANCE]		 = 0x10,
+		[ACTIVE_VOTE]			 = 0x30,
+		[SLEEP_VOTE]			 = 0x34,
+		[WAKE_VOTE]			 = 0x38,
+		[IRQ_STATUS]			 = 0xa0,
+		[IRQ_CLEAR]			 = 0xa4,
+		[IRQ_ENABLE]			 = 0xa8,
+		[CRMB_PT_TRIGGER]		 = 0x100,
+	},
+};
+
+static const struct of_device_id crm_drv_match[] = {
+	{ .compatible = "qcom,sm8750-disp-crm", .data = &disp_crm_desc_v2 },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, crm_drv_match);
+
+static struct platform_driver crm_driver = {
+	.probe = crm_probe,
+	.driver = {
+		  .name = "crm",
+		  .of_match_table = crm_drv_match,
+		  .suppress_bind_attrs = true,
+	},
+};
+module_platform_driver(crm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. (QTI) CRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/qcom/trace-crm.h b/drivers/soc/qcom/trace-crm.h
new file mode 100644
index 0000000000000000000000000000000000000000..838e4b51132a16ab6b1e73d3cccfd76859de2e95
--- /dev/null
+++ b/drivers/soc/qcom/trace-crm.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#if !defined(_TRACE_CRM_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CRM_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM crm
+
+#include <linux/tracepoint.h>
+#include <soc/qcom/crm.h>
+
+DECLARE_EVENT_CLASS(crm_vcd_votes,
+
+	TP_PROTO(const char *name, u32 vcd_type, u32 resource_idx, u32 pwr_state, u32 data),
+
+	TP_ARGS(name, vcd_type, resource_idx, pwr_state, data),
+
+	TP_STRUCT__entry(
+			 __string(name, name)
+			 __field(u32, vcd_type)
+			 __field(u32, resource_idx)
+			 __field(u32, pwr_state)
+			 __field(u32, data)
+	),
+
+	TP_fast_assign(
+		       __assign_str(name);
+		       __entry->vcd_type = vcd_type;
+		       __entry->resource_idx = resource_idx;
+		       __entry->pwr_state = pwr_state;
+		       __entry->data = data;
+	),
+
+	TP_printk("%s: vcd_type: %u resource_idx: %u pwr_state: %u data: %#x",
+		  __get_str(name), __entry->vcd_type, __entry->resource_idx,
+		   __entry->pwr_state, __entry->data)
+);
+
+DEFINE_EVENT(crm_vcd_votes, crm_cache_vcd_votes,
+
+	TP_PROTO(const char *name, u32 vcd_type, u32 resource_idx, u32 pwr_state, u32 data),
+
+	TP_ARGS(name, vcd_type, resource_idx, pwr_state, data)
+);
+
+DEFINE_EVENT(crm_vcd_votes, crm_write_vcd_votes,
+
+	TP_PROTO(const char *name, u32 vcd_type, u32 resource_idx, u32 pwr_state, u32 data),
+
+	TP_ARGS(name, vcd_type, resource_idx, pwr_state, data)
+);
+
+TRACE_EVENT(crm_irq,
+
+	TP_PROTO(const char *name, u32 vcd_type, u32 resource_idx, unsigned long irq_status),
+
+	TP_ARGS(name, vcd_type, resource_idx, irq_status),
+
+	TP_STRUCT__entry(
+			 __string(name, name)
+			 __field(u32, vcd_type)
+			 __field(u32, resource_idx)
+			 __field(unsigned long, irq_status)
+	),
+
+	TP_fast_assign(
+		       __assign_str(name);
+		       __entry->vcd_type = vcd_type;
+		       __entry->resource_idx = resource_idx;
+		       __entry->irq_status = irq_status;
+	),
+
+	TP_printk("%s: IRQ vcd_type: %u resource_idx: %u irq_status: %lu",
+		  __get_str(name), __entry->vcd_type, __entry->resource_idx,
+		  __entry->irq_status)
+);
+
+TRACE_EVENT(crm_switch_channel,
+
+	TP_PROTO(const char *name, int ch, int ret),
+
+	TP_ARGS(name, ch, ret),
+
+	TP_STRUCT__entry(
+			 __string(name, name)
+			 __field(int, ch)
+			 __field(int, ret)
+	),
+
+	TP_fast_assign(
+		       __assign_str(name);
+		       __entry->ch = ch;
+		       __entry->ret = ret;
+	),
+
+	TP_printk("%s: channel switched to: %d ret: %d",
+		  __get_str(name), __entry->ch, __entry->ret)
+);
+
+#endif /* _TRACE_CRM_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace-crm
+
+#include <trace/define_trace.h>
diff --git a/include/soc/qcom/crm.h b/include/soc/qcom/crm.h
new file mode 100644
index 0000000000000000000000000000000000000000..5411336f4cf222fcddf9197ba2e13aa3d41d16ec
--- /dev/null
+++ b/include/soc/qcom/crm.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __SOC_QCOM_CRM_H__
+#define __SOC_QCOM_CRM_H__
+
+#include <linux/platform_device.h>
+
+/**
+ * enum crm_sw_drv_state:  Power states for the SW DRV request
+ *
+ * @CRM_ACTIVE_STATE:       Active or AMC mode requests. Resource state
+ *                          is aggregated immediately.
+ * @CRM_SLEEP_STATE:        State of the resource when the subsystem is
+ *                          powered down. There is no client using the
+ *                          resource actively.
+ * @CRM_WAKE_STATE:         Resume resource state to the value previously
+ *                          requested before the subsystem was powered down.
+ */
+enum crm_sw_drv_state {
+	CRM_ACTIVE_STATE,
+	CRM_SLEEP_STATE,
+	CRM_WAKE_STATE,
+};
+
+/**
+ * struct crm_cmd: The message to be sent to CRM
+ *
+ * @pwr_state:     The pwr_state for HW/SW DRV
+ * @resource_idx:  The index of the VCD OR ND to apply data
+ * @data:          The Clock Plan index for the VCDs voted by PERF_OL.
+ *                 BW vote for the VCDs voted by BW.
+ * @wait:          Set by the client want if want to wait for the vote completion IRQ.
+ *                 Applicable for only SW DRV client.
+ *                 Don't care for HW DRV client.
+ */
+struct crm_cmd {
+	enum crm_sw_drv_state pwr_state;
+	u32 resource_idx;
+	u32 data;
+	bool wait;
+};
+
+#if IS_ENABLED(CONFIG_QCOM_CRM)
+int crm_write_perf_ol(const struct device *dev, u32 drv_id, const struct crm_cmd *cmd);
+int crm_write_bw_vote(const struct device *dev, u32 drv_id, const struct crm_cmd *cmd);
+int crm_write_bw_pt_vote(const struct device *dev, u32 drv_id, const struct crm_cmd *cmd);
+struct device *crm_get(const struct device *dev);
+#else
+
+static inline int crm_write_perf_ol(const struct device *dev,
+				    u32 drv_id,
+				    const struct crm_cmd *cmd)
+{ return -ENODEV; }
+
+static inline int crm_write_bw_vote(const struct device *dev,
+				    u32 drv_id,
+				    const struct crm_cmd *cmd)
+{ return -ENODEV; }
+
+static inline int crm_write_bw_pt_vote(const struct device *dev,
+				       u32 drv_id,
+				       const struct crm_cmd *cmd)
+{ return -ENODEV; }
+static inline struct device *crm_get(const struct device *dev)
+{ return NULL; }
+
+#endif /* CONFIG_QCOM_CRM */
+
+#endif /* __SOC_QCOM_CRM_H__ */

-- 
2.34.1


^ permalink raw reply related

* [PATCH 03/13] dt-bindings: mfd: syscon: Add qcom,crmc-syscon compatible
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

Document the qcom,crmc-syscon compatible for the CRMC
(CESTA Resource Manager Clock) syscon device. This syscon
provides access to the frequency lookup tables provided by
CRM (CESTA Resource Manager) for clocks controlled by CESTA
hardware, so the SW frequency tables of these clocks can be
populated from this data.

Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 Documentation/devicetree/bindings/mfd/syscon.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index e228670880631429836b542ea5d40100b73d69e1..231e30144a5f3704eacf3387187cebde39c7148d 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -107,6 +107,7 @@ select:
           - nxp,s32g3-gpr
           - qcom,apq8064-mmss-sfpb
           - qcom,apq8064-sps-sic
+          - qcom,crmc-syscon
           - rockchip,px30-qos
           - rockchip,rk3036-qos
           - rockchip,rk3066-qos
@@ -221,6 +222,7 @@ properties:
               - nxp,s32g3-gpr
               - qcom,apq8064-mmss-sfpb
               - qcom,apq8064-sps-sic
+              - qcom,crmc-syscon
               - rockchip,px30-qos
               - rockchip,rk3036-qos
               - rockchip,rk3066-qos

-- 
2.34.1


^ permalink raw reply related

* [PATCH 02/13] dt-bindings: clock: qcom,sm8550-dispcc: Add display CESTA support on SM8750
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

On SM8750, a subset of DISPCC clocks is controlled by the display CESTA
(Client State Aggregator) hardware. These clocks can be scaled to the
desired frequency by sending votes to the display CRM(CESTA Resource
manager) instead of programming DISPCC registers directly.

Add bindings to allow the DISPCC node to reference the display CRM node
for sending votes to CESTA hardware and the CRMC syscon regmap node
which is used to read the frequency look up tables provided by CRM and
populate this data in RCG's frequency tables for CESTA controlled clocks.

Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 .../bindings/clock/qcom,sm8550-dispcc.yaml         | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-dispcc.yaml
index 591ce91b8d54dd6f78a66d029882bcd94b53beda..3e049b2160a1781c866bb30b29f0da5c9e156b61 100644
--- a/Documentation/devicetree/bindings/clock/qcom,sm8550-dispcc.yaml
+++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-dispcc.yaml
@@ -60,6 +60,18 @@ properties:
       A phandle to an OPP node describing required MMCX performance point.
     maxItems: 1
 
+  qcom,crm:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the display CRM (CESTA Resource Manager) node, used to communicate
+      with the display CESTA (Client State Aggregator) hardware.
+
+  qcom,crmc-regmap:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the syscon providing the CRMC (CRM clock) regmap used
+      to read frequency LUTs (lookup tables) populated by the CRM.
+
 required:
   - compatible
   - clocks
@@ -100,4 +112,40 @@ examples:
       power-domains = <&rpmhpd RPMHPD_MMCX>;
       required-opps = <&rpmhpd_opp_low_svs>;
     };
+
+  - |
+    // Display clock controller node that controls some clocks
+    // using display CESTA(Client state Aggregator) hardware
+    #include <dt-bindings/clock/qcom,rpmh.h>
+    #include <dt-bindings/power/qcom,rpmhpd.h>
+
+    clock-controller@af00000 {
+      compatible = "qcom,sm8750-dispcc";
+      reg = <0x0af00000 0x20000>;
+      clocks = <&rpmhcc RPMH_CXO_CLK>,
+               <&rpmhcc RPMH_CXO_CLK_A>,
+               <&gcc GCC_DISP_AHB_CLK>,
+               <&sleep_clk>,
+               <&dsi0_phy 0>,
+               <&dsi0_phy 1>,
+               <&dsi1_phy 0>,
+               <&dsi1_phy 1>,
+               <&dp0_phy 0>,
+               <&dp0_phy 1>,
+               <&dp1_phy 0>,
+               <&dp1_phy 1>,
+               <&dp2_phy 0>,
+               <&dp2_phy 1>,
+               <&dp3_phy 0>,
+               <&dp3_phy 1>;
+      power-domains = <&rpmhpd RPMHPD_MMCX>;
+      required-opps = <&rpmhpd_opp_low_svs>;
+
+      qcom,crm = <&disp_crm>;
+      qcom,crmc-regmap = <&disp_crmc>;
+
+      #clock-cells = <1>;
+      #power-domain-cells = <1>;
+      #reset-cells = <1>;
+    };
 ...

-- 
2.34.1


^ permalink raw reply related

* [PATCH 01/13] dt-bindings: soc: Introduce device bindings for CESTA Resource Manager
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona
In-Reply-To: <20260420-cesta-sm870-dispcc-v1-0-eb27d845df9c@oss.qualcomm.com>

From: Maulik Shah <maulik.shah@oss.qualcomm.com>

Add device binding documentation for Qualcomm Technologies, Inc. (QTI)
CESTA (Client State Aggregation) Resource Manager which is responsible
for client side management and aggregation of local resources.

Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
 .../devicetree/bindings/soc/qcom/qcom,crm.yaml     | 83 ++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,crm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,crm.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a1485db8b7476b0cb0b4e8a41adc8fb4092b731f
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,crm.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/qcom/qcom,crm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies, Inc. (QTI) CESTA Resource Manager (CRM)
+
+maintainers:
+  - Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+description:
+  Support for communication with the hardened-CRM blocks. A set of SW
+  client Direct Resource Voters (DRVs) in CRM provides interface to vote
+  desired power state of resources local to a subsystem.
+
+properties:
+  compatible:
+    enum:
+      - qcom,sm8750-disp-crm
+
+  label:
+    $ref: /schemas/types.yaml#/definitions/string-array
+    oneOf:
+      - description: Specifies the name of the CRM.
+        items:
+          - enum:
+              - disp_crm
+
+  reg:
+    minItems: 2
+
+  power-domains:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 6
+    description:
+      One interrupt for each DRV. The interrupt trips when a message complete/response is
+      received from the accelerators for the desired power state. Number of interrupts must
+      match number of DRV blocks.
+
+  qcom,sw-drv-ids:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 1
+    maxItems: 6
+    items:
+      minimum: 0
+      maximum: 5
+    description:
+      List of SW DRV IDs used within kernel. May contain holes as some DRVs can
+      be allocated to DSPs / can be spare.
+
+required:
+  - compatible
+  - label
+  - reg
+  - interrupts
+  - qcom,sw-drv-ids
+
+additionalProperties: false
+
+examples:
+  # Example of Display CRM device with SW DRVs
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/qcom,rpmhpd.h>
+    disp_crm: crm@af21000 {
+      compatible = "qcom,sm8750-disp-crm";
+      label = "disp_crm";
+      reg = <0x0af21000 0x6000>,
+            <0x0af29f00 0x100>;
+      power-domains = <&rpmhpd RPMHPD_MMCX>;
+      interrupts = <GIC_SPI 703 IRQ_TYPE_EDGE_RISING>,
+                   <GIC_SPI 708 IRQ_TYPE_EDGE_RISING>,
+                   <GIC_SPI 714 IRQ_TYPE_EDGE_RISING>,
+                   <GIC_SPI 68 IRQ_TYPE_EDGE_RISING>,
+                   <GIC_SPI 96 IRQ_TYPE_EDGE_RISING>,
+                   <GIC_SPI 249 IRQ_TYPE_EDGE_RISING>;
+      qcom,sw-drv-ids = <0 1 2 3 4 5>;
+    };
+...

-- 
2.34.1


^ permalink raw reply related

* [PATCH 00/13] Add support to control clocks using CESTA Resource manager
From: Jagadeesh Kona @ 2026-04-20 16:28 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd, Neil Armstrong,
	Lee Jones
  Cc: Ajit Pandey, Imran Shaik, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Maulik Shah, Taniya Das, Jagadeesh Kona

This series adds support for managing SM8750 DISPCC MDP clocks through
CESTA (Client-side State Aggregation). CESTA provides resource management
(e.g clocks) for local subsystem resources.

Without CESTA, the clock driver directly programs the clock tree (PLLs,
RCGs, CBCs). With CESTA, the clock driver instead sends a performance
level request to the CESTA Resource Manager (CRM) driver. The CESTA
hardware then aggregates requests and scales clocks to the required
frequencies.

Without CESTA (direct clock programming)
----------------------------------------

+------------------+
|   Clock driver   |
+------------------+
          |
          |  programs PLLs / RCGs / CBCs directly
          v
+------------------+      +------------------+      +------------------+
|       PLLs       | ---> |       RCGs       | ---> |       CBCs       |
+------------------+      +------------------+      +------------------+

With CESTA (clock driver invokes CRM API's)
------------------------------------

+------------------+
|   Clock driver   |
+------------------+
          |
          |   sends perf_level request
          v
+------------------+
|    CRM driver    |
+------------------+
          |
          |   sends received request to CESTA HW 
          |  (CESTA HW programs PLLs / RCGs / CBCs)
          v
+------------------+      +------------------+      +------------------+
|       PLLs       | ---> |       RCGs       | ---> |       CBCs       |
+------------------+      +------------------+      +------------------+

This series includes:
1) Base support for the CRM drivers
2) Clock drivers support to communicate with CRM and control clocks via
CESTA
3) SM8750 DISPCC changes to control the DISPCC MDP clock and display PLL0
using CESTA

Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Signed-off-by: Taniya Das <taniya.das@oss.qualcomm.com>
Signed-off-by: Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>
---
Jagadeesh Kona (9):
      dt-bindings: clock: qcom,sm8550-dispcc: Add display CESTA support on SM8750
      dt-bindings: mfd: syscon: Add qcom,crmc-syscon compatible
      clk: qcom: common: Add helpers to control clocks using CRM
      clk: qcom: clk-alpha-pll: Add support for CRM based PLL ops
      clk: qcom: clk-alpha-pll: Add support to skip PLL configuration
      clk: qcom: clk-rcg2: Add support for CRM based RCG ops
      clk: qcom: common: Add support to register and control clocks using CRM
      clk: qcom: dispcc-sm8750: Add support to control MDP clocks using CESTA
      arm64: dts: qcom: sm8750: Add disp_crmc node and CRM properties to dispcc

Maulik Shah (4):
      dt-bindings: soc: Introduce device bindings for CESTA Resource Manager
      soc: qcom: Introduce CESTA resource manager driver
      arm64: dts: qcom: sm8750: Add Display CRM device
      arm64: defconfig: Enable Qualcomm CESTA Resource Manager

 .../bindings/clock/qcom,sm8550-dispcc.yaml         |  48 +
 Documentation/devicetree/bindings/mfd/syscon.yaml  |   2 +
 .../devicetree/bindings/soc/qcom/qcom,crm.yaml     |  83 ++
 arch/arm64/boot/dts/qcom/sm8750.dtsi               |  23 +
 arch/arm64/configs/defconfig                       |   1 +
 drivers/clk/qcom/clk-alpha-pll.c                   |  15 +-
 drivers/clk/qcom/clk-alpha-pll.h                   |   3 +
 drivers/clk/qcom/clk-rcg.h                         |  23 +-
 drivers/clk/qcom/clk-rcg2.c                        | 242 +++++
 drivers/clk/qcom/common.c                          |  93 ++
 drivers/clk/qcom/common.h                          |  57 +-
 drivers/clk/qcom/dispcc-sm8750.c                   |  89 +-
 drivers/soc/qcom/Kconfig                           |  10 +
 drivers/soc/qcom/Makefile                          |   2 +
 drivers/soc/qcom/crm.c                             | 996 +++++++++++++++++++++
 drivers/soc/qcom/trace-crm.h                       | 111 +++
 include/soc/qcom/crm.h                             |  72 ++
 17 files changed, 1834 insertions(+), 36 deletions(-)
---
base-commit: c7275b05bc428c7373d97aa2da02d3a7fa6b9f66
change-id: 20260419-cesta-sm870-dispcc-5e4fab72b53e

Best regards,
-- 
Jagadeesh Kona <jagadeesh.kona@oss.qualcomm.com>


^ permalink raw reply

* Re: [PATCH v3 2/7] spi: qcom-qspi: Fix incomplete error handling in runtime PM
From: Mark Brown @ 2026-04-20 16:29 UTC (permalink / raw)
  To: Viken Dadhaniya
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, cros-qcom-dts-watchers, linux-arm-msm, linux-spi,
	devicetree, linux-kernel
In-Reply-To: <20260420-spi-nor-v3-2-7de325a29010@oss.qualcomm.com>

[-- Attachment #1: Type: text/plain, Size: 629 bytes --]

On Mon, Apr 20, 2026 at 11:42:49AM +0530, Viken Dadhaniya wrote:
> The runtime PM functions had incomplete error handling that could leave the
> system in an inconsistent state. If any operation failed midway through
> suspend or resume, some resources would be left in the wrong state while
> others were already changed, leading to potential clock/power imbalances.

> +err_select_default_state:
> +	pinctrl_pm_select_default_state(dev);
> +err_enable_icc:
> +	icc_enable(ctrl->icc_path_cpu_to_qspi);
> +err_enable_clk:
> +	clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);

clk_bulk_prepare_enable() is marked __must_check.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v1 1/2] dt-bindings: trivial-devices: add toradex,lava-hat-spi
From: Conor Dooley @ 2026-04-20 16:26 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Francesco Dolcini, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Mark Brown, Francesco Dolcini, devicetree, linux-kernel,
	linux-spi
In-Reply-To: <005d96ea-b250-4af8-9c5b-d145bae892e5@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 1663 bytes --]

On Mon, Apr 20, 2026 at 06:10:08PM +0200, Krzysztof Kozlowski wrote:
> On 20/04/2026 18:02, Conor Dooley wrote:
> > On Mon, Apr 20, 2026 at 01:45:35PM +0200, Francesco Dolcini wrote:
> >> From: Francesco Dolcini <francesco.dolcini@toradex.com>
> >>
> >> Add a compatible string for the SPI loopback device present on the
> >> Toradex LAVA HAT test fixture.
> >>
> >> The Toradex LAVA HAT is a board used for automated hardware-in-the-loop
> >> (HIL) testing, it provides several test-related functionalities, and
> >> exists in multiple variants depending on the board it is mated with. The
> >> SPI function is implemented with a loopback on the MISO/MOSI signals.
> >>
> >> As the device can be fully described using only "compatible" and "reg",
> >> it is appropriate to list it under trivial-devices.yaml rather than
> >> introducing a dedicated binding.
> >>
> >> The SPI implementation is identical across all Toradex LAVA HAT
> >> variants, so a single compatible string is sufficient.
> >>
> >> Link: https://lore.kernel.org/all/20260310133254.GA51497@francesco-nb/
> >> Link: https://lore.kernel.org/all/20260316073547.11437-3-francesco@dolcini.it/
> >> Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
> > 
> > Acked-by: Conor Dooley <conor.dooley@microchip.com>
> > pw-bot: not-applicable
> 
> 
> Heh, let's wait. That's a test device, so it won't be ever used in any
> final product, right? So not in final DTS?
> 
> Then why it's here? I think I directed previously to document it with
> the rest of test devices - in incomplete-devices schema?

Oh it's that, I forgot about this discussion, sorry!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH v7 1/3] dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl
From: Conor Dooley @ 2026-04-20 16:25 UTC (permalink / raw)
  To: Billy Tsai
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery, Linus Walleij, Bartosz Golaszewski,
	Ryan Chen, Andrew Jeffery, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	openbmc@lists.ozlabs.org, linux-gpio@vger.kernel.org,
	linux-clk@vger.kernel.org
In-Reply-To: <OSQPR06MB7252BD7967D2567AD6DA7A1D8B2F2@OSQPR06MB7252.apcprd06.prod.outlook.com>

[-- Attachment #1: Type: text/plain, Size: 5939 bytes --]

On Mon, Apr 20, 2026 at 07:22:57AM +0000, Billy Tsai wrote:
> > > > > +    properties:
> > > > > +      function:
> > > > > +        enum:
> > > > > +          - EMMC
> > > > > +          - JTAGDDR
> > > > > +          - JTAGM0
> > > > > +          - JTAGPCIEA
> > > > > +          - JTAGPCIEB
> > > > > +          - JTAGPSP
> > > > > +          - JTAGSSP
> > > > > +          - JTAGTSP
> > > > > +          - JTAGUSB3A
> > > > > +          - JTAGUSB3B
> > > > > +          - PCIERC0PERST
> > > > > +          - PCIERC1PERST
> > > > > +          - TSPRSTN
> > > > > +          - UFSCLKI
> > > > > +          - USB2AD0
> > > > > +          - USB2AD1
> > > > > +          - USB2AH
> > > > > +          - USB2AHP
> > > > > +          - USB2AHPD0
> > > > > +          - USB2AXH
> > > > > +          - USB2AXH2B
> > > > > +          - USB2AXHD1
> > > > > +          - USB2AXHP
> > > > > +          - USB2AXHP2B
> > > > > +          - USB2AXHPD1
> > > > > +          - USB2BD0
> > > > > +          - USB2BD1
> > > > > +          - USB2BH
> > > > > +          - USB2BHP
> > > > > +          - USB2BHPD0
> > > > > +          - USB2BXH
> > > > > +          - USB2BXH2A
> > > > > +          - USB2BXHD1
> > > > > +          - USB2BXHP
> > > > > +          - USB2BXHP2A
> > > > > +          - USB2BXHPD1
> > > > > +          - USB3AXH
> > > > > +          - USB3AXH2B
> > > > > +          - USB3AXHD
> > > > > +          - USB3AXHP
> > > > > +          - USB3AXHP2B
> > > > > +          - USB3AXHPD
> > > > > +          - USB3BXH
> > > > > +          - USB3BXH2A
> > > > > +          - USB3BXHD
> > > > > +          - USB3BXHP
> > > > > +          - USB3BXHP2A
> > > > > +          - USB3BXHPD
> > > > > +          - VB
> > > > > +          - VGADDC
> > > > > +
> > > > > +      groups:
> > > > > +        enum:
> > > > > +          - EMMCCDN
> > > > > +          - EMMCG1
> > > > > +          - EMMCG4
> > > > > +          - EMMCG8
> > > > > +          - EMMCWPN
> > > > > +          - JTAG0
> > > > > +          - PCIERC0PERST
> > > > > +          - PCIERC1PERST
> > > > > +          - TSPRSTN
> > > > > +          - UFSCLKI
> > > > > +          - USB2A
> > > > > +          - USB2AAP
> > > > > +          - USB2ABP
> > > > > +          - USB2ADAP
> > > > > +          - USB2AH
> > > > > +          - USB2AHAP
> > > > > +          - USB2B
> > > > > +          - USB2BAP
> > > > > +          - USB2BBP
> > > > > +          - USB2BDBP
> > > > > +          - USB2BH
> > > > > +          - USB2BHBP
> > > > > +          - USB3A
> > > > > +          - USB3AAP
> > > > > +          - USB3ABP
> > > > > +          - USB3B
> > > > > +          - USB3BAP
> > > > > +          - USB3BBP
> > > > > +          - VB0
> > > > > +          - VB1
> > > > > +          - VGADDC
> > > > > +      pins:
> > > > > +        enum:
> > > > > +          - AB13
> > > > > +          - AB14
> > > > > +          - AC13
> > > > > +          - AC14
> > > > > +          - AD13
> > > > > +          - AD14
> > > > > +          - AE13
> > > > > +          - AE14
> > > > > +          - AE15
> > > > > +          - AF13
> > > > > +          - AF14
> > > > > +          - AF15
> 
> > > > Why do you have groups and pins?
> > > > Is it valid in your device to have groups and pins in the same node?
> 
> > > The intent is to support both group-based mux selection and
> > > configuration, as well as per-pin configuration.
> 
> > > In our hardware:
> > > - `function` + `groups` are used for pinmux selection.
> > > - `pins` is used for per-pin configuration (e.g. drive strength,
> > >   bias settings).
> > > - `groups` may also be used for group-level configuration.
> 
> > > As a result, both `groups` and `pins` may appear in the same node,
> > > but they serve different purposes and do not conflict:
> > > - `groups` selects the mux function and may apply configuration to
> > >   the entire group.
> > > - `pins` allows overriding or specifying configuration for individual
> > >   pins.
> 
> > > In most cases, only one of them is needed, but both are allowed when
> > > both group-level and per-pin configuration are required.
> 
> > To be honest, that sounds like your groups are not sufficiently
> > granular and should be reduced such that you can use them for pin
> > settings.
> 
> The intent was to keep the binding flexible, but in practice the mixed
> use of `groups` and `pins` in the same node is not expected to be used.
> 
> Given that, I agree this flexibility is unnecessary and makes the
> binding semantics less clear. I'll rework the binding to make the
> expected usage explicit rather than allowing combinations that do not
> correspond to a real use case.
> 
> In particular, I'll split the constraints as follows:
> 
> - For pinmux, the presence of `function` will require `groups`, and
>   `pins` will not be allowed. This reflects the hardware design, where
>   the groups are defined by the pins affected by a given mux expression
> 
> - For pin configuration, exactly one of `groups` or `pins` will be
>   required (using oneOf), so that configuration is applied either at
>   group level or per-pin, but not both.
> 
> 
> - if:
>     required:
>       - function
>   then:
>     required:
>       - groups
>     not:
>       required:
>         - pins
>   else:
>     oneOf:
>       - required:
>           - groups
>         not:
>           required:
>             - pins
>       - required:
>           - pins
>         not:
>           required:
>             - groups
> Does this match what you had in mind?

It's an improvement I think, but I am wondering why you cannot do
without pins entirely and apply pinconf stuff at the group level?
Of course that may not be possible with the current groups, but if you
made the groups more granular, would it be possible?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox