Merge tag 'xfs-4.20-merge-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux-2.6-block.git] / drivers / pci / hotplug / pciehp_pci.c
CommitLineData
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 30int 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 82void 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}