Commit | Line | Data |
---|---|---|
29310e5e GS |
1 | /* |
2 | * The file intends to implement the platform dependent EEH operations on | |
3 | * powernv platform. Actually, the powernv was created in order to fully | |
4 | * hypervisor support. | |
5 | * | |
6 | * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2013. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/atomic.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/export.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/list.h> | |
19 | #include <linux/msi.h> | |
20 | #include <linux/of.h> | |
21 | #include <linux/pci.h> | |
22 | #include <linux/proc_fs.h> | |
23 | #include <linux/rbtree.h> | |
24 | #include <linux/sched.h> | |
25 | #include <linux/seq_file.h> | |
26 | #include <linux/spinlock.h> | |
27 | ||
28 | #include <asm/eeh.h> | |
29 | #include <asm/eeh_event.h> | |
30 | #include <asm/firmware.h> | |
31 | #include <asm/io.h> | |
32 | #include <asm/iommu.h> | |
33 | #include <asm/machdep.h> | |
34 | #include <asm/msi_bitmap.h> | |
35 | #include <asm/opal.h> | |
36 | #include <asm/ppc-pci.h> | |
37 | ||
38 | #include "powernv.h" | |
39 | #include "pci.h" | |
40 | ||
41 | /** | |
42 | * powernv_eeh_init - EEH platform dependent initialization | |
43 | * | |
44 | * EEH platform dependent initialization on powernv | |
45 | */ | |
46 | static int powernv_eeh_init(void) | |
47 | { | |
48 | /* We require OPALv3 */ | |
49 | if (!firmware_has_feature(FW_FEATURE_OPALv3)) { | |
50 | pr_warning("%s: OPALv3 is required !\n", __func__); | |
51 | return -EINVAL; | |
52 | } | |
53 | ||
54 | /* Set EEH probe mode */ | |
55 | eeh_probe_mode_set(EEH_PROBE_MODE_DEV); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | /** | |
61 | * powernv_eeh_post_init - EEH platform dependent post initialization | |
62 | * | |
63 | * EEH platform dependent post initialization on powernv. When | |
64 | * the function is called, the EEH PEs and devices should have | |
65 | * been built. If the I/O cache staff has been built, EEH is | |
66 | * ready to supply service. | |
67 | */ | |
68 | static int powernv_eeh_post_init(void) | |
69 | { | |
70 | struct pci_controller *hose; | |
71 | struct pnv_phb *phb; | |
72 | int ret = 0; | |
73 | ||
74 | list_for_each_entry(hose, &hose_list, list_node) { | |
75 | phb = hose->private_data; | |
76 | ||
77 | if (phb->eeh_ops && phb->eeh_ops->post_init) { | |
78 | ret = phb->eeh_ops->post_init(hose); | |
79 | if (ret) | |
80 | break; | |
81 | } | |
82 | } | |
83 | ||
84 | return ret; | |
85 | } | |
86 | ||
87 | /** | |
88 | * powernv_eeh_dev_probe - Do probe on PCI device | |
89 | * @dev: PCI device | |
90 | * @flag: unused | |
91 | * | |
92 | * When EEH module is installed during system boot, all PCI devices | |
93 | * are checked one by one to see if it supports EEH. The function | |
94 | * is introduced for the purpose. By default, EEH has been enabled | |
95 | * on all PCI devices. That's to say, we only need do necessary | |
96 | * initialization on the corresponding eeh device and create PE | |
97 | * accordingly. | |
98 | * | |
99 | * It's notable that's unsafe to retrieve the EEH device through | |
100 | * the corresponding PCI device. During the PCI device hotplug, which | |
101 | * was possiblly triggered by EEH core, the binding between EEH device | |
102 | * and the PCI device isn't built yet. | |
103 | */ | |
104 | static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag) | |
105 | { | |
106 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | |
107 | struct pnv_phb *phb = hose->private_data; | |
108 | struct device_node *dn = pci_device_to_OF_node(dev); | |
109 | struct eeh_dev *edev = of_node_to_eeh_dev(dn); | |
110 | ||
111 | /* | |
112 | * When probing the root bridge, which doesn't have any | |
113 | * subordinate PCI devices. We don't have OF node for | |
114 | * the root bridge. So it's not reasonable to continue | |
115 | * the probing. | |
116 | */ | |
f5c57710 | 117 | if (!dn || !edev || edev->pe) |
29310e5e GS |
118 | return 0; |
119 | ||
120 | /* Skip for PCI-ISA bridge */ | |
121 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA) | |
122 | return 0; | |
123 | ||
124 | /* Initialize eeh device */ | |
ab55d218 GS |
125 | edev->class_code = dev->class; |
126 | edev->mode &= 0xFFFFFF00; | |
4b83bd45 GS |
127 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) |
128 | edev->mode |= EEH_DEV_BRIDGE; | |
129 | if (pci_is_pcie(dev)) { | |
130 | edev->pcie_cap = pci_pcie_cap(dev); | |
131 | ||
132 | if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) | |
133 | edev->mode |= EEH_DEV_ROOT_PORT; | |
134 | else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) | |
135 | edev->mode |= EEH_DEV_DS_PORT; | |
136 | } | |
137 | ||
29310e5e GS |
138 | edev->config_addr = ((dev->bus->number << 8) | dev->devfn); |
139 | edev->pe_config_addr = phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff); | |
140 | ||
141 | /* Create PE */ | |
142 | eeh_add_to_parent_pe(edev); | |
143 | ||
144 | /* | |
145 | * Enable EEH explicitly so that we will do EEH check | |
146 | * while accessing I/O stuff | |
147 | * | |
148 | * FIXME: Enable that for PHB3 later | |
149 | */ | |
150 | if (phb->type == PNV_PHB_IODA1) | |
151 | eeh_subsystem_enabled = 1; | |
152 | ||
153 | /* Save memory bars */ | |
154 | eeh_save_bars(edev); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | /** | |
160 | * powernv_eeh_set_option - Initialize EEH or MMIO/DMA reenable | |
161 | * @pe: EEH PE | |
162 | * @option: operation to be issued | |
163 | * | |
164 | * The function is used to control the EEH functionality globally. | |
165 | * Currently, following options are support according to PAPR: | |
166 | * Enable EEH, Disable EEH, Enable MMIO and Enable DMA | |
167 | */ | |
168 | static int powernv_eeh_set_option(struct eeh_pe *pe, int option) | |
169 | { | |
170 | struct pci_controller *hose = pe->phb; | |
171 | struct pnv_phb *phb = hose->private_data; | |
172 | int ret = -EEXIST; | |
173 | ||
174 | /* | |
175 | * What we need do is pass it down for hardware | |
176 | * implementation to handle it. | |
177 | */ | |
178 | if (phb->eeh_ops && phb->eeh_ops->set_option) | |
179 | ret = phb->eeh_ops->set_option(pe, option); | |
180 | ||
181 | return ret; | |
182 | } | |
183 | ||
184 | /** | |
185 | * powernv_eeh_get_pe_addr - Retrieve PE address | |
186 | * @pe: EEH PE | |
187 | * | |
188 | * Retrieve the PE address according to the given tranditional | |
189 | * PCI BDF (Bus/Device/Function) address. | |
190 | */ | |
191 | static int powernv_eeh_get_pe_addr(struct eeh_pe *pe) | |
192 | { | |
193 | return pe->addr; | |
194 | } | |
195 | ||
196 | /** | |
197 | * powernv_eeh_get_state - Retrieve PE state | |
198 | * @pe: EEH PE | |
199 | * @delay: delay while PE state is temporarily unavailable | |
200 | * | |
201 | * Retrieve the state of the specified PE. For IODA-compitable | |
202 | * platform, it should be retrieved from IODA table. Therefore, | |
203 | * we prefer passing down to hardware implementation to handle | |
204 | * it. | |
205 | */ | |
206 | static int powernv_eeh_get_state(struct eeh_pe *pe, int *delay) | |
207 | { | |
208 | struct pci_controller *hose = pe->phb; | |
209 | struct pnv_phb *phb = hose->private_data; | |
210 | int ret = EEH_STATE_NOT_SUPPORT; | |
211 | ||
212 | if (phb->eeh_ops && phb->eeh_ops->get_state) { | |
213 | ret = phb->eeh_ops->get_state(pe); | |
214 | ||
215 | /* | |
216 | * If the PE state is temporarily unavailable, | |
217 | * to inform the EEH core delay for default | |
218 | * period (1 second) | |
219 | */ | |
220 | if (delay) { | |
221 | *delay = 0; | |
222 | if (ret & EEH_STATE_UNAVAILABLE) | |
223 | *delay = 1000; | |
224 | } | |
225 | } | |
226 | ||
227 | return ret; | |
228 | } | |
229 | ||
230 | /** | |
231 | * powernv_eeh_reset - Reset the specified PE | |
232 | * @pe: EEH PE | |
233 | * @option: reset option | |
234 | * | |
235 | * Reset the specified PE | |
236 | */ | |
237 | static int powernv_eeh_reset(struct eeh_pe *pe, int option) | |
238 | { | |
239 | struct pci_controller *hose = pe->phb; | |
240 | struct pnv_phb *phb = hose->private_data; | |
241 | int ret = -EEXIST; | |
242 | ||
243 | if (phb->eeh_ops && phb->eeh_ops->reset) | |
244 | ret = phb->eeh_ops->reset(pe, option); | |
245 | ||
246 | return ret; | |
247 | } | |
248 | ||
249 | /** | |
250 | * powernv_eeh_wait_state - Wait for PE state | |
251 | * @pe: EEH PE | |
252 | * @max_wait: maximal period in microsecond | |
253 | * | |
254 | * Wait for the state of associated PE. It might take some time | |
255 | * to retrieve the PE's state. | |
256 | */ | |
257 | static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait) | |
258 | { | |
259 | int ret; | |
260 | int mwait; | |
261 | ||
262 | while (1) { | |
263 | ret = powernv_eeh_get_state(pe, &mwait); | |
264 | ||
265 | /* | |
266 | * If the PE's state is temporarily unavailable, | |
267 | * we have to wait for the specified time. Otherwise, | |
268 | * the PE's state will be returned immediately. | |
269 | */ | |
270 | if (ret != EEH_STATE_UNAVAILABLE) | |
271 | return ret; | |
272 | ||
273 | max_wait -= mwait; | |
274 | if (max_wait <= 0) { | |
275 | pr_warning("%s: Timeout getting PE#%x's state (%d)\n", | |
276 | __func__, pe->addr, max_wait); | |
277 | return EEH_STATE_NOT_SUPPORT; | |
278 | } | |
279 | ||
280 | msleep(mwait); | |
281 | } | |
282 | ||
283 | return EEH_STATE_NOT_SUPPORT; | |
284 | } | |
285 | ||
286 | /** | |
287 | * powernv_eeh_get_log - Retrieve error log | |
288 | * @pe: EEH PE | |
289 | * @severity: temporary or permanent error log | |
290 | * @drv_log: driver log to be combined with retrieved error log | |
291 | * @len: length of driver log | |
292 | * | |
293 | * Retrieve the temporary or permanent error from the PE. | |
294 | */ | |
295 | static int powernv_eeh_get_log(struct eeh_pe *pe, int severity, | |
296 | char *drv_log, unsigned long len) | |
297 | { | |
298 | struct pci_controller *hose = pe->phb; | |
299 | struct pnv_phb *phb = hose->private_data; | |
300 | int ret = -EEXIST; | |
301 | ||
302 | if (phb->eeh_ops && phb->eeh_ops->get_log) | |
303 | ret = phb->eeh_ops->get_log(pe, severity, drv_log, len); | |
304 | ||
305 | return ret; | |
306 | } | |
307 | ||
308 | /** | |
309 | * powernv_eeh_configure_bridge - Configure PCI bridges in the indicated PE | |
310 | * @pe: EEH PE | |
311 | * | |
312 | * The function will be called to reconfigure the bridges included | |
313 | * in the specified PE so that the mulfunctional PE would be recovered | |
314 | * again. | |
315 | */ | |
316 | static int powernv_eeh_configure_bridge(struct eeh_pe *pe) | |
317 | { | |
318 | struct pci_controller *hose = pe->phb; | |
319 | struct pnv_phb *phb = hose->private_data; | |
320 | int ret = 0; | |
321 | ||
322 | if (phb->eeh_ops && phb->eeh_ops->configure_bridge) | |
323 | ret = phb->eeh_ops->configure_bridge(pe); | |
324 | ||
325 | return ret; | |
326 | } | |
327 | ||
29310e5e GS |
328 | /** |
329 | * powernv_eeh_next_error - Retrieve next EEH error to handle | |
330 | * @pe: Affected PE | |
331 | * | |
332 | * Using OPAL API, to retrieve next EEH error for EEH core to handle | |
333 | */ | |
334 | static int powernv_eeh_next_error(struct eeh_pe **pe) | |
335 | { | |
336 | struct pci_controller *hose; | |
337 | struct pnv_phb *phb = NULL; | |
338 | ||
339 | list_for_each_entry(hose, &hose_list, list_node) { | |
340 | phb = hose->private_data; | |
341 | break; | |
342 | } | |
343 | ||
344 | if (phb && phb->eeh_ops->next_error) | |
345 | return phb->eeh_ops->next_error(pe); | |
346 | ||
347 | return -EEXIST; | |
348 | } | |
349 | ||
350 | static struct eeh_ops powernv_eeh_ops = { | |
351 | .name = "powernv", | |
352 | .init = powernv_eeh_init, | |
353 | .post_init = powernv_eeh_post_init, | |
354 | .of_probe = NULL, | |
355 | .dev_probe = powernv_eeh_dev_probe, | |
356 | .set_option = powernv_eeh_set_option, | |
357 | .get_pe_addr = powernv_eeh_get_pe_addr, | |
358 | .get_state = powernv_eeh_get_state, | |
359 | .reset = powernv_eeh_reset, | |
360 | .wait_state = powernv_eeh_wait_state, | |
361 | .get_log = powernv_eeh_get_log, | |
362 | .configure_bridge = powernv_eeh_configure_bridge, | |
9bf41be6 GS |
363 | .read_config = pnv_pci_cfg_read, |
364 | .write_config = pnv_pci_cfg_write, | |
29310e5e GS |
365 | .next_error = powernv_eeh_next_error |
366 | }; | |
367 | ||
368 | /** | |
369 | * eeh_powernv_init - Register platform dependent EEH operations | |
370 | * | |
371 | * EEH initialization on powernv platform. This function should be | |
372 | * called before any EEH related functions. | |
373 | */ | |
374 | static int __init eeh_powernv_init(void) | |
375 | { | |
376 | int ret = -EINVAL; | |
377 | ||
378 | if (!machine_is(powernv)) | |
379 | return ret; | |
380 | ||
381 | ret = eeh_ops_register(&powernv_eeh_ops); | |
382 | if (!ret) | |
383 | pr_info("EEH: PowerNV platform initialized\n"); | |
384 | else | |
385 | pr_info("EEH: Failed to initialize PowerNV platform (%d)\n", ret); | |
386 | ||
387 | return ret; | |
388 | } | |
389 | ||
390 | early_initcall(eeh_powernv_init); |