If devicetree describes power supplies related to a PCI device, we previously created a pwrctrl device even if CONFIG_PCI_PWRCTL was not enabled.
When pci_pwrctrl_create_device() creates and returns a pwrctrl device, pci_scan_device() doesn't enumerate the PCI device. It assumes the pwrctrl core will rescan the bus after turning on the power. However, if CONFIG_PCI_PWRCTL is not enabled, the rescan never happens.
This may break PCI enumeration on any system that describes power supplies in devicetree but does not use pwrctrl. Jim reported that some brcmstb platforms break this way.
While the actual fix would be to convert all the platforms to use pwrctrl framework, we also need to skip creating the pwrctrl device if CONFIG_PCI_PWRCTL is not enabled and let the PCI core scan the device normally (assuming it is already powered on or by the controller driver).
Cc: stable@vger.kernel.org # 6.15 Fixes: 957f40d039a9 ("PCI/pwrctrl: Move creation of pwrctrl devices to pci_scan_device()") Reported-by: Jim Quinlan james.quinlan@broadcom.com Closes: https://lore.kernel.org/r/CA+-6iNwgaByXEYD3j=-+H_PKAxXRU78svPMRHDKKci8AGXAUP... Signed-off-by: Manivannan Sadhasivam manivannan.sadhasivam@linaro.org ---
Changes in v2:
* Used the stub instead of returning NULL inside the function
drivers/pci/probe.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4b8693ec9e4c..e6a34db77826 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2508,6 +2508,7 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
+#if IS_ENABLED(CONFIG_PCI_PWRCTRL) static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) { struct pci_host_bridge *host = pci_find_host_bridge(bus); @@ -2537,6 +2538,12 @@ static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, in
return pdev; } +#else +static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) +{ + return NULL; +} +#endif
/* * Read the config data for a PCI device, sanity-check it,
On Tue, Jul 01, 2025 at 12:17:31PM +0530, Manivannan Sadhasivam wrote:
--- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2508,6 +2508,7 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); +#if IS_ENABLED(CONFIG_PCI_PWRCTRL) static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) {
Hm, why does pci_pwrctrl_create_device() return a pointer, even though the sole caller doesn't make any use of it? Why not return a negative errno?
Then you could just do this:
if (!IS_ENABLED(CONFIG_PCI_PWRCTRL)) return 0;
... at the top of the function and you don't need the extra LoC for the empty inline stub.
Another option is to set "struct pci_dev *pdev = NULL;" and #ifdef the body of the function, save for the "return pdev;" at the bottom.
Of course you could also do:
if (!IS_ENABLED(CONFIG_PCI_PWRCTRL)) return NULL;
... at the top of the function, but again, the caller doesn't make any use of the returned pointer.
Thanks,
Lukas
On Tue, Jul 01, 2025 at 09:00:34AM GMT, Lukas Wunner wrote:
On Tue, Jul 01, 2025 at 12:17:31PM +0530, Manivannan Sadhasivam wrote:
--- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2508,6 +2508,7 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); +#if IS_ENABLED(CONFIG_PCI_PWRCTRL) static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) {
Hm, why does pci_pwrctrl_create_device() return a pointer, even though the sole caller doesn't make any use of it? Why not return a negative errno?
Then you could just do this:
if (!IS_ENABLED(CONFIG_PCI_PWRCTRL)) return 0;
... at the top of the function and you don't need the extra LoC for the empty inline stub.
This is what I initially submitted [1] though that returned NULL, but the idea was the same. But Bjorn didn't like that.
Another option is to set "struct pci_dev *pdev = NULL;" and #ifdef the body of the function, save for the "return pdev;" at the bottom.
This is similar to what Bjorn submitted [2], but you were in favor of providing a stub instead [3]. It also looked better to my eyes.
Of course you could also do:
if (!IS_ENABLED(CONFIG_PCI_PWRCTRL)) return NULL;
... at the top of the function, but again, the caller doesn't make any use of the returned pointer.
Right. I could make it to return a errno, but that's not the scope of this patch. Bjorn wanted to have the #ifdef to be guarded to make the compiled out part more visible [4], so I ended up with this version.
But whatever the style is, we should make sure that the patch lands in 6.16-rcS. It is taking more time than needed.
- Mani
[1] https://lore.kernel.org/all/20250522140326.93869-1-manivannan.sadhasivam@lin... [2] https://lore.kernel.org/linux-pci/20250523201935.1586198-1-helgaas@kernel.or... [3] https://lore.kernel.org/linux-pci/aDFnWhFa9ZGqr67T@wunner.de/ [4] https://lore.kernel.org/linux-pci/20250629190219.GA1717534@bhelgaas/
Thanks,
Lukas
On Tue, Jul 01, 2025 at 05:27:27PM +0530, Manivannan Sadhasivam wrote:
On Tue, Jul 01, 2025 at 09:00:34AM GMT, Lukas Wunner wrote:
Hm, why does pci_pwrctrl_create_device() return a pointer, even though the sole caller doesn't make any use of it? Why not return a negative errno?
Then you could just do this:
if (!IS_ENABLED(CONFIG_PCI_PWRCTRL)) return 0;
... at the top of the function and you don't need the extra LoC for the empty inline stub.
This is what I initially submitted [1] though that returned NULL, but the idea was the same. But Bjorn didn't like that.
[...]
Thanks for summarizing the state of the discussion, I apologize for not having paid sufficient attention to the thread.
Reviewed-by: Lukas Wunner lukas@wunner.de
Lukas
[+cc Bart]
On Tue, Jul 01, 2025 at 12:17:31PM +0530, Manivannan Sadhasivam wrote:
If devicetree describes power supplies related to a PCI device, we previously created a pwrctrl device even if CONFIG_PCI_PWRCTL was not enabled.
When pci_pwrctrl_create_device() creates and returns a pwrctrl device, pci_scan_device() doesn't enumerate the PCI device. It assumes the pwrctrl core will rescan the bus after turning on the power. However, if CONFIG_PCI_PWRCTL is not enabled, the rescan never happens.
Separate from this patch, can we refine the comment in pci_scan_device() to explain *why* we should skip scanning if a pwrctrl device was created? The current comment leaves me with two questions:
1) How do we know the pwrctrl device is currently off? If it is already on, why should we defer enumerating the device?
2) If the pwrctrl device is currently off, won't the Vendor ID read just fail like it does for every other non-existent device? If so, why can't we just let that happen?
This behavior is from 2489eeb777af ("PCI/pwrctrl: Skip scanning for the device further if pwrctrl device is created"), which just says "there's no need to continue scanning." Prior to 2489eeb777af, it looks like we *did* what try to enumerate the device even if a pwrctrl device was created, and 2489eeb777af doesn't mention a bug fix, so I assume it's just an optimization.
Bjorn
[+cc Bart, Krzysztof, update Mani's addr to kernel.org]
On Tue, Jul 01, 2025 at 12:17:31PM +0530, Manivannan Sadhasivam wrote:
If devicetree describes power supplies related to a PCI device, we previously created a pwrctrl device even if CONFIG_PCI_PWRCTL was not enabled.
When pci_pwrctrl_create_device() creates and returns a pwrctrl device, pci_scan_device() doesn't enumerate the PCI device. It assumes the pwrctrl core will rescan the bus after turning on the power. However, if CONFIG_PCI_PWRCTL is not enabled, the rescan never happens.
This may break PCI enumeration on any system that describes power supplies in devicetree but does not use pwrctrl. Jim reported that some brcmstb platforms break this way.
While the actual fix would be to convert all the platforms to use pwrctrl framework, we also need to skip creating the pwrctrl device if CONFIG_PCI_PWRCTL is not enabled and let the PCI core scan the device normally (assuming it is already powered on or by the controller driver).
I'm fine with this change, but I think the commit log leaves the wrong impression. If CONFIG_PCI_PWRCTRL is not enabled, we shouldn't do anything related to it, independent of what other platforms or drivers do.
So I wouldn't describe this as "the actual fix is converting all platforms to use pwrctrl." Even if all platforms use pwrctrl, we *still* shouldn't run pci_pwrctrl_create_device() unless CONFIG_PCI_PWRCTRL is enabled.
I think all we need to say is something like this:
We only need pci_pwrctrl_create_device() when CONFIG_PCI_PWRCTRL is enabled. Compile it out when CONFIG_PCI_PWRCTRL is not enabled.
Cc: stable@vger.kernel.org # 6.15 Fixes: 957f40d039a9 ("PCI/pwrctrl: Move creation of pwrctrl devices to pci_scan_device()")
Not sure about this. If the problem we're solving is "we run pwrctrl code when CONFIG_PCI_PWRCTRL is not enabled," 957f40d039a9 is not the commit that added that behavior.
Maybe 8fb18619d910 ("PCI/pwrctl: Create platform devices for child OF nodes of the port node") would be more appropriate?
Reported-by: Jim Quinlan james.quinlan@broadcom.com Closes: https://lore.kernel.org/r/CA+-6iNwgaByXEYD3j=-+H_PKAxXRU78svPMRHDKKci8AGXAUP...
I'm also not sure this really merits a "Closes:" tag. All this does is enable a workaround (disable CONFIG_PCI_PWRCTRL when brcmstb is enabled). That's not a fix because we *should* be able to enable both pwrctrl and brcmstb at the same time.
If 2489eeb777af ("PCI/pwrctrl: Skip scanning for the device further if pwrctrl device is created") was purely an optimization (see https://lore.kernel.org/r/20250701203526.GA1849466@bhelgaas), I think I would:
- Revert 2489eeb777af with a stable tag for v6.15, and
- Apply this patch with a Fixes: 8fb18619d910 ("PCI/pwrctl: Create platform devices for child OF nodes of the port node") but no stable tag. 8fb18619d910 appeared in v6.11 and the "don't enable CONFIG_PCI_PWRCTRL" workaround was enough for brcmstb until 2489eeb777af, so if we revert 2489eeb777af, we wouldn't need to backport *this* patch.
Signed-off-by: Manivannan Sadhasivam manivannan.sadhasivam@linaro.org
Changes in v2:
- Used the stub instead of returning NULL inside the function
drivers/pci/probe.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4b8693ec9e4c..e6a34db77826 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2508,6 +2508,7 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); +#if IS_ENABLED(CONFIG_PCI_PWRCTRL) static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) { struct pci_host_bridge *host = pci_find_host_bridge(bus); @@ -2537,6 +2538,12 @@ static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, in return pdev; } +#else +static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) +{
- return NULL;
+} +#endif /*
- Read the config data for a PCI device, sanity-check it,
-- 2.43.0 {
linux-stable-mirror@lists.linaro.org