Skip to content

Commit 55d25a1

Browse files
committed
Merge branch 'pci/pwrctrl'
- Create pwrctrl devices in pci_scan_device() to make it more symmetric with pci_pwrctrl_unregister() and make pwrctrl devices for PCI bridges possible (Manivannan Sadhasivam) - Unregister pwrctrl devices in pci_destroy_dev() so DOE, ASPM, etc. can still access devices after pci_stop_dev() (Manivannan Sadhasivam) - If there's a pwrctrl device for a PCI device, skip scanning it because the pwrctrl core will rescan the bus after the device is powered on (Manivannan Sadhasivam) - Add a pwrctrl driver for PCI slots based on voltage regulators described via devicetree (Manivannan Sadhasivam) * pci/pwrctrl: PCI/pwrctrl: Add pwrctrl driver for PCI slots dt-bindings: vendor-prefixes: Document the 'pciclass' prefix PCI/pwrctrl: Skip scanning for the device further if pwrctrl device is created PCI/pwrctrl: Move pci_pwrctrl_unregister() to pci_destroy_dev() PCI/pwrctrl: Move creation of pwrctrl devices to pci_scan_device()
2 parents e91c25c + 75996c9 commit 55d25a1

File tree

8 files changed

+151
-46
lines changed

8 files changed

+151
-46
lines changed

Documentation/devicetree/bindings/vendor-prefixes.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ patternProperties:
1818
# DO NOT ADD NEW PROPERTIES TO THIS LIST
1919
"^(at25|bm|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio-key|gpio|gpmc|hdmi|i2c-gpio),.*": true
2020
"^(keypad|m25p|max8952|max8997|max8998|mpmc),.*": true
21-
"^(pinctrl-single|#pinctrl-single|PowerPC),.*": true
21+
"^(pciclass|pinctrl-single|#pinctrl-single|PowerPC),.*": true
2222
"^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true
2323
"^(simple-audio-card|st-plgpio|st-spics|ts),.*": true
2424

drivers/pci/bus.c

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -331,47 +331,6 @@ void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }
331331

332332
void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
333333

334-
/*
335-
* Create pwrctrl devices (if required) for the PCI devices to handle the power
336-
* state.
337-
*/
338-
static void pci_pwrctrl_create_devices(struct pci_dev *dev)
339-
{
340-
struct device_node *np = dev_of_node(&dev->dev);
341-
struct device *parent = &dev->dev;
342-
struct platform_device *pdev;
343-
344-
/*
345-
* First ensure that we are starting from a PCI bridge and it has a
346-
* corresponding devicetree node.
347-
*/
348-
if (np && pci_is_bridge(dev)) {
349-
/*
350-
* Now look for the child PCI device nodes and create pwrctrl
351-
* devices for them. The pwrctrl device drivers will manage the
352-
* power state of the devices.
353-
*/
354-
for_each_available_child_of_node_scoped(np, child) {
355-
/*
356-
* First check whether the pwrctrl device really
357-
* needs to be created or not. This is decided
358-
* based on at least one of the power supplies
359-
* being defined in the devicetree node of the
360-
* device.
361-
*/
362-
if (!of_pci_supply_present(child)) {
363-
pci_dbg(dev, "skipping OF node: %s\n", child->name);
364-
return;
365-
}
366-
367-
/* Now create the pwrctrl device */
368-
pdev = of_platform_device_create(child, NULL, parent);
369-
if (!pdev)
370-
pci_err(dev, "failed to create OF node: %s\n", child->name);
371-
}
372-
}
373-
}
374-
375334
/**
376335
* pci_bus_add_device - start driver for a single device
377336
* @dev: device to add
@@ -396,8 +355,6 @@ void pci_bus_add_device(struct pci_dev *dev)
396355
pci_proc_attach_device(dev);
397356
pci_bridge_d3_update(dev);
398357

399-
pci_pwrctrl_create_devices(dev);
400-
401358
/*
402359
* If the PCI device is associated with a pwrctrl device with a
403360
* power supply, create a device link between the PCI device and

drivers/pci/probe.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <linux/pci.h>
1010
#include <linux/msi.h>
1111
#include <linux/of_pci.h>
12+
#include <linux/of_platform.h>
13+
#include <linux/platform_device.h>
1214
#include <linux/pci_hotplug.h>
1315
#include <linux/slab.h>
1416
#include <linux/module.h>
@@ -2503,6 +2505,36 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
25032505
}
25042506
EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
25052507

2508+
static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn)
2509+
{
2510+
struct pci_host_bridge *host = pci_find_host_bridge(bus);
2511+
struct platform_device *pdev;
2512+
struct device_node *np;
2513+
2514+
np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn);
2515+
if (!np || of_find_device_by_node(np))
2516+
return NULL;
2517+
2518+
/*
2519+
* First check whether the pwrctrl device really needs to be created or
2520+
* not. This is decided based on at least one of the power supplies
2521+
* being defined in the devicetree node of the device.
2522+
*/
2523+
if (!of_pci_supply_present(np)) {
2524+
pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name);
2525+
return NULL;
2526+
}
2527+
2528+
/* Now create the pwrctrl device */
2529+
pdev = of_platform_device_create(np, NULL, &host->dev);
2530+
if (!pdev) {
2531+
pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name);
2532+
return NULL;
2533+
}
2534+
2535+
return pdev;
2536+
}
2537+
25062538
/*
25072539
* Read the config data for a PCI device, sanity-check it,
25082540
* and fill in the dev structure.
@@ -2512,6 +2544,15 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
25122544
struct pci_dev *dev;
25132545
u32 l;
25142546

2547+
/*
2548+
* Create pwrctrl device (if required) for the PCI device to handle the
2549+
* power state. If the pwrctrl device is created, then skip scanning
2550+
* further as the pwrctrl core will rescan the bus after powering on
2551+
* the device.
2552+
*/
2553+
if (pci_pwrctrl_create_device(bus, devfn))
2554+
return NULL;
2555+
25152556
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
25162557
return NULL;
25172558

