Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI/pwrctrl: Add APIs to create, destroy pwrctrl devices

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, its 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 work around these issues.

Introduce 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>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Chen-Yu Tsai <wenst@chromium.org>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Link: https://patch.msgid.link/20260115-pci-pwrctrl-rework-v5-10-9d26da3ce903@oss.qualcomm.com

authored by

Krishna Chaitanya Chundru and committed by
Bjorn Helgaas
4c413248 113f44ed

+122 -1
+1
drivers/pci/of.c
··· 867 867 868 868 return false; 869 869 } 870 + EXPORT_SYMBOL_GPL(of_pci_supply_present); 870 871 871 872 #endif /* CONFIG_PCI */ 872 873
+114
drivers/pci/pwrctrl/core.c
··· 3 3 * Copyright (C) 2024 Linaro Ltd. 4 4 */ 5 5 6 + #define dev_fmt(fmt) "pwrctrl: " fmt 7 + 6 8 #include <linux/device.h> 7 9 #include <linux/export.h> 8 10 #include <linux/kernel.h> 11 + #include <linux/of.h> 12 + #include <linux/of_platform.h> 9 13 #include <linux/pci.h> 10 14 #include <linux/pci-pwrctrl.h> 15 + #include <linux/platform_device.h> 11 16 #include <linux/property.h> 12 17 #include <linux/slab.h> 18 + 19 + #include "../pci.h" 13 20 14 21 static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, 15 22 void *data) ··· 151 144 pwrctrl); 152 145 } 153 146 EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); 147 + 148 + static int pci_pwrctrl_create_device(struct device_node *np, 149 + struct device *parent) 150 + { 151 + struct platform_device *pdev; 152 + int ret; 153 + 154 + for_each_available_child_of_node_scoped(np, child) { 155 + ret = pci_pwrctrl_create_device(child, parent); 156 + if (ret) 157 + return ret; 158 + } 159 + 160 + /* Bail out if the platform device is already available for the node */ 161 + pdev = of_find_device_by_node(np); 162 + if (pdev) { 163 + platform_device_put(pdev); 164 + return 0; 165 + } 166 + 167 + /* 168 + * Sanity check to make sure that the node has the compatible property 169 + * to allow driver binding. 170 + */ 171 + if (!of_property_present(np, "compatible")) 172 + return 0; 173 + 174 + /* 175 + * Check whether the pwrctrl device really needs to be created or not. 176 + * This is decided based on at least one of the power supplies being 177 + * defined in the devicetree node of the device. 178 + */ 179 + if (!of_pci_supply_present(np)) { 180 + dev_dbg(parent, "Skipping OF node: %s\n", np->name); 181 + return 0; 182 + } 183 + 184 + /* Now create the pwrctrl device */ 185 + pdev = of_platform_device_create(np, NULL, parent); 186 + if (!pdev) { 187 + dev_err(parent, "Failed to create pwrctrl device for node: %s\n", np->name); 188 + return -EINVAL; 189 + } 190 + 191 + return 0; 192 + } 193 + 194 + /** 195 + * pci_pwrctrl_create_devices - Create pwrctrl devices 196 + * 197 + * @parent: PCI host controller device 198 + * 199 + * Recursively create pwrctrl devices for the devicetree hierarchy below 200 + * the specified PCI host controller in a depth first manner. On error, all 201 + * created devices will be destroyed. 202 + * 203 + * Return: 0 on success, negative error number on error. 204 + */ 205 + int pci_pwrctrl_create_devices(struct device *parent) 206 + { 207 + int ret; 208 + 209 + for_each_available_child_of_node_scoped(parent->of_node, child) { 210 + ret = pci_pwrctrl_create_device(child, parent); 211 + if (ret) { 212 + pci_pwrctrl_destroy_devices(parent); 213 + return ret; 214 + } 215 + } 216 + 217 + return 0; 218 + } 219 + EXPORT_SYMBOL_GPL(pci_pwrctrl_create_devices); 220 + 221 + static void pci_pwrctrl_destroy_device(struct device_node *np) 222 + { 223 + struct platform_device *pdev; 224 + 225 + for_each_available_child_of_node_scoped(np, child) 226 + pci_pwrctrl_destroy_device(child); 227 + 228 + pdev = of_find_device_by_node(np); 229 + if (!pdev) 230 + return; 231 + 232 + of_device_unregister(pdev); 233 + platform_device_put(pdev); 234 + 235 + of_node_clear_flag(np, OF_POPULATED); 236 + } 237 + 238 + /** 239 + * pci_pwrctrl_destroy_devices - Destroy pwrctrl devices 240 + * 241 + * @parent: PCI host controller device 242 + * 243 + * Recursively destroy pwrctrl devices for the devicetree hierarchy below 244 + * the specified PCI host controller in a depth first manner. 245 + */ 246 + void pci_pwrctrl_destroy_devices(struct device *parent) 247 + { 248 + struct device_node *np = parent->of_node; 249 + 250 + for_each_available_child_of_node_scoped(np, child) 251 + pci_pwrctrl_destroy_device(child); 252 + } 253 + EXPORT_SYMBOL_GPL(pci_pwrctrl_destroy_devices); 154 254 155 255 MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>"); 156 256 MODULE_DESCRIPTION("PCI Device Power Control core driver");
+7 -1
include/linux/pci-pwrctrl.h
··· 54 54 void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl); 55 55 int devm_pci_pwrctrl_device_set_ready(struct device *dev, 56 56 struct pci_pwrctrl *pwrctrl); 57 - 57 + #if IS_ENABLED(CONFIG_PCI_PWRCTRL) 58 + int pci_pwrctrl_create_devices(struct device *parent); 59 + void pci_pwrctrl_destroy_devices(struct device *parent); 60 + #else 61 + static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; } 62 + static void pci_pwrctrl_destroy_devices(struct device *parent) { } 63 + #endif 58 64 #endif /* __PCI_PWRCTRL_H__ */