Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/platforms/pseries/pci_dlpar.c
10818 views
1
/*
2
* PCI Dynamic LPAR, PCI Hot Plug and PCI EEH recovery code
3
* for RPA-compliant PPC64 platform.
4
* Copyright (C) 2003 Linda Xie <[email protected]>
5
* Copyright (C) 2005 International Business Machines
6
*
7
* Updates, 2005, John Rose <[email protected]>
8
* Updates, 2005, Linas Vepstas <[email protected]>
9
*
10
* All rights reserved.
11
*
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or (at
15
* your option) any later version.
16
*
17
* This program is distributed in the hope that it will be useful, but
18
* WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
20
* NON INFRINGEMENT. See the GNU General Public License for more
21
* details.
22
*
23
* You should have received a copy of the GNU General Public License
24
* along with this program; if not, write to the Free Software
25
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
*/
27
28
#include <linux/pci.h>
29
#include <asm/pci-bridge.h>
30
#include <asm/ppc-pci.h>
31
#include <asm/firmware.h>
32
#include <asm/eeh.h>
33
34
static struct pci_bus *
35
find_bus_among_children(struct pci_bus *bus,
36
struct device_node *dn)
37
{
38
struct pci_bus *child = NULL;
39
struct list_head *tmp;
40
struct device_node *busdn;
41
42
busdn = pci_bus_to_OF_node(bus);
43
if (busdn == dn)
44
return bus;
45
46
list_for_each(tmp, &bus->children) {
47
child = find_bus_among_children(pci_bus_b(tmp), dn);
48
if (child)
49
break;
50
};
51
return child;
52
}
53
54
struct pci_bus *
55
pcibios_find_pci_bus(struct device_node *dn)
56
{
57
struct pci_dn *pdn = dn->data;
58
59
if (!pdn || !pdn->phb || !pdn->phb->bus)
60
return NULL;
61
62
return find_bus_among_children(pdn->phb->bus, dn);
63
}
64
EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
65
66
/**
67
* pcibios_remove_pci_devices - remove all devices under this bus
68
*
69
* Remove all of the PCI devices under this bus both from the
70
* linux pci device tree, and from the powerpc EEH address cache.
71
*/
72
void pcibios_remove_pci_devices(struct pci_bus *bus)
73
{
74
struct pci_dev *dev, *tmp;
75
struct pci_bus *child_bus;
76
77
/* First go down child busses */
78
list_for_each_entry(child_bus, &bus->children, node)
79
pcibios_remove_pci_devices(child_bus);
80
81
pr_debug("PCI: Removing devices on bus %04x:%02x\n",
82
pci_domain_nr(bus), bus->number);
83
list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
84
pr_debug(" * Removing %s...\n", pci_name(dev));
85
eeh_remove_bus_device(dev);
86
pci_remove_bus_device(dev);
87
}
88
}
89
EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
90
91
/**
92
* pcibios_add_pci_devices - adds new pci devices to bus
93
*
94
* This routine will find and fixup new pci devices under
95
* the indicated bus. This routine presumes that there
96
* might already be some devices under this bridge, so
97
* it carefully tries to add only new devices. (And that
98
* is how this routine differs from other, similar pcibios
99
* routines.)
100
*/
101
void pcibios_add_pci_devices(struct pci_bus * bus)
102
{
103
int slotno, num, mode, pass, max;
104
struct pci_dev *dev;
105
struct device_node *dn = pci_bus_to_OF_node(bus);
106
107
eeh_add_device_tree_early(dn);
108
109
mode = PCI_PROBE_NORMAL;
110
if (ppc_md.pci_probe_mode)
111
mode = ppc_md.pci_probe_mode(bus);
112
113
if (mode == PCI_PROBE_DEVTREE) {
114
/* use ofdt-based probe */
115
of_rescan_bus(dn, bus);
116
} else if (mode == PCI_PROBE_NORMAL) {
117
/* use legacy probe */
118
slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
119
num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
120
if (!num)
121
return;
122
pcibios_setup_bus_devices(bus);
123
max = bus->secondary;
124
for (pass=0; pass < 2; pass++)
125
list_for_each_entry(dev, &bus->devices, bus_list) {
126
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
127
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
128
max = pci_scan_bridge(bus, dev, max, pass);
129
}
130
}
131
pcibios_finish_adding_to_bus(bus);
132
}
133
EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
134
135
struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
136
{
137
struct pci_controller *phb;
138
139
pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name);
140
141
phb = pcibios_alloc_controller(dn);
142
if (!phb)
143
return NULL;
144
rtas_setup_phb(phb);
145
pci_process_bridge_OF_ranges(phb, dn, 0);
146
147
pci_devs_phb_init_dynamic(phb);
148
149
if (dn->child)
150
eeh_add_device_tree_early(dn);
151
152
pcibios_scan_phb(phb);
153
pcibios_finish_adding_to_bus(phb->bus);
154
155
return phb;
156
}
157
EXPORT_SYMBOL_GPL(init_phb_dynamic);
158
159
/* RPA-specific bits for removing PHBs */
160
int remove_phb_dynamic(struct pci_controller *phb)
161
{
162
struct pci_bus *b = phb->bus;
163
struct resource *res;
164
int rc, i;
165
166
pr_debug("PCI: Removing PHB %04x:%02x...\n",
167
pci_domain_nr(b), b->number);
168
169
/* We cannot to remove a root bus that has children */
170
if (!(list_empty(&b->children) && list_empty(&b->devices)))
171
return -EBUSY;
172
173
/* We -know- there aren't any child devices anymore at this stage
174
* and thus, we can safely unmap the IO space as it's not in use
175
*/
176
res = &phb->io_resource;
177
if (res->flags & IORESOURCE_IO) {
178
rc = pcibios_unmap_io_space(b);
179
if (rc) {
180
printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
181
__func__, b->name);
182
return 1;
183
}
184
}
185
186
/* Unregister the bridge device from sysfs and remove the PCI bus */
187
device_unregister(b->bridge);
188
phb->bus = NULL;
189
pci_remove_bus(b);
190
191
/* Now release the IO resource */
192
if (res->flags & IORESOURCE_IO)
193
release_resource(res);
194
195
/* Release memory resources */
196
for (i = 0; i < 3; ++i) {
197
res = &phb->mem_resources[i];
198
if (!(res->flags & IORESOURCE_MEM))
199
continue;
200
release_resource(res);
201
}
202
203
/* Free pci_controller data structure */
204
pcibios_free_controller(phb);
205
206
return 0;
207
}
208
EXPORT_SYMBOL_GPL(remove_phb_dynamic);
209
210