Commit | Line | Data |
---|---|---|
736759ef | 1 | // SPDX-License-Identifier: GPL-2.0+ |
1da177e4 LT |
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 | * | |
8cf4c195 | 12 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> |
1da177e4 LT |
13 | * |
14 | */ | |
15 | ||
1da177e4 LT |
16 | #include <linux/kernel.h> |
17 | #include <linux/types.h> | |
1da177e4 LT |
18 | #include <linux/pci.h> |
19 | #include "../pci.h" | |
20 | #include "pciehp.h" | |
1da177e4 | 21 | |
11e87702 LW |
22 | /** |
23 | * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge | |
5790a9c7 | 24 | * @ctrl: PCIe hotplug controller |
11e87702 LW |
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 | */ | |
5790a9c7 | 30 | int pciehp_configure_device(struct controller *ctrl) |
1da177e4 | 31 | { |
71b720c0 | 32 | struct pci_dev *dev; |
5790a9c7 | 33 | struct pci_dev *bridge = ctrl->pcie->port; |
9789ac97 | 34 | struct pci_bus *parent = bridge->subordinate; |
c4ec84c7 | 35 | int num, ret = 0; |
71b720c0 | 36 | |
c4ec84c7 RW |
37 | pci_lock_rescan_remove(); |
38 | ||
d689f7eb | 39 | dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); |
71b720c0 | 40 | if (dev) { |
49902239 MW |
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", | |
227f0647 | 46 | pci_name(dev), pci_domain_nr(parent), parent->number); |
56bfada3 | 47 | pci_dev_put(dev); |
50277c8b | 48 | ret = -EEXIST; |
c4ec84c7 | 49 | goto out; |
1da177e4 LT |
50 | } |
51 | ||
d689f7eb | 52 | num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); |
71b720c0 | 53 | if (num == 0) { |
7f2feec1 | 54 | ctrl_err(ctrl, "No new device found\n"); |
c4ec84c7 RW |
55 | ret = -ENODEV; |
56 | goto out; | |
71b720c0 | 57 | } |
1da177e4 | 58 | |
24a0c654 AS |
59 | for_each_pci_bridge(dev, parent) |
60 | pci_hp_add_bridge(dev); | |
9789ac97 YL |
61 | |
62 | pci_assign_unassigned_bridge_resources(bridge); | |
77094fb3 | 63 | pcie_bus_configure_settings(parent); |
71b720c0 | 64 | pci_bus_add_devices(parent); |
9789ac97 | 65 | |
c4ec84c7 RW |
66 | out: |
67 | pci_unlock_rescan_remove(); | |
68 | return ret; | |
1da177e4 LT |
69 | } |
70 | ||
11e87702 LW |
71 | /** |
72 | * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge | |
5790a9c7 | 73 | * @ctrl: PCIe hotplug controller |
11e87702 LW |
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 | */ | |
5790a9c7 | 82 | void pciehp_unconfigure_device(struct controller *ctrl, bool presence) |
1da177e4 | 83 | { |
ba518e3c | 84 | struct pci_dev *dev, *temp; |
5790a9c7 | 85 | struct pci_bus *parent = ctrl->pcie->port->subordinate; |
2326e2b9 | 86 | u16 command; |
1da177e4 | 87 | |
d689f7eb KK |
88 | ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", |
89 | __func__, pci_domain_nr(parent), parent->number); | |
1da177e4 | 90 | |
a50ac6bf LW |
91 | if (!presence) |
92 | pci_walk_bus(parent, pci_dev_set_disconnected, NULL); | |
93 | ||
c4ec84c7 RW |
94 | pci_lock_rescan_remove(); |
95 | ||
29ed1f29 YL |
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) { | |
ba518e3c | 104 | pci_dev_get(dev); |
ba518e3c | 105 | pci_stop_and_remove_bus_device(dev); |
2326e2b9 KK |
106 | /* |
107 | * Ensure that no new Requests will be generated from | |
108 | * the device. | |
109 | */ | |
110 | if (presence) { | |
ba518e3c | 111 | pci_read_config_word(dev, PCI_COMMAND, &command); |
2326e2b9 KK |
112 | command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); |
113 | command |= PCI_COMMAND_INTX_DISABLE; | |
ba518e3c | 114 | pci_write_config_word(dev, PCI_COMMAND, command); |
2326e2b9 | 115 | } |
ba518e3c | 116 | pci_dev_put(dev); |
1da177e4 | 117 | } |
9fe81645 | 118 | |
c4ec84c7 | 119 | pci_unlock_rescan_remove(); |
1da177e4 | 120 | } |