* [PATCH 1/5] PCI: qcom: Parse PERST# from all PCIe bridge nodes
2025-11-24 16:20 [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Manivannan Sadhasivam via B4 Relay
@ 2025-11-24 16:20 ` Manivannan Sadhasivam via B4 Relay
2025-11-24 16:20 ` [PATCH 2/5] PCI/pwrctrl: Add 'struct pci_pwrctrl::power_{on/off}' callbacks Manivannan Sadhasivam via B4 Relay
` (4 subsequent siblings)
5 siblings, 0 replies; 16+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2025-11-24 16:20 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
Cc: linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Manivannan Sadhasivam
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Devicetree schema allows the PERST# GPIO to be present in all PCIe bridge
nodes, not just in Root Port node. But the current logic parses PERST# only
from the Root Port nodes. Though it is not causing any issue on the current
platforms, the upcoming platforms will have PERST# in PCIe switch
downstream ports also. So this requires parsing all the PCIe bridge nodes
for the PERST# GPIO.
Hence, rework the parsing logic to extend to all PCIe bridge nodes starting
from the Root Port node. If the 'reset-gpios' property is found for a PCI
bridge node, the GPIO descriptor will be stored in qcom_pcie_perst::desc
and added to the qcom_pcie_port::perst list.
It should be noted that if more than one bridge node has the same GPIO for
PERST# (shared PERST#), the driver will error out. This is due to the
limitation in the GPIOLIB subsystem that allows only exclusive (non-shared)
access to GPIOs from consumers. But this is soon going to get fixed. Once
that happens, it will get incorporated in this driver.
So for now, PERST# sharing is not supported.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pcie-qcom.c | 102 +++++++++++++++++++++++++++------
1 file changed, 85 insertions(+), 17 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 805edbbfe7eb..7b6f4a391ce4 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -281,10 +281,15 @@ struct qcom_pcie_cfg {
bool no_l0s;
};
+struct qcom_pcie_perst {
+ struct list_head list;
+ struct gpio_desc *desc;
+};
+
struct qcom_pcie_port {
struct list_head list;
- struct gpio_desc *reset;
struct phy *phy;
+ struct list_head perst;
};
struct qcom_pcie {
@@ -305,11 +310,14 @@ struct qcom_pcie {
static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)
{
+ struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
int val = assert ? 1 : 0;
- list_for_each_entry(port, &pcie->ports, list)
- gpiod_set_value_cansleep(port->reset, val);
+ list_for_each_entry(port, &pcie->ports, list) {
+ list_for_each_entry(perst, &port->perst, list)
+ gpiod_set_value_cansleep(perst->desc, val);
+ }
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
@@ -1710,18 +1718,58 @@ static const struct pci_ecam_ops pci_qcom_ecam_ops = {
}
};
-static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
+/* Parse PERST# from all nodes in depth first manner starting from @np */
+static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
+ struct qcom_pcie_port *port,
+ struct device_node *np)
{
struct device *dev = pcie->pci->dev;
- struct qcom_pcie_port *port;
+ struct qcom_pcie_perst *perst;
struct gpio_desc *reset;
- struct phy *phy;
int ret;
- reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node),
- "reset", GPIOD_OUT_HIGH, "PERST#");
- if (IS_ERR(reset))
+ if (!of_find_property(np, "reset-gpios", NULL))
+ goto parse_child_node;
+
+ reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset",
+ GPIOD_OUT_HIGH, "PERST#");
+ if (IS_ERR(reset)) {
+ /*
+ * FIXME: GPIOLIB currently supports exclusive GPIO access only.
+ * Non exclusive access is broken. But shared PERST# requires
+ * non-exclusive access. So once GPIOLIB properly supports it,
+ * implement it here.
+ */
+ if (PTR_ERR(reset) == -EBUSY)
+ dev_err(dev, "Shared PERST# is not supported\n");
+
return PTR_ERR(reset);
+ }
+
+ perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
+ if (!perst)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&perst->list);
+ perst->desc = reset;
+ list_add_tail(&perst->list, &port->perst);
+
+parse_child_node:
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = qcom_pcie_parse_perst(pcie, port, child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
+{
+ struct device *dev = pcie->pci->dev;
+ struct qcom_pcie_port *port;
+ struct phy *phy;
+ int ret;
phy = devm_of_phy_get(dev, node, NULL);
if (IS_ERR(phy))
@@ -1735,7 +1783,12 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
if (ret)
return ret;
- port->reset = reset;
+ INIT_LIST_HEAD(&port->perst);
+
+ ret = qcom_pcie_parse_perst(pcie, port, node);
+ if (ret)
+ return ret;
+
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
@@ -1745,9 +1798,10 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
{
+ struct qcom_pcie_perst *perst, *tmp_perst;
+ struct qcom_pcie_port *port, *tmp_port;
struct device *dev = pcie->pci->dev;
- struct qcom_pcie_port *port, *tmp;
- int ret = -ENOENT;
+ int ret = -ENODEV;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
@@ -1760,7 +1814,9 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
return ret;
err_port_del:
- list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
+ list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
+ list_del(&perst->list);
phy_exit(port->phy);
list_del(&port->list);
}
@@ -1771,6 +1827,7 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
{
struct device *dev = pcie->pci->dev;
+ struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
struct gpio_desc *reset;
struct phy *phy;
@@ -1792,19 +1849,28 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
if (!port)
return -ENOMEM;
- port->reset = reset;
+ perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
+ if (!perst)
+ return -ENOMEM;
+
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
+ perst->desc = reset;
+ INIT_LIST_HEAD(&port->perst);
+ INIT_LIST_HEAD(&perst->list);
+ list_add_tail(&perst->list, &port->perst);
+
return 0;
}
static int qcom_pcie_probe(struct platform_device *pdev)
{
+ struct qcom_pcie_perst *perst, *tmp_perst;
+ struct qcom_pcie_port *port, *tmp_port;
const struct qcom_pcie_cfg *pcie_cfg;
unsigned long max_freq = ULONG_MAX;
- struct qcom_pcie_port *port, *tmp;
struct device *dev = &pdev->dev;
struct dev_pm_opp *opp;
struct qcom_pcie *pcie;
@@ -1945,7 +2011,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
ret = qcom_pcie_parse_ports(pcie);
if (ret) {
- if (ret != -ENOENT) {
+ if (ret != -ENODEV) {
dev_err_probe(pci->dev, ret,
"Failed to parse Root Port: %d\n", ret);
goto err_pm_runtime_put;
@@ -2004,7 +2070,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
err_host_deinit:
dw_pcie_host_deinit(pp);
err_phy_exit:
- list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
+ list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
+ list_del(&perst->list);
phy_exit(port->phy);
list_del(&port->list);
}
--
2.48.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 2/5] PCI/pwrctrl: Add 'struct pci_pwrctrl::power_{on/off}' callbacks
2025-11-24 16:20 [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Manivannan Sadhasivam via B4 Relay
2025-11-24 16:20 ` [PATCH 1/5] PCI: qcom: Parse PERST# from all PCIe bridge nodes Manivannan Sadhasivam via B4 Relay
@ 2025-11-24 16:20 ` Manivannan Sadhasivam via B4 Relay
2025-11-24 16:20 ` [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices Manivannan Sadhasivam via B4 Relay
` (3 subsequent siblings)
5 siblings, 0 replies; 16+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2025-11-24 16:20 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
Cc: linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Manivannan Sadhasivam
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
To allow the pwrctrl core to control the power on/off sequences of the
pwrctrl drivers, add the 'struct pci_pwrctrl::power_{on/off}' callbacks and
populate them in the respective pwrctrl drivers.
The pwrctrl drivers still power on the resources on their own now. So there
is no functional change.
Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 27 +++++++++++++++---
drivers/pci/pwrctrl/slot.c | 48 ++++++++++++++++++++++----------
include/linux/pci-pwrctrl.h | 4 +++
3 files changed, 61 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
index 4e664e7b8dd2..0fb9038a1d18 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
@@ -52,11 +52,27 @@ static const struct pci_pwrctrl_pwrseq_pdata pci_pwrctrl_pwrseq_qcom_wcn_pdata =
.validate_device = pci_pwrctrl_pwrseq_qcm_wcn_validate_device,
};
+static int pci_pwrctrl_pwrseq_power_on(struct pci_pwrctrl *ctx)
+{
+ struct pci_pwrctrl_pwrseq_data *data = container_of(ctx, struct pci_pwrctrl_pwrseq_data,
+ ctx);
+
+ return pwrseq_power_on(data->pwrseq);
+}
+
+static void pci_pwrctrl_pwrseq_power_off(struct pci_pwrctrl *ctx)
+{
+ struct pci_pwrctrl_pwrseq_data *data = container_of(ctx, struct pci_pwrctrl_pwrseq_data,
+ ctx);
+
+ pwrseq_power_off(data->pwrseq);
+}
+
static void devm_pci_pwrctrl_pwrseq_power_off(void *data)
{
- struct pwrseq_desc *pwrseq = data;
+ struct pci_pwrctrl_pwrseq_data *pwrseq_data = data;
- pwrseq_power_off(pwrseq);
+ pci_pwrctrl_pwrseq_power_off(&pwrseq_data->ctx);
}
static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
@@ -85,16 +101,19 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(data->pwrseq),
"Failed to get the power sequencer\n");
- ret = pwrseq_power_on(data->pwrseq);
+ ret = pci_pwrctrl_pwrseq_power_on(&data->ctx);
if (ret)
return dev_err_probe(dev, ret,
"Failed to power-on the device\n");
ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off,
- data->pwrseq);
+ data);
if (ret)
return ret;
+ data->ctx.power_on = pci_pwrctrl_pwrseq_power_on;
+ data->ctx.power_off = pci_pwrctrl_pwrseq_power_off;
+
pci_pwrctrl_init(&data->ctx, dev);
ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx);
diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c
index 3320494b62d8..14701f65f1f2 100644
--- a/drivers/pci/pwrctrl/slot.c
+++ b/drivers/pci/pwrctrl/slot.c
@@ -17,13 +17,36 @@ struct pci_pwrctrl_slot_data {
struct pci_pwrctrl ctx;
struct regulator_bulk_data *supplies;
int num_supplies;
+ struct clk *clk;
};
-static void devm_pci_pwrctrl_slot_power_off(void *data)
+static int pci_pwrctrl_slot_power_on(struct pci_pwrctrl *ctx)
{
- struct pci_pwrctrl_slot_data *slot = data;
+ struct pci_pwrctrl_slot_data *slot = container_of(ctx, struct pci_pwrctrl_slot_data, ctx);
+ int ret;
+
+ ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
+ if (ret < 0) {
+ dev_err(slot->ctx.dev, "Failed to enable slot regulators\n");
+ return ret;
+ }
+
+ return clk_prepare_enable(slot->clk);
+}
+
+static void pci_pwrctrl_slot_power_off(struct pci_pwrctrl *ctx)
+{
+ struct pci_pwrctrl_slot_data *slot = container_of(ctx, struct pci_pwrctrl_slot_data, ctx);
regulator_bulk_disable(slot->num_supplies, slot->supplies);
+ clk_disable_unprepare(slot->clk);
+}
+
+static void devm_pci_pwrctrl_slot_release(void *data)
+{
+ struct pci_pwrctrl_slot_data *slot = data;
+
+ pci_pwrctrl_slot_power_off(&slot->ctx);
regulator_bulk_free(slot->num_supplies, slot->supplies);
}
@@ -31,7 +54,6 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
{
struct pci_pwrctrl_slot_data *slot;
struct device *dev = &pdev->dev;
- struct clk *clk;
int ret;
slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
@@ -46,23 +68,21 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
}
slot->num_supplies = ret;
- ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
- if (ret < 0) {
- dev_err_probe(dev, ret, "Failed to enable slot regulators\n");
- regulator_bulk_free(slot->num_supplies, slot->supplies);
- return ret;
- }
- ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off,
+ ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_release,
slot);
if (ret)
return ret;
- clk = devm_clk_get_optional_enabled(dev, NULL);
- if (IS_ERR(clk)) {
- return dev_err_probe(dev, PTR_ERR(clk),
+ slot->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(slot->clk))
+ return dev_err_probe(dev, PTR_ERR(slot->clk),
"Failed to enable slot clock\n");
- }
+
+ pci_pwrctrl_slot_power_on(&slot->ctx);
+
+ slot->ctx.power_on = pci_pwrctrl_slot_power_on;
+ slot->ctx.power_off = pci_pwrctrl_slot_power_off;
pci_pwrctrl_init(&slot->ctx, dev);
diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h
index 4aefc7901cd1..bd0ee9998125 100644
--- a/include/linux/pci-pwrctrl.h
+++ b/include/linux/pci-pwrctrl.h
@@ -31,6 +31,8 @@ struct device_link;
/**
* struct pci_pwrctrl - PCI device power control context.
* @dev: Address of the power controlling device.
+ * @power_on: Callback to power on the power controlling device.
+ * @power_off: Callback to power off the power controlling device.
*
* An object of this type must be allocated by the PCI power control device and
* passed to the pwrctrl subsystem to trigger a bus rescan and setup a device
@@ -38,6 +40,8 @@ struct device_link;
*/
struct pci_pwrctrl {
struct device *dev;
+ int (*power_on)(struct pci_pwrctrl *pwrctrl);
+ void (*power_off)(struct pci_pwrctrl *pwrctrl);
/* private: internal use only */
struct notifier_block nb;
--
2.48.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices
2025-11-24 16:20 [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Manivannan Sadhasivam via B4 Relay
2025-11-24 16:20 ` [PATCH 1/5] PCI: qcom: Parse PERST# from all PCIe bridge nodes Manivannan Sadhasivam via B4 Relay
2025-11-24 16:20 ` [PATCH 2/5] PCI/pwrctrl: Add 'struct pci_pwrctrl::power_{on/off}' callbacks Manivannan Sadhasivam via B4 Relay
@ 2025-11-24 16:20 ` Manivannan Sadhasivam via B4 Relay
2025-11-25 8:18 ` Chen-Yu Tsai
2025-11-26 17:44 ` kernel test robot
2025-11-24 16:20 ` [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the " Manivannan Sadhasivam via B4 Relay
` (2 subsequent siblings)
5 siblings, 2 replies; 16+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2025-11-24 16:20 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
Cc: linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Manivannan Sadhasivam
From: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Previously, the PCI core created pwrctrl devices during pci_scan_device()
on its own and then skipped enumeration of those devices, hoping the
pwrctrl driver would power them on and trigger a bus rescan.
This approach works for endpoint devices directly connected to Root Ports,
but it fails for PCIe switches acting as bus extenders. When the switch
requires pwrctrl support, and the pwrctrl driver is not available during
the pwrctrl device creation, it's enumeration will be skipped during the
initial PCI bus scan.
This premature scan leads the PCI core to allocate resources (bridge
windows, bus numbers) for the upstream bridge based on available downstream
buses at scan time. For non-hotplug capable bridges, PCI core typically
allocates resources based on the number of buses available during the
initial bus scan, which happens to be just one if the switch is not powered
on and enumerated at that time. When the switch gets enumerated later on,
it will fail due to the lack of upstream resources.
As a result, a PCIe switch powered on by the pwrctrl driver cannot be
reliably enumerated currently. Either the switch has to be enabled in the
bootloader or the switch pwrctrl driver has to be loaded during the pwrctrl
device creation time to workaround these issues.
This commit introduces new APIs to explicitly create and destroy pwrctrl
devices from controller drivers by recursively scanning the PCI child nodes
of the controller. These APIs allow creating pwrctrl devices based on the
original criteria and are intended to be called during controller probe and
removal.
These APIs, together with the upcoming APIs for power on/off will allow the
controller drivers to power on all the devices before starting the initial
bus scan, thereby solving the resource allocation issue.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
[mani: splitted the patch, cleaned up the code, and rewrote description]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/pci/pwrctrl/core.c | 112 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci-pwrctrl.h | 8 +++-
2 files changed, 119 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index 6bdbfed584d6..6eca54e0d540 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -3,14 +3,21 @@
* Copyright (C) 2024 Linaro Ltd.
*/
+#define dev_fmt(fmt) "Pwrctrl: " fmt
+
#include <linux/device.h>
#include <linux/export.h>
#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/pci-pwrctrl.h>
+#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include "../pci.h"
+
static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
@@ -145,6 +152,111 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
+static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = pci_pwrctrl_create_device(child, parent);
+ if (ret)
+ return ret;
+ }
+
+ /* Bail out if the platform device is already available for the node */
+ pdev = of_find_device_by_node(np);
+ if (pdev) {
+ put_device(&pdev->dev);
+ return 0;
+ }
+
+ /*
+ * Sanity check to make sure that the node has the compatible property
+ * to allow driver binding.
+ */
+ if (!of_property_present(np, "compatible"))
+ return 0;
+
+ /*
+ * Check whether the pwrctrl device really needs to be created or not.
+ * This is decided based on at least one of the power supplies being
+ * defined in the devicetree node of the device.
+ */
+ if (!of_pci_supply_present(np)) {
+ dev_dbg(parent, "Skipping OF node: %s\n", np->name);
+ return 0;
+ }
+
+ /* Now create the pwrctrl device */
+ pdev = of_platform_device_create(np, NULL, parent);
+ if (!pdev) {
+ dev_err(parent, "Failed to create pwrctrl device for node: %s\n", np->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * pci_pwrctrl_create_devices - Create pwrctrl devices
+ *
+ * @parent: Parent PCI device for which the pwrctrl devices need to be created.
+ *
+ * This function recursively creates pwrctrl devices for the child nodes
+ * of the specified PCI parent device in a depth first manner.
+ *
+ * Returns: 0 on success, negative error number on error.
+ */
+int pci_pwrctrl_create_devices(struct device *parent)
+{
+ int ret;
+
+ for_each_available_child_of_node_scoped(parent->of_node, child) {
+ ret = pci_pwrctrl_create_device(child, parent);
+ if (ret) {
+ pci_pwrctrl_destroy_devices(parent);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_create_devices);
+
+static void pci_pwrctrl_destroy_device(struct device_node *np)
+{
+ struct platform_device *pdev;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_destroy_device(child);
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return;
+
+ of_device_unregister(pdev);
+ put_device(&pdev->dev);
+
+ of_node_clear_flag(np, OF_POPULATED);
+}
+
+/**
+ * pci_pwrctrl_destroy_devices - Destroy pwrctrl devices
+ *
+ * @parent: Parent PCI device for which the pwrctrl devices need to be destroyed.
+ *
+ * This function recursively destroys pwrctrl devices for the child nodes
+ * of the specified PCI parent device in a depth first manner.
+ */
+void pci_pwrctrl_destroy_devices(struct device *parent)
+{
+ struct device_node *np = parent->of_node;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_destroy_device(child);
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_destroy_devices);
+
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
MODULE_DESCRIPTION("PCI Device Power Control core driver");
MODULE_LICENSE("GPL");
diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h
index bd0ee9998125..5590ffec0bea 100644
--- a/include/linux/pci-pwrctrl.h
+++ b/include/linux/pci-pwrctrl.h
@@ -54,5 +54,11 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl);
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl);
int devm_pci_pwrctrl_device_set_ready(struct device *dev,
struct pci_pwrctrl *pwrctrl);
-
+#if IS_ENABLED(CONFIG_PCI_PWRCTRL)
+int pci_pwrctrl_create_devices(struct device *parent);
+void pci_pwrctrl_destroy_devices(struct device *parent);
+#else
+static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; }
+static void pci_pwrctrl_destroy_devices(struct device *parent) { }
+#endif
#endif /* __PCI_PWRCTRL_H__ */
--
2.48.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices
2025-11-24 16:20 ` [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices Manivannan Sadhasivam via B4 Relay
@ 2025-11-25 8:18 ` Chen-Yu Tsai
2025-11-25 13:42 ` Manivannan Sadhasivam
2025-11-26 17:44 ` kernel test robot
1 sibling, 1 reply; 16+ messages in thread
From: Chen-Yu Tsai @ 2025-11-25 8:18 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski, linux-pci, linux-arm-msm, linux-kernel,
Chen-Yu Tsai, Brian Norris, Krishna Chaitanya Chundru,
Niklas Cassel, Alex Elder
On Tue, Nov 25, 2025 at 3:13 PM Manivannan Sadhasivam
<manivannan.sadhasivam@oss.qualcomm.com> wrote:
>
> From: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
>
> Previously, the PCI core created pwrctrl devices during pci_scan_device()
> on its own and then skipped enumeration of those devices, hoping the
> pwrctrl driver would power them on and trigger a bus rescan.
>
> This approach works for endpoint devices directly connected to Root Ports,
> but it fails for PCIe switches acting as bus extenders. When the switch
> requires pwrctrl support, and the pwrctrl driver is not available during
> the pwrctrl device creation, it's enumeration will be skipped during the
> initial PCI bus scan.
>
> This premature scan leads the PCI core to allocate resources (bridge
> windows, bus numbers) for the upstream bridge based on available downstream
> buses at scan time. For non-hotplug capable bridges, PCI core typically
> allocates resources based on the number of buses available during the
> initial bus scan, which happens to be just one if the switch is not powered
> on and enumerated at that time. When the switch gets enumerated later on,
> it will fail due to the lack of upstream resources.
>
> As a result, a PCIe switch powered on by the pwrctrl driver cannot be
> reliably enumerated currently. Either the switch has to be enabled in the
> bootloader or the switch pwrctrl driver has to be loaded during the pwrctrl
> device creation time to workaround these issues.
>
> This commit introduces new APIs to explicitly create and destroy pwrctrl
> devices from controller drivers by recursively scanning the PCI child nodes
> of the controller. These APIs allow creating pwrctrl devices based on the
> original criteria and are intended to be called during controller probe and
> removal.
>
> These APIs, together with the upcoming APIs for power on/off will allow the
> controller drivers to power on all the devices before starting the initial
> bus scan, thereby solving the resource allocation issue.
>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> [mani: splitted the patch, cleaned up the code, and rewrote description]
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
> drivers/pci/pwrctrl/core.c | 112 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci-pwrctrl.h | 8 +++-
> 2 files changed, 119 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
> index 6bdbfed584d6..6eca54e0d540 100644
> --- a/drivers/pci/pwrctrl/core.c
> +++ b/drivers/pci/pwrctrl/core.c
> @@ -3,14 +3,21 @@
> * Copyright (C) 2024 Linaro Ltd.
> */
>
> +#define dev_fmt(fmt) "Pwrctrl: " fmt
> +
> #include <linux/device.h>
> #include <linux/export.h>
> #include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> #include <linux/pci.h>
> #include <linux/pci-pwrctrl.h>
> +#include <linux/platform_device.h>
> #include <linux/property.h>
> #include <linux/slab.h>
>
> +#include "../pci.h"
> +
> static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
> void *data)
> {
> @@ -145,6 +152,111 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
> }
> EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
>
> +static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent)
> +{
> + struct platform_device *pdev;
> + int ret;
> +
> + for_each_available_child_of_node_scoped(np, child) {
> + ret = pci_pwrctrl_create_device(child, parent);
> + if (ret)
> + return ret;
> + }
> +
> + /* Bail out if the platform device is already available for the node */
> + pdev = of_find_device_by_node(np);
> + if (pdev) {
> + put_device(&pdev->dev);
> + return 0;
> + }
> +
> + /*
> + * Sanity check to make sure that the node has the compatible property
> + * to allow driver binding.
> + */
> + if (!of_property_present(np, "compatible"))
> + return 0;
> +
> + /*
> + * Check whether the pwrctrl device really needs to be created or not.
> + * This is decided based on at least one of the power supplies being
> + * defined in the devicetree node of the device.
> + */
> + if (!of_pci_supply_present(np)) {
This symbol is not exported for modules to use, and will cause the build
to fail if PCI_PWRCTRL* is m.
[...]
ChenYu
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices
2025-11-25 8:18 ` Chen-Yu Tsai
@ 2025-11-25 13:42 ` Manivannan Sadhasivam
0 siblings, 0 replies; 16+ messages in thread
From: Manivannan Sadhasivam @ 2025-11-25 13:42 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski, linux-pci, linux-arm-msm, linux-kernel,
Chen-Yu Tsai, Brian Norris, Krishna Chaitanya Chundru,
Niklas Cassel, Alex Elder
On Tue, Nov 25, 2025 at 04:18:49PM +0800, Chen-Yu Tsai wrote:
> On Tue, Nov 25, 2025 at 3:13 PM Manivannan Sadhasivam
> <manivannan.sadhasivam@oss.qualcomm.com> wrote:
> >
> > From: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> >
> > Previously, the PCI core created pwrctrl devices during pci_scan_device()
> > on its own and then skipped enumeration of those devices, hoping the
> > pwrctrl driver would power them on and trigger a bus rescan.
> >
> > This approach works for endpoint devices directly connected to Root Ports,
> > but it fails for PCIe switches acting as bus extenders. When the switch
> > requires pwrctrl support, and the pwrctrl driver is not available during
> > the pwrctrl device creation, it's enumeration will be skipped during the
> > initial PCI bus scan.
> >
> > This premature scan leads the PCI core to allocate resources (bridge
> > windows, bus numbers) for the upstream bridge based on available downstream
> > buses at scan time. For non-hotplug capable bridges, PCI core typically
> > allocates resources based on the number of buses available during the
> > initial bus scan, which happens to be just one if the switch is not powered
> > on and enumerated at that time. When the switch gets enumerated later on,
> > it will fail due to the lack of upstream resources.
> >
> > As a result, a PCIe switch powered on by the pwrctrl driver cannot be
> > reliably enumerated currently. Either the switch has to be enabled in the
> > bootloader or the switch pwrctrl driver has to be loaded during the pwrctrl
> > device creation time to workaround these issues.
> >
> > This commit introduces new APIs to explicitly create and destroy pwrctrl
> > devices from controller drivers by recursively scanning the PCI child nodes
> > of the controller. These APIs allow creating pwrctrl devices based on the
> > original criteria and are intended to be called during controller probe and
> > removal.
> >
> > These APIs, together with the upcoming APIs for power on/off will allow the
> > controller drivers to power on all the devices before starting the initial
> > bus scan, thereby solving the resource allocation issue.
> >
> > Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> > [mani: splitted the patch, cleaned up the code, and rewrote description]
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> > ---
> > drivers/pci/pwrctrl/core.c | 112 ++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/pci-pwrctrl.h | 8 +++-
> > 2 files changed, 119 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
> > index 6bdbfed584d6..6eca54e0d540 100644
> > --- a/drivers/pci/pwrctrl/core.c
> > +++ b/drivers/pci/pwrctrl/core.c
> > @@ -3,14 +3,21 @@
> > * Copyright (C) 2024 Linaro Ltd.
> > */
> >
> > +#define dev_fmt(fmt) "Pwrctrl: " fmt
> > +
> > #include <linux/device.h>
> > #include <linux/export.h>
> > #include <linux/kernel.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > #include <linux/pci.h>
> > #include <linux/pci-pwrctrl.h>
> > +#include <linux/platform_device.h>
> > #include <linux/property.h>
> > #include <linux/slab.h>
> >
> > +#include "../pci.h"
> > +
> > static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
> > void *data)
> > {
> > @@ -145,6 +152,111 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
> > }
> > EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
> >
> > +static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent)
> > +{
> > + struct platform_device *pdev;
> > + int ret;
> > +
> > + for_each_available_child_of_node_scoped(np, child) {
> > + ret = pci_pwrctrl_create_device(child, parent);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + /* Bail out if the platform device is already available for the node */
> > + pdev = of_find_device_by_node(np);
> > + if (pdev) {
> > + put_device(&pdev->dev);
> > + return 0;
> > + }
> > +
> > + /*
> > + * Sanity check to make sure that the node has the compatible property
> > + * to allow driver binding.
> > + */
> > + if (!of_property_present(np, "compatible"))
> > + return 0;
> > +
> > + /*
> > + * Check whether the pwrctrl device really needs to be created or not.
> > + * This is decided based on at least one of the power supplies being
> > + * defined in the devicetree node of the device.
> > + */
> > + if (!of_pci_supply_present(np)) {
>
> This symbol is not exported for modules to use, and will cause the build
> to fail if PCI_PWRCTRL* is m.
>
Ok. I'll export this symbol in next version. Thanks for spotting it!
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices
2025-11-24 16:20 ` [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices Manivannan Sadhasivam via B4 Relay
2025-11-25 8:18 ` Chen-Yu Tsai
@ 2025-11-26 17:44 ` kernel test robot
1 sibling, 0 replies; 16+ messages in thread
From: kernel test robot @ 2025-11-26 17:44 UTC (permalink / raw)
To: Manivannan Sadhasivam via B4 Relay, Manivannan Sadhasivam,
Lorenzo Pieralisi, Krzysztof Wilczyński, Rob Herring,
Bjorn Helgaas, Bartosz Golaszewski
Cc: oe-kbuild-all, linux-pci, linux-arm-msm, linux-kernel,
Chen-Yu Tsai, Brian Norris, Krishna Chaitanya Chundru,
Niklas Cassel, Alex Elder
Hi Manivannan,
kernel test robot noticed the following build errors:
[auto build test ERROR on 3a8660878839faadb4f1a6dd72c3179c1df56787]
url: https://github.com/intel-lab-lkp/linux/commits/Manivannan-Sadhasivam-via-B4-Relay/PCI-qcom-Parse-PERST-from-all-PCIe-bridge-nodes/20251125-002444
base: 3a8660878839faadb4f1a6dd72c3179c1df56787
patch link: https://lore.kernel.org/r/20251124-pci-pwrctrl-rework-v1-3-78a72627683d%40oss.qualcomm.com
patch subject: [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices
config: loongarch-randconfig-r121-20251126 (https://download.01.org/0day-ci/archive/20251127/202511270103.uCCr0RCQ-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251127/202511270103.uCCr0RCQ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511270103.uCCr0RCQ-lkp@intel.com/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "of_pci_supply_present" [drivers/pci/pwrctrl/pci-pwrctrl-core.ko] undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the pwrctrl devices
2025-11-24 16:20 [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Manivannan Sadhasivam via B4 Relay
` (2 preceding siblings ...)
2025-11-24 16:20 ` [PATCH 3/5] PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices Manivannan Sadhasivam via B4 Relay
@ 2025-11-24 16:20 ` Manivannan Sadhasivam via B4 Relay
2025-11-25 13:34 ` Bartosz Golaszewski
2025-11-25 13:40 ` Bartosz Golaszewski
2025-11-24 16:20 ` [PATCH 5/5] PCI/pwrctrl: Switch to the new pwrctrl APIs Manivannan Sadhasivam via B4 Relay
2025-11-26 7:22 ` [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Chen-Yu Tsai
5 siblings, 2 replies; 16+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2025-11-24 16:20 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
Cc: linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Manivannan Sadhasivam
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
To fix PCIe bridge resource allocation issues when powering PCIe
switches with the pwrctrl driver, introduce APIs to explicitly power
on and off all related devices simultaneously.
Previously, the individual pwrctrl drivers powered on/off the PCIe devices
autonomously, without any control from the controller drivers. But to
enforce ordering w.r.t powering on the devices, these APIs will power
on/off all the devices at the same time.
The pci_pwrctrl_power_on_devices() API recursively scans the PCI child
nodes, makes sure that pwrctrl drivers are bind to devices, and calls their
power_on() callbacks.
Similarly, pci_pwrctrl_power_off_devices() API powers off devices
recursively via their power_off() callbacks.
These APIs are expected to be called during the controller probe and
suspend/resume time to power on/off the devices. But before calling these
APIs, the pwrctrl devices should've been created beforehand using the
pci_pwrctrl_{create/destroy}_devices() APIs.
Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/pci/pwrctrl/core.c | 121 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci-pwrctrl.h | 4 ++
2 files changed, 125 insertions(+)
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index 6eca54e0d540..e0a0cf015bd0 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -65,6 +65,7 @@ void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
{
pwrctrl->dev = dev;
INIT_WORK(&pwrctrl->work, rescan_work_func);
+ dev_set_drvdata(dev, pwrctrl);
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
@@ -152,6 +153,126 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
+static int __pci_pwrctrl_power_on_device(struct device *dev)
+{
+ struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
+
+ if (!pwrctrl)
+ return 0;
+
+ return pwrctrl->power_on(pwrctrl);
+}
+
+/*
+ * Power on the devices in a depth first manner. Before powering on the device,
+ * make sure its driver is bound.
+ */
+static int pci_pwrctrl_power_on_device(struct device_node *np)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = pci_pwrctrl_power_on_device(child);
+ if (ret)
+ return ret;
+ }
+
+ pdev = of_find_device_by_node(np);
+ if (pdev) {
+ if (!device_is_bound(&pdev->dev)) {
+ dev_err(&pdev->dev, "driver is not bound\n");
+ ret = -EPROBE_DEFER;
+ } else {
+ ret = __pci_pwrctrl_power_on_device(&pdev->dev);
+ }
+ put_device(&pdev->dev);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * pci_pwrctrl_power_on_devices - Power on the pwrctrl devices
+ *
+ * @parent: Parent PCI device for which the pwrctrl devices need to be powered
+ * on.
+ *
+ * This function recursively traverses all pwrctrl devices for the child nodes
+ * of the specified PCI parent device, and powers them on in a depth first
+ * manner.
+ *
+ * Returns: 0 on success, negative error number on error.
+ */
+int pci_pwrctrl_power_on_devices(struct device *parent)
+{
+ struct device_node *np = parent->of_node;
+ int ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = pci_pwrctrl_power_on_device(child);
+ if (ret) {
+ pci_pwrctrl_power_off_devices(parent);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices);
+
+static void __pci_pwrctrl_power_off_device(struct device *dev)
+{
+ struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
+
+ if (!pwrctrl)
+ return;
+
+ return pwrctrl->power_off(pwrctrl);
+}
+
+static int pci_pwrctrl_power_off_device(struct device_node *np)
+{
+ struct platform_device *pdev;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_power_off_device(child);
+
+ pdev = of_find_device_by_node(np);
+ if (pdev) {
+ if (device_is_bound(&pdev->dev))
+ __pci_pwrctrl_power_off_device(&pdev->dev);
+
+ put_device(&pdev->dev);
+ }
+
+ return 0;
+}
+
+/**
+ * pci_pwrctrl_power_off_devices - Power off the pwrctrl devices
+ *
+ * @parent: Parent PCI device for which the pwrctrl devices need to be powered
+ * off.
+ *
+ * This function recursively traverses all pwrctrl devices for the child nodes
+ * of the specified PCI parent device, and powers them off in a depth first
+ * manner.
+ *
+ * Returns: 0 on success, negative error number on error.
+ */
+void pci_pwrctrl_power_off_devices(struct device *parent)
+{
+ struct device_node *np = parent->of_node;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_power_off_device(child);
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_power_off_devices);
+
static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent)
{
struct platform_device *pdev;
diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h
index 5590ffec0bea..1b77769eebbe 100644
--- a/include/linux/pci-pwrctrl.h
+++ b/include/linux/pci-pwrctrl.h
@@ -57,8 +57,12 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
#if IS_ENABLED(CONFIG_PCI_PWRCTRL)
int pci_pwrctrl_create_devices(struct device *parent);
void pci_pwrctrl_destroy_devices(struct device *parent);
+int pci_pwrctrl_power_on_devices(struct device *parent);
+void pci_pwrctrl_power_off_devices(struct device *parent);
#else
static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; }
static void pci_pwrctrl_destroy_devices(struct device *parent) { }
+static inline int pci_pwrctrl_power_on_devices(struct device *parent) { return 0; }
+static void pci_pwrctrl_power_off_devices(struct device *parent) { }
#endif
#endif /* __PCI_PWRCTRL_H__ */
--
2.48.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the pwrctrl devices
2025-11-24 16:20 ` [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the " Manivannan Sadhasivam via B4 Relay
@ 2025-11-25 13:34 ` Bartosz Golaszewski
2025-11-25 13:50 ` Manivannan Sadhasivam
2025-11-25 13:40 ` Bartosz Golaszewski
1 sibling, 1 reply; 16+ messages in thread
From: Bartosz Golaszewski @ 2025-11-25 13:34 UTC (permalink / raw)
To: manivannan.sadhasivam
Cc: Manivannan Sadhasivam via B4 Relay, linux-pci, linux-arm-msm,
linux-kernel, Chen-Yu Tsai, Brian Norris,
Krishna Chaitanya Chundru, Niklas Cassel, Alex Elder,
Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
On Mon, 24 Nov 2025 17:20:47 +0100, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> To fix PCIe bridge resource allocation issues when powering PCIe
> switches with the pwrctrl driver, introduce APIs to explicitly power
> on and off all related devices simultaneously.
>
> Previously, the individual pwrctrl drivers powered on/off the PCIe devices
> autonomously, without any control from the controller drivers. But to
> enforce ordering w.r.t powering on the devices, these APIs will power
> on/off all the devices at the same time.
>
> The pci_pwrctrl_power_on_devices() API recursively scans the PCI child
> nodes, makes sure that pwrctrl drivers are bind to devices, and calls their
> power_on() callbacks.
>
> Similarly, pci_pwrctrl_power_off_devices() API powers off devices
> recursively via their power_off() callbacks.
>
> These APIs are expected to be called during the controller probe and
> suspend/resume time to power on/off the devices. But before calling these
> APIs, the pwrctrl devices should've been created beforehand using the
> pci_pwrctrl_{create/destroy}_devices() APIs.
>
> Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
> drivers/pci/pwrctrl/core.c | 121 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci-pwrctrl.h | 4 ++
> 2 files changed, 125 insertions(+)
>
> diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
> index 6eca54e0d540..e0a0cf015bd0 100644
> --- a/drivers/pci/pwrctrl/core.c
> +++ b/drivers/pci/pwrctrl/core.c
> @@ -65,6 +65,7 @@ void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
> {
> pwrctrl->dev = dev;
> INIT_WORK(&pwrctrl->work, rescan_work_func);
> + dev_set_drvdata(dev, pwrctrl);
> }
> EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
>
> @@ -152,6 +153,126 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
> }
> EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
>
> +static int __pci_pwrctrl_power_on_device(struct device *dev)
Both this and __pci_pwrctrl_power_off_device() are only used once each. Does
it really make sense to split it out?
> +{
> + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
> +
> + if (!pwrctrl)
> + return 0;
> +
> + return pwrctrl->power_on(pwrctrl);
> +}
> +
> +/*
> + * Power on the devices in a depth first manner. Before powering on the device,
> + * make sure its driver is bound.
> + */
> +static int pci_pwrctrl_power_on_device(struct device_node *np)
> +{
> + struct platform_device *pdev;
> + int ret;
> +
> + for_each_available_child_of_node_scoped(np, child) {
> + ret = pci_pwrctrl_power_on_device(child);
> + if (ret)
> + return ret;
> + }
> +
> + pdev = of_find_device_by_node(np);
> + if (pdev) {
> + if (!device_is_bound(&pdev->dev)) {
> + dev_err(&pdev->dev, "driver is not bound\n");
This is not an error though, is it? If there are multiple deferalls, we'll
spam the kernel log.
> + ret = -EPROBE_DEFER;
> + } else {
> + ret = __pci_pwrctrl_power_on_device(&pdev->dev);
> + }
> + put_device(&pdev->dev);
> +
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
Bart
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the pwrctrl devices
2025-11-25 13:34 ` Bartosz Golaszewski
@ 2025-11-25 13:50 ` Manivannan Sadhasivam
2025-11-25 13:55 ` Bartosz Golaszewski
0 siblings, 1 reply; 16+ messages in thread
From: Manivannan Sadhasivam @ 2025-11-25 13:50 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: manivannan.sadhasivam, Manivannan Sadhasivam via B4 Relay,
linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Lorenzo Pieralisi, Krzysztof Wilczyński,
Rob Herring, Bjorn Helgaas
On Tue, Nov 25, 2025 at 05:34:04AM -0800, Bartosz Golaszewski wrote:
> On Mon, 24 Nov 2025 17:20:47 +0100, Manivannan Sadhasivam via B4 Relay
> <devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> > From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> >
> > To fix PCIe bridge resource allocation issues when powering PCIe
> > switches with the pwrctrl driver, introduce APIs to explicitly power
> > on and off all related devices simultaneously.
> >
> > Previously, the individual pwrctrl drivers powered on/off the PCIe devices
> > autonomously, without any control from the controller drivers. But to
> > enforce ordering w.r.t powering on the devices, these APIs will power
> > on/off all the devices at the same time.
> >
> > The pci_pwrctrl_power_on_devices() API recursively scans the PCI child
> > nodes, makes sure that pwrctrl drivers are bind to devices, and calls their
> > power_on() callbacks.
> >
> > Similarly, pci_pwrctrl_power_off_devices() API powers off devices
> > recursively via their power_off() callbacks.
> >
> > These APIs are expected to be called during the controller probe and
> > suspend/resume time to power on/off the devices. But before calling these
> > APIs, the pwrctrl devices should've been created beforehand using the
> > pci_pwrctrl_{create/destroy}_devices() APIs.
> >
> > Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> > Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> > ---
> > drivers/pci/pwrctrl/core.c | 121 ++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/pci-pwrctrl.h | 4 ++
> > 2 files changed, 125 insertions(+)
> >
> > diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
> > index 6eca54e0d540..e0a0cf015bd0 100644
> > --- a/drivers/pci/pwrctrl/core.c
> > +++ b/drivers/pci/pwrctrl/core.c
> > @@ -65,6 +65,7 @@ void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
> > {
> > pwrctrl->dev = dev;
> > INIT_WORK(&pwrctrl->work, rescan_work_func);
> > + dev_set_drvdata(dev, pwrctrl);
> > }
> > EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
> >
> > @@ -152,6 +153,126 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
> > }
> > EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
> >
> > +static int __pci_pwrctrl_power_on_device(struct device *dev)
>
> Both this and __pci_pwrctrl_power_off_device() are only used once each. Does
> it really make sense to split it out?
>
I just find it neat to split it out. Otherwise, the else condition looks clumsy
in pci_pwrctrl_power_on_device().
> > +{
> > + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
> > +
> > + if (!pwrctrl)
> > + return 0;
> > +
> > + return pwrctrl->power_on(pwrctrl);
> > +}
> > +
> > +/*
> > + * Power on the devices in a depth first manner. Before powering on the device,
> > + * make sure its driver is bound.
> > + */
> > +static int pci_pwrctrl_power_on_device(struct device_node *np)
> > +{
> > + struct platform_device *pdev;
> > + int ret;
> > +
> > + for_each_available_child_of_node_scoped(np, child) {
> > + ret = pci_pwrctrl_power_on_device(child);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + pdev = of_find_device_by_node(np);
> > + if (pdev) {
> > + if (!device_is_bound(&pdev->dev)) {
> > + dev_err(&pdev->dev, "driver is not bound\n");
>
> This is not an error though, is it? If there are multiple deferalls, we'll
> spam the kernel log.
>
Good question. Initially, I made it as a debug log, but then realized that
people may wonder why their controller driver encounters probe deferral without
much clue, especially when the driver spits out other logs before calling this
API. So decided to make it dev_err() to give a visual indication.
If it is not preferred, I can demote it to debug log.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the pwrctrl devices
2025-11-25 13:50 ` Manivannan Sadhasivam
@ 2025-11-25 13:55 ` Bartosz Golaszewski
0 siblings, 0 replies; 16+ messages in thread
From: Bartosz Golaszewski @ 2025-11-25 13:55 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: manivannan.sadhasivam, Manivannan Sadhasivam via B4 Relay,
linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Lorenzo Pieralisi, Krzysztof Wilczyński,
Rob Herring, Bjorn Helgaas
On Tue, Nov 25, 2025 at 2:50 PM Manivannan Sadhasivam <mani@kernel.org> wrote:
>
> >
> > This is not an error though, is it? If there are multiple deferalls, we'll
> > spam the kernel log.
> >
>
> Good question. Initially, I made it as a debug log, but then realized that
> people may wonder why their controller driver encounters probe deferral without
> much clue, especially when the driver spits out other logs before calling this
> API. So decided to make it dev_err() to give a visual indication.
>
> If it is not preferred, I can demote it to debug log.
If we must log it, I'd say a dev_dbg() is enough. Probe deferral is not unusual.
It would be awesome to be able to synchronize the controller probe
with the pwrctl device binding but I don't have an idea on how to do
it yet. :(
Bart
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the pwrctrl devices
2025-11-24 16:20 ` [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the " Manivannan Sadhasivam via B4 Relay
2025-11-25 13:34 ` Bartosz Golaszewski
@ 2025-11-25 13:40 ` Bartosz Golaszewski
1 sibling, 0 replies; 16+ messages in thread
From: Bartosz Golaszewski @ 2025-11-25 13:40 UTC (permalink / raw)
To: manivannan.sadhasivam
Cc: Manivannan Sadhasivam via B4 Relay, linux-pci, linux-arm-msm,
linux-kernel, Chen-Yu Tsai, Brian Norris,
Krishna Chaitanya Chundru, Niklas Cassel, Alex Elder,
Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
On Mon, 24 Nov 2025 17:20:47 +0100, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> To fix PCIe bridge resource allocation issues when powering PCIe
> switches with the pwrctrl driver, introduce APIs to explicitly power
> on and off all related devices simultaneously.
>
> Previously, the individual pwrctrl drivers powered on/off the PCIe devices
> autonomously, without any control from the controller drivers. But to
> enforce ordering w.r.t powering on the devices, these APIs will power
> on/off all the devices at the same time.
>
> The pci_pwrctrl_power_on_devices() API recursively scans the PCI child
> nodes, makes sure that pwrctrl drivers are bind to devices, and calls their
> power_on() callbacks.
>
> Similarly, pci_pwrctrl_power_off_devices() API powers off devices
> recursively via their power_off() callbacks.
>
> These APIs are expected to be called during the controller probe and
> suspend/resume time to power on/off the devices. But before calling these
> APIs, the pwrctrl devices should've been created beforehand using the
> pci_pwrctrl_{create/destroy}_devices() APIs.
>
> Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
Makes sense.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 5/5] PCI/pwrctrl: Switch to the new pwrctrl APIs
2025-11-24 16:20 [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Manivannan Sadhasivam via B4 Relay
` (3 preceding siblings ...)
2025-11-24 16:20 ` [PATCH 4/5] PCI/pwrctrl: Add APIs to power on/off the " Manivannan Sadhasivam via B4 Relay
@ 2025-11-24 16:20 ` Manivannan Sadhasivam via B4 Relay
2025-11-25 7:18 ` Chen-Yu Tsai
2025-11-26 7:22 ` [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Chen-Yu Tsai
5 siblings, 1 reply; 16+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2025-11-24 16:20 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski
Cc: linux-pci, linux-arm-msm, linux-kernel, Chen-Yu Tsai,
Brian Norris, Krishna Chaitanya Chundru, Niklas Cassel,
Alex Elder, Manivannan Sadhasivam
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Adopt the recently introduced pwrctrl APIs to create, power on, destroy,
and power off pwrctrl devices. In qcom_pcie_host_init(), call
pci_pwrctrl_create_devices() to create devices, then
pci_pwrctrl_power_on_devices() to power them on, both after controller
resource initialization. Once successful, deassert PERST# for all devices.
In qcom_pcie_host_deinit(), call pci_pwrctrl_power_off_devices() after
asserting PERST#. Note that pci_pwrctrl_destroy_devices() is not called
here, as deinit is only invoked during system suspend where device
destruction is unnecessary. If the driver becomes removable in future,
pci_pwrctrl_destroy_devices() should be called in the remove() handler.
At last, remove the old pwrctrl framework code from the PCI core, as the
new APIs are now the sole consumer of pwrctrl functionality. And also do
not power on the pwrctrl drivers during probe() as this is now handled by
the APIs.
Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pcie-qcom.c | 22 ++++++++++--
drivers/pci/probe.c | 59 --------------------------------
drivers/pci/pwrctrl/core.c | 15 --------
drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 5 ---
drivers/pci/pwrctrl/slot.c | 2 --
drivers/pci/remove.c | 20 -----------
6 files changed, 20 insertions(+), 103 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 7b6f4a391ce4..691ba4243342 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -24,6 +24,7 @@
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
+#include <linux/pci-pwrctrl.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
@@ -1352,10 +1353,18 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err_deinit;
+ ret = pci_pwrctrl_create_devices(pci->dev);
+ if (ret)
+ goto err_disable_phy;
+
+ ret = pci_pwrctrl_power_on_devices(pci->dev);
+ if (ret)
+ goto err_pwrctrl_destroy;
+
if (pcie->cfg->ops->post_init) {
ret = pcie->cfg->ops->post_init(pcie);
if (ret)
- goto err_disable_phy;
+ goto err_pwrctrl_power_off;
}
qcom_ep_reset_deassert(pcie);
@@ -1370,6 +1379,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
err_assert_reset:
qcom_ep_reset_assert(pcie);
+err_pwrctrl_power_off:
+ pci_pwrctrl_power_off_devices(pci->dev);
+err_pwrctrl_destroy:
+ pci_pwrctrl_destroy_devices(pci->dev);
err_disable_phy:
qcom_pcie_phy_power_off(pcie);
err_deinit:
@@ -1384,6 +1397,11 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
struct qcom_pcie *pcie = to_qcom_pcie(pci);
qcom_ep_reset_assert(pcie);
+ /*
+ * No need to destroy pwrctrl devices as this function only gets called
+ * during system suspend as of now.
+ */
+ pci_pwrctrl_power_off_devices(pci->dev);
qcom_pcie_phy_power_off(pcie);
pcie->cfg->ops->deinit(pcie);
}
@@ -2035,7 +2053,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
ret = dw_pcie_host_init(pp);
if (ret) {
- dev_err(dev, "cannot initialize host\n");
+ dev_err_probe(dev, ret, "cannot initialize host\n");
goto err_phy_exit;
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c83e75a0ec12..34033605adf3 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2533,56 +2533,6 @@ 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);
- struct platform_device *pdev;
- struct device_node *np;
-
- np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn);
- if (!np)
- return NULL;
-
- pdev = of_find_device_by_node(np);
- if (pdev) {
- put_device(&pdev->dev);
- goto err_put_of_node;
- }
-
- /*
- * First check whether the pwrctrl device really needs to be created or
- * not. This is decided based on at least one of the power supplies
- * being defined in the devicetree node of the device.
- */
- if (!of_pci_supply_present(np)) {
- pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name);
- goto err_put_of_node;
- }
-
- /* Now create the pwrctrl device */
- pdev = of_platform_device_create(np, NULL, &host->dev);
- if (!pdev) {
- pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name);
- goto err_put_of_node;
- }
-
- of_node_put(np);
-
- return pdev;
-
-err_put_of_node:
- of_node_put(np);
-
- return NULL;
-}
-#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,
* and fill in the dev structure.
@@ -2592,15 +2542,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
struct pci_dev *dev;
u32 l;
- /*
- * Create pwrctrl device (if required) for the PCI device to handle the
- * power state. If the pwrctrl device is created, then skip scanning
- * further as the pwrctrl core will rescan the bus after powering on
- * the device.
- */
- if (pci_pwrctrl_create_device(bus, devfn))
- return NULL;
-
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL;
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index e0a0cf015bd0..69bf2514c420 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -45,16 +45,6 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_DONE;
}
-static void rescan_work_func(struct work_struct *work)
-{
- struct pci_pwrctrl *pwrctrl = container_of(work,
- struct pci_pwrctrl, work);
-
- pci_lock_rescan_remove();
- pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus);
- pci_unlock_rescan_remove();
-}
-
/**
* pci_pwrctrl_init() - Initialize the PCI power control context struct
*
@@ -64,7 +54,6 @@ static void rescan_work_func(struct work_struct *work)
void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
{
pwrctrl->dev = dev;
- INIT_WORK(&pwrctrl->work, rescan_work_func);
dev_set_drvdata(dev, pwrctrl);
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
@@ -95,8 +84,6 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl)
if (ret)
return ret;
- schedule_work(&pwrctrl->work);
-
return 0;
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
@@ -109,8 +96,6 @@ EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
*/
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl)
{
- cancel_work_sync(&pwrctrl->work);
-
/*
* We don't have to delete the link here. Typically, this function
* is only called when the power control device is being detached. If
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
index 0fb9038a1d18..7697a8a5cdde 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
@@ -101,11 +101,6 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(data->pwrseq),
"Failed to get the power sequencer\n");
- ret = pci_pwrctrl_pwrseq_power_on(&data->ctx);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to power-on the device\n");
-
ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off,
data);
if (ret)
diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c
index 14701f65f1f2..888300aeefec 100644
--- a/drivers/pci/pwrctrl/slot.c
+++ b/drivers/pci/pwrctrl/slot.c
@@ -79,8 +79,6 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(slot->clk),
"Failed to enable slot clock\n");
- pci_pwrctrl_slot_power_on(&slot->ctx);
-
slot->ctx.power_on = pci_pwrctrl_slot_power_on;
slot->ctx.power_off = pci_pwrctrl_slot_power_off;
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index ce5c25adef55..2a22595f38af 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -17,25 +17,6 @@ static void pci_free_resources(struct pci_dev *dev)
}
}
-static void pci_pwrctrl_unregister(struct device *dev)
-{
- struct device_node *np;
- struct platform_device *pdev;
-
- np = dev_of_node(dev);
- if (!np)
- return;
-
- pdev = of_find_device_by_node(np);
- if (!pdev)
- return;
-
- of_device_unregister(pdev);
- put_device(&pdev->dev);
-
- of_node_clear_flag(np, OF_POPULATED);
-}
-
static void pci_stop_dev(struct pci_dev *dev)
{
pci_pme_active(dev, false);
@@ -66,7 +47,6 @@ static void pci_destroy_dev(struct pci_dev *dev)
pci_doe_destroy(dev);
pcie_aspm_exit_link_state(dev);
pci_bridge_d3_update(dev);
- pci_pwrctrl_unregister(&dev->dev);
pci_free_resources(dev);
put_device(&dev->dev);
}
--
2.48.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 5/5] PCI/pwrctrl: Switch to the new pwrctrl APIs
2025-11-24 16:20 ` [PATCH 5/5] PCI/pwrctrl: Switch to the new pwrctrl APIs Manivannan Sadhasivam via B4 Relay
@ 2025-11-25 7:18 ` Chen-Yu Tsai
2025-11-25 7:30 ` Manivannan Sadhasivam
0 siblings, 1 reply; 16+ messages in thread
From: Chen-Yu Tsai @ 2025-11-25 7:18 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski, linux-pci, linux-arm-msm, linux-kernel,
Chen-Yu Tsai, Brian Norris, Krishna Chaitanya Chundru,
Niklas Cassel, Alex Elder
On Tue, Nov 25, 2025 at 3:15 PM Manivannan Sadhasivam
<manivannan.sadhasivam@oss.qualcomm.com> wrote:
>
> Adopt the recently introduced pwrctrl APIs to create, power on, destroy,
> and power off pwrctrl devices. In qcom_pcie_host_init(), call
> pci_pwrctrl_create_devices() to create devices, then
> pci_pwrctrl_power_on_devices() to power them on, both after controller
> resource initialization. Once successful, deassert PERST# for all devices.
>
> In qcom_pcie_host_deinit(), call pci_pwrctrl_power_off_devices() after
> asserting PERST#. Note that pci_pwrctrl_destroy_devices() is not called
> here, as deinit is only invoked during system suspend where device
> destruction is unnecessary. If the driver becomes removable in future,
> pci_pwrctrl_destroy_devices() should be called in the remove() handler.
>
> At last, remove the old pwrctrl framework code from the PCI core, as the
> new APIs are now the sole consumer of pwrctrl functionality. And also do
> not power on the pwrctrl drivers during probe() as this is now handled by
> the APIs.
>
> Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
> drivers/pci/controller/dwc/pcie-qcom.c | 22 ++++++++++--
> drivers/pci/probe.c | 59 --------------------------------
> drivers/pci/pwrctrl/core.c | 15 --------
> drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 5 ---
> drivers/pci/pwrctrl/slot.c | 2 --
> drivers/pci/remove.c | 20 -----------
> 6 files changed, 20 insertions(+), 103 deletions(-)
[...]
> @@ -66,7 +47,6 @@ static void pci_destroy_dev(struct pci_dev *dev)
> pci_doe_destroy(dev);
> pcie_aspm_exit_link_state(dev);
> pci_bridge_d3_update(dev);
> - pci_pwrctrl_unregister(&dev->dev);
> pci_free_resources(dev);
> put_device(&dev->dev);
This hunk has a minor conflict with
079115370d00 PCI/IDE: Initialize an ID for all IDE streams
already in linux-next.
> }
>
> --
> 2.48.1
>
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 5/5] PCI/pwrctrl: Switch to the new pwrctrl APIs
2025-11-25 7:18 ` Chen-Yu Tsai
@ 2025-11-25 7:30 ` Manivannan Sadhasivam
0 siblings, 0 replies; 16+ messages in thread
From: Manivannan Sadhasivam @ 2025-11-25 7:30 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski, linux-pci, linux-arm-msm, linux-kernel,
Chen-Yu Tsai, Brian Norris, Krishna Chaitanya Chundru,
Niklas Cassel, Alex Elder
On Tue, Nov 25, 2025 at 03:18:12PM +0800, Chen-Yu Tsai wrote:
> On Tue, Nov 25, 2025 at 3:15 PM Manivannan Sadhasivam
> <manivannan.sadhasivam@oss.qualcomm.com> wrote:
> >
> > Adopt the recently introduced pwrctrl APIs to create, power on, destroy,
> > and power off pwrctrl devices. In qcom_pcie_host_init(), call
> > pci_pwrctrl_create_devices() to create devices, then
> > pci_pwrctrl_power_on_devices() to power them on, both after controller
> > resource initialization. Once successful, deassert PERST# for all devices.
> >
> > In qcom_pcie_host_deinit(), call pci_pwrctrl_power_off_devices() after
> > asserting PERST#. Note that pci_pwrctrl_destroy_devices() is not called
> > here, as deinit is only invoked during system suspend where device
> > destruction is unnecessary. If the driver becomes removable in future,
> > pci_pwrctrl_destroy_devices() should be called in the remove() handler.
> >
> > At last, remove the old pwrctrl framework code from the PCI core, as the
> > new APIs are now the sole consumer of pwrctrl functionality. And also do
> > not power on the pwrctrl drivers during probe() as this is now handled by
> > the APIs.
> >
> > Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> > Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> > ---
> > drivers/pci/controller/dwc/pcie-qcom.c | 22 ++++++++++--
> > drivers/pci/probe.c | 59 --------------------------------
> > drivers/pci/pwrctrl/core.c | 15 --------
> > drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 5 ---
> > drivers/pci/pwrctrl/slot.c | 2 --
> > drivers/pci/remove.c | 20 -----------
> > 6 files changed, 20 insertions(+), 103 deletions(-)
>
> [...]
>
> > @@ -66,7 +47,6 @@ static void pci_destroy_dev(struct pci_dev *dev)
> > pci_doe_destroy(dev);
> > pcie_aspm_exit_link_state(dev);
> > pci_bridge_d3_update(dev);
> > - pci_pwrctrl_unregister(&dev->dev);
> > pci_free_resources(dev);
> > put_device(&dev->dev);
>
> This hunk has a minor conflict with
>
> 079115370d00 PCI/IDE: Initialize an ID for all IDE streams
>
> already in linux-next.
>
This series in based on v6.18-rc1, so there might be conflicts when applied to
-next. I'm not targeting v6.19 anyway as we are pretty close to merge window. We
have also merged a new pwrctrl driver for v6.19 recently and that too needs some
work, but shouldn't impact non-Qcom QCS6490 platforms.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers
2025-11-24 16:20 [PATCH 0/5] PCI/pwrctrl: Major rework to integrate pwrctrl devices with controller drivers Manivannan Sadhasivam via B4 Relay
` (4 preceding siblings ...)
2025-11-24 16:20 ` [PATCH 5/5] PCI/pwrctrl: Switch to the new pwrctrl APIs Manivannan Sadhasivam via B4 Relay
@ 2025-11-26 7:22 ` Chen-Yu Tsai
5 siblings, 0 replies; 16+ messages in thread
From: Chen-Yu Tsai @ 2025-11-26 7:22 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Bartosz Golaszewski, linux-pci, linux-arm-msm, linux-kernel,
Chen-Yu Tsai, Brian Norris, Krishna Chaitanya Chundru,
Niklas Cassel, Alex Elder
On Tue, Nov 25, 2025 at 3:13 PM Manivannan Sadhasivam
<manivannan.sadhasivam@oss.qualcomm.com> wrote:
>
> Hi,
>
> This series provides a major rework for the PCI power control (pwrctrl)
> framework to enable the pwrctrl devices to be controlled by the PCI controller
> drivers.
>
> Problem Statement
> =================
>
> Currently, the pwrctrl framework faces two major issues:
>
> 1. Missing PERST# integration
> 2. Inability to properly handle bus extenders such as PCIe switch devices
>
> First issue arises from the disconnect between the PCI controller drivers and
> pwrctrl framework. At present, the pwrctrl framework just operates on its own
> with the help of the PCI core. The pwrctrl devices are created by the PCI core
> during initial bus scan and the pwrctrl drivers once bind, just power on the
> PCI devices during their probe(). This design conflicts with the PCI Express
> Card Electromechanical Specification requirements for PERST# timing. The reason
> is, PERST# signals are mostly handled by the controller drivers and often
> deasserted even before the pwrctrl drivers probe. According to the spec, PERST#
> should be deasserted only after power and reference clock to the device are
> stable, within predefined timing parameters.
>
> The second issue stems from the PCI bus scan completing before pwrctrl drivers
> probe. This poses a significant problem for PCI bus extenders like switches
> because the PCI core allocates upstream bridge resources during the initial
> scan. If the upstream bridge is not hotplug capable, resources are allocated
> only for the number of downstream buses detected at scan time, which might be
> just one if the switch was not powered and enumerated at that time. Later, when
> the pwrctrl driver powers on and enumerates the switch, enumeration fails due to
> insufficient upstream bridge resources.
>
> Proposal
> ========
>
> This series addresses both issues by introducing new individual APIs for pwrctrl
> device creation, destruction, power on, and power off operations. Controller
> drivers are expected to invoke these APIs during their probe(), remove(),
> suspend(), and resume() operations. This integration allows better coordination
> between controller drivers and the pwrctrl framework, enabling enhanced features
> such as D3Cold support.
>
> The original design aimed to avoid modifying controller drivers for pwrctrl
> integration. However, this approach lacked scalability because different
> controllers have varying requirements for when devices should be powered on. For
> example, controller drivers require devices to be powered on early for
> successful PHY initialization.
>
> By using these explicit APIs, controller drivers gain fine grained control over
> their associated pwrctrl devices.
>
> This series modified the pcie-qcom driver (only consumer of pwrctrl framework)
> to adopt to these APIs and also removed the old pwrctrl code from PCI core. This
> could be used as a reference to add pwrctrl support for other controller drivers
> also.
>
> For example, to control the 3.3v supply to the PCI slot where the NVMe device is
> connected, below modifications are required:
>
> Devicetree
> ----------
>
> // In SoC dtsi:
>
> pci@1bf8000 { // controller node
> ...
> pcie1_port0: pcie@0 { // PCI Root Port node
> compatible = "pciclass,0604"; // required for pwrctrl
> driver bind
> ...
> };
> };
>
> // In board dts:
>
> &pcie1_port0 {
> reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; // optional
> vpcie3v3-supply = <&vreg_nvme>; // NVMe power supply
> };
>
> Controller driver
> -----------------
>
> // Select PCI_PWRCTRL_SLOT in controller Kconfig
>
> probe() {
> ...
> // Initialize controller resources
> pci_pwrctrl_create_devices(&pdev->dev);
> pci_pwrctrl_power_on_devices(&pdev->dev);
> // Deassert PERST# (optional)
> ...
> pci_host_probe(); // Allocate host bridge and start bus scan
> }
>
> suspend {
> // PME_Turn_Off broadcast
> // Assert PERST# (optional)
> pci_pwrctrl_power_off_devices(&pdev->dev);
> ...
> }
>
> resume {
> ...
> pci_pwrctrl_power_on_devices(&pdev->dev);
> // Deassert PERST# (optional)
> }
>
> I will add a documentation for the pwrctrl framework in the coming days to make
> it easier to use.
>
> Testing
> =======
>
> This series is tested on the Lenovo Thinkpad T14s laptop based on Qcom X1E
> chipset and RB3Gen2 development board with TC9563 switch based on Qcom QCS6490
> chipset.
>
> **NOTE**: With this series, the controller driver may undergo multiple probe
> deferral if the pwrctrl driver was not available during the controller driver
> probe. This is pretty much required to avoid the resource allocation issue.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
> Krishna Chaitanya Chundru (1):
> PCI/pwrctrl: Add APIs for explicitly creating and destroying pwrctrl devices
>
> Manivannan Sadhasivam (4):
> PCI: qcom: Parse PERST# from all PCIe bridge nodes
I see this is still handled in the driver. Reading the cover letter,
it seems the plan is to move this into pwrctrl, perhaps as a separate
function?
> PCI/pwrctrl: Add 'struct pci_pwrctrl::power_{on/off}' callbacks
> PCI/pwrctrl: Add APIs to power on/off the pwrctrl devices
> PCI/pwrctrl: Switch to the new pwrctrl APIs
Tested-by: Chen-Yu Tsai <wenst@chromium.org>
on an MT8188-based Chromebook which has on-board PCIe-based WiFi.
The pcie-mediatek-gen3.c driver was reworked to integrate the new API.
> drivers/pci/controller/dwc/pcie-qcom.c | 124 +++++++++++++---
> drivers/pci/probe.c | 59 --------
> drivers/pci/pwrctrl/core.c | 248 +++++++++++++++++++++++++++++--
> drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 30 +++-
> drivers/pci/pwrctrl/slot.c | 46 ++++--
> drivers/pci/remove.c | 20 ---
> include/linux/pci-pwrctrl.h | 16 +-
> 7 files changed, 407 insertions(+), 136 deletions(-)
> ---
> base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787
> change-id: 20251124-pci-pwrctrl-rework-c91a6e16c2f6
>
> Best regards,
> --
> Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
^ permalink raw reply [flat|nested] 16+ messages in thread