mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
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()
This commit is contained in:
commit
55d25a101d
|
|
@ -18,7 +18,7 @@ patternProperties:
|
|||
# DO NOT ADD NEW PROPERTIES TO THIS LIST
|
||||
"^(at25|bm|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio-key|gpio|gpmc|hdmi|i2c-gpio),.*": true
|
||||
"^(keypad|m25p|max8952|max8997|max8998|mpmc),.*": true
|
||||
"^(pinctrl-single|#pinctrl-single|PowerPC),.*": true
|
||||
"^(pciclass|pinctrl-single|#pinctrl-single|PowerPC),.*": true
|
||||
"^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true
|
||||
"^(simple-audio-card|st-plgpio|st-spics|ts),.*": true
|
||||
|
||||
|
|
|
|||
|
|
@ -331,47 +331,6 @@ void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }
|
|||
|
||||
void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
|
||||
|
||||
/*
|
||||
* Create pwrctrl devices (if required) for the PCI devices to handle the power
|
||||
* state.
|
||||
*/
|
||||
static void pci_pwrctrl_create_devices(struct pci_dev *dev)
|
||||
{
|
||||
struct device_node *np = dev_of_node(&dev->dev);
|
||||
struct device *parent = &dev->dev;
|
||||
struct platform_device *pdev;
|
||||
|
||||
/*
|
||||
* First ensure that we are starting from a PCI bridge and it has a
|
||||
* corresponding devicetree node.
|
||||
*/
|
||||
if (np && pci_is_bridge(dev)) {
|
||||
/*
|
||||
* Now look for the child PCI device nodes and create pwrctrl
|
||||
* devices for them. The pwrctrl device drivers will manage the
|
||||
* power state of the devices.
|
||||
*/
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
/*
|
||||
* 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(child)) {
|
||||
pci_dbg(dev, "skipping OF node: %s\n", child->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now create the pwrctrl device */
|
||||
pdev = of_platform_device_create(child, NULL, parent);
|
||||
if (!pdev)
|
||||
pci_err(dev, "failed to create OF node: %s\n", child->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_bus_add_device - start driver for a single device
|
||||
* @dev: device to add
|
||||
|
|
@ -396,8 +355,6 @@ void pci_bus_add_device(struct pci_dev *dev)
|
|||
pci_proc_attach_device(dev);
|
||||
pci_bridge_d3_update(dev);
|
||||
|
||||
pci_pwrctrl_create_devices(dev);
|
||||
|
||||
/*
|
||||
* If the PCI device is associated with a pwrctrl device with a
|
||||
* power supply, create a device link between the PCI device and
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -2503,6 +2505,36 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
|
|||
}
|
||||
EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
|
||||
|
||||
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 || of_find_device_by_node(np))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the config data for a PCI device, sanity-check it,
|
||||
* and fill in the dev structure.
|
||||
|
|
@ -2512,6 +2544,15 @@ 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,14 @@ config PCI_PWRCTL_PWRSEQ
|
|||
tristate
|
||||
select POWER_SEQUENCING
|
||||
select PCI_PWRCTL
|
||||
|
||||
config PCI_PWRCTL_SLOT
|
||||
tristate "PCI Power Control driver for PCI slots"
|
||||
select PCI_PWRCTL
|
||||
help
|
||||
Say Y here to enable the PCI Power Control driver to control the power
|
||||
state of PCI slots.
|
||||
|
||||
This is a generic driver that controls the power state of different
|
||||
PCI slots. The voltage regulators powering the rails of the PCI slots
|
||||
are expected to be defined in the devicetree node of the PCI bridge.
|
||||
|
|
|
|||
|
|
@ -4,3 +4,6 @@ obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctrl-core.o
|
|||
pci-pwrctrl-core-y := core.o
|
||||
|
||||
obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctrl-pwrseq.o
|
||||
|
||||
obj-$(CONFIG_PCI_PWRCTL_SLOT) += pci-pwrctl-slot.o
|
||||
pci-pwrctl-slot-y := slot.o
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ static void rescan_work_func(struct work_struct *work)
|
|||
struct pci_pwrctrl, work);
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
pci_rescan_bus(to_pci_dev(pwrctrl->dev->parent)->bus);
|
||||
pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus);
|
||||
pci_unlock_rescan_remove();
|
||||
}
|
||||
|
||||
|
|
|
|||
93
drivers/pci/pwrctrl/slot.c
Normal file
93
drivers/pci/pwrctrl/slot.c
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2024 Linaro Ltd.
|
||||
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci-pwrctrl.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct pci_pwrctrl_slot_data {
|
||||
struct pci_pwrctrl ctx;
|
||||
struct regulator_bulk_data *supplies;
|
||||
int num_supplies;
|
||||
};
|
||||
|
||||
static void devm_pci_pwrctrl_slot_power_off(void *data)
|
||||
{
|
||||
struct pci_pwrctrl_slot_data *slot = data;
|
||||
|
||||
regulator_bulk_disable(slot->num_supplies, slot->supplies);
|
||||
regulator_bulk_free(slot->num_supplies, slot->supplies);
|
||||
}
|
||||
|
||||
static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pci_pwrctrl_slot_data *slot;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
|
||||
if (!slot)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_regulator_bulk_get_all(dev, dev_of_node(dev),
|
||||
&slot->supplies);
|
||||
if (ret < 0) {
|
||||
dev_err_probe(dev, ret, "Failed to get slot regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
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");
|
||||
goto err_regulator_free;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off,
|
||||
slot);
|
||||
if (ret)
|
||||
goto err_regulator_disable;
|
||||
|
||||
pci_pwrctrl_init(&slot->ctx, dev);
|
||||
|
||||
ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator_disable:
|
||||
regulator_bulk_disable(slot->num_supplies, slot->supplies);
|
||||
err_regulator_free:
|
||||
regulator_bulk_free(slot->num_supplies, slot->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id pci_pwrctrl_slot_of_match[] = {
|
||||
{
|
||||
.compatible = "pciclass,0604",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pci_pwrctrl_slot_of_match);
|
||||
|
||||
static struct platform_driver pci_pwrctrl_slot_driver = {
|
||||
.driver = {
|
||||
.name = "pci-pwrctrl-slot",
|
||||
.of_match_table = pci_pwrctrl_slot_of_match,
|
||||
},
|
||||
.probe = pci_pwrctrl_slot_probe,
|
||||
};
|
||||
module_platform_driver(pci_pwrctrl_slot_driver);
|
||||
|
||||
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
|
||||
MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -41,7 +41,6 @@ static void pci_stop_dev(struct pci_dev *dev)
|
|||
if (!pci_dev_test_and_clear_added(dev))
|
||||
return;
|
||||
|
||||
pci_pwrctrl_unregister(&dev->dev);
|
||||
device_release_driver(&dev->dev);
|
||||
pci_proc_detach_device(dev);
|
||||
pci_remove_sysfs_dev_files(dev);
|
||||
|
|
@ -65,6 +64,7 @@ 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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user