xref: /linux/drivers/pci/pcie/err.c (revision 2e28bc84cf6eecd3759d7ae723bb0f5f09becf76)
1*2e28bc84SOza Pawandeep // SPDX-License-Identifier: GPL-2.0
2*2e28bc84SOza Pawandeep /*
3*2e28bc84SOza Pawandeep  * This file implements the error recovery as a core part of PCIe error
4*2e28bc84SOza Pawandeep  * reporting. When a PCIe error is delivered, an error message will be
5*2e28bc84SOza Pawandeep  * collected and printed to console, then, an error recovery procedure
6*2e28bc84SOza Pawandeep  * will be executed by following the PCI error recovery rules.
7*2e28bc84SOza Pawandeep  *
8*2e28bc84SOza Pawandeep  * Copyright (C) 2006 Intel Corp.
9*2e28bc84SOza Pawandeep  *	Tom Long Nguyen (tom.l.nguyen@intel.com)
10*2e28bc84SOza Pawandeep  *	Zhang Yanmin (yanmin.zhang@intel.com)
11*2e28bc84SOza Pawandeep  */
12*2e28bc84SOza Pawandeep 
13*2e28bc84SOza Pawandeep #include <linux/pci.h>
14*2e28bc84SOza Pawandeep #include <linux/module.h>
15*2e28bc84SOza Pawandeep #include <linux/pci.h>
16*2e28bc84SOza Pawandeep #include <linux/kernel.h>
17*2e28bc84SOza Pawandeep #include <linux/errno.h>
18*2e28bc84SOza Pawandeep #include <linux/aer.h>
19*2e28bc84SOza Pawandeep #include "portdrv.h"
20*2e28bc84SOza Pawandeep #include "../pci.h"
21*2e28bc84SOza Pawandeep 
22*2e28bc84SOza Pawandeep struct aer_broadcast_data {
23*2e28bc84SOza Pawandeep 	enum pci_channel_state state;
24*2e28bc84SOza Pawandeep 	enum pci_ers_result result;
25*2e28bc84SOza Pawandeep };
26*2e28bc84SOza Pawandeep 
27*2e28bc84SOza Pawandeep static pci_ers_result_t merge_result(enum pci_ers_result orig,
28*2e28bc84SOza Pawandeep 				  enum pci_ers_result new)
29*2e28bc84SOza Pawandeep {
30*2e28bc84SOza Pawandeep 	if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
31*2e28bc84SOza Pawandeep 		return PCI_ERS_RESULT_NO_AER_DRIVER;
32*2e28bc84SOza Pawandeep 
33*2e28bc84SOza Pawandeep 	if (new == PCI_ERS_RESULT_NONE)
34*2e28bc84SOza Pawandeep 		return orig;
35*2e28bc84SOza Pawandeep 
36*2e28bc84SOza Pawandeep 	switch (orig) {
37*2e28bc84SOza Pawandeep 	case PCI_ERS_RESULT_CAN_RECOVER:
38*2e28bc84SOza Pawandeep 	case PCI_ERS_RESULT_RECOVERED:
39*2e28bc84SOza Pawandeep 		orig = new;
40*2e28bc84SOza Pawandeep 		break;
41*2e28bc84SOza Pawandeep 	case PCI_ERS_RESULT_DISCONNECT:
42*2e28bc84SOza Pawandeep 		if (new == PCI_ERS_RESULT_NEED_RESET)
43*2e28bc84SOza Pawandeep 			orig = PCI_ERS_RESULT_NEED_RESET;
44*2e28bc84SOza Pawandeep 		break;
45*2e28bc84SOza Pawandeep 	default:
46*2e28bc84SOza Pawandeep 		break;
47*2e28bc84SOza Pawandeep 	}
48*2e28bc84SOza Pawandeep 
49*2e28bc84SOza Pawandeep 	return orig;
50*2e28bc84SOza Pawandeep }
51*2e28bc84SOza Pawandeep 
52*2e28bc84SOza Pawandeep static int report_error_detected(struct pci_dev *dev, void *data)
53*2e28bc84SOza Pawandeep {
54*2e28bc84SOza Pawandeep 	pci_ers_result_t vote;
55*2e28bc84SOza Pawandeep 	const struct pci_error_handlers *err_handler;
56*2e28bc84SOza Pawandeep 	struct aer_broadcast_data *result_data;
57*2e28bc84SOza Pawandeep 
58*2e28bc84SOza Pawandeep 	result_data = (struct aer_broadcast_data *) data;
59*2e28bc84SOza Pawandeep 
60*2e28bc84SOza Pawandeep 	device_lock(&dev->dev);
61*2e28bc84SOza Pawandeep 	dev->error_state = result_data->state;
62*2e28bc84SOza Pawandeep 
63*2e28bc84SOza Pawandeep 	if (!dev->driver ||
64*2e28bc84SOza Pawandeep 		!dev->driver->err_handler ||
65*2e28bc84SOza Pawandeep 		!dev->driver->err_handler->error_detected) {
66*2e28bc84SOza Pawandeep 		if (result_data->state == pci_channel_io_frozen &&
67*2e28bc84SOza Pawandeep 			dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
68*2e28bc84SOza Pawandeep 			/*
69*2e28bc84SOza Pawandeep 			 * In case of fatal recovery, if one of down-
70*2e28bc84SOza Pawandeep 			 * stream device has no driver. We might be
71*2e28bc84SOza Pawandeep 			 * unable to recover because a later insmod
72*2e28bc84SOza Pawandeep 			 * of a driver for this device is unaware of
73*2e28bc84SOza Pawandeep 			 * its hw state.
74*2e28bc84SOza Pawandeep 			 */
75*2e28bc84SOza Pawandeep 			pci_printk(KERN_DEBUG, dev, "device has %s\n",
76*2e28bc84SOza Pawandeep 				   dev->driver ?
77*2e28bc84SOza Pawandeep 				   "no AER-aware driver" : "no driver");
78*2e28bc84SOza Pawandeep 		}
79*2e28bc84SOza Pawandeep 
80*2e28bc84SOza Pawandeep 		/*
81*2e28bc84SOza Pawandeep 		 * If there's any device in the subtree that does not
82*2e28bc84SOza Pawandeep 		 * have an error_detected callback, returning
83*2e28bc84SOza Pawandeep 		 * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
84*2e28bc84SOza Pawandeep 		 * the subsequent mmio_enabled/slot_reset/resume
85*2e28bc84SOza Pawandeep 		 * callbacks of "any" device in the subtree. All the
86*2e28bc84SOza Pawandeep 		 * devices in the subtree are left in the error state
87*2e28bc84SOza Pawandeep 		 * without recovery.
88*2e28bc84SOza Pawandeep 		 */
89*2e28bc84SOza Pawandeep 
90*2e28bc84SOza Pawandeep 		if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
91*2e28bc84SOza Pawandeep 			vote = PCI_ERS_RESULT_NO_AER_DRIVER;
92*2e28bc84SOza Pawandeep 		else
93*2e28bc84SOza Pawandeep 			vote = PCI_ERS_RESULT_NONE;
94*2e28bc84SOza Pawandeep 	} else {
95*2e28bc84SOza Pawandeep 		err_handler = dev->driver->err_handler;
96*2e28bc84SOza Pawandeep 		vote = err_handler->error_detected(dev, result_data->state);
97*2e28bc84SOza Pawandeep 		pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
98*2e28bc84SOza Pawandeep 	}
99*2e28bc84SOza Pawandeep 
100*2e28bc84SOza Pawandeep 	result_data->result = merge_result(result_data->result, vote);
101*2e28bc84SOza Pawandeep 	device_unlock(&dev->dev);
102*2e28bc84SOza Pawandeep 	return 0;
103*2e28bc84SOza Pawandeep }
104*2e28bc84SOza Pawandeep 
105*2e28bc84SOza Pawandeep static int report_mmio_enabled(struct pci_dev *dev, void *data)
106*2e28bc84SOza Pawandeep {
107*2e28bc84SOza Pawandeep 	pci_ers_result_t vote;
108*2e28bc84SOza Pawandeep 	const struct pci_error_handlers *err_handler;
109*2e28bc84SOza Pawandeep 	struct aer_broadcast_data *result_data;
110*2e28bc84SOza Pawandeep 
111*2e28bc84SOza Pawandeep 	result_data = (struct aer_broadcast_data *) data;
112*2e28bc84SOza Pawandeep 
113*2e28bc84SOza Pawandeep 	device_lock(&dev->dev);
114*2e28bc84SOza Pawandeep 	if (!dev->driver ||
115*2e28bc84SOza Pawandeep 		!dev->driver->err_handler ||
116*2e28bc84SOza Pawandeep 		!dev->driver->err_handler->mmio_enabled)
117*2e28bc84SOza Pawandeep 		goto out;
118*2e28bc84SOza Pawandeep 
119*2e28bc84SOza Pawandeep 	err_handler = dev->driver->err_handler;
120*2e28bc84SOza Pawandeep 	vote = err_handler->mmio_enabled(dev);
121*2e28bc84SOza Pawandeep 	result_data->result = merge_result(result_data->result, vote);
122*2e28bc84SOza Pawandeep out:
123*2e28bc84SOza Pawandeep 	device_unlock(&dev->dev);
124*2e28bc84SOza Pawandeep 	return 0;
125*2e28bc84SOza Pawandeep }
126*2e28bc84SOza Pawandeep 
127*2e28bc84SOza Pawandeep static int report_slot_reset(struct pci_dev *dev, void *data)
128*2e28bc84SOza Pawandeep {
129*2e28bc84SOza Pawandeep 	pci_ers_result_t vote;
130*2e28bc84SOza Pawandeep 	const struct pci_error_handlers *err_handler;
131*2e28bc84SOza Pawandeep 	struct aer_broadcast_data *result_data;
132*2e28bc84SOza Pawandeep 
133*2e28bc84SOza Pawandeep 	result_data = (struct aer_broadcast_data *) data;
134*2e28bc84SOza Pawandeep 
135*2e28bc84SOza Pawandeep 	device_lock(&dev->dev);
136*2e28bc84SOza Pawandeep 	if (!dev->driver ||
137*2e28bc84SOza Pawandeep 		!dev->driver->err_handler ||
138*2e28bc84SOza Pawandeep 		!dev->driver->err_handler->slot_reset)
139*2e28bc84SOza Pawandeep 		goto out;
140*2e28bc84SOza Pawandeep 
141*2e28bc84SOza Pawandeep 	err_handler = dev->driver->err_handler;
142*2e28bc84SOza Pawandeep 	vote = err_handler->slot_reset(dev);
143*2e28bc84SOza Pawandeep 	result_data->result = merge_result(result_data->result, vote);
144*2e28bc84SOza Pawandeep out:
145*2e28bc84SOza Pawandeep 	device_unlock(&dev->dev);
146*2e28bc84SOza Pawandeep 	return 0;
147*2e28bc84SOza Pawandeep }
148*2e28bc84SOza Pawandeep 
149*2e28bc84SOza Pawandeep static int report_resume(struct pci_dev *dev, void *data)
150*2e28bc84SOza Pawandeep {
151*2e28bc84SOza Pawandeep 	const struct pci_error_handlers *err_handler;
152*2e28bc84SOza Pawandeep 
153*2e28bc84SOza Pawandeep 	device_lock(&dev->dev);
154*2e28bc84SOza Pawandeep 	dev->error_state = pci_channel_io_normal;
155*2e28bc84SOza Pawandeep 
156*2e28bc84SOza Pawandeep 	if (!dev->driver ||
157*2e28bc84SOza Pawandeep 		!dev->driver->err_handler ||
158*2e28bc84SOza Pawandeep 		!dev->driver->err_handler->resume)
159*2e28bc84SOza Pawandeep 		goto out;
160*2e28bc84SOza Pawandeep 
161*2e28bc84SOza Pawandeep 	err_handler = dev->driver->err_handler;
162*2e28bc84SOza Pawandeep 	err_handler->resume(dev);
163*2e28bc84SOza Pawandeep 	pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
164*2e28bc84SOza Pawandeep out:
165*2e28bc84SOza Pawandeep 	device_unlock(&dev->dev);
166*2e28bc84SOza Pawandeep 	return 0;
167*2e28bc84SOza Pawandeep }
168*2e28bc84SOza Pawandeep 
169*2e28bc84SOza Pawandeep /**
170*2e28bc84SOza Pawandeep  * default_reset_link - default reset function
171*2e28bc84SOza Pawandeep  * @dev: pointer to pci_dev data structure
172*2e28bc84SOza Pawandeep  *
173*2e28bc84SOza Pawandeep  * Invoked when performing link reset on a Downstream Port or a
174*2e28bc84SOza Pawandeep  * Root Port with no aer driver.
175*2e28bc84SOza Pawandeep  */
176*2e28bc84SOza Pawandeep static pci_ers_result_t default_reset_link(struct pci_dev *dev)
177*2e28bc84SOza Pawandeep {
178*2e28bc84SOza Pawandeep 	pci_reset_bridge_secondary_bus(dev);
179*2e28bc84SOza Pawandeep 	pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
180*2e28bc84SOza Pawandeep 	return PCI_ERS_RESULT_RECOVERED;
181*2e28bc84SOza Pawandeep }
182*2e28bc84SOza Pawandeep 
183*2e28bc84SOza Pawandeep static pci_ers_result_t reset_link(struct pci_dev *dev)
184*2e28bc84SOza Pawandeep {
185*2e28bc84SOza Pawandeep 	struct pci_dev *udev;
186*2e28bc84SOza Pawandeep 	pci_ers_result_t status;
187*2e28bc84SOza Pawandeep 	struct pcie_port_service_driver *driver = NULL;
188*2e28bc84SOza Pawandeep 
189*2e28bc84SOza Pawandeep 	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
190*2e28bc84SOza Pawandeep 		/* Reset this port for all subordinates */
191*2e28bc84SOza Pawandeep 		udev = dev;
192*2e28bc84SOza Pawandeep 	} else {
193*2e28bc84SOza Pawandeep 		/* Reset the upstream component (likely downstream port) */
194*2e28bc84SOza Pawandeep 		udev = dev->bus->self;
195*2e28bc84SOza Pawandeep 	}
196*2e28bc84SOza Pawandeep 
197*2e28bc84SOza Pawandeep #if IS_ENABLED(CONFIG_PCIEAER)
198*2e28bc84SOza Pawandeep 	/* Use the aer driver of the component firstly */
199*2e28bc84SOza Pawandeep 	driver = find_aer_service(udev);
200*2e28bc84SOza Pawandeep #endif
201*2e28bc84SOza Pawandeep 
202*2e28bc84SOza Pawandeep 	if (driver && driver->reset_link) {
203*2e28bc84SOza Pawandeep 		status = driver->reset_link(udev);
204*2e28bc84SOza Pawandeep 	} else if (udev->has_secondary_link) {
205*2e28bc84SOza Pawandeep 		status = default_reset_link(udev);
206*2e28bc84SOza Pawandeep 	} else {
207*2e28bc84SOza Pawandeep 		pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
208*2e28bc84SOza Pawandeep 			pci_name(udev));
209*2e28bc84SOza Pawandeep 		return PCI_ERS_RESULT_DISCONNECT;
210*2e28bc84SOza Pawandeep 	}
211*2e28bc84SOza Pawandeep 
212*2e28bc84SOza Pawandeep 	if (status != PCI_ERS_RESULT_RECOVERED) {
213*2e28bc84SOza Pawandeep 		pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
214*2e28bc84SOza Pawandeep 			pci_name(udev));
215*2e28bc84SOza Pawandeep 		return PCI_ERS_RESULT_DISCONNECT;
216*2e28bc84SOza Pawandeep 	}
217*2e28bc84SOza Pawandeep 
218*2e28bc84SOza Pawandeep 	return status;
219*2e28bc84SOza Pawandeep }
220*2e28bc84SOza Pawandeep 
221*2e28bc84SOza Pawandeep /**
222*2e28bc84SOza Pawandeep  * broadcast_error_message - handle message broadcast to downstream drivers
223*2e28bc84SOza Pawandeep  * @dev: pointer to from where in a hierarchy message is broadcasted down
224*2e28bc84SOza Pawandeep  * @state: error state
225*2e28bc84SOza Pawandeep  * @error_mesg: message to print
226*2e28bc84SOza Pawandeep  * @cb: callback to be broadcasted
227*2e28bc84SOza Pawandeep  *
228*2e28bc84SOza Pawandeep  * Invoked during error recovery process. Once being invoked, the content
229*2e28bc84SOza Pawandeep  * of error severity will be broadcasted to all downstream drivers in a
230*2e28bc84SOza Pawandeep  * hierarchy in question.
231*2e28bc84SOza Pawandeep  */
232*2e28bc84SOza Pawandeep static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
233*2e28bc84SOza Pawandeep 	enum pci_channel_state state,
234*2e28bc84SOza Pawandeep 	char *error_mesg,
235*2e28bc84SOza Pawandeep 	int (*cb)(struct pci_dev *, void *))
236*2e28bc84SOza Pawandeep {
237*2e28bc84SOza Pawandeep 	struct aer_broadcast_data result_data;
238*2e28bc84SOza Pawandeep 
239*2e28bc84SOza Pawandeep 	pci_printk(KERN_DEBUG, dev, "broadcast %s message\n", error_mesg);
240*2e28bc84SOza Pawandeep 	result_data.state = state;
241*2e28bc84SOza Pawandeep 	if (cb == report_error_detected)
242*2e28bc84SOza Pawandeep 		result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
243*2e28bc84SOza Pawandeep 	else
244*2e28bc84SOza Pawandeep 		result_data.result = PCI_ERS_RESULT_RECOVERED;
245*2e28bc84SOza Pawandeep 
246*2e28bc84SOza Pawandeep 	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
247*2e28bc84SOza Pawandeep 		/*
248*2e28bc84SOza Pawandeep 		 * If the error is reported by a bridge, we think this error
249*2e28bc84SOza Pawandeep 		 * is related to the downstream link of the bridge, so we
250*2e28bc84SOza Pawandeep 		 * do error recovery on all subordinates of the bridge instead
251*2e28bc84SOza Pawandeep 		 * of the bridge and clear the error status of the bridge.
252*2e28bc84SOza Pawandeep 		 */
253*2e28bc84SOza Pawandeep 		if (cb == report_error_detected)
254*2e28bc84SOza Pawandeep 			dev->error_state = state;
255*2e28bc84SOza Pawandeep 		pci_walk_bus(dev->subordinate, cb, &result_data);
256*2e28bc84SOza Pawandeep 		if (cb == report_resume) {
257*2e28bc84SOza Pawandeep 			pci_cleanup_aer_uncorrect_error_status(dev);
258*2e28bc84SOza Pawandeep 			dev->error_state = pci_channel_io_normal;
259*2e28bc84SOza Pawandeep 		}
260*2e28bc84SOza Pawandeep 	} else {
261*2e28bc84SOza Pawandeep 		/*
262*2e28bc84SOza Pawandeep 		 * If the error is reported by an end point, we think this
263*2e28bc84SOza Pawandeep 		 * error is related to the upstream link of the end point.
264*2e28bc84SOza Pawandeep 		 */
265*2e28bc84SOza Pawandeep 		if (state == pci_channel_io_normal)
266*2e28bc84SOza Pawandeep 			/*
267*2e28bc84SOza Pawandeep 			 * the error is non fatal so the bus is ok, just invoke
268*2e28bc84SOza Pawandeep 			 * the callback for the function that logged the error.
269*2e28bc84SOza Pawandeep 			 */
270*2e28bc84SOza Pawandeep 			cb(dev, &result_data);
271*2e28bc84SOza Pawandeep 		else
272*2e28bc84SOza Pawandeep 			pci_walk_bus(dev->bus, cb, &result_data);
273*2e28bc84SOza Pawandeep 	}
274*2e28bc84SOza Pawandeep 
275*2e28bc84SOza Pawandeep 	return result_data.result;
276*2e28bc84SOza Pawandeep }
277*2e28bc84SOza Pawandeep 
278*2e28bc84SOza Pawandeep /**
279*2e28bc84SOza Pawandeep  * pcie_do_fatal_recovery - handle fatal error recovery process
280*2e28bc84SOza Pawandeep  * @dev: pointer to a pci_dev data structure of agent detecting an error
281*2e28bc84SOza Pawandeep  *
282*2e28bc84SOza Pawandeep  * Invoked when an error is fatal. Once being invoked, removes the devices
283*2e28bc84SOza Pawandeep  * beneath this AER agent, followed by reset link e.g. secondary bus reset
284*2e28bc84SOza Pawandeep  * followed by re-enumeration of devices.
285*2e28bc84SOza Pawandeep  */
286*2e28bc84SOza Pawandeep void pcie_do_fatal_recovery(struct pci_dev *dev)
287*2e28bc84SOza Pawandeep {
288*2e28bc84SOza Pawandeep 	struct pci_dev *udev;
289*2e28bc84SOza Pawandeep 	struct pci_bus *parent;
290*2e28bc84SOza Pawandeep 	struct pci_dev *pdev, *temp;
291*2e28bc84SOza Pawandeep 	pci_ers_result_t result;
292*2e28bc84SOza Pawandeep 
293*2e28bc84SOza Pawandeep 	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
294*2e28bc84SOza Pawandeep 		udev = dev;
295*2e28bc84SOza Pawandeep 	else
296*2e28bc84SOza Pawandeep 		udev = dev->bus->self;
297*2e28bc84SOza Pawandeep 
298*2e28bc84SOza Pawandeep 	parent = udev->subordinate;
299*2e28bc84SOza Pawandeep 	pci_lock_rescan_remove();
300*2e28bc84SOza Pawandeep 	list_for_each_entry_safe_reverse(pdev, temp, &parent->devices,
301*2e28bc84SOza Pawandeep 					 bus_list) {
302*2e28bc84SOza Pawandeep 		pci_dev_get(pdev);
303*2e28bc84SOza Pawandeep 		pci_dev_set_disconnected(pdev, NULL);
304*2e28bc84SOza Pawandeep 		if (pci_has_subordinate(pdev))
305*2e28bc84SOza Pawandeep 			pci_walk_bus(pdev->subordinate,
306*2e28bc84SOza Pawandeep 				     pci_dev_set_disconnected, NULL);
307*2e28bc84SOza Pawandeep 		pci_stop_and_remove_bus_device(pdev);
308*2e28bc84SOza Pawandeep 		pci_dev_put(pdev);
309*2e28bc84SOza Pawandeep 	}
310*2e28bc84SOza Pawandeep 
311*2e28bc84SOza Pawandeep 	result = reset_link(udev);
312*2e28bc84SOza Pawandeep 
313*2e28bc84SOza Pawandeep 	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
314*2e28bc84SOza Pawandeep 		/*
315*2e28bc84SOza Pawandeep 		 * If the error is reported by a bridge, we think this error
316*2e28bc84SOza Pawandeep 		 * is related to the downstream link of the bridge, so we
317*2e28bc84SOza Pawandeep 		 * do error recovery on all subordinates of the bridge instead
318*2e28bc84SOza Pawandeep 		 * of the bridge and clear the error status of the bridge.
319*2e28bc84SOza Pawandeep 		 */
320*2e28bc84SOza Pawandeep 		pci_cleanup_aer_uncorrect_error_status(dev);
321*2e28bc84SOza Pawandeep 	}
322*2e28bc84SOza Pawandeep 
323*2e28bc84SOza Pawandeep 	if (result == PCI_ERS_RESULT_RECOVERED) {
324*2e28bc84SOza Pawandeep 		if (pcie_wait_for_link(udev, true))
325*2e28bc84SOza Pawandeep 			pci_rescan_bus(udev->bus);
326*2e28bc84SOza Pawandeep 		pci_info(dev, "Device recovery from fatal error successful\n");
327*2e28bc84SOza Pawandeep 	} else {
328*2e28bc84SOza Pawandeep 		pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
329*2e28bc84SOza Pawandeep 		pci_info(dev, "Device recovery from fatal error failed\n");
330*2e28bc84SOza Pawandeep 	}
331*2e28bc84SOza Pawandeep 
332*2e28bc84SOza Pawandeep 	pci_unlock_rescan_remove();
333*2e28bc84SOza Pawandeep }
334*2e28bc84SOza Pawandeep 
335*2e28bc84SOza Pawandeep /**
336*2e28bc84SOza Pawandeep  * pcie_do_nonfatal_recovery - handle nonfatal error recovery process
337*2e28bc84SOza Pawandeep  * @dev: pointer to a pci_dev data structure of agent detecting an error
338*2e28bc84SOza Pawandeep  *
339*2e28bc84SOza Pawandeep  * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
340*2e28bc84SOza Pawandeep  * error detected message to all downstream drivers within a hierarchy in
341*2e28bc84SOza Pawandeep  * question and return the returned code.
342*2e28bc84SOza Pawandeep  */
343*2e28bc84SOza Pawandeep void pcie_do_nonfatal_recovery(struct pci_dev *dev)
344*2e28bc84SOza Pawandeep {
345*2e28bc84SOza Pawandeep 	pci_ers_result_t status;
346*2e28bc84SOza Pawandeep 	enum pci_channel_state state;
347*2e28bc84SOza Pawandeep 
348*2e28bc84SOza Pawandeep 	state = pci_channel_io_normal;
349*2e28bc84SOza Pawandeep 
350*2e28bc84SOza Pawandeep 	status = broadcast_error_message(dev,
351*2e28bc84SOza Pawandeep 			state,
352*2e28bc84SOza Pawandeep 			"error_detected",
353*2e28bc84SOza Pawandeep 			report_error_detected);
354*2e28bc84SOza Pawandeep 
355*2e28bc84SOza Pawandeep 	if (status == PCI_ERS_RESULT_CAN_RECOVER)
356*2e28bc84SOza Pawandeep 		status = broadcast_error_message(dev,
357*2e28bc84SOza Pawandeep 				state,
358*2e28bc84SOza Pawandeep 				"mmio_enabled",
359*2e28bc84SOza Pawandeep 				report_mmio_enabled);
360*2e28bc84SOza Pawandeep 
361*2e28bc84SOza Pawandeep 	if (status == PCI_ERS_RESULT_NEED_RESET) {
362*2e28bc84SOza Pawandeep 		/*
363*2e28bc84SOza Pawandeep 		 * TODO: Should call platform-specific
364*2e28bc84SOza Pawandeep 		 * functions to reset slot before calling
365*2e28bc84SOza Pawandeep 		 * drivers' slot_reset callbacks?
366*2e28bc84SOza Pawandeep 		 */
367*2e28bc84SOza Pawandeep 		status = broadcast_error_message(dev,
368*2e28bc84SOza Pawandeep 				state,
369*2e28bc84SOza Pawandeep 				"slot_reset",
370*2e28bc84SOza Pawandeep 				report_slot_reset);
371*2e28bc84SOza Pawandeep 	}
372*2e28bc84SOza Pawandeep 
373*2e28bc84SOza Pawandeep 	if (status != PCI_ERS_RESULT_RECOVERED)
374*2e28bc84SOza Pawandeep 		goto failed;
375*2e28bc84SOza Pawandeep 
376*2e28bc84SOza Pawandeep 	broadcast_error_message(dev,
377*2e28bc84SOza Pawandeep 				state,
378*2e28bc84SOza Pawandeep 				"resume",
379*2e28bc84SOza Pawandeep 				report_resume);
380*2e28bc84SOza Pawandeep 
381*2e28bc84SOza Pawandeep 	pci_info(dev, "AER: Device recovery successful\n");
382*2e28bc84SOza Pawandeep 	return;
383*2e28bc84SOza Pawandeep 
384*2e28bc84SOza Pawandeep failed:
385*2e28bc84SOza Pawandeep 	pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
386*2e28bc84SOza Pawandeep 
387*2e28bc84SOza Pawandeep 	/* TODO: Should kernel panic here? */
388*2e28bc84SOza Pawandeep 	pci_info(dev, "AER: Device recovery failed\n");
389*2e28bc84SOza Pawandeep }
390