From 73ae5392b0cfcce02efa23ee7123d8764ef070a8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 21 Apr 2026 16:11:01 +0530 Subject: [PATCH 1/3] PCI/pwrctrl: Move pci_pwrctrl_is_required() earlier in file Move pci_pwrctrl_is_required() earlier in the file so it can be used by pci_pwrctrl_power_off_device() and pci_pwrctrl_power_on_device(). Signed-off-by: Manivannan Sadhasivam [bhelgaas: split to its own patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260421104102.12322-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/pci/pwrctrl/core.c | 84 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 97cff5b8ca88..a692aeaee81a 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -139,6 +139,48 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +/* + * Check whether the pwrctrl device really needs to be created or not. The + * pwrctrl device will only be created if the node satisfies below requirements: + * + * 1. Presence of compatible property with "pci" prefix to match against the + * pwrctrl driver (AND) + * 2. At least one of the power supplies defined in the devicetree node of the + * device (OR) in the remote endpoint parent node to indicate pwrctrl + * requirement. + */ +static bool pci_pwrctrl_is_required(struct device_node *np) +{ + struct device_node *endpoint; + const char *compat; + int ret; + + ret = of_property_read_string(np, "compatible", &compat); + if (ret < 0) + return false; + + if (!strstarts(compat, "pci")) + return false; + + if (of_pci_supply_present(np)) + return true; + + if (of_graph_is_present(np)) { + for_each_endpoint_of_node(np, endpoint) { + struct device_node *remote __free(device_node) = + of_graph_get_remote_port_parent(endpoint); + if (remote) { + if (of_pci_supply_present(remote)) { + of_node_put(endpoint); + return true; + } + } + } + } + + return false; +} + static int __pci_pwrctrl_power_off_device(struct device *dev) { struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); @@ -268,48 +310,6 @@ int pci_pwrctrl_power_on_devices(struct device *parent) } EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices); -/* - * Check whether the pwrctrl device really needs to be created or not. The - * pwrctrl device will only be created if the node satisfies below requirements: - * - * 1. Presence of compatible property with "pci" prefix to match against the - * pwrctrl driver (AND) - * 2. At least one of the power supplies defined in the devicetree node of the - * device (OR) in the remote endpoint parent node to indicate pwrctrl - * requirement. - */ -static bool pci_pwrctrl_is_required(struct device_node *np) -{ - struct device_node *endpoint; - const char *compat; - int ret; - - ret = of_property_read_string(np, "compatible", &compat); - if (ret < 0) - return false; - - if (!strstarts(compat, "pci")) - return false; - - if (of_pci_supply_present(np)) - return true; - - if (of_graph_is_present(np)) { - for_each_endpoint_of_node(np, endpoint) { - struct device_node *remote __free(device_node) = - of_graph_get_remote_port_parent(endpoint); - if (remote) { - if (of_pci_supply_present(remote)) { - of_node_put(endpoint); - return true; - } - } - } - } - - return false; -} - static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent) { From 95c4920701dfca6a8cf4986112898382fa7afc0f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 21 Apr 2026 16:11:01 +0530 Subject: [PATCH 2/3] PCI/pwrctrl: Do not try to power on/off devices that don't need pwrctrl pci_pwrctrl_is_required() detects whether a device needs PCI pwrctrl support. It is currently used in pci_pwrctrl_create_device(), but not in pci_pwrctrl_power_{on/off}_device() APIs. This leads to pwrctrl core trying to power on/off incompatible devices like USB hub downstream ports defined in DT. Add this check to prevent pwrctrl core from poking at wrong devices. Fixes: b35cf3b6aa1e ("PCI/pwrctrl: Add APIs to power on/off pwrctrl devices") Reported-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam [bhelgaas: split pci_pwrctrl_is_required() move to separate patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260421104102.12322-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/pci/pwrctrl/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index a692aeaee81a..b5a0a14d316e 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -199,6 +199,9 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) for_each_available_child_of_node_scoped(np, child) pci_pwrctrl_power_off_device(child); + if (!pci_pwrctrl_is_required(np)) + return; + pdev = of_find_device_by_node(np); if (!pdev) return; @@ -255,6 +258,9 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) return ret; } + if (!pci_pwrctrl_is_required(np)) + return 0; + pdev = of_find_device_by_node(np); if (!pdev) return 0; From 548f3d287d92bcbc908b02db57fb889f8a8276a0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 May 2026 12:07:00 +0200 Subject: [PATCH 3/3] PCI/pwrctrl: Lock device when calling device_is_bound() The kerneldoc for device_is_bound() states that it must be called with the device lock taken. Synchronize the two calls in pwrctrl core. Fixes: b35cf3b6aa1e ("PCI/pwrctrl: Add APIs to power on/off pwrctrl devices") Signed-off-by: Bartosz Golaszewski Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260518100700.47581-1-bartosz.golaszewski@oss.qualcomm.com --- drivers/pci/pwrctrl/core.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index b5a0a14d316e..de3b78fa4b5b 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -206,10 +206,12 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) if (!pdev) return; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_off_device(&pdev->dev); - if (ret) - dev_err(&pdev->dev, "Failed to power off device: %d", ret); + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_off_device(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to power off device: %d", ret); + } } platform_device_put(pdev); @@ -250,7 +252,7 @@ static int __pci_pwrctrl_power_on_device(struct device *dev) static int pci_pwrctrl_power_on_device(struct device_node *np) { struct platform_device *pdev; - int ret; + int ret = 0; for_each_available_child_of_node_scoped(np, child) { ret = pci_pwrctrl_power_on_device(child); @@ -265,12 +267,14 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) if (!pdev) return 0; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_on_device(&pdev->dev); - } else { - /* FIXME: Use blocking wait instead of probe deferral */ - dev_dbg(&pdev->dev, "driver is not bound\n"); - ret = -EPROBE_DEFER; + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_on_device(&pdev->dev); + } else { + /* FIXME: Use blocking wait instead of probe deferral */ + dev_dbg(&pdev->dev, "driver is not bound\n"); + ret = -EPROBE_DEFER; + } } platform_device_put(pdev);