From nobody Thu Dec 18 05:16:23 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78821C197A0 for ; Thu, 16 Nov 2023 10:43:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235234AbjKPKn1 (ORCPT ); Thu, 16 Nov 2023 05:43:27 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57792 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230348AbjKPKnW (ORCPT ); Thu, 16 Nov 2023 05:43:22 -0500 Received: from mail-pl1-x62e.google.com (mail-pl1-x62e.google.com [IPv6:2607:f8b0:4864:20::62e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 594DEB8 for ; Thu, 16 Nov 2023 02:43:19 -0800 (PST) Received: by mail-pl1-x62e.google.com with SMTP id d9443c01a7336-1cc9b626a96so5772005ad.2 for ; Thu, 16 Nov 2023 02:43:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1700131399; x=1700736199; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UtC8Hkqp39VdOkkGa+oSqv3EzGyQo+AktZrizfGIeEc=; b=k+3Uj5GrEcIGlGmv8swGd0kfO07hKdSpoSlw9c2SPvFG8dyQk7KwzOwskhER+nqO40 a/2Z31pnBGnTnRLhOlDghU3BqpRDl+hsRPZbWjfnqL+/c5j/J23bqKGjMqoyM2LNLzOf dWJxINBKVGqmeATjsNVVAZPFEhcKY5bmX/Nrh8u/rhvtIH8acMnzdkvrMZGom95JKQvn mR4cqpuFCSIu6ATuaeHXCiAU5tBp2MS9GjBTMcHTOg4uRNAMqT/vx1eDbqEc1Ax5wKWk ZzuqdmcfnPLwQ5fIfJY/fWVKnm8SNYxIvxgK9R5VS35mnALb5NGc5ABySLsodAAY2tQX F6gA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700131399; x=1700736199; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UtC8Hkqp39VdOkkGa+oSqv3EzGyQo+AktZrizfGIeEc=; b=ZLiR08GraqazlH4FkT/LgWM5kGiCptocs4ibo3QrT1hglRP/CsEzA3p8UGImrhL6b9 WXq6rZEHfMpe9DiJ9Jm6y1aVkDVrdbrwv247vb3fEewc4xNwKg+pQxHhFk+juPx5P/eS 3L/RBiaw0/jj/k7NgOt+TmJlRs0vi/dJIOYHFQR4spU6PyguJYwuU3t5RqV/foiWyPb+ uZJ7+/B1dlbSKUnB2cEbPktrKCADdVr1p1iFXAmMjdyt7tm8OpDkmyWoaJNHZR4OHU1q AXhxcGn2mbWsufSGAdW9mSF7o0cmGljDmVyHM4t36VmKIwR7tWfoyFzSAaAuhVyDXb4Z D57w== X-Gm-Message-State: AOJu0YzLEx4nPM34VlLII5SOqBuO+D9kcuXyfr/WFYz+pFREFlFNzCC6 /g1pw3a1EcILrpDyoeK3sSbBDQ== X-Google-Smtp-Source: AGHT+IF+lLCjAJlkXERdKhnkQRcLuiOMhIXIZ8Un4tFtKHyyQLgswdzWIFtSbmdHtKcVwWQaySXPZA== X-Received: by 2002:a17:902:ce86:b0:1b5:561a:5ca9 with SMTP id f6-20020a170902ce8600b001b5561a5ca9mr9977070plg.50.1700131398716; Thu, 16 Nov 2023 02:43:18 -0800 (PST) Received: from localhost ([122.172.82.6]) by smtp.gmail.com with ESMTPSA id n2-20020a1709026a8200b001c44dbc92a2sm8859446plk.184.2023.11.16.02.43.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Nov 2023 02:43:18 -0800 (PST) From: Viresh Kumar To: Ulf Hansson , Stephan Gerhold , Viresh Kumar , Nishanth Menon , Stephen Boyd Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , "Rafael J. Wysocki" , Konrad Dybcio , Manivannan Sadhasivam , linux-kernel@vger.kernel.org Subject: [PATCH V3 1/3] OPP: Use _set_opp_level() for single genpd case Date: Thu, 16 Nov 2023 16:13:05 +0530 Message-Id: <5e04c29cd98cf309cd9810dcb13e7789b4a17ada.1700131353.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" There are two genpd (as required-opp) cases that we need to handle, devices with a single genpd and ones with multiple genpds. The multiple genpds case is clear, where the OPP core calls dev_pm_domain_attach_by_name() for them and uses the virtual devices returned by this helper to call dev_pm_domain_set_performance_state() later to change the performance state. The single genpd case however requires special handling as we need to use the same `dev` structure (instead of a virtual one provided by genpd core) for setting the performance state via dev_pm_domain_set_performance_state(). As we move towards more generic code to take care of the required OPPs, where we will recursively call dev_pm_opp_set_opp() for all the required OPPs, the above special case becomes a problem. It doesn't make sense for a device's DT entry to have both "opp-level" and single "required-opps" entry pointing to a genpd's OPP, as that would make the OPP core call dev_pm_domain_set_performance_state() for two different values for the same device structure. And so we can reuse the 'opp->level" field in such a case and call _set_opp_level() for the device. Reviewed-by: Ulf Hansson Signed-off-by: Viresh Kumar Tested-by: Stephan Gerhold --- drivers/opp/core.c | 6 ++++-- drivers/opp/of.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index f2e2aa07b431..aeb216f7e978 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1088,10 +1088,12 @@ static int _opp_set_required_opps_generic(struct de= vice *dev, static int _opp_set_required_opps_genpd(struct device *dev, struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down) { - struct device **genpd_virt_devs =3D - opp_table->genpd_virt_devs ? opp_table->genpd_virt_devs : &dev; + struct device **genpd_virt_devs =3D opp_table->genpd_virt_devs; int index, target, delta, ret; =20 + if (!genpd_virt_devs) + return 0; + /* Scaling up? Set required OPPs in normal order, else reverse */ if (!scaling_down) { index =3D 0; diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 85fad7ca0007..4cdeeab5ceee 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -296,7 +296,7 @@ void _of_clear_opp(struct opp_table *opp_table, struct = dev_pm_opp *opp) of_node_put(opp->np); } =20 -static int _link_required_opps(struct dev_pm_opp *opp, +static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *o= pp_table, struct opp_table *required_table, int index) { struct device_node *np; @@ -314,6 +314,31 @@ static int _link_required_opps(struct dev_pm_opp *opp, return -ENODEV; } =20 + /* + * There are two genpd (as required-opp) cases that we need to handle, + * devices with a single genpd and ones with multiple genpds. + * + * The single genpd case requires special handling as we need to use the + * same `dev` structure (instead of a virtual one provided by genpd + * core) for setting the performance state. + * + * It doesn't make sense for a device's DT entry to have both + * "opp-level" and single "required-opps" entry pointing to a genpd's + * OPP, as that would make the OPP core call + * dev_pm_domain_set_performance_state() for two different values for + * the same device structure. Lets treat single genpd configuration as a + * case where the OPP's level is directly available without required-opp + * link in the DT. + * + * Just update the `level` with the right value, which + * dev_pm_opp_set_opp() will take care of in the normal path itself. + */ + if (required_table->is_genpd && opp_table->required_opp_count =3D=3D 1 && + !opp_table->genpd_virt_devs) { + if (!WARN_ON(opp->level !=3D OPP_LEVEL_UNSET)) + opp->level =3D opp->required_opps[0]->level; + } + return 0; } =20 @@ -338,7 +363,7 @@ static int _of_opp_alloc_required_opps(struct opp_table= *opp_table, if (IS_ERR_OR_NULL(required_table)) continue; =20 - ret =3D _link_required_opps(opp, required_table, i); + ret =3D _link_required_opps(opp, opp_table, required_table, i); if (ret) goto free_required_opps; } @@ -359,7 +384,7 @@ static int lazy_link_required_opps(struct opp_table *op= p_table, int ret; =20 list_for_each_entry(opp, &opp_table->opp_list, node) { - ret =3D _link_required_opps(opp, new_table, index); + ret =3D _link_required_opps(opp, opp_table, new_table, index); if (ret) return ret; } --=20 2.31.1.272.g89b43f80a514 From nobody Thu Dec 18 05:16:23 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 98B4CC54FB9 for ; Thu, 16 Nov 2023 10:43:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345027AbjKPKna (ORCPT ); Thu, 16 Nov 2023 05:43:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57848 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344985AbjKPKn1 (ORCPT ); Thu, 16 Nov 2023 05:43:27 -0500 Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D21EF1A7 for ; Thu, 16 Nov 2023 02:43:22 -0800 (PST) Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-1cc4f777ab9so6021705ad.0 for ; Thu, 16 Nov 2023 02:43:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1700131402; x=1700736202; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=N9BTLIceKJGW92qKd3ko8bGti/Wwlg3EknEGU5HzzG8=; b=ZJdA3UgW5qM31WGwmfS1N/ABXivzwRJRqCX+fe4VMpMFl1ry7xUqZ3ayZYfP9sEkVC jZTBaWsv1BHzyvhF7hiYJq7yFGuEISL0B8y0Xtum0ZiKSWY5AZfcrHJd7q/NQsz9lN6i qVPfggn8RcDxUdnmiYzG18sNBtszsx9Yye0V5aipji/Fymz1AWbvrB/WBlu1HPxHR6Ve nlcndLc/N7VAerxN810GNAlVdVKM7EOiP6skZSj5CkNt7DtwJw4QjKeH+1LnFL0pywd8 M0D9sxHdbFPbv6QmE2YiEozgEaSwPMSknUG9aF/L00F9nB6ckDj2gFr1GOoYefs14GfB 0y1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700131402; x=1700736202; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=N9BTLIceKJGW92qKd3ko8bGti/Wwlg3EknEGU5HzzG8=; b=flX/R9+xDwOCAyImyag2fELSGwhLjvY0+3wOV6U4NAZjetku1xSyn2jbyfa0EC5guC z9GZBsgAnlQQkk9o6tv+7aUjdNmC/O78N8wKLo6BOzqNZMaRoTiFSmMA5ViRNitNAcq0 7blXj+regqL0Jrm190DuZ5u0bfVcos74GImlP0eausvKj2+FR2giDoHsh0/zI4NOQooA DX6N8EMFgmiMe2VxYLR3hGz9vlu4sx+aIT0QEvZH/f20i9IsLzlRWjEjFhtwqykrMwuD ELG29MKPnzVS5N772RHCSTWA+fs8WNrjd9VPkdU87qx+TJY9ipwXEVfvYxIj8+ZozqQM qwAQ== X-Gm-Message-State: AOJu0YwYjk3wk66r70UMeMc3bDupQmHt1KwEpzhMJJglJli/BWJBh7NQ dSjI5TrK5oSTdRQqyMuh6VZyXg== X-Google-Smtp-Source: AGHT+IH1j0bxjDkRH01TYL8RmeGGUObFnJhKV7GL8eCxgG0N5Mc9E0g5N/jXAb/814C9ibjPgPqeJQ== X-Received: by 2002:a17:902:dace:b0:1bb:6875:5a73 with SMTP id q14-20020a170902dace00b001bb68755a73mr9742084plx.2.1700131402233; Thu, 16 Nov 2023 02:43:22 -0800 (PST) Received: from localhost ([122.172.82.6]) by smtp.gmail.com with ESMTPSA id u10-20020a170903124a00b001c9c879ee4asm8906686plh.17.2023.11.16.02.43.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Nov 2023 02:43:21 -0800 (PST) From: Viresh Kumar To: Ulf Hansson , Stephan Gerhold , Viresh Kumar , Nishanth Menon , Stephen Boyd , "Rafael J. Wysocki" Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , Konrad Dybcio , Manivannan Sadhasivam , linux-kernel@vger.kernel.org Subject: [PATCH V3 2/3] OPP: Call dev_pm_opp_set_opp() for required OPPs Date: Thu, 16 Nov 2023 16:13:06 +0530 Message-Id: <57ba55c5568ac4131429124661b37581b6cd6b7a.1700131353.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Configuring the required OPP was never properly implemented, we just took an exception for genpds and configured them directly, while leaving out all other required OPP types. Now that a standard call to dev_pm_opp_set_opp() takes care of configuring the opp->level too, the special handling for genpds can be avoided by simply calling dev_pm_opp_set_opp() for the required OPPs, which shall eventually configure the corresponding level for genpds. This also makes it possible for us to configure other type of required OPPs (no concrete users yet though), via the same path. This is how other frameworks take care of parent nodes, like clock, regulators, etc, where we recursively call the same helper. In order to call dev_pm_opp_set_opp() for the virtual genpd devices, they must share the OPP table of the genpd. Call _add_opp_dev() for them to get that done. This commit also extends the struct dev_pm_opp_config to pass required devices, for non-genpd cases, which can be used to call dev_pm_opp_set_opp() for the non-genpd required devices. Signed-off-by: Viresh Kumar Reviewed-by: Ulf Hansson Tested-by: Stephan Gerhold --- drivers/opp/core.c | 168 ++++++++++++++++++++--------------------- drivers/opp/of.c | 17 +++-- drivers/opp/opp.h | 8 +- include/linux/pm_opp.h | 7 +- 4 files changed, 100 insertions(+), 100 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index aeb216f7e978..e08375ed50aa 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1054,48 +1054,22 @@ static int _set_opp_bw(const struct opp_table *opp_= table, return 0; } =20 -static int _set_performance_state(struct device *dev, struct device *pd_de= v, - struct dev_pm_opp *opp, int i) -{ - unsigned int pstate =3D 0; - int ret; - - if (!pd_dev) - return 0; - - if (likely(opp)) { - pstate =3D opp->required_opps[i]->level; - if (pstate =3D=3D OPP_LEVEL_UNSET) - return 0; - } - - ret =3D dev_pm_domain_set_performance_state(pd_dev, pstate); - if (ret) { - dev_err(dev, "Failed to set performance state of %s: %d (%d)\n", - dev_name(pd_dev), pstate, ret); - } - - return ret; -} - -static int _opp_set_required_opps_generic(struct device *dev, - struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down) -{ - dev_err(dev, "setting required-opps isn't supported for non-genpd devices= \n"); - return -ENOENT; -} - -static int _opp_set_required_opps_genpd(struct device *dev, - struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down) +/* This is only called for PM domain for now */ +static int _set_required_opps(struct device *dev, struct opp_table *opp_ta= ble, + struct dev_pm_opp *opp, bool up) { - struct device **genpd_virt_devs =3D opp_table->genpd_virt_devs; + struct device **devs =3D opp_table->required_devs; int index, target, delta, ret; =20 - if (!genpd_virt_devs) + if (!devs) return 0; =20 + /* required-opps not fully initialized yet */ + if (lazy_linking_pending(opp_table)) + return -EBUSY; + /* Scaling up? Set required OPPs in normal order, else reverse */ - if (!scaling_down) { + if (up) { index =3D 0; target =3D opp_table->required_opp_count; delta =3D 1; @@ -1106,9 +1080,11 @@ static int _opp_set_required_opps_genpd(struct devic= e *dev, } =20 while (index !=3D target) { - ret =3D _set_performance_state(dev, genpd_virt_devs[index], opp, index); - if (ret) - return ret; + if (devs[index]) { + ret =3D dev_pm_opp_set_opp(devs[index], opp->required_opps[index]); + if (ret) + return ret; + } =20 index +=3D delta; } @@ -1116,34 +1092,6 @@ static int _opp_set_required_opps_genpd(struct devic= e *dev, return 0; } =20 -/* This is only called for PM domain for now */ -static int _set_required_opps(struct device *dev, struct opp_table *opp_ta= ble, - struct dev_pm_opp *opp, bool up) -{ - /* required-opps not fully initialized yet */ - if (lazy_linking_pending(opp_table)) - return -EBUSY; - - if (opp_table->set_required_opps) - return opp_table->set_required_opps(dev, opp_table, opp, up); - - return 0; -} - -/* Update set_required_opps handler */ -void _update_set_required_opps(struct opp_table *opp_table) -{ - /* Already set */ - if (opp_table->set_required_opps) - return; - - /* All required OPPs will belong to genpd or none */ - if (opp_table->required_opp_tables[0]->is_genpd) - opp_table->set_required_opps =3D _opp_set_required_opps_genpd; - else - opp_table->set_required_opps =3D _opp_set_required_opps_generic; -} - static int _set_opp_level(struct device *dev, struct opp_table *opp_table, struct dev_pm_opp *opp) { @@ -2406,19 +2354,13 @@ static void _opp_detach_genpd(struct opp_table *opp= _table) { int index; =20 - if (!opp_table->genpd_virt_devs) - return; - for (index =3D 0; index < opp_table->required_opp_count; index++) { - if (!opp_table->genpd_virt_devs[index]) + if (!opp_table->required_devs[index]) continue; =20 - dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false); - opp_table->genpd_virt_devs[index] =3D NULL; + dev_pm_domain_detach(opp_table->required_devs[index], false); + opp_table->required_devs[index] =3D NULL; } - - kfree(opp_table->genpd_virt_devs); - opp_table->genpd_virt_devs =3D NULL; } =20 /* @@ -2445,14 +2387,14 @@ static int _opp_attach_genpd(struct opp_table *opp_= table, struct device *dev, int index =3D 0, ret =3D -EINVAL; const char * const *name =3D names; =20 - if (opp_table->genpd_virt_devs) - return 0; + if (!opp_table->required_devs) { + dev_err(dev, "Required OPPs not available, can't attach genpd\n"); + return -EINVAL; + } =20 - opp_table->genpd_virt_devs =3D kcalloc(opp_table->required_opp_count, - sizeof(*opp_table->genpd_virt_devs), - GFP_KERNEL); - if (!opp_table->genpd_virt_devs) - return -ENOMEM; + /* Checking only the first one is enough ? */ + if (opp_table->required_devs[0]) + return 0; =20 while (*name) { if (index >=3D opp_table->required_opp_count) { @@ -2468,13 +2410,25 @@ static int _opp_attach_genpd(struct opp_table *opp_= table, struct device *dev, goto err; } =20 - opp_table->genpd_virt_devs[index] =3D virt_dev; + /* + * Add the virtual genpd device as a user of the OPP table, so + * we can call dev_pm_opp_set_opp() on it directly. + * + * This will be automatically removed when the OPP table is + * removed, don't need to handle that here. + */ + if (!_add_opp_dev(virt_dev, opp_table->required_opp_tables[index])) { + ret =3D -ENOMEM; + goto err; + } + + opp_table->required_devs[index] =3D virt_dev; index++; name++; } =20 if (virt_devs) - *virt_devs =3D opp_table->genpd_virt_devs; + *virt_devs =3D opp_table->required_devs; =20 return 0; =20 @@ -2484,10 +2438,42 @@ static int _opp_attach_genpd(struct opp_table *opp_= table, struct device *dev, =20 } =20 +static int _opp_set_required_devs(struct opp_table *opp_table, + struct device *dev, + struct device **required_devs) +{ + int i; + + if (!opp_table->required_devs) { + dev_err(dev, "Required OPPs not available, can't set required devs\n"); + return -EINVAL; + } + + /* Another device that shares the OPP table has set the required devs ? */ + if (opp_table->required_devs[0]) + return 0; + + for (i =3D 0; i < opp_table->required_opp_count; i++) + opp_table->required_devs[i] =3D required_devs[i]; + + return 0; +} + +static void _opp_put_required_devs(struct opp_table *opp_table) +{ + int i; + + for (i =3D 0; i < opp_table->required_opp_count; i++) + opp_table->required_devs[i] =3D NULL; +} + static void _opp_clear_config(struct opp_config_data *data) { - if (data->flags & OPP_CONFIG_GENPD) + if (data->flags & OPP_CONFIG_REQUIRED_DEVS) + _opp_put_required_devs(data->opp_table); + else if (data->flags & OPP_CONFIG_GENPD) _opp_detach_genpd(data->opp_table); + if (data->flags & OPP_CONFIG_REGULATOR) _opp_put_regulators(data->opp_table); if (data->flags & OPP_CONFIG_SUPPORTED_HW) @@ -2601,12 +2587,22 @@ int dev_pm_opp_set_config(struct device *dev, struc= t dev_pm_opp_config *config) =20 /* Attach genpds */ if (config->genpd_names) { + if (config->required_devs) + goto err; + ret =3D _opp_attach_genpd(opp_table, dev, config->genpd_names, config->virt_devs); if (ret) goto err; =20 data->flags |=3D OPP_CONFIG_GENPD; + } else if (config->required_devs) { + ret =3D _opp_set_required_devs(opp_table, dev, + config->required_devs); + if (ret) + goto err; + + data->flags |=3D OPP_CONFIG_REQUIRED_DEVS; } =20 ret =3D xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX), diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 4cdeeab5ceee..5a7e294e56b7 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -165,7 +165,7 @@ static void _opp_table_alloc_required_tables(struct opp= _table *opp_table, struct opp_table **required_opp_tables; struct device_node *required_np, *np; bool lazy =3D false; - int count, i; + int count, i, size; =20 /* Traversing the first OPP node is all we need */ np =3D of_get_next_available_child(opp_np, NULL); @@ -179,12 +179,13 @@ static void _opp_table_alloc_required_tables(struct o= pp_table *opp_table, if (count <=3D 0) goto put_np; =20 - required_opp_tables =3D kcalloc(count, sizeof(*required_opp_tables), - GFP_KERNEL); + size =3D sizeof(*required_opp_tables) + sizeof(*opp_table->required_devs); + required_opp_tables =3D kcalloc(count, size, GFP_KERNEL); if (!required_opp_tables) goto put_np; =20 opp_table->required_opp_tables =3D required_opp_tables; + opp_table->required_devs =3D (void *)(required_opp_tables + count); opp_table->required_opp_count =3D count; =20 for (i =3D 0; i < count; i++) { @@ -208,8 +209,6 @@ static void _opp_table_alloc_required_tables(struct opp= _table *opp_table, mutex_lock(&opp_table_lock); list_add(&opp_table->lazy, &lazy_opp_tables); mutex_unlock(&opp_table_lock); - } else { - _update_set_required_opps(opp_table); } =20 goto put_np; @@ -332,9 +331,14 @@ static int _link_required_opps(struct dev_pm_opp *opp,= struct opp_table *opp_tab * * Just update the `level` with the right value, which * dev_pm_opp_set_opp() will take care of in the normal path itself. + * + * There is another case though, where a genpd's OPP table has + * required-opps set to a parent genpd. The OPP core expects the user to + * set the respective required `struct device` pointer via + * dev_pm_opp_set_config(). */ if (required_table->is_genpd && opp_table->required_opp_count =3D=3D 1 && - !opp_table->genpd_virt_devs) { + !opp_table->required_devs[0]) { if (!WARN_ON(opp->level !=3D OPP_LEVEL_UNSET)) opp->level =3D opp->required_opps[0]->level; } @@ -447,7 +451,6 @@ static void lazy_link_required_opp_table(struct opp_tab= le *new_table) =20 /* All required opp-tables found, remove from lazy list */ if (!lazy) { - _update_set_required_opps(opp_table); list_del_init(&opp_table->lazy); =20 list_for_each_entry(opp, &opp_table->opp_list, node) diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 08366f90f16b..23dcb2fbf8c3 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -35,6 +35,7 @@ extern struct list_head opp_tables; #define OPP_CONFIG_PROP_NAME BIT(3) #define OPP_CONFIG_SUPPORTED_HW BIT(4) #define OPP_CONFIG_GENPD BIT(5) +#define OPP_CONFIG_REQUIRED_DEVS BIT(6) =20 /** * struct opp_config_data - data for set config operations @@ -160,9 +161,9 @@ enum opp_table_access { * @rate_clk_single: Currently configured frequency for single clk. * @current_opp: Currently configured OPP for the table. * @suspend_opp: Pointer to OPP to be used during device suspend. - * @genpd_virt_devs: List of virtual devices for multiple genpd support. * @required_opp_tables: List of device OPP tables that are required by OP= Ps in * this table. + * @required_devs: List of devices for required OPP tables. * @required_opp_count: Number of required devices. * @supported_hw: Array of version number to support. * @supported_hw_count: Number of elements in supported_hw array. @@ -180,7 +181,6 @@ enum opp_table_access { * @path_count: Number of interconnect paths * @enabled: Set to true if the device's resources are enabled/configured. * @is_genpd: Marks if the OPP table belongs to a genpd. - * @set_required_opps: Helper responsible to set required OPPs. * @dentry: debugfs dentry pointer of the real device directory (not links= ). * @dentry_name: Name of the real dentry. * @@ -211,8 +211,8 @@ struct opp_table { struct dev_pm_opp *current_opp; struct dev_pm_opp *suspend_opp; =20 - struct device **genpd_virt_devs; struct opp_table **required_opp_tables; + struct device **required_devs; unsigned int required_opp_count; =20 unsigned int *supported_hw; @@ -229,8 +229,6 @@ struct opp_table { unsigned int path_count; bool enabled; bool is_genpd; - int (*set_required_opps)(struct device *dev, - struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down); =20 #ifdef CONFIG_DEBUG_FS struct dentry *dentry; diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index af53101a1383..81dff7facdc9 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -74,8 +74,10 @@ typedef int (*config_clks_t)(struct device *dev, struct = opp_table *opp_table, * @supported_hw_count: Number of elements in the array. * @regulator_names: Array of pointers to the names of the regulator, NULL= terminated. * @genpd_names: Null terminated array of pointers containing names of gen= pd to - * attach. - * @virt_devs: Pointer to return the array of virtual devices. + * attach. Mutually exclusive with required_devs. + * @virt_devs: Pointer to return the array of genpd virtual devices. Mutua= lly + * exclusive with required_devs. + * @required_devs: Required OPP devices. Mutually exclusive with genpd_nam= es/virt_devs. * * This structure contains platform specific OPP configurations for the de= vice. */ @@ -90,6 +92,7 @@ struct dev_pm_opp_config { const char * const *regulator_names; const char * const *genpd_names; struct device ***virt_devs; + struct device **required_devs; }; =20 #define OPP_LEVEL_UNSET U32_MAX --=20 2.31.1.272.g89b43f80a514 From nobody Thu Dec 18 05:16:23 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF4E7C197A0 for ; Thu, 16 Nov 2023 10:43:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345034AbjKPKnh (ORCPT ); Thu, 16 Nov 2023 05:43:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57870 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345013AbjKPKna (ORCPT ); Thu, 16 Nov 2023 05:43:30 -0500 Received: from mail-pl1-x635.google.com (mail-pl1-x635.google.com [IPv6:2607:f8b0:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6AD70B8 for ; Thu, 16 Nov 2023 02:43:26 -0800 (PST) Received: by mail-pl1-x635.google.com with SMTP id d9443c01a7336-1cc58219376so5875305ad.1 for ; Thu, 16 Nov 2023 02:43:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1700131406; x=1700736206; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=exfqg5Gy1TL1CFxTgMKzEKKiOxoyEHWs3MeOTbQpbPM=; b=oakk90eGdb/FNiJPskBRpKIq57Win0VAbBKOf+E4B/7IdKDzVD7ITZkmh6KdJQ2u9z Y7j+PyUb0WZ9ErwLXaMGDo7yCQjOpbV/idfDkKfph7kzKNuxznhQNZMEB5zit6nODio7 sfJ6qLREHVGTPFix6winUMF0u4FeKb3JJ2bIK1UYN50q9ikI7Gpwg/+P2bYO8KZJIyM7 MDWDcCsagV0OSNUV5fGCNLsdidfdSqgUrBbIzs3mr+0HMaCi8xNrfNLOM794o65fsys3 w+WWA350h0bIHQjjpFNh+lcHeyJIZUBVqC5FVdNqV63aVMPzi7cuS7a9tzsbyq3SEyBv OR+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700131406; x=1700736206; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=exfqg5Gy1TL1CFxTgMKzEKKiOxoyEHWs3MeOTbQpbPM=; b=wbH+mA9lrAzJwfm/4RtRUb1Eb8TvWa1Ufe78GcaPjAoGS/dOPcVGhy4BOg622i8VIF THoFSsTXJKyCNHjWDjcBaozQrD1CC5S+F74JBnTJQDxRieFNqOyRT6vxpvgFYNBnCJt2 Y5cszSNWr5LF9+vbncMwYsXyp3egfR8Uq3wBE42i+rcN0qvgJvaM14U95Ji8Tex8Y43i 8wt9dk0jg9LgDJcAHexPkInmTiJQo1hG6mxCZMyHukFtI/XaEO/HqqYfUcYUlu833qJV pv/iBVHuWOVMoV35Apy0RyE04afwViso2KP5Ptr+99+bJDHwFMNVkWo12+2+UqZsdejH MYxw== X-Gm-Message-State: AOJu0Yy7V+GR/6DBaZXOPUV1Tzdzx+MsIEcMbT0Z2XlOD9MeEg8mdUj9 pFRUa20RA3Aq6nViUG3zGv/FxQ== X-Google-Smtp-Source: AGHT+IEs0EGGMmo1oeYYkUgsL8ss6a7i6oyoEG3zWDZDx5n+X+ng1KMN/g8+LTbsbtgNIcCU9isEcw== X-Received: by 2002:a17:90b:3e89:b0:281:40b:5a7a with SMTP id rj9-20020a17090b3e8900b00281040b5a7amr16857952pjb.8.1700131405640; Thu, 16 Nov 2023 02:43:25 -0800 (PST) Received: from localhost ([122.172.82.6]) by smtp.gmail.com with ESMTPSA id sr5-20020a17090b4e8500b00280c6f35546sm1307044pjb.49.2023.11.16.02.43.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Nov 2023 02:43:25 -0800 (PST) From: Viresh Kumar To: Ulf Hansson , Stephan Gerhold , Viresh Kumar , Nishanth Menon , Stephen Boyd Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , "Rafael J. Wysocki" , Konrad Dybcio , Manivannan Sadhasivam , linux-kernel@vger.kernel.org Subject: [PATCH V3 3/3] OPP: Don't set OPP recursively for a parent genpd Date: Thu, 16 Nov 2023 16:13:07 +0530 Message-Id: <084b6088106da837abc43526c11d7d8bec850c5c.1700131353.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Like other frameworks (clk, regulator, etc.) genpd core too takes care of propagation to performance state to parent genpds. The OPP core shouldn't attempt the same, or it may result in undefined behavior. Add checks at various places to take care of the same. Signed-off-by: Viresh Kumar Reviewed-by: Ulf Hansson Tested-by: Stephan Gerhold --- drivers/opp/core.c | 16 +++++++++++++++- drivers/opp/of.c | 7 +++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index e08375ed50aa..4f1ca84d9ed0 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2392,6 +2392,12 @@ static int _opp_attach_genpd(struct opp_table *opp_t= able, struct device *dev, return -EINVAL; } =20 + /* Genpd core takes care of propagation to parent genpd */ + if (opp_table->is_genpd) { + dev_err(dev, "%s: Operation not supported for genpds\n", __func__); + return -EOPNOTSUPP; + } + /* Checking only the first one is enough ? */ if (opp_table->required_devs[0]) return 0; @@ -2453,8 +2459,16 @@ static int _opp_set_required_devs(struct opp_table *= opp_table, if (opp_table->required_devs[0]) return 0; =20 - for (i =3D 0; i < opp_table->required_opp_count; i++) + for (i =3D 0; i < opp_table->required_opp_count; i++) { + /* Genpd core takes care of propagation to parent genpd */ + if (required_devs[i] && opp_table->is_genpd && + opp_table->required_opp_tables[i]->is_genpd) { + dev_err(dev, "%s: Operation not supported for genpds\n", __func__); + return -EOPNOTSUPP; + } + opp_table->required_devs[i] =3D required_devs[i]; + } =20 return 0; } diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 5a7e294e56b7..f9f0b22bccbb 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -339,8 +339,11 @@ static int _link_required_opps(struct dev_pm_opp *opp,= struct opp_table *opp_tab */ if (required_table->is_genpd && opp_table->required_opp_count =3D=3D 1 && !opp_table->required_devs[0]) { - if (!WARN_ON(opp->level !=3D OPP_LEVEL_UNSET)) - opp->level =3D opp->required_opps[0]->level; + /* Genpd core takes care of propagation to parent genpd */ + if (!opp_table->is_genpd) { + if (!WARN_ON(opp->level !=3D OPP_LEVEL_UNSET)) + opp->level =3D opp->required_opps[0]->level; + } } =20 return 0; --=20 2.31.1.272.g89b43f80a514