xref: /openbmc/linux/arch/powerpc/kernel/eeh_driver.c (revision fb48dc22)
1317f06deSGavin Shan /*
2317f06deSGavin Shan  * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
3317f06deSGavin Shan  * Copyright IBM Corp. 2004 2005
4317f06deSGavin Shan  * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
5317f06deSGavin Shan  *
6317f06deSGavin Shan  * All rights reserved.
7317f06deSGavin Shan  *
8317f06deSGavin Shan  * This program is free software; you can redistribute it and/or modify
9317f06deSGavin Shan  * it under the terms of the GNU General Public License as published by
10317f06deSGavin Shan  * the Free Software Foundation; either version 2 of the License, or (at
11317f06deSGavin Shan  * your option) any later version.
12317f06deSGavin Shan  *
13317f06deSGavin Shan  * This program is distributed in the hope that it will be useful, but
14317f06deSGavin Shan  * WITHOUT ANY WARRANTY; without even the implied warranty of
15317f06deSGavin Shan  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
16317f06deSGavin Shan  * NON INFRINGEMENT.  See the GNU General Public License for more
17317f06deSGavin Shan  * details.
18317f06deSGavin Shan  *
19317f06deSGavin Shan  * You should have received a copy of the GNU General Public License
20317f06deSGavin Shan  * along with this program; if not, write to the Free Software
21317f06deSGavin Shan  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22317f06deSGavin Shan  *
23317f06deSGavin Shan  * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
24317f06deSGavin Shan  */
25317f06deSGavin Shan #include <linux/delay.h>
26317f06deSGavin Shan #include <linux/interrupt.h>
27317f06deSGavin Shan #include <linux/irq.h>
28317f06deSGavin Shan #include <linux/module.h>
29317f06deSGavin Shan #include <linux/pci.h>
30317f06deSGavin Shan #include <asm/eeh.h>
31317f06deSGavin Shan #include <asm/eeh_event.h>
32317f06deSGavin Shan #include <asm/ppc-pci.h>
33317f06deSGavin Shan #include <asm/pci-bridge.h>
34317f06deSGavin Shan #include <asm/prom.h>
35317f06deSGavin Shan #include <asm/rtas.h>
36317f06deSGavin Shan 
37317f06deSGavin Shan /**
38317f06deSGavin Shan  * eeh_pcid_name - Retrieve name of PCI device driver
39317f06deSGavin Shan  * @pdev: PCI device
40317f06deSGavin Shan  *
41317f06deSGavin Shan  * This routine is used to retrieve the name of PCI device driver
42317f06deSGavin Shan  * if that's valid.
43317f06deSGavin Shan  */
44317f06deSGavin Shan static inline const char *eeh_pcid_name(struct pci_dev *pdev)
45317f06deSGavin Shan {
46317f06deSGavin Shan 	if (pdev && pdev->dev.driver)
47317f06deSGavin Shan 		return pdev->dev.driver->name;
48317f06deSGavin Shan 	return "";
49317f06deSGavin Shan }
50317f06deSGavin Shan 
51317f06deSGavin Shan /**
52317f06deSGavin Shan  * eeh_pcid_get - Get the PCI device driver
53317f06deSGavin Shan  * @pdev: PCI device
54317f06deSGavin Shan  *
55317f06deSGavin Shan  * The function is used to retrieve the PCI device driver for
56317f06deSGavin Shan  * the indicated PCI device. Besides, we will increase the reference
57317f06deSGavin Shan  * of the PCI device driver to prevent that being unloaded on
58317f06deSGavin Shan  * the fly. Otherwise, kernel crash would be seen.
59317f06deSGavin Shan  */
60317f06deSGavin Shan static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
61317f06deSGavin Shan {
62317f06deSGavin Shan 	if (!pdev || !pdev->driver)
63317f06deSGavin Shan 		return NULL;
64317f06deSGavin Shan 
65317f06deSGavin Shan 	if (!try_module_get(pdev->driver->driver.owner))
66317f06deSGavin Shan 		return NULL;
67317f06deSGavin Shan 
68317f06deSGavin Shan 	return pdev->driver;
69317f06deSGavin Shan }
70317f06deSGavin Shan 
71317f06deSGavin Shan /**
72317f06deSGavin Shan  * eeh_pcid_put - Dereference on the PCI device driver
73317f06deSGavin Shan  * @pdev: PCI device
74317f06deSGavin Shan  *
75317f06deSGavin Shan  * The function is called to do dereference on the PCI device
76317f06deSGavin Shan  * driver of the indicated PCI device.
77317f06deSGavin Shan  */
78317f06deSGavin Shan static inline void eeh_pcid_put(struct pci_dev *pdev)
79317f06deSGavin Shan {
80317f06deSGavin Shan 	if (!pdev || !pdev->driver)
81317f06deSGavin Shan 		return;
82317f06deSGavin Shan 
83317f06deSGavin Shan 	module_put(pdev->driver->driver.owner);
84317f06deSGavin Shan }
85317f06deSGavin Shan 
86317f06deSGavin Shan #if 0
87317f06deSGavin Shan static void print_device_node_tree(struct pci_dn *pdn, int dent)
88317f06deSGavin Shan {
89317f06deSGavin Shan 	int i;
90317f06deSGavin Shan 	struct device_node *pc;
91317f06deSGavin Shan 
92317f06deSGavin Shan 	if (!pdn)
93317f06deSGavin Shan 		return;
94317f06deSGavin Shan 	for (i = 0; i < dent; i++)
95317f06deSGavin Shan 		printk(" ");
96317f06deSGavin Shan 	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
97317f06deSGavin Shan 		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
98317f06deSGavin Shan 		pdn->eeh_pe_config_addr, pdn->node->full_name);
99317f06deSGavin Shan 	dent += 3;
100317f06deSGavin Shan 	pc = pdn->node->child;
101317f06deSGavin Shan 	while (pc) {
102317f06deSGavin Shan 		print_device_node_tree(PCI_DN(pc), dent);
103317f06deSGavin Shan 		pc = pc->sibling;
104317f06deSGavin Shan 	}
105317f06deSGavin Shan }
106317f06deSGavin Shan #endif
107317f06deSGavin Shan 
108317f06deSGavin Shan /**
109317f06deSGavin Shan  * eeh_disable_irq - Disable interrupt for the recovering device
110317f06deSGavin Shan  * @dev: PCI device
111317f06deSGavin Shan  *
112317f06deSGavin Shan  * This routine must be called when reporting temporary or permanent
113317f06deSGavin Shan  * error to the particular PCI device to disable interrupt of that
114317f06deSGavin Shan  * device. If the device has enabled MSI or MSI-X interrupt, we needn't
115317f06deSGavin Shan  * do real work because EEH should freeze DMA transfers for those PCI
116317f06deSGavin Shan  * devices encountering EEH errors, which includes MSI or MSI-X.
117317f06deSGavin Shan  */
118317f06deSGavin Shan static void eeh_disable_irq(struct pci_dev *dev)
119317f06deSGavin Shan {
120317f06deSGavin Shan 	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
121317f06deSGavin Shan 
122317f06deSGavin Shan 	/* Don't disable MSI and MSI-X interrupts. They are
123317f06deSGavin Shan 	 * effectively disabled by the DMA Stopped state
124317f06deSGavin Shan 	 * when an EEH error occurs.
125317f06deSGavin Shan 	 */
126317f06deSGavin Shan 	if (dev->msi_enabled || dev->msix_enabled)
127317f06deSGavin Shan 		return;
128317f06deSGavin Shan 
129317f06deSGavin Shan 	if (!irq_has_action(dev->irq))
130317f06deSGavin Shan 		return;
131317f06deSGavin Shan 
132317f06deSGavin Shan 	edev->mode |= EEH_DEV_IRQ_DISABLED;
133317f06deSGavin Shan 	disable_irq_nosync(dev->irq);
134317f06deSGavin Shan }
135317f06deSGavin Shan 
136317f06deSGavin Shan /**
137317f06deSGavin Shan  * eeh_enable_irq - Enable interrupt for the recovering device
138317f06deSGavin Shan  * @dev: PCI device
139317f06deSGavin Shan  *
140317f06deSGavin Shan  * This routine must be called to enable interrupt while failed
141317f06deSGavin Shan  * device could be resumed.
142317f06deSGavin Shan  */
143317f06deSGavin Shan static void eeh_enable_irq(struct pci_dev *dev)
144317f06deSGavin Shan {
145317f06deSGavin Shan 	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
14691150af3SGavin Shan 	struct irq_desc *desc;
147317f06deSGavin Shan 
148317f06deSGavin Shan 	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
149317f06deSGavin Shan 		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
15091150af3SGavin Shan 
15191150af3SGavin Shan 		desc = irq_to_desc(dev->irq);
15291150af3SGavin Shan 		if (desc && desc->depth > 0)
153317f06deSGavin Shan 			enable_irq(dev->irq);
154317f06deSGavin Shan 	}
155317f06deSGavin Shan }
156317f06deSGavin Shan 
157317f06deSGavin Shan /**
158317f06deSGavin Shan  * eeh_report_error - Report pci error to each device driver
159317f06deSGavin Shan  * @data: eeh device
160317f06deSGavin Shan  * @userdata: return value
161317f06deSGavin Shan  *
162317f06deSGavin Shan  * Report an EEH error to each device driver, collect up and
163317f06deSGavin Shan  * merge the device driver responses. Cumulative response
164317f06deSGavin Shan  * passed back in "userdata".
165317f06deSGavin Shan  */
166317f06deSGavin Shan static void *eeh_report_error(void *data, void *userdata)
167317f06deSGavin Shan {
168317f06deSGavin Shan 	struct eeh_dev *edev = (struct eeh_dev *)data;
169317f06deSGavin Shan 	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
170317f06deSGavin Shan 	enum pci_ers_result rc, *res = userdata;
171317f06deSGavin Shan 	struct pci_driver *driver;
172317f06deSGavin Shan 
173317f06deSGavin Shan 	/* We might not have the associated PCI device,
174317f06deSGavin Shan 	 * then we should continue for next one.
175317f06deSGavin Shan 	 */
176317f06deSGavin Shan 	if (!dev) return NULL;
177317f06deSGavin Shan 	dev->error_state = pci_channel_io_frozen;
178317f06deSGavin Shan 
179317f06deSGavin Shan 	driver = eeh_pcid_get(dev);
180317f06deSGavin Shan 	if (!driver) return NULL;
181317f06deSGavin Shan 
182317f06deSGavin Shan 	eeh_disable_irq(dev);
183317f06deSGavin Shan 
184317f06deSGavin Shan 	if (!driver->err_handler ||
185317f06deSGavin Shan 	    !driver->err_handler->error_detected) {
186317f06deSGavin Shan 		eeh_pcid_put(dev);
187317f06deSGavin Shan 		return NULL;
188317f06deSGavin Shan 	}
189317f06deSGavin Shan 
190317f06deSGavin Shan 	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
191317f06deSGavin Shan 
192317f06deSGavin Shan 	/* A driver that needs a reset trumps all others */
193317f06deSGavin Shan 	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
194317f06deSGavin Shan 	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
195317f06deSGavin Shan 
196317f06deSGavin Shan 	eeh_pcid_put(dev);
197317f06deSGavin Shan 	return NULL;
198317f06deSGavin Shan }
199317f06deSGavin Shan 
200317f06deSGavin Shan /**
201317f06deSGavin Shan  * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
202317f06deSGavin Shan  * @data: eeh device
203317f06deSGavin Shan  * @userdata: return value
204317f06deSGavin Shan  *
205317f06deSGavin Shan  * Tells each device driver that IO ports, MMIO and config space I/O
206317f06deSGavin Shan  * are now enabled. Collects up and merges the device driver responses.
207317f06deSGavin Shan  * Cumulative response passed back in "userdata".
208317f06deSGavin Shan  */
209317f06deSGavin Shan static void *eeh_report_mmio_enabled(void *data, void *userdata)
210317f06deSGavin Shan {
211317f06deSGavin Shan 	struct eeh_dev *edev = (struct eeh_dev *)data;
212317f06deSGavin Shan 	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
213317f06deSGavin Shan 	enum pci_ers_result rc, *res = userdata;
214317f06deSGavin Shan 	struct pci_driver *driver;
215317f06deSGavin Shan 
216317f06deSGavin Shan 	driver = eeh_pcid_get(dev);
217317f06deSGavin Shan 	if (!driver) return NULL;
218317f06deSGavin Shan 
219317f06deSGavin Shan 	if (!driver->err_handler ||
220317f06deSGavin Shan 	    !driver->err_handler->mmio_enabled) {
221317f06deSGavin Shan 		eeh_pcid_put(dev);
222317f06deSGavin Shan 		return NULL;
223317f06deSGavin Shan 	}
224317f06deSGavin Shan 
225317f06deSGavin Shan 	rc = driver->err_handler->mmio_enabled(dev);
226317f06deSGavin Shan 
227317f06deSGavin Shan 	/* A driver that needs a reset trumps all others */
228317f06deSGavin Shan 	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
229317f06deSGavin Shan 	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
230317f06deSGavin Shan 
231317f06deSGavin Shan 	eeh_pcid_put(dev);
232317f06deSGavin Shan 	return NULL;
233317f06deSGavin Shan }
234317f06deSGavin Shan 
235317f06deSGavin Shan /**
236317f06deSGavin Shan  * eeh_report_reset - Tell device that slot has been reset
237317f06deSGavin Shan  * @data: eeh device
238317f06deSGavin Shan  * @userdata: return value
239317f06deSGavin Shan  *
240317f06deSGavin Shan  * This routine must be called while EEH tries to reset particular
241317f06deSGavin Shan  * PCI device so that the associated PCI device driver could take
242317f06deSGavin Shan  * some actions, usually to save data the driver needs so that the
243317f06deSGavin Shan  * driver can work again while the device is recovered.
244317f06deSGavin Shan  */
245317f06deSGavin Shan static void *eeh_report_reset(void *data, void *userdata)
246317f06deSGavin Shan {
247317f06deSGavin Shan 	struct eeh_dev *edev = (struct eeh_dev *)data;
248317f06deSGavin Shan 	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
249317f06deSGavin Shan 	enum pci_ers_result rc, *res = userdata;
250317f06deSGavin Shan 	struct pci_driver *driver;
251317f06deSGavin Shan 
252317f06deSGavin Shan 	if (!dev) return NULL;
253317f06deSGavin Shan 	dev->error_state = pci_channel_io_normal;
254317f06deSGavin Shan 
255317f06deSGavin Shan 	driver = eeh_pcid_get(dev);
256317f06deSGavin Shan 	if (!driver) return NULL;
257317f06deSGavin Shan 
258317f06deSGavin Shan 	eeh_enable_irq(dev);
259317f06deSGavin Shan 
260317f06deSGavin Shan 	if (!driver->err_handler ||
261317f06deSGavin Shan 	    !driver->err_handler->slot_reset) {
262317f06deSGavin Shan 		eeh_pcid_put(dev);
263317f06deSGavin Shan 		return NULL;
264317f06deSGavin Shan 	}
265317f06deSGavin Shan 
266317f06deSGavin Shan 	rc = driver->err_handler->slot_reset(dev);
267317f06deSGavin Shan 	if ((*res == PCI_ERS_RESULT_NONE) ||
268317f06deSGavin Shan 	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
269317f06deSGavin Shan 	if (*res == PCI_ERS_RESULT_DISCONNECT &&
270317f06deSGavin Shan 	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
271317f06deSGavin Shan 
272317f06deSGavin Shan 	eeh_pcid_put(dev);
273317f06deSGavin Shan 	return NULL;
274317f06deSGavin Shan }
275317f06deSGavin Shan 
276317f06deSGavin Shan /**
277317f06deSGavin Shan  * eeh_report_resume - Tell device to resume normal operations
278317f06deSGavin Shan  * @data: eeh device
279317f06deSGavin Shan  * @userdata: return value
280317f06deSGavin Shan  *
281317f06deSGavin Shan  * This routine must be called to notify the device driver that it
282317f06deSGavin Shan  * could resume so that the device driver can do some initialization
283317f06deSGavin Shan  * to make the recovered device work again.
284317f06deSGavin Shan  */
285317f06deSGavin Shan static void *eeh_report_resume(void *data, void *userdata)
286317f06deSGavin Shan {
287317f06deSGavin Shan 	struct eeh_dev *edev = (struct eeh_dev *)data;
288317f06deSGavin Shan 	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
289317f06deSGavin Shan 	struct pci_driver *driver;
290317f06deSGavin Shan 
291317f06deSGavin Shan 	if (!dev) return NULL;
292317f06deSGavin Shan 	dev->error_state = pci_channel_io_normal;
293317f06deSGavin Shan 
294317f06deSGavin Shan 	driver = eeh_pcid_get(dev);
295317f06deSGavin Shan 	if (!driver) return NULL;
296317f06deSGavin Shan 
297317f06deSGavin Shan 	eeh_enable_irq(dev);
298317f06deSGavin Shan 
299317f06deSGavin Shan 	if (!driver->err_handler ||
300317f06deSGavin Shan 	    !driver->err_handler->resume) {
301317f06deSGavin Shan 		eeh_pcid_put(dev);
302317f06deSGavin Shan 		return NULL;
303317f06deSGavin Shan 	}
304317f06deSGavin Shan 
305317f06deSGavin Shan 	driver->err_handler->resume(dev);
306317f06deSGavin Shan 
307317f06deSGavin Shan 	eeh_pcid_put(dev);
308317f06deSGavin Shan 	return NULL;
309317f06deSGavin Shan }
310317f06deSGavin Shan 
311317f06deSGavin Shan /**
312317f06deSGavin Shan  * eeh_report_failure - Tell device driver that device is dead.
313317f06deSGavin Shan  * @data: eeh device
314317f06deSGavin Shan  * @userdata: return value
315317f06deSGavin Shan  *
316317f06deSGavin Shan  * This informs the device driver that the device is permanently
317317f06deSGavin Shan  * dead, and that no further recovery attempts will be made on it.
318317f06deSGavin Shan  */
319317f06deSGavin Shan static void *eeh_report_failure(void *data, void *userdata)
320317f06deSGavin Shan {
321317f06deSGavin Shan 	struct eeh_dev *edev = (struct eeh_dev *)data;
322317f06deSGavin Shan 	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
323317f06deSGavin Shan 	struct pci_driver *driver;
324317f06deSGavin Shan 
325317f06deSGavin Shan 	if (!dev) return NULL;
326317f06deSGavin Shan 	dev->error_state = pci_channel_io_perm_failure;
327317f06deSGavin Shan 
328317f06deSGavin Shan 	driver = eeh_pcid_get(dev);
329317f06deSGavin Shan 	if (!driver) return NULL;
330317f06deSGavin Shan 
331317f06deSGavin Shan 	eeh_disable_irq(dev);
332317f06deSGavin Shan 
333317f06deSGavin Shan 	if (!driver->err_handler ||
334317f06deSGavin Shan 	    !driver->err_handler->error_detected) {
335317f06deSGavin Shan 		eeh_pcid_put(dev);
336317f06deSGavin Shan 		return NULL;
337317f06deSGavin Shan 	}
338317f06deSGavin Shan 
339317f06deSGavin Shan 	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
340317f06deSGavin Shan 
341317f06deSGavin Shan 	eeh_pcid_put(dev);
342317f06deSGavin Shan 	return NULL;
343317f06deSGavin Shan }
344317f06deSGavin Shan 
345f5c57710SGavin Shan static void *eeh_rmv_device(void *data, void *userdata)
346f5c57710SGavin Shan {
347f5c57710SGavin Shan 	struct pci_driver *driver;
348f5c57710SGavin Shan 	struct eeh_dev *edev = (struct eeh_dev *)data;
349f5c57710SGavin Shan 	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
350f5c57710SGavin Shan 	int *removed = (int *)userdata;
351f5c57710SGavin Shan 
352f5c57710SGavin Shan 	/*
353f5c57710SGavin Shan 	 * Actually, we should remove the PCI bridges as well.
354f5c57710SGavin Shan 	 * However, that's lots of complexity to do that,
355f5c57710SGavin Shan 	 * particularly some of devices under the bridge might
356f5c57710SGavin Shan 	 * support EEH. So we just care about PCI devices for
357f5c57710SGavin Shan 	 * simplicity here.
358f5c57710SGavin Shan 	 */
359f5c57710SGavin Shan 	if (!dev || (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
360f5c57710SGavin Shan 		return NULL;
361f5c57710SGavin Shan 	driver = eeh_pcid_get(dev);
362f5c57710SGavin Shan 	if (driver && driver->err_handler)
363f5c57710SGavin Shan 		return NULL;
364f5c57710SGavin Shan 
365f5c57710SGavin Shan 	/* Remove it from PCI subsystem */
366f5c57710SGavin Shan 	pr_debug("EEH: Removing %s without EEH sensitive driver\n",
367f5c57710SGavin Shan 		 pci_name(dev));
368f5c57710SGavin Shan 	edev->bus = dev->bus;
369f5c57710SGavin Shan 	edev->mode |= EEH_DEV_DISCONNECTED;
370f5c57710SGavin Shan 	(*removed)++;
371f5c57710SGavin Shan 
372f5c57710SGavin Shan 	pci_stop_and_remove_bus_device(dev);
373f5c57710SGavin Shan 
374f5c57710SGavin Shan 	return NULL;
375f5c57710SGavin Shan }
376f5c57710SGavin Shan 
377f5c57710SGavin Shan static void *eeh_pe_detach_dev(void *data, void *userdata)
378f5c57710SGavin Shan {
379f5c57710SGavin Shan 	struct eeh_pe *pe = (struct eeh_pe *)data;
380f5c57710SGavin Shan 	struct eeh_dev *edev, *tmp;
381f5c57710SGavin Shan 
382f5c57710SGavin Shan 	eeh_pe_for_each_dev(pe, edev, tmp) {
383f5c57710SGavin Shan 		if (!(edev->mode & EEH_DEV_DISCONNECTED))
384f5c57710SGavin Shan 			continue;
385f5c57710SGavin Shan 
386f5c57710SGavin Shan 		edev->mode &= ~(EEH_DEV_DISCONNECTED | EEH_DEV_IRQ_DISABLED);
387f5c57710SGavin Shan 		eeh_rmv_from_parent_pe(edev);
388f5c57710SGavin Shan 	}
389f5c57710SGavin Shan 
390f5c57710SGavin Shan 	return NULL;
391f5c57710SGavin Shan }
392f5c57710SGavin Shan 
393317f06deSGavin Shan /**
394317f06deSGavin Shan  * eeh_reset_device - Perform actual reset of a pci slot
395317f06deSGavin Shan  * @pe: EEH PE
396317f06deSGavin Shan  * @bus: PCI bus corresponding to the isolcated slot
397317f06deSGavin Shan  *
398317f06deSGavin Shan  * This routine must be called to do reset on the indicated PE.
399317f06deSGavin Shan  * During the reset, udev might be invoked because those affected
400317f06deSGavin Shan  * PCI devices will be removed and then added.
401317f06deSGavin Shan  */
402317f06deSGavin Shan static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
403317f06deSGavin Shan {
404f5c57710SGavin Shan 	struct pci_bus *frozen_bus = eeh_pe_bus_get(pe);
4055a71978eSGavin Shan 	struct timeval tstamp;
406f5c57710SGavin Shan 	int cnt, rc, removed = 0;
407317f06deSGavin Shan 
408317f06deSGavin Shan 	/* pcibios will clear the counter; save the value */
409317f06deSGavin Shan 	cnt = pe->freeze_count;
4105a71978eSGavin Shan 	tstamp = pe->tstamp;
411317f06deSGavin Shan 
412317f06deSGavin Shan 	/*
413317f06deSGavin Shan 	 * We don't remove the corresponding PE instances because
414317f06deSGavin Shan 	 * we need the information afterwords. The attached EEH
415317f06deSGavin Shan 	 * devices are expected to be attached soon when calling
416317f06deSGavin Shan 	 * into pcibios_add_pci_devices().
417317f06deSGavin Shan 	 */
418807a827dSGavin Shan 	eeh_pe_state_mark(pe, EEH_PE_KEEP);
419f5c57710SGavin Shan 	if (bus)
420807a827dSGavin Shan 		pcibios_remove_pci_devices(bus);
421f5c57710SGavin Shan 	else if (frozen_bus)
422f5c57710SGavin Shan 		eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed);
423317f06deSGavin Shan 
424317f06deSGavin Shan 	/* Reset the pci controller. (Asserts RST#; resets config space).
425317f06deSGavin Shan 	 * Reconfigure bridges and devices. Don't try to bring the system
426317f06deSGavin Shan 	 * up if the reset failed for some reason.
427317f06deSGavin Shan 	 */
428317f06deSGavin Shan 	rc = eeh_reset_pe(pe);
429317f06deSGavin Shan 	if (rc)
430317f06deSGavin Shan 		return rc;
431317f06deSGavin Shan 
432317f06deSGavin Shan 	/* Restore PE */
433317f06deSGavin Shan 	eeh_ops->configure_bridge(pe);
434317f06deSGavin Shan 	eeh_pe_restore_bars(pe);
435317f06deSGavin Shan 
436317f06deSGavin Shan 	/* Give the system 5 seconds to finish running the user-space
437317f06deSGavin Shan 	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes,
438317f06deSGavin Shan 	 * this is a hack, but if we don't do this, and try to bring
439317f06deSGavin Shan 	 * the device up before the scripts have taken it down,
440317f06deSGavin Shan 	 * potentially weird things happen.
441317f06deSGavin Shan 	 */
442317f06deSGavin Shan 	if (bus) {
443f5c57710SGavin Shan 		pr_info("EEH: Sleep 5s ahead of complete hotplug\n");
444317f06deSGavin Shan 		ssleep(5);
445f5c57710SGavin Shan 
446f5c57710SGavin Shan 		/*
447f5c57710SGavin Shan 		 * The EEH device is still connected with its parent
448f5c57710SGavin Shan 		 * PE. We should disconnect it so the binding can be
449f5c57710SGavin Shan 		 * rebuilt when adding PCI devices.
450f5c57710SGavin Shan 		 */
451f5c57710SGavin Shan 		eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL);
452317f06deSGavin Shan 		pcibios_add_pci_devices(bus);
453f5c57710SGavin Shan 	} else if (frozen_bus && removed) {
454f5c57710SGavin Shan 		pr_info("EEH: Sleep 5s ahead of partial hotplug\n");
455f5c57710SGavin Shan 		ssleep(5);
456f5c57710SGavin Shan 
457f5c57710SGavin Shan 		eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL);
458f5c57710SGavin Shan 		pcibios_add_pci_devices(frozen_bus);
459317f06deSGavin Shan 	}
460f5c57710SGavin Shan 	eeh_pe_state_clear(pe, EEH_PE_KEEP);
4615a71978eSGavin Shan 
4625a71978eSGavin Shan 	pe->tstamp = tstamp;
463317f06deSGavin Shan 	pe->freeze_count = cnt;
464317f06deSGavin Shan 
465317f06deSGavin Shan 	return 0;
466317f06deSGavin Shan }
467317f06deSGavin Shan 
468317f06deSGavin Shan /* The longest amount of time to wait for a pci device
469317f06deSGavin Shan  * to come back on line, in seconds.
470317f06deSGavin Shan  */
471fb48dc22SBrian King #define MAX_WAIT_FOR_RECOVERY 300
472317f06deSGavin Shan 
4738a6b1bc7SGavin Shan static void eeh_handle_normal_event(struct eeh_pe *pe)
474317f06deSGavin Shan {
475317f06deSGavin Shan 	struct pci_bus *frozen_bus;
476317f06deSGavin Shan 	int rc = 0;
477317f06deSGavin Shan 	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
478317f06deSGavin Shan 
479317f06deSGavin Shan 	frozen_bus = eeh_pe_bus_get(pe);
480317f06deSGavin Shan 	if (!frozen_bus) {
481317f06deSGavin Shan 		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
482317f06deSGavin Shan 			__func__, pe->phb->global_number, pe->addr);
483317f06deSGavin Shan 		return;
484317f06deSGavin Shan 	}
485317f06deSGavin Shan 
4865a71978eSGavin Shan 	eeh_pe_update_time_stamp(pe);
487317f06deSGavin Shan 	pe->freeze_count++;
488317f06deSGavin Shan 	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
489317f06deSGavin Shan 		goto excess_failures;
490317f06deSGavin Shan 	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
491317f06deSGavin Shan 		pe->freeze_count);
492317f06deSGavin Shan 
493317f06deSGavin Shan 	/* Walk the various device drivers attached to this slot through
494317f06deSGavin Shan 	 * a reset sequence, giving each an opportunity to do what it needs
495317f06deSGavin Shan 	 * to accomplish the reset.  Each child gets a report of the
496317f06deSGavin Shan 	 * status ... if any child can't handle the reset, then the entire
497317f06deSGavin Shan 	 * slot is dlpar removed and added.
498317f06deSGavin Shan 	 */
49956ca4fdeSGavin Shan 	pr_info("EEH: Notify device drivers to shutdown\n");
500317f06deSGavin Shan 	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
501317f06deSGavin Shan 
502317f06deSGavin Shan 	/* Get the current PCI slot state. This can take a long time,
503317f06deSGavin Shan 	 * sometimes over 3 seconds for certain systems.
504317f06deSGavin Shan 	 */
505317f06deSGavin Shan 	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
506317f06deSGavin Shan 	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
50756ca4fdeSGavin Shan 		pr_warning("EEH: Permanent failure\n");
508317f06deSGavin Shan 		goto hard_fail;
509317f06deSGavin Shan 	}
510317f06deSGavin Shan 
511317f06deSGavin Shan 	/* Since rtas may enable MMIO when posting the error log,
512317f06deSGavin Shan 	 * don't post the error log until after all dev drivers
513317f06deSGavin Shan 	 * have been informed.
514317f06deSGavin Shan 	 */
51556ca4fdeSGavin Shan 	pr_info("EEH: Collect temporary log\n");
516317f06deSGavin Shan 	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
517317f06deSGavin Shan 
518317f06deSGavin Shan 	/* If all device drivers were EEH-unaware, then shut
519317f06deSGavin Shan 	 * down all of the device drivers, and hope they
520317f06deSGavin Shan 	 * go down willingly, without panicing the system.
521317f06deSGavin Shan 	 */
522317f06deSGavin Shan 	if (result == PCI_ERS_RESULT_NONE) {
52356ca4fdeSGavin Shan 		pr_info("EEH: Reset with hotplug activity\n");
524317f06deSGavin Shan 		rc = eeh_reset_device(pe, frozen_bus);
525317f06deSGavin Shan 		if (rc) {
52656ca4fdeSGavin Shan 			pr_warning("%s: Unable to reset, err=%d\n",
52756ca4fdeSGavin Shan 				   __func__, rc);
528317f06deSGavin Shan 			goto hard_fail;
529317f06deSGavin Shan 		}
530317f06deSGavin Shan 	}
531317f06deSGavin Shan 
532317f06deSGavin Shan 	/* If all devices reported they can proceed, then re-enable MMIO */
533317f06deSGavin Shan 	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
53456ca4fdeSGavin Shan 		pr_info("EEH: Enable I/O for affected devices\n");
535317f06deSGavin Shan 		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
536317f06deSGavin Shan 
537317f06deSGavin Shan 		if (rc < 0)
538317f06deSGavin Shan 			goto hard_fail;
539317f06deSGavin Shan 		if (rc) {
540317f06deSGavin Shan 			result = PCI_ERS_RESULT_NEED_RESET;
541317f06deSGavin Shan 		} else {
54256ca4fdeSGavin Shan 			pr_info("EEH: Notify device drivers to resume I/O\n");
543317f06deSGavin Shan 			result = PCI_ERS_RESULT_NONE;
544317f06deSGavin Shan 			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
545317f06deSGavin Shan 		}
546317f06deSGavin Shan 	}
547317f06deSGavin Shan 
548317f06deSGavin Shan 	/* If all devices reported they can proceed, then re-enable DMA */
549317f06deSGavin Shan 	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
55056ca4fdeSGavin Shan 		pr_info("EEH: Enabled DMA for affected devices\n");
551317f06deSGavin Shan 		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
552317f06deSGavin Shan 
553317f06deSGavin Shan 		if (rc < 0)
554317f06deSGavin Shan 			goto hard_fail;
555317f06deSGavin Shan 		if (rc)
556317f06deSGavin Shan 			result = PCI_ERS_RESULT_NEED_RESET;
557317f06deSGavin Shan 		else
558317f06deSGavin Shan 			result = PCI_ERS_RESULT_RECOVERED;
559317f06deSGavin Shan 	}
560317f06deSGavin Shan 
561317f06deSGavin Shan 	/* If any device has a hard failure, then shut off everything. */
562317f06deSGavin Shan 	if (result == PCI_ERS_RESULT_DISCONNECT) {
56356ca4fdeSGavin Shan 		pr_warning("EEH: Device driver gave up\n");
564317f06deSGavin Shan 		goto hard_fail;
565317f06deSGavin Shan 	}
566317f06deSGavin Shan 
567317f06deSGavin Shan 	/* If any device called out for a reset, then reset the slot */
568317f06deSGavin Shan 	if (result == PCI_ERS_RESULT_NEED_RESET) {
56956ca4fdeSGavin Shan 		pr_info("EEH: Reset without hotplug activity\n");
570317f06deSGavin Shan 		rc = eeh_reset_device(pe, NULL);
571317f06deSGavin Shan 		if (rc) {
57256ca4fdeSGavin Shan 			pr_warning("%s: Cannot reset, err=%d\n",
57356ca4fdeSGavin Shan 				   __func__, rc);
574317f06deSGavin Shan 			goto hard_fail;
575317f06deSGavin Shan 		}
57656ca4fdeSGavin Shan 
57756ca4fdeSGavin Shan 		pr_info("EEH: Notify device drivers "
57856ca4fdeSGavin Shan 			"the completion of reset\n");
579317f06deSGavin Shan 		result = PCI_ERS_RESULT_NONE;
580317f06deSGavin Shan 		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
581317f06deSGavin Shan 	}
582317f06deSGavin Shan 
583317f06deSGavin Shan 	/* All devices should claim they have recovered by now. */
584317f06deSGavin Shan 	if ((result != PCI_ERS_RESULT_RECOVERED) &&
585317f06deSGavin Shan 	    (result != PCI_ERS_RESULT_NONE)) {
58656ca4fdeSGavin Shan 		pr_warning("EEH: Not recovered\n");
587317f06deSGavin Shan 		goto hard_fail;
588317f06deSGavin Shan 	}
589317f06deSGavin Shan 
590317f06deSGavin Shan 	/* Tell all device drivers that they can resume operations */
59156ca4fdeSGavin Shan 	pr_info("EEH: Notify device driver to resume\n");
592317f06deSGavin Shan 	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
593317f06deSGavin Shan 
594317f06deSGavin Shan 	return;
595317f06deSGavin Shan 
596317f06deSGavin Shan excess_failures:
597317f06deSGavin Shan 	/*
598317f06deSGavin Shan 	 * About 90% of all real-life EEH failures in the field
599317f06deSGavin Shan 	 * are due to poorly seated PCI cards. Only 10% or so are
600317f06deSGavin Shan 	 * due to actual, failed cards.
601317f06deSGavin Shan 	 */
602317f06deSGavin Shan 	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
603317f06deSGavin Shan 	       "last hour and has been permanently disabled.\n"
604317f06deSGavin Shan 	       "Please try reseating or replacing it.\n",
605317f06deSGavin Shan 		pe->phb->global_number, pe->addr,
606317f06deSGavin Shan 		pe->freeze_count);
607317f06deSGavin Shan 	goto perm_error;
608317f06deSGavin Shan 
609317f06deSGavin Shan hard_fail:
610317f06deSGavin Shan 	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
611317f06deSGavin Shan 	       "Please try reseating or replacing it\n",
612317f06deSGavin Shan 		pe->phb->global_number, pe->addr);
613317f06deSGavin Shan 
614317f06deSGavin Shan perm_error:
615317f06deSGavin Shan 	eeh_slot_error_detail(pe, EEH_LOG_PERM);
616317f06deSGavin Shan 
617317f06deSGavin Shan 	/* Notify all devices that they're about to go down. */
618317f06deSGavin Shan 	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
619317f06deSGavin Shan 
620317f06deSGavin Shan 	/* Shut down the device drivers for good. */
621317f06deSGavin Shan 	if (frozen_bus)
622317f06deSGavin Shan 		pcibios_remove_pci_devices(frozen_bus);
623317f06deSGavin Shan }
6248a6b1bc7SGavin Shan 
6258a6b1bc7SGavin Shan static void eeh_handle_special_event(void)
6268a6b1bc7SGavin Shan {
6278a6b1bc7SGavin Shan 	struct eeh_pe *pe, *phb_pe;
6288a6b1bc7SGavin Shan 	struct pci_bus *bus;
6298a6b1bc7SGavin Shan 	struct pci_controller *hose, *tmp;
6308a6b1bc7SGavin Shan 	unsigned long flags;
6318a6b1bc7SGavin Shan 	int rc = 0;
6328a6b1bc7SGavin Shan 
6338a6b1bc7SGavin Shan 	/*
6348a6b1bc7SGavin Shan 	 * The return value from next_error() has been classified as follows.
6358a6b1bc7SGavin Shan 	 * It might be good to enumerate them. However, next_error() is only
6368a6b1bc7SGavin Shan 	 * supported by PowerNV platform for now. So it would be fine to use
6378a6b1bc7SGavin Shan 	 * integer directly:
6388a6b1bc7SGavin Shan 	 *
6398a6b1bc7SGavin Shan 	 * 4 - Dead IOC           3 - Dead PHB
6408a6b1bc7SGavin Shan 	 * 2 - Fenced PHB         1 - Frozen PE
6418a6b1bc7SGavin Shan 	 * 0 - No error found
6428a6b1bc7SGavin Shan 	 *
6438a6b1bc7SGavin Shan 	 */
6448a6b1bc7SGavin Shan 	rc = eeh_ops->next_error(&pe);
6458a6b1bc7SGavin Shan 	if (rc <= 0)
6468a6b1bc7SGavin Shan 		return;
6478a6b1bc7SGavin Shan 
6488a6b1bc7SGavin Shan 	switch (rc) {
6498a6b1bc7SGavin Shan 	case 4:
6508a6b1bc7SGavin Shan 		/* Mark all PHBs in dead state */
6518a6b1bc7SGavin Shan 		eeh_serialize_lock(&flags);
6528a6b1bc7SGavin Shan 		list_for_each_entry_safe(hose, tmp,
6538a6b1bc7SGavin Shan 				&hose_list, list_node) {
6548a6b1bc7SGavin Shan 			phb_pe = eeh_phb_pe_get(hose);
6558a6b1bc7SGavin Shan 			if (!phb_pe) continue;
6568a6b1bc7SGavin Shan 
6578a6b1bc7SGavin Shan 			eeh_pe_state_mark(phb_pe,
6588a6b1bc7SGavin Shan 				EEH_PE_ISOLATED | EEH_PE_PHB_DEAD);
6598a6b1bc7SGavin Shan 		}
6608a6b1bc7SGavin Shan 		eeh_serialize_unlock(flags);
6618a6b1bc7SGavin Shan 
6628a6b1bc7SGavin Shan 		/* Purge all events */
6638a6b1bc7SGavin Shan 		eeh_remove_event(NULL);
6648a6b1bc7SGavin Shan 		break;
6658a6b1bc7SGavin Shan 	case 3:
6668a6b1bc7SGavin Shan 	case 2:
6678a6b1bc7SGavin Shan 	case 1:
6688a6b1bc7SGavin Shan 		/* Mark the PE in fenced state */
6698a6b1bc7SGavin Shan 		eeh_serialize_lock(&flags);
6708a6b1bc7SGavin Shan 		if (rc == 3)
6718a6b1bc7SGavin Shan 			eeh_pe_state_mark(pe,
6728a6b1bc7SGavin Shan 				EEH_PE_ISOLATED | EEH_PE_PHB_DEAD);
6738a6b1bc7SGavin Shan 		else
6748a6b1bc7SGavin Shan 			eeh_pe_state_mark(pe,
6758a6b1bc7SGavin Shan 				EEH_PE_ISOLATED | EEH_PE_RECOVERING);
6768a6b1bc7SGavin Shan 		eeh_serialize_unlock(flags);
6778a6b1bc7SGavin Shan 
6788a6b1bc7SGavin Shan 		/* Purge all events of the PHB */
6798a6b1bc7SGavin Shan 		eeh_remove_event(pe);
6808a6b1bc7SGavin Shan 		break;
6818a6b1bc7SGavin Shan 	default:
6828a6b1bc7SGavin Shan 		pr_err("%s: Invalid value %d from next_error()\n",
6838a6b1bc7SGavin Shan 		       __func__, rc);
6848a6b1bc7SGavin Shan 		return;
6858a6b1bc7SGavin Shan 	}
6868a6b1bc7SGavin Shan 
6878a6b1bc7SGavin Shan 	/*
6888a6b1bc7SGavin Shan 	 * For fenced PHB and frozen PE, it's handled as normal
6898a6b1bc7SGavin Shan 	 * event. We have to remove the affected PHBs for dead
6908a6b1bc7SGavin Shan 	 * PHB and IOC
6918a6b1bc7SGavin Shan 	 */
6928a6b1bc7SGavin Shan 	if (rc == 2 || rc == 1)
6938a6b1bc7SGavin Shan 		eeh_handle_normal_event(pe);
6948a6b1bc7SGavin Shan 	else {
6958a6b1bc7SGavin Shan 		list_for_each_entry_safe(hose, tmp,
6968a6b1bc7SGavin Shan 			&hose_list, list_node) {
6978a6b1bc7SGavin Shan 			phb_pe = eeh_phb_pe_get(hose);
6988a6b1bc7SGavin Shan 			if (!phb_pe || !(phb_pe->state & EEH_PE_PHB_DEAD))
6998a6b1bc7SGavin Shan 				continue;
7008a6b1bc7SGavin Shan 
7018a6b1bc7SGavin Shan 			bus = eeh_pe_bus_get(phb_pe);
7028a6b1bc7SGavin Shan 			/* Notify all devices that they're about to go down. */
7038a6b1bc7SGavin Shan 			eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
7048a6b1bc7SGavin Shan 			pcibios_remove_pci_devices(bus);
7058a6b1bc7SGavin Shan 		}
7068a6b1bc7SGavin Shan 	}
7078a6b1bc7SGavin Shan }
7088a6b1bc7SGavin Shan 
7098a6b1bc7SGavin Shan /**
7108a6b1bc7SGavin Shan  * eeh_handle_event - Reset a PCI device after hard lockup.
7118a6b1bc7SGavin Shan  * @pe: EEH PE
7128a6b1bc7SGavin Shan  *
7138a6b1bc7SGavin Shan  * While PHB detects address or data parity errors on particular PCI
7148a6b1bc7SGavin Shan  * slot, the associated PE will be frozen. Besides, DMA's occurring
7158a6b1bc7SGavin Shan  * to wild addresses (which usually happen due to bugs in device
7168a6b1bc7SGavin Shan  * drivers or in PCI adapter firmware) can cause EEH error. #SERR,
7178a6b1bc7SGavin Shan  * #PERR or other misc PCI-related errors also can trigger EEH errors.
7188a6b1bc7SGavin Shan  *
7198a6b1bc7SGavin Shan  * Recovery process consists of unplugging the device driver (which
7208a6b1bc7SGavin Shan  * generated hotplug events to userspace), then issuing a PCI #RST to
7218a6b1bc7SGavin Shan  * the device, then reconfiguring the PCI config space for all bridges
7228a6b1bc7SGavin Shan  * & devices under this slot, and then finally restarting the device
7238a6b1bc7SGavin Shan  * drivers (which cause a second set of hotplug events to go out to
7248a6b1bc7SGavin Shan  * userspace).
7258a6b1bc7SGavin Shan  */
7268a6b1bc7SGavin Shan void eeh_handle_event(struct eeh_pe *pe)
7278a6b1bc7SGavin Shan {
7288a6b1bc7SGavin Shan 	if (pe)
7298a6b1bc7SGavin Shan 		eeh_handle_normal_event(pe);
7308a6b1bc7SGavin Shan 	else
7318a6b1bc7SGavin Shan 		eeh_handle_special_event();
7328a6b1bc7SGavin Shan }
733