drivers/pci/pwrctrl/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,14 @@ config PCI_PWRCTL_PWRSEQ
1010
tristate
1111
select POWER_SEQUENCING
1212
select PCI_PWRCTL
13+
14+
config PCI_PWRCTL_SLOT
15+
tristate "PCI Power Control driver for PCI slots"
16+
select PCI_PWRCTL
17+
help
18+
Say Y here to enable the PCI Power Control driver to control the power
19+
state of PCI slots.
20+
21+
This is a generic driver that controls the power state of different
22+
PCI slots. The voltage regulators powering the rails of the PCI slots
23+
are expected to be defined in the devicetree node of the PCI bridge.

drivers/pci/pwrctrl/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctrl-core.o
44
pci-pwrctrl-core-y := core.o
55

66
obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctrl-pwrseq.o
7+
8+
obj-$(CONFIG_PCI_PWRCTL_SLOT) += pci-pwrctl-slot.o
9+
pci-pwrctl-slot-y := slot.o

drivers/pci/pwrctrl/core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ static void rescan_work_func(struct work_struct *work)
4444
struct pci_pwrctrl, work);
4545

4646
pci_lock_rescan_remove();
47-
pci_rescan_bus(to_pci_dev(pwrctrl->dev->parent)->bus);
47+
pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus);
4848
pci_unlock_rescan_remove();
4949
}
5050

drivers/pci/pwrctrl/slot.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2024 Linaro Ltd.
4+
* Author: Manivannan Sadhasivam <[email protected]>
5+
*/
6+
7+
#include <linux/device.h>
8+
#include <linux/mod_devicetable.h>
9+
#include <linux/module.h>
10+
#include <linux/pci-pwrctrl.h>
11+
#include <linux/platform_device.h>
12+
#include <linux/regulator/consumer.h>
13+
#include <linux/slab.h>
14+
15+
struct pci_pwrctrl_slot_data {
16+
struct pci_pwrctrl ctx;
17+
struct regulator_bulk_data *supplies;
18+
int num_supplies;
19+
};
20+
21+
static void devm_pci_pwrctrl_slot_power_off(void *data)
22+
{
23+
struct pci_pwrctrl_slot_data *slot = data;
24+
25+
regulator_bulk_disable(slot->num_supplies, slot->supplies);
26+
regulator_bulk_free(slot->num_supplies, slot->supplies);
27+
}
28+
29+
static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
30+
{
31+
struct pci_pwrctrl_slot_data *slot;
32+
struct device *dev = &pdev->dev;
33+
int ret;
34+
35+
slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
36+
if (!slot)
37+
return -ENOMEM;
38+
39+
ret = of_regulator_bulk_get_all(dev, dev_of_node(dev),
40+
&slot->supplies);
41+
if (ret < 0) {
42+
dev_err_probe(dev, ret, "Failed to get slot regulators\n");
43+
return ret;
44+
}
45+
46+
slot->num_supplies = ret;
47+
ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
48+
if (ret < 0) {
49+
dev_err_probe(dev, ret, "Failed to enable slot regulators\n");
50+
goto err_regulator_free;
51+
}
52+
53+
ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off,
54+
slot);
55+
if (ret)
56+
goto err_regulator_disable;
57+
58+
pci_pwrctrl_init(&slot->ctx, dev);
59+
60+
ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx);
61+
if (ret)
62+
return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n");
63+
64+
return 0;
65+
66+
err_regulator_disable:
67+
regulator_bulk_disable(slot->num_supplies, slot->supplies);
68+
err_regulator_free:
69+
regulator_bulk_free(slot->num_supplies, slot->supplies);
70+
71+
return ret;
72+
}
73+
74+
static const struct of_device_id pci_pwrctrl_slot_of_match[] = {
75+
{
76+
.compatible = "pciclass,0604",
77+
},
78+
{ }
79+
};
80+
MODULE_DEVICE_TABLE(of, pci_pwrctrl_slot_of_match);
81+
82+
static struct platform_driver pci_pwrctrl_slot_driver = {
83+
.driver = {
84+
.name = "pci-pwrctrl-slot",
85+
.of_match_table = pci_pwrctrl_slot_of_match,
86+
},
87+
.probe = pci_pwrctrl_slot_probe,
88+
};
89+
module_platform_driver(pci_pwrctrl_slot_driver);
90+
91+
MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
92+
MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots");
93+
MODULE_LICENSE("GPL");

drivers/pci/remove.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ static void pci_stop_dev(struct pci_dev *dev)
4141
if (!pci_dev_test_and_clear_added(dev))
4242
return;
4343

44-
pci_pwrctrl_unregister(&dev->dev);
4544
device_release_driver(&dev->dev);
4645
pci_proc_detach_device(dev);
4746
pci_remove_sysfs_dev_files(dev);
@@ -65,6 +64,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
6564
pci_doe_destroy(dev);
6665
pcie_aspm_exit_link_state(dev);
6766
pci_bridge_d3_update(dev);
67+
pci_pwrctrl_unregister(&dev->dev);
6868
pci_free_resources(dev);
6969
put_device(&dev->dev);
7070
}

0 commit comments

Comments
 (0)