Commit | Line | Data |
---|---|---|
55037d17 GS |
1 | /* |
2 | * The file intends to implement PE based on the information from | |
3 | * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device. | |
4 | * All the PEs should be organized as hierarchy tree. The first level | |
5 | * of the tree will be associated to existing PHBs since the particular | |
6 | * PE is only meaningful in one PHB domain. | |
7 | * | |
8 | * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | */ | |
24 | ||
25 | #include <linux/export.h> | |
26 | #include <linux/gfp.h> | |
27 | #include <linux/init.h> | |
28 | #include <linux/kernel.h> | |
29 | #include <linux/pci.h> | |
30 | #include <linux/string.h> | |
31 | ||
32 | #include <asm/pci-bridge.h> | |
33 | #include <asm/ppc-pci.h> | |
34 | ||
35 | static LIST_HEAD(eeh_phb_pe); | |
36 | ||
37 | /** | |
38 | * eeh_pe_alloc - Allocate PE | |
39 | * @phb: PCI controller | |
40 | * @type: PE type | |
41 | * | |
42 | * Allocate PE instance dynamically. | |
43 | */ | |
44 | static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type) | |
45 | { | |
46 | struct eeh_pe *pe; | |
47 | ||
48 | /* Allocate PHB PE */ | |
49 | pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL); | |
50 | if (!pe) return NULL; | |
51 | ||
52 | /* Initialize PHB PE */ | |
53 | pe->type = type; | |
54 | pe->phb = phb; | |
55 | INIT_LIST_HEAD(&pe->child_list); | |
56 | INIT_LIST_HEAD(&pe->child); | |
57 | INIT_LIST_HEAD(&pe->edevs); | |
58 | ||
59 | return pe; | |
60 | } | |
61 | ||
62 | /** | |
63 | * eeh_phb_pe_create - Create PHB PE | |
64 | * @phb: PCI controller | |
65 | * | |
66 | * The function should be called while the PHB is detected during | |
67 | * system boot or PCI hotplug in order to create PHB PE. | |
68 | */ | |
69 | int __devinit eeh_phb_pe_create(struct pci_controller *phb) | |
70 | { | |
71 | struct eeh_pe *pe; | |
72 | ||
73 | /* Allocate PHB PE */ | |
74 | pe = eeh_pe_alloc(phb, EEH_PE_PHB); | |
75 | if (!pe) { | |
76 | pr_err("%s: out of memory!\n", __func__); | |
77 | return -ENOMEM; | |
78 | } | |
79 | ||
80 | /* Put it into the list */ | |
81 | eeh_lock(); | |
82 | list_add_tail(&pe->child, &eeh_phb_pe); | |
83 | eeh_unlock(); | |
84 | ||
85 | pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | /** | |
91 | * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB | |
92 | * @phb: PCI controller | |
93 | * | |
94 | * The overall PEs form hierarchy tree. The first layer of the | |
95 | * hierarchy tree is composed of PHB PEs. The function is used | |
96 | * to retrieve the corresponding PHB PE according to the given PHB. | |
97 | */ | |
98 | static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) | |
99 | { | |
100 | struct eeh_pe *pe; | |
101 | ||
102 | eeh_lock(); | |
103 | ||
104 | list_for_each_entry(pe, &eeh_phb_pe, child) { | |
105 | /* | |
106 | * Actually, we needn't check the type since | |
107 | * the PE for PHB has been determined when that | |
108 | * was created. | |
109 | */ | |
110 | if (pe->type == EEH_PE_PHB && | |
111 | pe->phb == phb) { | |
112 | eeh_unlock(); | |
113 | return pe; | |
114 | } | |
115 | } | |
116 | ||
117 | eeh_unlock(); | |
118 | ||
119 | return NULL; | |
120 | } | |
22f4ab12 GS |
121 | |
122 | /** | |
123 | * eeh_pe_next - Retrieve the next PE in the tree | |
124 | * @pe: current PE | |
125 | * @root: root PE | |
126 | * | |
127 | * The function is used to retrieve the next PE in the | |
128 | * hierarchy PE tree. | |
129 | */ | |
130 | static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, | |
131 | struct eeh_pe *root) | |
132 | { | |
133 | struct list_head *next = pe->child_list.next; | |
134 | ||
135 | if (next == &pe->child_list) { | |
136 | while (1) { | |
137 | if (pe == root) | |
138 | return NULL; | |
139 | next = pe->child.next; | |
140 | if (next != &pe->parent->child_list) | |
141 | break; | |
142 | pe = pe->parent; | |
143 | } | |
144 | } | |
145 | ||
146 | return list_entry(next, struct eeh_pe, child); | |
147 | } | |
148 | ||
149 | /** | |
150 | * eeh_pe_traverse - Traverse PEs in the specified PHB | |
151 | * @root: root PE | |
152 | * @fn: callback | |
153 | * @flag: extra parameter to callback | |
154 | * | |
155 | * The function is used to traverse the specified PE and its | |
156 | * child PEs. The traversing is to be terminated once the | |
157 | * callback returns something other than NULL, or no more PEs | |
158 | * to be traversed. | |
159 | */ | |
160 | static void *eeh_pe_traverse(struct eeh_pe *root, | |
161 | eeh_traverse_func fn, void *flag) | |
162 | { | |
163 | struct eeh_pe *pe; | |
164 | void *ret; | |
165 | ||
166 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | |
167 | ret = fn(pe, flag); | |
168 | if (ret) return ret; | |
169 | } | |
170 | ||
171 | return NULL; | |
172 | } | |
173 | ||
174 | /** | |
175 | * __eeh_pe_get - Check the PE address | |
176 | * @data: EEH PE | |
177 | * @flag: EEH device | |
178 | * | |
179 | * For one particular PE, it can be identified by PE address | |
180 | * or tranditional BDF address. BDF address is composed of | |
181 | * Bus/Device/Function number. The extra data referred by flag | |
182 | * indicates which type of address should be used. | |
183 | */ | |
184 | static void *__eeh_pe_get(void *data, void *flag) | |
185 | { | |
186 | struct eeh_pe *pe = (struct eeh_pe *)data; | |
187 | struct eeh_dev *edev = (struct eeh_dev *)flag; | |
188 | ||
189 | /* Unexpected PHB PE */ | |
190 | if (pe->type == EEH_PE_PHB) | |
191 | return NULL; | |
192 | ||
193 | /* We prefer PE address */ | |
194 | if (edev->pe_config_addr && | |
195 | (edev->pe_config_addr == pe->addr)) | |
196 | return pe; | |
197 | ||
198 | /* Try BDF address */ | |
199 | if (edev->pe_config_addr && | |
200 | (edev->config_addr == pe->config_addr)) | |
201 | return pe; | |
202 | ||
203 | return NULL; | |
204 | } | |
205 | ||
206 | /** | |
207 | * eeh_pe_get - Search PE based on the given address | |
208 | * @edev: EEH device | |
209 | * | |
210 | * Search the corresponding PE based on the specified address which | |
211 | * is included in the eeh device. The function is used to check if | |
212 | * the associated PE has been created against the PE address. It's | |
213 | * notable that the PE address has 2 format: traditional PE address | |
214 | * which is composed of PCI bus/device/function number, or unified | |
215 | * PE address. | |
216 | */ | |
217 | static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) | |
218 | { | |
219 | struct eeh_pe *root = eeh_phb_pe_get(edev->phb); | |
220 | struct eeh_pe *pe; | |
221 | ||
222 | eeh_lock(); | |
223 | pe = eeh_pe_traverse(root, __eeh_pe_get, edev); | |
224 | eeh_unlock(); | |
225 | ||
226 | return pe; | |
227 | } | |
228 | ||
229 | /** | |
230 | * eeh_pe_get_parent - Retrieve the parent PE | |
231 | * @edev: EEH device | |
232 | * | |
233 | * The whole PEs existing in the system are organized as hierarchy | |
234 | * tree. The function is used to retrieve the parent PE according | |
235 | * to the parent EEH device. | |
236 | */ | |
237 | static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) | |
238 | { | |
239 | struct device_node *dn; | |
240 | struct eeh_dev *parent; | |
241 | ||
242 | /* | |
243 | * It might have the case for the indirect parent | |
244 | * EEH device already having associated PE, but | |
245 | * the direct parent EEH device doesn't have yet. | |
246 | */ | |
247 | dn = edev->dn->parent; | |
248 | while (dn) { | |
249 | /* We're poking out of PCI territory */ | |
250 | if (!PCI_DN(dn)) return NULL; | |
251 | ||
252 | parent = of_node_to_eeh_dev(dn); | |
253 | /* We're poking out of PCI territory */ | |
254 | if (!parent) return NULL; | |
255 | ||
256 | if (parent->pe) | |
257 | return parent->pe; | |
258 | ||
259 | dn = dn->parent; | |
260 | } | |
261 | ||
262 | return NULL; | |
263 | } |