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 EEA66CD98E2 for ; Wed, 17 Jun 2026 10:00:13 +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=m44Ib0EQT411uaa4+6+6sv+sAn3Hjcho2ZU3LjiYU7M=; b=Isd9BMo7YitJOW7sGx3rAQuSGl n+81WbLF9vBE7IfI1AKroEaOKiK4jrLC5w0zUAhzg//GRuWiPHW8JQ9j1eR/cpAunsIaJksdmSvgY MxYPs2USpvWFy3kTWoA1jiTjD/E7kRcpigwERKCG6u1tZlQCti3BRWFQw9sUlYlzfFZLi2GYGCOgG t0LMqgKTCaydQRApquEaDHFC9CMknloCJNnlQfdttyw1J2D0xh6P07I9IRgkjLz+SuRW75WtzTqEs Z+tbuo8kRHNReIdTHhhgCnZP6pswB4XdQ8yW7Ij5OjHNcxPMPamSyEhogfPV33B9SKhKhkBkvc1Hi Kz/9VaMw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wZn43-0000000H063-2Hfr; Wed, 17 Jun 2026 10:00:07 +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 1wZn3t-0000000GztE-2DmK for linux-arm-kernel@lists.infradead.org; Wed, 17 Jun 2026 09:59:58 +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 621954800; Wed, 17 Jun 2026 02:59:52 -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 B69213F915; Wed, 17 Jun 2026 02:59:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1781690397; bh=DnbYuP1p4H3RSxY3W/5qQW7k9Y1jiRH6deGHPh5cxWg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=feVdqgGFGwuLhRyVjcb/65JsIPkMvgkUBEmH4HK+QOuho/uHgbMw9Kdz/w5ssPLN4 RxvujQTO9NyNPPF7A83itJLWtcZKR+riPOYAW3lqdCiTTDSGPMpa9qOBvDxDvb1P84 dhvm+GMgzeHbmA3jm8bYLjU08+WWQGlkoOi0cCGw= 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 v7 11/13] powercap: arm_scmi: Create synthetic parent node for multi-instance Date: Wed, 17 Jun 2026 10:59:08 +0100 Message-ID: <20260617095910.1963578-12-philip.radford@arm.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com> References: <20260617095910.1963578-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-20260617_025957_660817_96C2C3C7 X-CRM114-Status: GOOD ( 18.10 ) 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 --- V6->V7 - Added cleanup for zones during scmi_powercap_probe V5->V6 - Amended omission of spz initialization - Tested unloading and loading powercap module - Re-wrote commit message --- drivers/powercap/arm_scmi_powercap.c | 93 +++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c index 2f8a1e0ecccf..d45e4af0cdc7 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -38,6 +38,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 LIST_HEAD(scmi_powercap_roots); @@ -401,18 +402,61 @@ 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; - list_for_each_entry(spz, &pr->registered_zones[i], node) + list_for_each_entry(spz, &pr->registered_zones[i], node) { + if (!spz->registered) + continue; + + spz->registered = false; powercap_unregister_zone(scmi_top_pcntrl, &spz->zone); + } } } } @@ -451,7 +495,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", @@ -522,6 +569,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; @@ -562,8 +611,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; @@ -591,7 +643,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) @@ -599,6 +651,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. @@ -611,7 +666,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); /* @@ -629,19 +683,44 @@ 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. */ ret = scmi_zones_register(dev, pr); - if (ret) + if (ret) { + scmi_powercap_unregister_all_zones(pr); return ret; + } INIT_LIST_HEAD(&pr->node); ret = scmi_powercap_read_root_children_enable_state(pr, &pr->enabled); - if (ret) + if (ret) { + scmi_powercap_unregister_all_zones(pr); return ret; + } mutex_lock(&scmi_powercap_roots_lock); list_add_tail(&pr->node, &scmi_powercap_roots); -- 2.47.3