mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
Merge branch 'pci/pwrctl'
- Use of_platform_device_create() instead of of_platform_populate() to create pwrctl platform devices so we can control it based on the child nodes (Manivannan Sadhasivam) - Create pwrctrl platform devices only if there's a relevant power supply property (Manivannan Sadhasivam) - Add device link from the pwrctl supplier to the PCI dev to ensure pwrctl drivers are probed before the PCI dev driver; this avoids a race where pwrctl could change device power state while the PCI driver was active (Manivannan Sadhasivam) - Find pwrctl device for removal with of_find_device_by_node() instead of searching all children of the parent (Manivannan Sadhasivam) - Rename 'pwrctl' to 'pwrctrl' to use the same 'ctrl' suffix as 'bwctrl' and other PCI files to reduce confusion (Bjorn Helgaas) * pci/pwrctl: PCI/pwrctrl: Rename pwrctrl functions and structures PCI/pwrctrl: Rename pwrctl files to pwrctrl PCI/pwrctl: Remove pwrctl device without iterating over all children of pwrctl parent PCI/pwrctl: Ensure that pwrctl drivers are probed before PCI client drivers PCI/pwrctl: Create pwrctl device only if at least one power supply is present PCI/pwrctl: Use of_platform_device_create() to create pwrctl devices # Conflicts: # drivers/pci/bus.c # drivers/pci/remove.c
This commit is contained in:
commit
ce1deca962
@ -17916,8 +17916,8 @@ M: Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
|
||||
F: drivers/pci/pwrctl/*
|
||||
F: include/linux/pci-pwrctl.h
|
||||
F: drivers/pci/pwrctrl/*
|
||||
F: include/linux/pci-pwrctrl.h
|
||||
|
||||
PCI SUBSYSTEM
|
||||
M: Bjorn Helgaas <bhelgaas@google.com>
|
||||
|
@ -305,6 +305,6 @@ source "drivers/pci/hotplug/Kconfig"
|
||||
source "drivers/pci/controller/Kconfig"
|
||||
source "drivers/pci/endpoint/Kconfig"
|
||||
source "drivers/pci/switch/Kconfig"
|
||||
source "drivers/pci/pwrctl/Kconfig"
|
||||
source "drivers/pci/pwrctrl/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -9,7 +9,7 @@ obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
|
||||
|
||||
obj-$(CONFIG_PCI) += msi/
|
||||
obj-$(CONFIG_PCI) += pcie/
|
||||
obj-$(CONFIG_PCI) += pwrctl/
|
||||
obj-$(CONFIG_PCI) += pwrctrl/
|
||||
|
||||
ifdef CONFIG_PCI
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -330,6 +331,47 @@ 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
|
||||
@ -339,6 +381,7 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
|
||||
void pci_bus_add_device(struct pci_dev *dev)
|
||||
{
|
||||
struct device_node *dn = dev->dev.of_node;
|
||||
struct platform_device *pdev;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
@ -353,20 +396,28 @@ 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
|
||||
* pwrctrl device. This ensures that pwrctrl drivers are probed
|
||||
* before PCI client drivers.
|
||||
*/
|
||||
pdev = of_find_device_by_node(dn);
|
||||
if (pdev && of_pci_supply_present(dn)) {
|
||||
if (!device_link_add(&dev->dev, &pdev->dev,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER))
|
||||
pci_err(dev, "failed to add device link to power control device %s\n",
|
||||
pdev->name);
|
||||
}
|
||||
|
||||
dev->match_driver = !dn || of_device_is_available(dn);
|
||||
retval = device_attach(&dev->dev);
|
||||
if (retval < 0 && retval != -EPROBE_DEFER)
|
||||
pci_warn(dev, "device attach failed (%d)\n", retval);
|
||||
|
||||
pci_dev_assign_added(dev);
|
||||
|
||||
if (dev_of_node(&dev->dev) && pci_is_bridge(dev)) {
|
||||
retval = of_platform_populate(dev_of_node(&dev->dev), NULL, NULL,
|
||||
&dev->dev);
|
||||
if (retval)
|
||||
pci_err(dev, "failed to populate child OF nodes (%d)\n",
|
||||
retval);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_bus_add_device);
|
||||
|
||||
|
@ -728,6 +728,33 @@ void of_pci_make_dev_node(struct pci_dev *pdev)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* of_pci_supply_present() - Check if the power supply is present for the PCI
|
||||
* device
|
||||
* @np: Device tree node
|
||||
*
|
||||
* Check if the power supply for the PCI device is present in the device tree
|
||||
* node or not.
|
||||
*
|
||||
* Return: true if at least one power supply exists; false otherwise.
|
||||
*/
|
||||
bool of_pci_supply_present(struct device_node *np)
|
||||
{
|
||||
struct property *prop;
|
||||
char *supply;
|
||||
|
||||
if (!np)
|
||||
return false;
|
||||
|
||||
for_each_property_of_node(np, prop) {
|
||||
supply = strrchr(prop->name, '-');
|
||||
if (supply && !strcmp(supply, "-supply"))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/**
|
||||
|
@ -797,6 +797,7 @@ void pci_set_bus_of_node(struct pci_bus *bus);
|
||||
void pci_release_bus_of_node(struct pci_bus *bus);
|
||||
|
||||
int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge);
|
||||
bool of_pci_supply_present(struct device_node *np);
|
||||
|
||||
#else
|
||||
static inline int
|
||||
@ -844,6 +845,10 @@ static inline int devm_of_pci_bridge_init(struct device *dev, struct pci_host_br
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool of_pci_supply_present(struct device_node *np)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
struct of_changeset;
|
||||
|
@ -1,6 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctl-core.o
|
||||
pci-pwrctl-core-y := core.o
|
||||
|
||||
obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctl-pwrseq.o
|
@ -1,157 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2024 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-pwrctl.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static int pci_pwrctl_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct pci_pwrctl *pwrctl = container_of(nb, struct pci_pwrctl, nb);
|
||||
struct device *dev = data;
|
||||
|
||||
if (dev_fwnode(dev) != dev_fwnode(pwrctl->dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
/*
|
||||
* We will have two struct device objects bound to two different
|
||||
* drivers on different buses but consuming the same DT node. We
|
||||
* must not bind the pins twice in this case but only once for
|
||||
* the first device to be added.
|
||||
*
|
||||
* If we got here then the PCI device is the second after the
|
||||
* power control platform device. Mark its OF node as reused.
|
||||
*/
|
||||
dev->of_node_reused = true;
|
||||
break;
|
||||
case BUS_NOTIFY_BOUND_DRIVER:
|
||||
pwrctl->link = device_link_add(dev, pwrctl->dev,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||
if (!pwrctl->link)
|
||||
dev_err(pwrctl->dev, "Failed to add device link\n");
|
||||
break;
|
||||
case BUS_NOTIFY_UNBOUND_DRIVER:
|
||||
if (pwrctl->link)
|
||||
device_link_remove(dev, pwrctl->dev);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void rescan_work_func(struct work_struct *work)
|
||||
{
|
||||
struct pci_pwrctl *pwrctl = container_of(work, struct pci_pwrctl, work);
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
pci_rescan_bus(to_pci_dev(pwrctl->dev->parent)->bus);
|
||||
pci_unlock_rescan_remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_pwrctl_init() - Initialize the PCI power control context struct
|
||||
*
|
||||
* @pwrctl: PCI power control data
|
||||
* @dev: Parent device
|
||||
*/
|
||||
void pci_pwrctl_init(struct pci_pwrctl *pwrctl, struct device *dev)
|
||||
{
|
||||
pwrctl->dev = dev;
|
||||
INIT_WORK(&pwrctl->work, rescan_work_func);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pwrctl_init);
|
||||
|
||||
/**
|
||||
* pci_pwrctl_device_set_ready() - Notify the pwrctl subsystem that the PCI
|
||||
* device is powered-up and ready to be detected.
|
||||
*
|
||||
* @pwrctl: PCI power control data.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error number on error.
|
||||
*
|
||||
* Note:
|
||||
* This function returning 0 doesn't mean the device was detected. It means,
|
||||
* that the bus rescan was successfully started. The device will get bound to
|
||||
* its PCI driver asynchronously.
|
||||
*/
|
||||
int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!pwrctl->dev)
|
||||
return -ENODEV;
|
||||
|
||||
pwrctl->nb.notifier_call = pci_pwrctl_notify;
|
||||
ret = bus_register_notifier(&pci_bus_type, &pwrctl->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
schedule_work(&pwrctl->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pwrctl_device_set_ready);
|
||||
|
||||
/**
|
||||
* pci_pwrctl_device_unset_ready() - Notify the pwrctl subsystem that the PCI
|
||||
* device is about to be powered-down.
|
||||
*
|
||||
* @pwrctl: PCI power control data.
|
||||
*/
|
||||
void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl)
|
||||
{
|
||||
/*
|
||||
* We don't have to delete the link here. Typically, this function
|
||||
* is only called when the power control device is being detached. If
|
||||
* it is being detached then the child PCI device must have already
|
||||
* been unbound too or the device core wouldn't let us unbind.
|
||||
*/
|
||||
bus_unregister_notifier(&pci_bus_type, &pwrctl->nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pwrctl_device_unset_ready);
|
||||
|
||||
static void devm_pci_pwrctl_device_unset_ready(void *data)
|
||||
{
|
||||
struct pci_pwrctl *pwrctl = data;
|
||||
|
||||
pci_pwrctl_device_unset_ready(pwrctl);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_pci_pwrctl_device_set_ready - Managed variant of
|
||||
* pci_pwrctl_device_set_ready().
|
||||
*
|
||||
* @dev: Device managing this pwrctl provider.
|
||||
* @pwrctl: PCI power control data.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error number on error.
|
||||
*/
|
||||
int devm_pci_pwrctl_device_set_ready(struct device *dev,
|
||||
struct pci_pwrctl *pwrctl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_pwrctl_device_set_ready(pwrctl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev,
|
||||
devm_pci_pwrctl_device_unset_ready,
|
||||
pwrctl);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_pci_pwrctl_device_set_ready);
|
||||
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
|
||||
MODULE_DESCRIPTION("PCI Device Power Control core driver");
|
||||
MODULE_LICENSE("GPL");
|
6
drivers/pci/pwrctrl/Makefile
Normal file
6
drivers/pci/pwrctrl/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctrl-core.o
|
||||
pci-pwrctrl-core-y := core.o
|
||||
|
||||
obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctrl-pwrseq.o
|
148
drivers/pci/pwrctrl/core.c
Normal file
148
drivers/pci/pwrctrl/core.c
Normal file
@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2024 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-pwrctrl.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct pci_pwrctrl *pwrctrl = container_of(nb, struct pci_pwrctrl, nb);
|
||||
struct device *dev = data;
|
||||
|
||||
if (dev_fwnode(dev) != dev_fwnode(pwrctrl->dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
/*
|
||||
* We will have two struct device objects bound to two different
|
||||
* drivers on different buses but consuming the same DT node. We
|
||||
* must not bind the pins twice in this case but only once for
|
||||
* the first device to be added.
|
||||
*
|
||||
* If we got here then the PCI device is the second after the
|
||||
* power control platform device. Mark its OF node as reused.
|
||||
*/
|
||||
dev->of_node_reused = true;
|
||||
break;
|
||||
}
|
||||
|
||||
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_dev(pwrctrl->dev->parent)->bus);
|
||||
pci_unlock_rescan_remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_pwrctrl_init() - Initialize the PCI power control context struct
|
||||
*
|
||||
* @pwrctrl: PCI power control data
|
||||
* @dev: Parent device
|
||||
*/
|
||||
void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
|
||||
{
|
||||
pwrctrl->dev = dev;
|
||||
INIT_WORK(&pwrctrl->work, rescan_work_func);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
|
||||
|
||||
/**
|
||||
* pci_pwrctrl_device_set_ready() - Notify the pwrctrl subsystem that the PCI
|
||||
* device is powered-up and ready to be detected.
|
||||
*
|
||||
* @pwrctrl: PCI power control data.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error number on error.
|
||||
*
|
||||
* Note:
|
||||
* This function returning 0 doesn't mean the device was detected. It means,
|
||||
* that the bus rescan was successfully started. The device will get bound to
|
||||
* its PCI driver asynchronously.
|
||||
*/
|
||||
int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!pwrctrl->dev)
|
||||
return -ENODEV;
|
||||
|
||||
pwrctrl->nb.notifier_call = pci_pwrctrl_notify;
|
||||
ret = bus_register_notifier(&pci_bus_type, &pwrctrl->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
schedule_work(&pwrctrl->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
|
||||
|
||||
/**
|
||||
* pci_pwrctrl_device_unset_ready() - Notify the pwrctrl subsystem that the PCI
|
||||
* device is about to be powered-down.
|
||||
*
|
||||
* @pwrctrl: PCI power control data.
|
||||
*/
|
||||
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl)
|
||||
{
|
||||
/*
|
||||
* We don't have to delete the link here. Typically, this function
|
||||
* is only called when the power control device is being detached. If
|
||||
* it is being detached then the child PCI device must have already
|
||||
* been unbound too or the device core wouldn't let us unbind.
|
||||
*/
|
||||
bus_unregister_notifier(&pci_bus_type, &pwrctrl->nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pwrctrl_device_unset_ready);
|
||||
|
||||
static void devm_pci_pwrctrl_device_unset_ready(void *data)
|
||||
{
|
||||
struct pci_pwrctrl *pwrctrl = data;
|
||||
|
||||
pci_pwrctrl_device_unset_ready(pwrctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_pci_pwrctrl_device_set_ready - Managed variant of
|
||||
* pci_pwrctrl_device_set_ready().
|
||||
*
|
||||
* @dev: Device managing this pwrctrl provider.
|
||||
* @pwrctrl: PCI power control data.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error number on error.
|
||||
*/
|
||||
int devm_pci_pwrctrl_device_set_ready(struct device *dev,
|
||||
struct pci_pwrctrl *pwrctrl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_pwrctrl_device_set_ready(pwrctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev,
|
||||
devm_pci_pwrctrl_device_unset_ready,
|
||||
pwrctrl);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
|
||||
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
|
||||
MODULE_DESCRIPTION("PCI Device Power Control core driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -7,27 +7,27 @@
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci-pwrctl.h>
|
||||
#include <linux/pci-pwrctrl.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwrseq/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct pci_pwrctl_pwrseq_data {
|
||||
struct pci_pwrctl ctx;
|
||||
struct pci_pwrctrl_pwrseq_data {
|
||||
struct pci_pwrctrl ctx;
|
||||
struct pwrseq_desc *pwrseq;
|
||||
};
|
||||
|
||||
static void devm_pci_pwrctl_pwrseq_power_off(void *data)
|
||||
static void devm_pci_pwrctrl_pwrseq_power_off(void *data)
|
||||
{
|
||||
struct pwrseq_desc *pwrseq = data;
|
||||
|
||||
pwrseq_power_off(pwrseq);
|
||||
}
|
||||
|
||||
static int pci_pwrctl_pwrseq_probe(struct platform_device *pdev)
|
||||
static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pci_pwrctl_pwrseq_data *data;
|
||||
struct pci_pwrctrl_pwrseq_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
@ -45,22 +45,22 @@ static int pci_pwrctl_pwrseq_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to power-on the device\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, devm_pci_pwrctl_pwrseq_power_off,
|
||||
ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off,
|
||||
data->pwrseq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_pwrctl_init(&data->ctx, dev);
|
||||
pci_pwrctrl_init(&data->ctx, dev);
|
||||
|
||||
ret = devm_pci_pwrctl_device_set_ready(dev, &data->ctx);
|
||||
ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to register the pwrctl wrapper\n");
|
||||
"Failed to register the pwrctrl wrapper\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pci_pwrctl_pwrseq_of_match[] = {
|
||||
static const struct of_device_id pci_pwrctrl_pwrseq_of_match[] = {
|
||||
{
|
||||
/* ATH11K in QCA6390 package. */
|
||||
.compatible = "pci17cb,1101",
|
||||
@ -78,16 +78,16 @@ static const struct of_device_id pci_pwrctl_pwrseq_of_match[] = {
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pci_pwrctl_pwrseq_of_match);
|
||||
MODULE_DEVICE_TABLE(of, pci_pwrctrl_pwrseq_of_match);
|
||||
|
||||
static struct platform_driver pci_pwrctl_pwrseq_driver = {
|
||||
static struct platform_driver pci_pwrctrl_pwrseq_driver = {
|
||||
.driver = {
|
||||
.name = "pci-pwrctl-pwrseq",
|
||||
.of_match_table = pci_pwrctl_pwrseq_of_match,
|
||||
.name = "pci-pwrctrl-pwrseq",
|
||||
.of_match_table = pci_pwrctrl_pwrseq_of_match,
|
||||
},
|
||||
.probe = pci_pwrctl_pwrseq_probe,
|
||||
.probe = pci_pwrctrl_pwrseq_probe,
|
||||
};
|
||||
module_platform_driver(pci_pwrctl_pwrseq_driver);
|
||||
module_platform_driver(pci_pwrctrl_pwrseq_driver);
|
||||
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
|
||||
MODULE_DESCRIPTION("Generic PCI Power Control module for power sequenced devices");
|
@ -17,16 +17,16 @@ static void pci_free_resources(struct pci_dev *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_pwrctl_unregister(struct device *dev, void *data)
|
||||
static void pci_pwrctrl_unregister(struct device *dev)
|
||||
{
|
||||
struct device_node *pci_node = data, *plat_node = dev_of_node(dev);
|
||||
struct platform_device *pdev;
|
||||
|
||||
if (dev_is_platform(dev) && plat_node && plat_node == pci_node) {
|
||||
of_device_unregister(to_platform_device(dev));
|
||||
of_node_clear_flag(plat_node, OF_POPULATED);
|
||||
}
|
||||
pdev = of_find_device_by_node(dev_of_node(dev));
|
||||
if (!pdev)
|
||||
return;
|
||||
|
||||
return 0;
|
||||
of_device_unregister(pdev);
|
||||
of_node_clear_flag(dev_of_node(dev), OF_POPULATED);
|
||||
}
|
||||
|
||||
static void pci_stop_dev(struct pci_dev *dev)
|
||||
@ -36,8 +36,7 @@ static void pci_stop_dev(struct pci_dev *dev)
|
||||
if (!pci_dev_test_and_clear_added(dev))
|
||||
return;
|
||||
|
||||
device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev),
|
||||
pci_pwrctl_unregister);
|
||||
pci_pwrctrl_unregister(&dev->dev);
|
||||
device_release_driver(&dev->dev);
|
||||
pci_proc_detach_device(dev);
|
||||
pci_remove_sysfs_dev_files(dev);
|
||||
|
@ -3,8 +3,8 @@
|
||||
* Copyright (C) 2024 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __PCI_PWRCTL_H__
|
||||
#define __PCI_PWRCTL_H__
|
||||
#ifndef __PCI_PWRCTRL_H__
|
||||
#define __PCI_PWRCTRL_H__
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/workqueue.h>
|
||||
@ -29,14 +29,14 @@ struct device_link;
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct pci_pwrctl - PCI device power control context.
|
||||
* struct pci_pwrctrl - PCI device power control context.
|
||||
* @dev: Address of the power controlling device.
|
||||
*
|
||||
* An object of this type must be allocated by the PCI power control device and
|
||||
* passed to the pwrctl subsystem to trigger a bus rescan and setup a device
|
||||
* passed to the pwrctrl subsystem to trigger a bus rescan and setup a device
|
||||
* link with the device once it's up.
|
||||
*/
|
||||
struct pci_pwrctl {
|
||||
struct pci_pwrctrl {
|
||||
struct device *dev;
|
||||
|
||||
/* Private: don't use. */
|
||||
@ -45,10 +45,10 @@ struct pci_pwrctl {
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
void pci_pwrctl_init(struct pci_pwrctl *pwrctl, struct device *dev);
|
||||
int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl);
|
||||
void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl);
|
||||
int devm_pci_pwrctl_device_set_ready(struct device *dev,
|
||||
struct pci_pwrctl *pwrctl);
|
||||
void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev);
|
||||
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);
|
||||
|
||||
#endif /* __PCI_PWRCTL_H__ */
|
||||
#endif /* __PCI_PWRCTRL_H__ */
|
Loading…
Reference in New Issue
Block a user