Re: [PATCH 04/13] soc: qcom: Introduce CESTA resource manager driver

Krzysztof Kozlowski
2026-04-22T07:50:26Z

On Mon, Apr 20, 2026 at 09:58:57PM +0530, Jagadeesh Kona wrote:
> 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"

Just "Qualcomm CESTA Resource Manager (CRM) driver"

Eveyrthing is Qualcomm Technologies, Inc. (QTI) and you make it
completely unreadable for the menuconfig.

> + depends on QCOM_RPMH && (ARCH_QCOM || COMPILE_TEST)

What do you need from rpmh for 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.

Wrap the code to match Linux coding style.

...

> +static int crm_probe(struct platform_device *pdev)
> +{
> + struct device_node *dn = pdev->dev.of_node;

This is NOT called 'dn', but 'np'. Always. Don't copy buggy rpmh code or
other weird drivers.

> + 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);

How is this an error? If FW did not enable CRM, it did it on purpose, so
why are you reporting it as an error to the user? What user should do
with that? File a bug report for the FW?

> + 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[] = {

Why isn't this const?

> + [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,
> +};

Best regards,
Krzysztof