From nobody Tue Feb 10 01:19:51 2026 Received: from mail-ed1-f54.google.com (mail-ed1-f54.google.com [209.85.208.54]) (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 AAF62350A2C for ; Fri, 9 Jan 2026 08:38:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767947933; cv=none; b=kyqPjhPAN3F/BJsfjLeMgMyicdCXQT5uSf7x4ZecuI/CfkUQDqP4rGKaWVrwQzyba0HzR5LZEvHhfNnAIqDq93IyXzZToNJZRY5gCqjLZZzoGMZSXvQpN9j6kCM5tIU72y7vkJIpUI7quFggjTqJX2Xy0VpvOX4sKS44aqHlrSM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767947933; c=relaxed/simple; bh=QIKs3GJ5GULNM8vWmOIRu/hSMW7MZfqLfr2sWZ9It0s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QzjabiAVn28t9CLZp5G9tDAna2WPahbta/UtpIFiunIshME1KsFm8MMkRW0KM8w4dhyoCTvbNYUscP5vcnBlS0WEKCvZ7a/GIEA01JlPYFSEsCqMy/3nsLWM3Ru/WcbJ2Pqm4fLwcwNz0vi5cuw4DK1lzLQEYXnqzxJtq2nrpZI= 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=TUchLwD9; arc=none smtp.client-ip=209.85.208.54 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="TUchLwD9" Received: by mail-ed1-f54.google.com with SMTP id 4fb4d7f45d1cf-64d0d41404cso6079420a12.0 for ; Fri, 09 Jan 2026 00:38:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1767947926; x=1768552726; 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=l8pQ2O89CDfbM4KnmV94WjzlfRn0Vqk2cTQV/55mLOw=; b=TUchLwD9O3i5MPedjhLl65nFzq9qA5LvcHyYX9logsDOuBpnsWlZ4+7fyG/L4pJpcR 66+ExKg1Wa+YOc7N1upuWzGyjZC61Pju9J5+3efi74H39RjaU/2gemLH9WQ2Nahc9Jj3 cNWRvlfKBPQVHmwdDdcPUF8hDAbKmtlqegHgSbQl71QW2e9kk6KpG5oVN4rNA3YFCW63 uWR0Y3vjeOXNFVm03KqVTn3zoZuc/ls32fL7UxrM6T6BMYh+qSFGhv+gv+v//io/G2fz E1Ve02YZdJ+hE4SpUr6m2Tuo/dJjy5YHvpXOZ26ctbTAHYqM9KW/QHrBgTE5t4XRFvNj xDkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767947926; x=1768552726; 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=l8pQ2O89CDfbM4KnmV94WjzlfRn0Vqk2cTQV/55mLOw=; b=EeUlxipa6kBibll4ZKJO99yyhK6K9n5tpstolxAIPmZdJEV9B6Uh+9r9xpSh9WPUXa 5RQH+z00WjJtVHN04YrM2MhxEWs4SgStdcJUGnv0WgXVdrE1vwpe3ewFoHXuzFAVrh3i qgtsnOjEBNw9niF1NlRdQbNHMTsKakCUHFPym1eDBtu4PdvwfIfWdowEyrifaFxF0FTE wNmF5Tu16tRDdvqKXPtr12bGBfnZCyGwh+4Q1k16ivgPbJkAH0HXFhR71mu+UiBHtRTh HhNmoENjpjQM1hTgkFBIs37TCm6q24yNtRm9vaVQJGW0pmET3IeVrHO/xc+N06FtdzAf XjUA== X-Forwarded-Encrypted: i=1; AJvYcCVhe4lXVDfhDUsn80CsR/X2f46u/OIMGDYLk2NWPDpSHYdwyKs+jI1Y0Ly0NKGfFIqrHvaJnzLx1DbnIkk=@vger.kernel.org X-Gm-Message-State: AOJu0YzC8jJQfSQLfPz7Hqs0mn04/c0x2QW0D+A2UKo0Iv1lA48CS1m4 jiNUhid2gOpytJENgmfzhyL1mMpqwcumPXPWJcHwJxRsy5uVY5zpaYppmr8oQgLYqig= X-Gm-Gg: AY/fxX4f1lWjzMwIAUhkzDb4dW1CQETG8dXBRQwKzkr0QWnKgF2ByWd6qWKx8Gi7j0I W+1g6FqBDFq3TG8OFTAQBpa/wlWi/liC8T5MzCLElZ5rdbnwK7FGbBr5L/bfelN35259yP9Q8Em SZv0rh2gG5mergUtHrA47kXxNF852K5x83flmOEc08pTQecvL6lfX9PXccXYNwLjiibmIEwapC2 SPiLwvqJ1iu1m3GPKQT+o/WgSJE1//THVUcLl0SL/ZGRudZcYhs5ue4K/N/QGvtOsLyOhmEHhJO kSTPPICQH5+ISZS7wcCPvN9H/bKTyFmn6SHjwcbN0PlzuOw/d+/KWqZfSgAs9GNzXpILi4vv/9V x5XcaS+oGdBKBrLUfA0FDym8vHQ0e+uYhdSYpelTGv3l+GvWH20x4rjTq3rl4rmOJS+31II3gvT 3PVARKBKfmN+pcVRsWQOw+YUTHtdJUtgwGRiEDxLBNrmZ8Fc8NqxJrx/LNF/P73kw7TNpUmMfrF /LIcQ== X-Google-Smtp-Source: AGHT+IGBH9buOdSCIMONSIdN9fsAAzFbWizOF21BannRPrJ1XSgK1lKuKzgSqqnYqrCKckDZKQwrWg== X-Received: by 2002:a17:907:2d2b:b0:b77:18a0:3c8b with SMTP id a640c23a62f3a-b8444c4d2bamr764454266b.1.1767947925731; Fri, 09 Jan 2026 00:38:45 -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 4fb4d7f45d1cf-6507be658b3sm9472950a12.18.2026.01.09.00.38.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 09 Jan 2026 00:38:45 -0800 (PST) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Fri, 09 Jan 2026 08:38:44 +0000 Subject: [PATCH v2 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: <20260109-regulators-defer-v2-8-1a25dc968e60@linaro.org> References: <20260109-regulators-defer-v2-0-1a25dc968e60@linaro.org> In-Reply-To: <20260109-regulators-defer-v2-0-1a25dc968e60@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.457.g6b5491de43-goog