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 power on/off pwrctrl devices

To fix bridge resource allocation issues when powering PCI bridges 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 PCI devices
autonomously, without any control from the controller drivers. But to
enforce ordering with respect to 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 bound to devices, and calls
their power_on() callbacks. If any pwrctrl driver is not bound, it will
return -EPROBE_DEFER.

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

authored by

Manivannan Sadhasivam and committed by
Bjorn Helgaas
b35cf3b6 4c413248

+134
+130
drivers/pci/pwrctrl/core.c
··· 65 65 { 66 66 pwrctrl->dev = dev; 67 67 INIT_WORK(&pwrctrl->work, rescan_work_func); 68 + dev_set_drvdata(dev, pwrctrl); 68 69 } 69 70 EXPORT_SYMBOL_GPL(pci_pwrctrl_init); 70 71 ··· 152 151 pwrctrl); 153 152 } 154 153 EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); 154 + 155 + static int __pci_pwrctrl_power_off_device(struct device *dev) 156 + { 157 + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); 158 + 159 + if (!pwrctrl) 160 + return 0; 161 + 162 + return pwrctrl->power_off(pwrctrl); 163 + } 164 + 165 + static void pci_pwrctrl_power_off_device(struct device_node *np) 166 + { 167 + struct platform_device *pdev; 168 + int ret; 169 + 170 + for_each_available_child_of_node_scoped(np, child) 171 + pci_pwrctrl_power_off_device(child); 172 + 173 + pdev = of_find_device_by_node(np); 174 + if (!pdev) 175 + return; 176 + 177 + if (device_is_bound(&pdev->dev)) { 178 + ret = __pci_pwrctrl_power_off_device(&pdev->dev); 179 + if (ret) 180 + dev_err(&pdev->dev, "Failed to power off device: %d", ret); 181 + } 182 + 183 + platform_device_put(pdev); 184 + } 185 + 186 + /** 187 + * pci_pwrctrl_power_off_devices - Power off pwrctrl devices 188 + * 189 + * @parent: PCI host controller device 190 + * 191 + * Recursively traverse all pwrctrl devices for the devicetree hierarchy 192 + * below the specified PCI host controller and power them off in a depth 193 + * first manner. 194 + */ 195 + void pci_pwrctrl_power_off_devices(struct device *parent) 196 + { 197 + struct device_node *np = parent->of_node; 198 + 199 + for_each_available_child_of_node_scoped(np, child) 200 + pci_pwrctrl_power_off_device(child); 201 + } 202 + EXPORT_SYMBOL_GPL(pci_pwrctrl_power_off_devices); 203 + 204 + static int __pci_pwrctrl_power_on_device(struct device *dev) 205 + { 206 + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); 207 + 208 + if (!pwrctrl) 209 + return 0; 210 + 211 + return pwrctrl->power_on(pwrctrl); 212 + } 213 + 214 + /* 215 + * Power on the devices in a depth first manner. Before powering on the device, 216 + * make sure its driver is bound. 217 + */ 218 + static int pci_pwrctrl_power_on_device(struct device_node *np) 219 + { 220 + struct platform_device *pdev; 221 + int ret; 222 + 223 + for_each_available_child_of_node_scoped(np, child) { 224 + ret = pci_pwrctrl_power_on_device(child); 225 + if (ret) 226 + return ret; 227 + } 228 + 229 + pdev = of_find_device_by_node(np); 230 + if (!pdev) 231 + return 0; 232 + 233 + if (device_is_bound(&pdev->dev)) { 234 + ret = __pci_pwrctrl_power_on_device(&pdev->dev); 235 + } else { 236 + /* FIXME: Use blocking wait instead of probe deferral */ 237 + dev_dbg(&pdev->dev, "driver is not bound\n"); 238 + ret = -EPROBE_DEFER; 239 + } 240 + 241 + platform_device_put(pdev); 242 + 243 + return ret; 244 + } 245 + 246 + /** 247 + * pci_pwrctrl_power_on_devices - Power on pwrctrl devices 248 + * 249 + * @parent: PCI host controller device 250 + * 251 + * Recursively traverse all pwrctrl devices for the devicetree hierarchy 252 + * below the specified PCI host controller and power them on in a depth 253 + * first manner. On error, all powered on devices will be powered off. 254 + * 255 + * Return: 0 on success, -EPROBE_DEFER if any pwrctrl driver is not bound, an 256 + * appropriate error code otherwise. 257 + */ 258 + int pci_pwrctrl_power_on_devices(struct device *parent) 259 + { 260 + struct device_node *np = parent->of_node; 261 + struct device_node *child = NULL; 262 + int ret; 263 + 264 + for_each_available_child_of_node(np, child) { 265 + ret = pci_pwrctrl_power_on_device(child); 266 + if (ret) 267 + goto err_power_off; 268 + } 269 + 270 + return 0; 271 + 272 + err_power_off: 273 + for_each_available_child_of_node_scoped(np, tmp) { 274 + if (tmp == child) 275 + break; 276 + pci_pwrctrl_power_off_device(tmp); 277 + } 278 + of_node_put(child); 279 + 280 + return ret; 281 + } 282 + EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices); 155 283 156 284 static int pci_pwrctrl_create_device(struct device_node *np, 157 285 struct device *parent)
+4
include/linux/pci-pwrctrl.h
··· 57 57 #if IS_ENABLED(CONFIG_PCI_PWRCTRL) 58 58 int pci_pwrctrl_create_devices(struct device *parent); 59 59 void pci_pwrctrl_destroy_devices(struct device *parent); 60 + int pci_pwrctrl_power_on_devices(struct device *parent); 61 + void pci_pwrctrl_power_off_devices(struct device *parent); 60 62 #else 61 63 static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; } 62 64 static void pci_pwrctrl_destroy_devices(struct device *parent) { } 65 + static inline int pci_pwrctrl_power_on_devices(struct device *parent) { return 0; } 66 + static void pci_pwrctrl_power_off_devices(struct device *parent) { } 63 67 #endif 64 68 #endif /* __PCI_PWRCTRL_H__ */