From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D2AB7CD4F4A for ; Mon, 18 May 2026 13:53:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=yDDIVDmmy7D8/Eh/ANKZdgfqc/DdY3a7Dbnb/qdyJO8=; b=Bwo0q3ZoFTLZTtf2teqDpmRDMx hh+4C6aeeZFFGO/RnwFTTCCoGnYBW8PWivu5X9jhGsM/oHmTpv0KWPkJOTS6cm1fjKIt2HnUhF7Oe 5nDmTn+9jhntWKgvp9zOaRMkqvWN2jrrvzCn4yKjcGuUrx4e0DyFPd2DfSABUqw3IAPJoksleEybn JflhUF5NpyGGc7QVuXznFIhhMB7uC/VoASzGUvWEUSKvI2wMEa0HgJxFLCsDyCsT+Kixn/nRe/8ch uBh4WcEh53tYGee0RYqPemA9GO8XPao9rk2K11X9rlayB8hAuMgoD9HrEziu8jV1H++FonBgp02Ri 4UJA49rQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wOyPJ-0000000Fs9C-0yH9; Mon, 18 May 2026 13:53:21 +0000 Received: from foss.arm.com ([217.140.110.172]) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wOyPA-0000000Fs0y-3MMY for linux-arm-kernel@lists.infradead.org; Mon, 18 May 2026 13:53:14 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2245C4688; Mon, 18 May 2026 06:53:07 -0700 (PDT) Received: from donnerap.manchester.arm.com (donnerap.manchester.arm.com [10.33.8.81]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 49F693F7B4; Mon, 18 May 2026 06:53:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1779112392; bh=NhwevsMydHj3IoadxuHQ7kTAs2o+tf+EmgIAvzhPSc8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QaKqpgM9mXYUwFXEUqgyOtYaQqfVt/BOeW6liI1FLehQAMDKgHwRiC6AhS28M6uhC xfviLW5MrU1eGEH0NGWPHplY+LyYSw1+YN1/yS7sjJ3J2tWg2mffgIxHpSNdJGck0E VIEsuPz2eHpMOhCZjG5r7m3/NhunCLukCQwynBHk= From: Philip Radford To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, james.quinlan@broadcom.com, f.fainelli@gmail.com, vincent.guittot@linaro.org, etienne.carriere@st.com, peng.fan@oss.nxp.com, michal.simek@amd.com, quic_sibis@quicinc.com, dan.carpenter@linaro.org, d-gole@ti.com, souvik.chakravarty@arm.com, Philip Radford Subject: [PATCH v6 10/12] powercap: arm_scmi: Create synthetic parent node for multi-instance Date: Mon, 18 May 2026 14:52:32 +0100 Message-ID: <20260518135234.2953532-11-philip.radford@arm.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260518135234.2953532-1-philip.radford@arm.com> References: <20260518135234.2953532-1-philip.radford@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260518_065312_962336_542C7BD2 X-CRM114-Status: GOOD ( 18.15 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org An SCMI powercap instance may expose a hierarchy of domains, or even a forest of multiple domain trees rooted at SCMI_POWERCAP_ROOT_ZONE_ID. Those hierarchies are valid within the namespace of a single SCMI instance. Currently, the powercap framework has no notion of SCMI instances. If root domains from multiple SCMI instances are registered directly under the same Linux powercap control type, the per-instance boundaries are lost and the resulting Linux hierarchy becomes a merge of otherwise independent SCMI topologies. Add a synthetic top-level powercap zone per SCMI instance and register that instance's SCMI root domains beneath it. This keeps each instance's SCMI hierarchy grouped together. Signed-off-by: Philip Radford --- V5->V6 - Amended omission of spz initialization - Tested unloading and loading powercap module - Re-wrote commit message --- drivers/powercap/arm_scmi_powercap.c | 78 ++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c index 7f2bb162f96c..5e7b7a3b79f2 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -36,6 +36,7 @@ struct scmi_powercap_root { struct scmi_powercap_zone *spzones; struct list_head *registered_zones; struct list_head scmi_zones; + struct scmi_powercap_zone instance_root; }; static struct powercap_control_type *scmi_top_pcntrl; @@ -263,12 +264,50 @@ static const struct powercap_zone_constraint_ops constraint_ops = { .get_name = scmi_powercap_get_name, }; +/* + * Multi-instance constraints to meet driver requrements due to the fact + * that full zone semantics aren't available for the synthetic zone. + */ +static int instance_root_release(struct powercap_zone *pz) +{ + return 0; +} + +static int instance_root_get_power_uw(struct powercap_zone *pz, u64 *v) +{ + *v = 0; + return 0; +} + +static int instance_root_set_constraint(struct powercap_zone *pz, int cid, u64 v) +{ + return -EOPNOTSUPP; +} + +static int instance_root_get_constraint(struct powercap_zone *pz, int cid, u64 *v) +{ + return -EOPNOTSUPP; +} + +static const struct powercap_zone_ops instance_root_ops = { + .get_max_power_range_uw = scmi_powercap_get_max_power_range_uw, + .get_power_uw = instance_root_get_power_uw, + .release = instance_root_release, +}; + +static const struct powercap_zone_constraint_ops instance_root_const_ops = { + .set_power_limit_uw = instance_root_set_constraint, + .get_power_limit_uw = instance_root_get_constraint, + .set_time_window_us = instance_root_set_constraint, + .get_time_window_us = instance_root_get_constraint, +}; + static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr) { int i; /* Un-register children zones first starting from the leaves */ - for (i = pr->num_zones - 1; i >= 0; i--) { + for (i = pr->num_zones; i >= 0; i--) { if (!list_empty(&pr->registered_zones[i])) { struct scmi_powercap_zone *spz; @@ -313,7 +352,10 @@ static int scmi_powercap_register_zone(struct scmi_powercap_root *pr, parent ? &parent->zone : NULL, &zone_ops, spz->info->num_cpli, &constraint_ops); if (!IS_ERR(z)) { - spz->height = scmi_powercap_get_zone_height(spz); + if (parent) + spz->height = parent->height + 1; + else + spz->height = 0; spz->registered = true; list_move(&spz->node, &pr->registered_zones[spz->height]); dev_dbg(spz->dev, "Registered node %s - parent %s - height:%d\n", @@ -384,6 +426,8 @@ static int scmi_zones_register(struct device *dev, struct scmi_powercap_zone *parent; parent = scmi_powercap_get_parent_zone(spz); + if (!parent) + parent = &pr->instance_root; if (parent && !parent->registered) { zones_stack[sp++] = spz; spz = parent; @@ -424,8 +468,11 @@ static int scmi_powercap_probe(struct scmi_device *sdev) int ret, i; struct scmi_powercap_root *pr; struct scmi_powercap_zone *spz; + struct scmi_powercap_zone *ir; struct scmi_protocol_handle *ph; struct device *dev = &sdev->dev; + char *instance_name; + struct powercap_zone *z; if (!sdev->handle) return -ENODEV; @@ -453,7 +500,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev) return -ENOMEM; /* Allocate for worst possible scenario of maximum tree height. */ - pr->registered_zones = devm_kcalloc(dev, pr->num_zones, + pr->registered_zones = devm_kcalloc(dev, pr->num_zones + 1, sizeof(*pr->registered_zones), GFP_KERNEL); if (!pr->registered_zones) @@ -461,6 +508,9 @@ static int scmi_powercap_probe(struct scmi_device *sdev) INIT_LIST_HEAD(&pr->scmi_zones); + for (i = 0; i <= pr->num_zones; i++) + INIT_LIST_HEAD(&pr->registered_zones[i]); + for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) { /* * Powercap domains are validate by the protocol layer, i.e. @@ -473,7 +523,6 @@ static int scmi_powercap_probe(struct scmi_device *sdev) spz->ph = ph; spz->spzones = pr->spzones; INIT_LIST_HEAD(&spz->node); - INIT_LIST_HEAD(&pr->registered_zones[i]); list_add_tail(&spz->node, &pr->scmi_zones); /* @@ -491,6 +540,27 @@ static int scmi_powercap_probe(struct scmi_device *sdev) } } + ir = &pr->instance_root; + ir->dev = dev; + INIT_LIST_HEAD(&ir->node); + instance_name = devm_kasprintf(dev, GFP_KERNEL, "instance_%s", dev_name(dev)); + if (!instance_name) + return -ENOMEM; + + z = powercap_register_zone(&ir->zone, scmi_top_pcntrl, + instance_name, NULL, &instance_root_ops, 0, + &instance_root_const_ops); + + if (IS_ERR(z)) { + ret = PTR_ERR(z); + dev_err(dev, "Failed to register sysnthetic instance root: %d\n", ret); + return ret; + } + + ir->registered = true; + ir->height = 0; + list_add_tail(&ir->node, &pr->registered_zones[0]); + /* * Scan array of retrieved SCMI powercap domains and register them * recursively starting from the root domains. -- 2.47.3