Commit ce1deca9 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
parents 95e93032 3f925cd6
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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>
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+59 −8
Original line number Diff line number Diff line
@@ -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);

+27 −0
Original line number Diff line number Diff line
@@ -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 */

/**
Loading