From nobody Mon Feb 9 19:25:53 2026 Received: from mail-ed1-f53.google.com (mail-ed1-f53.google.com [209.85.208.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C26731619A for ; Sat, 27 Dec 2025 12:17:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766837886; cv=none; b=PTgb9LFzTwod1Jd8OYAGNixXMJzmifZ7UGQyPNsB31Svli6IECbtyz8UH1FaINZ4FF7Cbl41XnC7li4kTD+O/Rp/OppJV9sJ2Jb4Mcc6Fr4dMrEgSgk6QSQDOQQ5R8zlzCbzC48nUAgL74TGUUjor93w678Kj3kqHlMBKLYFEVM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766837886; c=relaxed/simple; bh=+wEqtGb1mT5TBazth4wWosQDATUcEUvo7aZ1Vrf3o+4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N2qsQMJkRwL63gdCF+8ZXjGb3dskJYhiBQdnMF6d+2RWOSvDy5et3IlrUtfL1xrnXTDWXmr8IoHlzdivTFz9OPnMhhaykUwVApzjarNEynOVMhXHBLYWZkGs5CAhBZ/9NRjI5TSh7KO+MwN/WwdayDlT9kj6IjK06UMZRExrCIU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=vvQVQtTv; arc=none smtp.client-ip=209.85.208.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="vvQVQtTv" Received: by mail-ed1-f53.google.com with SMTP id 4fb4d7f45d1cf-64b921d9e67so10840367a12.3 for ; Sat, 27 Dec 2025 04:17:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1766837876; x=1767442676; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=f6eq2tvfa3ZOSLXibtVic90tZ1iImogx/6bP2FWF+Uw=; b=vvQVQtTvcp6G9i3D3p2qfslRswljvvceRYOkkWtEW6xizGINPS0+UtzedRB66uobVp D1DSoWJb8tsWQe+sFM7SmLRDOpw3Hn3KGChkJfH74XRKCrkNNUVRsohzP7s3AAQNqJZe d6VexucK4/E+KnPqQjbi/EEm21xsORhxpA98rhifFSzcKfkTjqDXwO+E/fNkYwb3LFUU JZsbHl0bnV7sgyfBpRh5frrJIdXKl+ef3M25plaulFCIpu4pwC7meiDMsXqv/8uGpsVq E7ffO0/Oi9zYkzJZDC6QpFsiw96MxJ6hduVp8UvatcYlMfZSmaERtHetYYnrcvGZnDae +slw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766837876; x=1767442676; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=f6eq2tvfa3ZOSLXibtVic90tZ1iImogx/6bP2FWF+Uw=; b=XQT78KhUU43qe5m+RU8JDGZC1Y+3Z95HkVrxvY0D/QzCNCI0ImgK7e5aL7EUOuORzV cgbrD1PdD/WE4EpmxxyEZUXM4K+OXBadtlbQes03nn7x6Q21pVE5/bWhckR99o5E3PZw qs9CjuuQsA322mc8wB4PAQBPzhkuoOtLrQASclhVjHrlExuBybC7pEDTn4Go50mz34hf HAd4wjkZm4xDn71YGmRhox8VsFZbjY3rJxXDL0Stg7NEYGusalHfonpwmg5XnxwaoEkb v1LZIvjwown3F4PPfrAH7gdbYsFdsprEXtBqXP+zTnz2IDx+f4RDc1mi+P55SBFbzxlO iMkg== X-Forwarded-Encrypted: i=1; AJvYcCV6SkPa1RFeRjfDTGcIehxd3fDfih/4fkBhWrhgmdKNhyWM50a59sXLRjRRGWNNvGWOq4Z22V+5QIfNfH0=@vger.kernel.org X-Gm-Message-State: AOJu0YxONa8MFQOS7V1sHFGyk9pBk1orivzFH4v8MS20goxjZnwjClo7 qTYVjcjm4eGjuWShPW1HG9EzO3dmg+dkQw/wphzXzS+auug9EIjnM7jGvlQkYmFFsuw= X-Gm-Gg: AY/fxX42X9Exy8yr0IxyAcxreEBpbSUMGziFVi1rLAkmurNM0xIrwCiTNBkdZ/EahgH JSquTXa0dLHh8g2+++LydIyZxfh4BuDsF1au7qnx+hPgQiehiAdeJHXPDDy34xXxBeDiCcNR0lO P64kRFjGle5kCCiuJ7m003OwOsZ2KccId1EOWKFzYKnluz97bPUjezsH38ddzw2+k+Kk7MG3clb FeeocEn3R6baEqqt7uh/gV2wFrZgEPd9cIzrscfQ0LQBx7Iaz7lMFg79IBZzkvGMSHb4jwCvS6s EuaI1GeXmXKlzBgOopjSPP/qWSr/H4ijkEoKAMX6hFlAeScKD+hb2tGthBspVTDr0um3OfymgnH LmHvpL7duuI5MP/u4sIBRh44pdpUNIXN1ZascqMKgII5iSI1RkOErgvgOShIbRB5qNx0g28B8lS z2hHMbAtJzqkGSQtJhHQ4J87lIgWeL9jKUPnfh4JMCc96f/4tdvHeKwetUh6jp3ZXnI00pYj5rE DgD8g== X-Google-Smtp-Source: AGHT+IFNOOczDHpioRtQCE0+GGGgWj80+rcrFYYJMFAUFQyyh69c5IFa8olrIAoVk1WBi/k2ouOjpw== X-Received: by 2002:a17:907:9408:b0:b73:8f33:eed3 with SMTP id a640c23a62f3a-b8036f49371mr2496537266b.26.1766837871744; Sat, 27 Dec 2025 04:17:51 -0800 (PST) Received: from puffmais2.c.googlers.com (244.175.141.34.bc.googleusercontent.com. [34.141.175.244]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b8037f18575sm2616274666b.54.2025.12.27.04.17.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 27 Dec 2025 04:17:51 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Sat, 27 Dec 2025 12:17:52 +0000 Subject: [PATCH 8/8] regulator: core: don't fail regulator_register() with missing required supply Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251227-regulators-defer-v1-8-3104b22d84cb@linaro.org> References: <20251227-regulators-defer-v1-0-3104b22d84cb@linaro.org> In-Reply-To: <20251227-regulators-defer-v1-0-3104b22d84cb@linaro.org> To: Liam Girdwood , Mark Brown , Javier Martinez Canillas , Jon Hunter , Dmitry Baryshkov , Oleksij Rempel Cc: Peter Griffin , Tudor Ambarus , Will McVicker , Juan Yescas , kernel-team@android.com, linux-kernel@vger.kernel.org, =?utf-8?q?Andr=C3=A9_Draszik?= X-Mailer: b4 0.14.2 Since commit 98e48cd9283d ("regulator: core: resolve supply for boot-on/always-on regulators"), the regulator core returns -EPROBE_DEFER if a supply can not be resolved at regulator_register() time due to set_machine_constraints() requiring that supply (e.g. because of always-on or boot-on). In some hardware designs, multiple PMICs are used where individual rails of each act as supplies for rails of the other, and vice-versa. In such a design no PMIC driver can probe when registering one top- level regulator device (as is common practice for almost all regulator drivers in Linux) since that commit. Supplies are only considered when their driver has fully bound, but because in a design like the above two drivers / devices depend on each other, neither will have fully bound while the other probes. The Google Pixel 6 and 6 Pro (oriole and raven) are examples of such a design. One way to make this work would be to register each rail as an individual device, rather than just one top-level regulator device. Then, fw-devlink and Linux' driver core could do their usual handling of deferred device probe as each rail would be probed individually. This approach was dismissed in [1] as each regulator driver would have to take care of this itself. Alternatively, we can change the regulator core to not fail regulator_register() if a rail's required supply can not be resolved while keeping the intended change from above mentioned commit, and instead retry whenever a new rail is registered. This commit implements such an approach: If set_machine_constraints() requests probe deferral, regulator_register() still succeeds and we retry setting constraints as part of regulator_resolve_supply(). We still do not enable the regulator or allow consumers to use it until constraints have been set (including resolution of the supply) to prevent enabling of a regulator before its supply. With this change, we keep track of regulators with missing required supplies and can therefore try to resolve them again and try to set the constraints again once more regulators become available. Care has to be taken to not allow consumers to use regulators that haven't had their constraints set yet. regulator_get() ensures that and now returns -EPROBE_DEFER in that case. The implementation is straight-forward, thanks to our newly introduced regulator-bus. Locking in regulator_resolve_supply() has to be done carefully, as a combination of regulator_(un)lock_two() and regulator_(un)lock_dependent() is needed. The reason is that set_machine_constraints() might call regulator_enable() which needs rdev and all its dependents locked, but everything else requires to only have rdev and its supply locked. Link: https://lore.kernel.org/all/aRn_-o-vie_QoDXD@sirena.co.uk/ [1] Signed-off-by: Andr=C3=A9 Draszik --- drivers/regulator/core.c | 148 +++++++++++++++++++++++++++++++----= ---- include/linux/regulator/driver.h | 1 + 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 08e92b1ba2dc2ff9efdabaa16187a4a38cf66fb2..8c2fd20edd50591c962454a3584= 59e52e97c8ac0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -98,6 +98,7 @@ struct regulator_event_work { unsigned long event; }; =20 +static int _regulator_enable(struct regulator *regulator); static int _regulator_is_enabled(struct regulator_dev *rdev); static int _regulator_disable(struct regulator *regulator); static int _regulator_get_error_flags(struct regulator_dev *rdev, unsigned= int *flags); @@ -1432,6 +1433,7 @@ static int handle_notify_limits(struct regulator_dev = *rdev, /** * set_machine_constraints - sets regulator constraints * @rdev: regulator source + * @is_locked: whether or not this is called with locks held already * * Allows platform initialisation code to define and constrain * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: @@ -1441,7 +1443,8 @@ static int handle_notify_limits(struct regulator_dev = *rdev, * * Return: 0 on success or a negative error number on failure. */ -static int set_machine_constraints(struct regulator_dev *rdev) +static int set_machine_constraints(struct regulator_dev *rdev, + bool is_locked) { int ret =3D 0; const struct regulator_ops *ops =3D rdev->desc->ops; @@ -1653,7 +1656,9 @@ static int set_machine_constraints(struct regulator_d= ev *rdev) if (rdev->supply && (rdev->constraints->always_on || !regulator_is_enabled(rdev->supply))) { - ret =3D regulator_enable(rdev->supply); + ret =3D (is_locked + ? _regulator_enable(rdev->supply) + : regulator_enable(rdev->supply)); if (ret < 0) { _regulator_put(rdev->supply); rdev->supply =3D NULL; @@ -1781,6 +1786,15 @@ static int register_regulator_event_forwarding(struc= t regulator_dev *rdev) return 0; } =20 +static void unregister_regulator_event_forwarding(struct regulator_dev *rd= ev) +{ + if (!rdev->supply_fwd_nb.notifier_call) + return; + + regulator_unregister_notifier(rdev->supply, &rdev->supply_fwd_nb); + rdev->supply_fwd_nb.notifier_call =3D NULL; +} + /** * set_supply - set regulator supply regulator * @rdev: regulator (locked) @@ -2169,6 +2183,8 @@ static int regulator_resolve_supply(struct regulator_= dev *rdev) struct regulator_dev *r; struct device *dev =3D rdev->dev.parent; struct ww_acquire_ctx ww_ctx; + struct regulator *supply; + bool do_final_setup; int ret =3D 0; =20 /* No supply to resolve? */ @@ -2176,7 +2192,7 @@ static int regulator_resolve_supply(struct regulator_= dev *rdev) return 0; =20 /* Supply already resolved? (fast-path without locking contention) */ - if (rdev->supply) + if (rdev->supply && !rdev->constraints_pending) return 0; =20 /* first do a dt based lookup on the node described in the virtual @@ -2257,46 +2273,115 @@ static int regulator_resolve_supply(struct regulat= or_dev *rdev) =20 /* Supply just resolved by a concurrent task? */ if (rdev->supply) { + /* Constraints might still be pending due to concurrency. */ + bool done =3D !rdev->constraints_pending; + + supply =3D rdev->supply; + regulator_unlock_two(rdev, r, &ww_ctx); put_device(&r->dev); - goto out; - } =20 - ret =3D set_supply(rdev, r); - if (ret < 0) { + /* + * Supply resolved by concurrent task, and constraints set as + * well (or not required): fast path. + */ + if (done) + goto out; + + do_final_setup =3D false; + } else { + ret =3D set_supply(rdev, r); + if (ret < 0) { + regulator_unlock_two(rdev, r, &ww_ctx); + put_device(&r->dev); + goto out; + } + + supply =3D rdev->supply; + + /* + * Automatically register for event forwarding from the new + * supply. This creates the downstream propagation link for + * events like under-voltage. + */ + ret =3D register_regulator_event_forwarding(rdev); + if (ret < 0) { + rdev_warn(rdev, + "Failed to register event forwarding: %pe\n", + ERR_PTR(ret)); + + goto unset_supply; + } + regulator_unlock_two(rdev, r, &ww_ctx); - put_device(&r->dev); - goto out; + + do_final_setup =3D true; } =20 /* - * Automatically register for event forwarding from the new supply. - * This creates the downstream propagation link for events like - * under-voltage. + * Now that we have the supply, we can retry setting the machine + * constraints, if necessary. */ - ret =3D register_regulator_event_forwarding(rdev); - if (ret < 0) { - struct regulator *supply; - - rdev_warn(rdev, "Failed to register event forwarding: %pe\n", - ERR_PTR(ret)); - - supply =3D rdev->supply; - rdev->supply =3D NULL; + regulator_lock_dependent(rdev, &ww_ctx); + if (rdev->constraints_pending) { + if (!rdev->supply) { + /* + * Supply could have been released by another task that + * failed to set the constraints or event forwarding. + */ + regulator_unlock_dependent(rdev, &ww_ctx); + ret =3D -EPROBE_DEFER; + goto out; + } =20 - regulator_unlock_two(rdev, supply->rdev, &ww_ctx); + ret =3D set_machine_constraints(rdev, true); + if (ret < 0) { + regulator_unlock_dependent(rdev, &ww_ctx); + + rdev_warn(rdev, + "Failed to set machine constraints: %pe\n", + ERR_PTR(ret)); + + regulator_lock_two(rdev, r, &ww_ctx); + + if (supply !=3D rdev->supply) { + /* + * Supply could have been released by another + * task that got here before us. If it did, it + * will have released 'supply' (i.e. the + * previous rdev->supply) and we shouldn't do + * that again via unset_supply. + */ + regulator_unlock_two(rdev, r, &ww_ctx); + goto out; + } =20 - regulator_put(supply); - goto out; + unregister_regulator_event_forwarding(rdev); + rdev->constraints_pending =3D true; + goto unset_supply; + } + rdev->constraints_pending =3D false; } + regulator_unlock_dependent(rdev, &ww_ctx); =20 - regulator_unlock_two(rdev, r, &ww_ctx); + if (!do_final_setup) + goto out; =20 /* rdev->supply was created in set_supply() */ - link_and_create_debugfs(rdev->supply, r, &rdev->dev); + link_and_create_debugfs(rdev->supply, rdev->supply->rdev, &rdev->dev); =20 out: return ret; + +unset_supply: + lockdep_assert_held_once(&rdev->mutex.base); + lockdep_assert_held_once(&r->mutex.base); + rdev->supply =3D NULL; + regulator_unlock_two(rdev, supply->rdev, &ww_ctx); + + regulator_put(supply); + + return ret; } =20 /* common pre-checks for regulator requests */ @@ -6067,7 +6152,7 @@ regulator_register(struct device *dev, dangling_of_gpiod =3D false; } =20 - ret =3D set_machine_constraints(rdev); + ret =3D set_machine_constraints(rdev, false); if (ret =3D=3D -EPROBE_DEFER) { /* Regulator might be in bypass mode or an always-on or boot-on * regulator and so needs its supply to set the constraints or @@ -6081,14 +6166,17 @@ regulator_register(struct device *dev, rdev->supply_name); ret =3D regulator_resolve_supply(rdev); if (!ret) - ret =3D set_machine_constraints(rdev); + ret =3D set_machine_constraints(rdev, false); else rdev_dbg(rdev, "unable to resolve supply early: %pe\n", ERR_PTR(ret)); tried_supply_resolve =3D true; } - if (ret < 0) - goto wash; + if (ret < 0) { + if (ret !=3D -EPROBE_DEFER) + goto wash; + rdev->constraints_pending =3D true; + } =20 ret =3D regulator_init_coupling(rdev); if (ret < 0) diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/dri= ver.h index d38353f2b56f8bbab865d903ad0ec97ca0b5c834..09f3b67638f9e63a32cfdbaf9c8= 654afbd02a547 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -650,6 +650,7 @@ struct regulator_dev { struct regulator_enable_gpio *ena_pin; unsigned int ena_gpio_state:1; =20 + unsigned int constraints_pending:1; unsigned int is_switch:1; =20 /* time when this regulator was disabled last time */ --=20 2.52.0.351.gbe84eed79e-goog