xref: /openbmc/linux/drivers/pci/hotplug/pciehp_pci.c (revision ccb01374)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * PCI Express Hot Plug Controller Driver
4  *
5  * Copyright (C) 1995,2001 Compaq Computer Corporation
6  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
7  * Copyright (C) 2001 IBM Corp.
8  * Copyright (C) 2003-2004 Intel Corporation
9  *
10  * All rights reserved.
11  *
12  * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
13  *
14  */
15 
16 #include <linux/kernel.h>
17 #include <linux/types.h>
18 #include <linux/pci.h>
19 #include "../pci.h"
20 #include "pciehp.h"
21 
22 /**
23  * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge
24  * @ctrl: PCIe hotplug controller
25  *
26  * Enumerate PCI devices below a hotplug bridge and add them to the system.
27  * Return 0 on success, %-EEXIST if the devices are already enumerated or
28  * %-ENODEV if enumeration failed.
29  */
30 int pciehp_configure_device(struct controller *ctrl)
31 {
32 	struct pci_dev *dev;
33 	struct pci_dev *bridge = ctrl->pcie->port;
34 	struct pci_bus *parent = bridge->subordinate;
35 	int num, ret = 0;
36 
37 	pci_lock_rescan_remove();
38 
39 	dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
40 	if (dev) {
41 		/*
42 		 * The device is already there. Either configured by the
43 		 * boot firmware or a previous hotplug event.
44 		 */
45 		ctrl_dbg(ctrl, "Device %s already exists at %04x:%02x:00, skipping hot-add\n",
46 			 pci_name(dev), pci_domain_nr(parent), parent->number);
47 		pci_dev_put(dev);
48 		ret = -EEXIST;
49 		goto out;
50 	}
51 
52 	num = pci_scan_slot(parent, PCI_DEVFN(0, 0));
53 	if (num == 0) {
54 		ctrl_err(ctrl, "No new device found\n");
55 		ret = -ENODEV;
56 		goto out;
57 	}
58 
59 	for_each_pci_bridge(dev, parent)
60 		pci_hp_add_bridge(dev);
61 
62 	pci_assign_unassigned_bridge_resources(bridge);
63 	pcie_bus_configure_settings(parent);
64 	pci_bus_add_devices(parent);
65 
66  out:
67 	pci_unlock_rescan_remove();
68 	return ret;
69 }
70 
71 /**
72  * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge
73  * @ctrl: PCIe hotplug controller
74  * @presence: whether the card is still present in the slot;
75  *	true for safe removal via sysfs or an Attention Button press,
76  *	false for surprise removal
77  *
78  * Unbind PCI devices below a hotplug bridge from their drivers and remove
79  * them from the system.  Safely removed devices are quiesced.  Surprise
80  * removed devices are marked as such to prevent further accesses.
81  */
82 void pciehp_unconfigure_device(struct controller *ctrl, bool presence)
83 {
84 	struct pci_dev *dev, *temp;
85 	struct pci_bus *parent = ctrl->pcie->port->subordinate;
86 	u16 command;
87 
88 	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
89 		 __func__, pci_domain_nr(parent), parent->number);
90 
91 	if (!presence)
92 		pci_walk_bus(parent, pci_dev_set_disconnected, NULL);
93 
94 	pci_lock_rescan_remove();
95 
96 	/*
97 	 * Stopping an SR-IOV PF device removes all the associated VFs,
98 	 * which will update the bus->devices list and confuse the
99 	 * iterator.  Therefore, iterate in reverse so we remove the VFs
100 	 * first, then the PF.  We do the same in pci_stop_bus_device().
101 	 */
102 	list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
103 					 bus_list) {
104 		pci_dev_get(dev);
105 		pci_stop_and_remove_bus_device(dev);
106 		/*
107 		 * Ensure that no new Requests will be generated from
108 		 * the device.
109 		 */
110 		if (presence) {
111 			pci_read_config_word(dev, PCI_COMMAND, &command);
112 			command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
113 			command |= PCI_COMMAND_INTX_DISABLE;
114 			pci_write_config_word(dev, PCI_COMMAND, command);
115 		}
116 		pci_dev_put(dev);
117 	}
118 
119 	pci_unlock_rescan_remove();
120 }
121