brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · e4ce114 Raw
194 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/pci.h>3#include <linux/module.h>4#include <linux/of.h>5#include <linux/of_platform.h>6#include <linux/platform_device.h>7 8#include "pci.h"9 10static void pci_free_resources(struct pci_dev *dev)11{12	struct resource *res;13 14	pci_dev_for_each_resource(dev, res) {15		if (res->parent)16			release_resource(res);17	}18}19 20static int pci_pwrctl_unregister(struct device *dev, void *data)21{22	struct device_node *pci_node = data, *plat_node = dev_of_node(dev);23 24	if (dev_is_platform(dev) && plat_node && plat_node == pci_node) {25		of_device_unregister(to_platform_device(dev));26		of_node_clear_flag(plat_node, OF_POPULATED);27	}28 29	return 0;30}31 32static void pci_stop_dev(struct pci_dev *dev)33{34	pci_pme_active(dev, false);35 36	if (pci_dev_is_added(dev)) {37		device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev),38				      pci_pwrctl_unregister);39		device_release_driver(&dev->dev);40		pci_proc_detach_device(dev);41		pci_remove_sysfs_dev_files(dev);42		of_pci_remove_node(dev);43 44		pci_dev_assign_added(dev, false);45	}46}47 48static void pci_destroy_dev(struct pci_dev *dev)49{50	if (!dev->dev.kobj.parent)51		return;52 53	pci_npem_remove(dev);54 55	device_del(&dev->dev);56 57	down_write(&pci_bus_sem);58	list_del(&dev->bus_list);59	up_write(&pci_bus_sem);60 61	pci_doe_destroy(dev);62	pcie_aspm_exit_link_state(dev);63	pci_bridge_d3_update(dev);64	pci_free_resources(dev);65	put_device(&dev->dev);66}67 68void pci_remove_bus(struct pci_bus *bus)69{70	pci_proc_detach_bus(bus);71 72	down_write(&pci_bus_sem);73	list_del(&bus->node);74	pci_bus_release_busn_res(bus);75	up_write(&pci_bus_sem);76	pci_remove_legacy_files(bus);77 78	if (bus->ops->remove_bus)79		bus->ops->remove_bus(bus);80 81	pcibios_remove_bus(bus);82	device_unregister(&bus->dev);83}84EXPORT_SYMBOL(pci_remove_bus);85 86static void pci_stop_bus_device(struct pci_dev *dev)87{88	struct pci_bus *bus = dev->subordinate;89	struct pci_dev *child, *tmp;90 91	/*92	 * Stopping an SR-IOV PF device removes all the associated VFs,93	 * which will update the bus->devices list and confuse the94	 * iterator.  Therefore, iterate in reverse so we remove the VFs95	 * first, then the PF.96	 */97	if (bus) {98		list_for_each_entry_safe_reverse(child, tmp,99						 &bus->devices, bus_list)100			pci_stop_bus_device(child);101	}102 103	pci_stop_dev(dev);104}105 106static void pci_remove_bus_device(struct pci_dev *dev)107{108	struct pci_bus *bus = dev->subordinate;109	struct pci_dev *child, *tmp;110 111	if (bus) {112		list_for_each_entry_safe(child, tmp,113					 &bus->devices, bus_list)114			pci_remove_bus_device(child);115 116		pci_remove_bus(bus);117		dev->subordinate = NULL;118	}119 120	pci_destroy_dev(dev);121}122 123/**124 * pci_stop_and_remove_bus_device - remove a PCI device and any children125 * @dev: the device to remove126 *127 * Remove a PCI device from the device lists, informing the drivers128 * that the device has been removed.  We also remove any subordinate129 * buses and children in a depth-first manner.130 *131 * For each device we remove, delete the device structure from the132 * device lists, remove the /proc entry, and notify userspace133 * (/sbin/hotplug).134 */135void pci_stop_and_remove_bus_device(struct pci_dev *dev)136{137	pci_stop_bus_device(dev);138	pci_remove_bus_device(dev);139}140EXPORT_SYMBOL(pci_stop_and_remove_bus_device);141 142void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)143{144	pci_lock_rescan_remove();145	pci_stop_and_remove_bus_device(dev);146	pci_unlock_rescan_remove();147}148EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);149 150void pci_stop_root_bus(struct pci_bus *bus)151{152	struct pci_dev *child, *tmp;153	struct pci_host_bridge *host_bridge;154 155	if (!pci_is_root_bus(bus))156		return;157 158	host_bridge = to_pci_host_bridge(bus->bridge);159	list_for_each_entry_safe_reverse(child, tmp,160					 &bus->devices, bus_list)161		pci_stop_bus_device(child);162 163	/* stop the host bridge */164	device_release_driver(&host_bridge->dev);165}166EXPORT_SYMBOL_GPL(pci_stop_root_bus);167 168void pci_remove_root_bus(struct pci_bus *bus)169{170	struct pci_dev *child, *tmp;171	struct pci_host_bridge *host_bridge;172 173	if (!pci_is_root_bus(bus))174		return;175 176	host_bridge = to_pci_host_bridge(bus->bridge);177	list_for_each_entry_safe(child, tmp,178				 &bus->devices, bus_list)179		pci_remove_bus_device(child);180 181#ifdef CONFIG_PCI_DOMAINS_GENERIC182	/* Release domain_nr if it was dynamically allocated */183	if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)184		pci_bus_release_domain_nr(host_bridge->dev.parent, bus->domain_nr);185#endif186 187	pci_remove_bus(bus);188	host_bridge->bus = NULL;189 190	/* remove the host bridge */191	device_del(&host_bridge->dev);192}193EXPORT_SYMBOL_GPL(pci_remove_root_bus);194