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 | #define dev_fmt(fmt) "pciehp: " fmt |
17 | |
18 | #include <linux/kernel.h> |
19 | #include <linux/types.h> |
20 | #include <linux/pci.h> |
21 | #include "../pci.h" |
22 | #include "pciehp.h" |
23 | |
24 | /** |
25 | * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge |
26 | * @ctrl: PCIe hotplug controller |
27 | * |
28 | * Enumerate PCI devices below a hotplug bridge and add them to the system. |
29 | * Return 0 on success, %-EEXIST if the devices are already enumerated or |
30 | * %-ENODEV if enumeration failed. |
31 | */ |
32 | int pciehp_configure_device(struct controller *ctrl) |
33 | { |
34 | struct pci_dev *dev; |
35 | struct pci_dev *bridge = ctrl->pcie->port; |
36 | struct pci_bus *parent = bridge->subordinate; |
37 | int num, ret = 0; |
38 | |
39 | pci_lock_rescan_remove(); |
40 | |
41 | dev = pci_get_slot(bus: parent, PCI_DEVFN(0, 0)); |
42 | if (dev) { |
43 | /* |
44 | * The device is already there. Either configured by the |
45 | * boot firmware or a previous hotplug event. |
46 | */ |
47 | ctrl_dbg(ctrl, "Device %s already exists at %04x:%02x:00, skipping hot-add\n" , |
48 | pci_name(dev), pci_domain_nr(parent), parent->number); |
49 | pci_dev_put(dev); |
50 | ret = -EEXIST; |
51 | goto out; |
52 | } |
53 | |
54 | num = pci_scan_slot(bus: parent, PCI_DEVFN(0, 0)); |
55 | if (num == 0) { |
56 | ctrl_err(ctrl, "No new device found\n" ); |
57 | ret = -ENODEV; |
58 | goto out; |
59 | } |
60 | |
61 | for_each_pci_bridge(dev, parent) |
62 | pci_hp_add_bridge(dev); |
63 | |
64 | pci_assign_unassigned_bridge_resources(bridge); |
65 | pcie_bus_configure_settings(bus: parent); |
66 | |
67 | /* |
68 | * Release reset_lock during driver binding |
69 | * to avoid AB-BA deadlock with device_lock. |
70 | */ |
71 | up_read(sem: &ctrl->reset_lock); |
72 | pci_bus_add_devices(bus: parent); |
73 | down_read_nested(sem: &ctrl->reset_lock, subclass: ctrl->depth); |
74 | |
75 | out: |
76 | pci_unlock_rescan_remove(); |
77 | return ret; |
78 | } |
79 | |
80 | /** |
81 | * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge |
82 | * @ctrl: PCIe hotplug controller |
83 | * @presence: whether the card is still present in the slot; |
84 | * true for safe removal via sysfs or an Attention Button press, |
85 | * false for surprise removal |
86 | * |
87 | * Unbind PCI devices below a hotplug bridge from their drivers and remove |
88 | * them from the system. Safely removed devices are quiesced. Surprise |
89 | * removed devices are marked as such to prevent further accesses. |
90 | */ |
91 | void pciehp_unconfigure_device(struct controller *ctrl, bool presence) |
92 | { |
93 | struct pci_dev *dev, *temp; |
94 | struct pci_bus *parent = ctrl->pcie->port->subordinate; |
95 | u16 command; |
96 | |
97 | ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n" , |
98 | __func__, pci_domain_nr(parent), parent->number); |
99 | |
100 | if (!presence) |
101 | pci_walk_bus(top: parent, cb: pci_dev_set_disconnected, NULL); |
102 | |
103 | pci_lock_rescan_remove(); |
104 | |
105 | /* |
106 | * Stopping an SR-IOV PF device removes all the associated VFs, |
107 | * which will update the bus->devices list and confuse the |
108 | * iterator. Therefore, iterate in reverse so we remove the VFs |
109 | * first, then the PF. We do the same in pci_stop_bus_device(). |
110 | */ |
111 | list_for_each_entry_safe_reverse(dev, temp, &parent->devices, |
112 | bus_list) { |
113 | pci_dev_get(dev); |
114 | |
115 | /* |
116 | * Release reset_lock during driver unbinding |
117 | * to avoid AB-BA deadlock with device_lock. |
118 | */ |
119 | up_read(sem: &ctrl->reset_lock); |
120 | pci_stop_and_remove_bus_device(dev); |
121 | down_read_nested(sem: &ctrl->reset_lock, subclass: ctrl->depth); |
122 | |
123 | /* |
124 | * Ensure that no new Requests will be generated from |
125 | * the device. |
126 | */ |
127 | if (presence) { |
128 | pci_read_config_word(dev, PCI_COMMAND, val: &command); |
129 | command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); |
130 | command |= PCI_COMMAND_INTX_DISABLE; |
131 | pci_write_config_word(dev, PCI_COMMAND, val: command); |
132 | } |
133 | pci_dev_put(dev); |
134 | } |
135 | |
136 | pci_unlock_rescan_remove(); |
137 | } |
138 | |