Commit 270c66be9b4a6f2be53ef3aec5dc8e7b07782ec9
Committed by
Jesse Barnes
1 parent
0927678f55
Exists in
master
and in
4 other branches
PCI: fix AER capability check
The 'use pci_find_ext_capability everywhere' cleanup brought a new bug, which makes the AER stop working. Fix it by actually using find_ext_cap instead of just find_cap. Drop the unused config space size define while we're at it. Signed-off-by: Yu Zhao <yu.zhao@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Showing 4 changed files with 5 additions and 12 deletions Inline Diff
Documentation/PCI/pcieaer-howto.txt
1 | The PCI Express Advanced Error Reporting Driver Guide HOWTO | 1 | The PCI Express Advanced Error Reporting Driver Guide HOWTO |
2 | T. Long Nguyen <tom.l.nguyen@intel.com> | 2 | T. Long Nguyen <tom.l.nguyen@intel.com> |
3 | Yanmin Zhang <yanmin.zhang@intel.com> | 3 | Yanmin Zhang <yanmin.zhang@intel.com> |
4 | 07/29/2006 | 4 | 07/29/2006 |
5 | 5 | ||
6 | 6 | ||
7 | 1. Overview | 7 | 1. Overview |
8 | 8 | ||
9 | 1.1 About this guide | 9 | 1.1 About this guide |
10 | 10 | ||
11 | This guide describes the basics of the PCI Express Advanced Error | 11 | This guide describes the basics of the PCI Express Advanced Error |
12 | Reporting (AER) driver and provides information on how to use it, as | 12 | Reporting (AER) driver and provides information on how to use it, as |
13 | well as how to enable the drivers of endpoint devices to conform with | 13 | well as how to enable the drivers of endpoint devices to conform with |
14 | PCI Express AER driver. | 14 | PCI Express AER driver. |
15 | 15 | ||
16 | 1.2 Copyright © Intel Corporation 2006. | 16 | 1.2 Copyright © Intel Corporation 2006. |
17 | 17 | ||
18 | 1.3 What is the PCI Express AER Driver? | 18 | 1.3 What is the PCI Express AER Driver? |
19 | 19 | ||
20 | PCI Express error signaling can occur on the PCI Express link itself | 20 | PCI Express error signaling can occur on the PCI Express link itself |
21 | or on behalf of transactions initiated on the link. PCI Express | 21 | or on behalf of transactions initiated on the link. PCI Express |
22 | defines two error reporting paradigms: the baseline capability and | 22 | defines two error reporting paradigms: the baseline capability and |
23 | the Advanced Error Reporting capability. The baseline capability is | 23 | the Advanced Error Reporting capability. The baseline capability is |
24 | required of all PCI Express components providing a minimum defined | 24 | required of all PCI Express components providing a minimum defined |
25 | set of error reporting requirements. Advanced Error Reporting | 25 | set of error reporting requirements. Advanced Error Reporting |
26 | capability is implemented with a PCI Express advanced error reporting | 26 | capability is implemented with a PCI Express advanced error reporting |
27 | extended capability structure providing more robust error reporting. | 27 | extended capability structure providing more robust error reporting. |
28 | 28 | ||
29 | The PCI Express AER driver provides the infrastructure to support PCI | 29 | The PCI Express AER driver provides the infrastructure to support PCI |
30 | Express Advanced Error Reporting capability. The PCI Express AER | 30 | Express Advanced Error Reporting capability. The PCI Express AER |
31 | driver provides three basic functions: | 31 | driver provides three basic functions: |
32 | 32 | ||
33 | - Gathers the comprehensive error information if errors occurred. | 33 | - Gathers the comprehensive error information if errors occurred. |
34 | - Reports error to the users. | 34 | - Reports error to the users. |
35 | - Performs error recovery actions. | 35 | - Performs error recovery actions. |
36 | 36 | ||
37 | AER driver only attaches root ports which support PCI-Express AER | 37 | AER driver only attaches root ports which support PCI-Express AER |
38 | capability. | 38 | capability. |
39 | 39 | ||
40 | 40 | ||
41 | 2. User Guide | 41 | 2. User Guide |
42 | 42 | ||
43 | 2.1 Include the PCI Express AER Root Driver into the Linux Kernel | 43 | 2.1 Include the PCI Express AER Root Driver into the Linux Kernel |
44 | 44 | ||
45 | The PCI Express AER Root driver is a Root Port service driver attached | 45 | The PCI Express AER Root driver is a Root Port service driver attached |
46 | to the PCI Express Port Bus driver. If a user wants to use it, the driver | 46 | to the PCI Express Port Bus driver. If a user wants to use it, the driver |
47 | has to be compiled. Option CONFIG_PCIEAER supports this capability. It | 47 | has to be compiled. Option CONFIG_PCIEAER supports this capability. It |
48 | depends on CONFIG_PCIEPORTBUS, so pls. set CONFIG_PCIEPORTBUS=y and | 48 | depends on CONFIG_PCIEPORTBUS, so pls. set CONFIG_PCIEPORTBUS=y and |
49 | CONFIG_PCIEAER = y. | 49 | CONFIG_PCIEAER = y. |
50 | 50 | ||
51 | 2.2 Load PCI Express AER Root Driver | 51 | 2.2 Load PCI Express AER Root Driver |
52 | There is a case where a system has AER support in BIOS. Enabling the AER | 52 | There is a case where a system has AER support in BIOS. Enabling the AER |
53 | Root driver and having AER support in BIOS may result unpredictable | 53 | Root driver and having AER support in BIOS may result unpredictable |
54 | behavior. To avoid this conflict, a successful load of the AER Root driver | 54 | behavior. To avoid this conflict, a successful load of the AER Root driver |
55 | requires ACPI _OSC support in the BIOS to allow the AER Root driver to | 55 | requires ACPI _OSC support in the BIOS to allow the AER Root driver to |
56 | request for native control of AER. See the PCI FW 3.0 Specification for | 56 | request for native control of AER. See the PCI FW 3.0 Specification for |
57 | details regarding OSC usage. Currently, lots of firmwares don't provide | 57 | details regarding OSC usage. Currently, lots of firmwares don't provide |
58 | _OSC support while they use PCI Express. To support such firmwares, | 58 | _OSC support while they use PCI Express. To support such firmwares, |
59 | forceload, a parameter of type bool, could enable AER to continue to | 59 | forceload, a parameter of type bool, could enable AER to continue to |
60 | be initiated although firmwares have no _OSC support. To enable the | 60 | be initiated although firmwares have no _OSC support. To enable the |
61 | walkaround, pls. add aerdriver.forceload=y to kernel boot parameter line | 61 | walkaround, pls. add aerdriver.forceload=y to kernel boot parameter line |
62 | when booting kernel. Note that forceload=n by default. | 62 | when booting kernel. Note that forceload=n by default. |
63 | 63 | ||
64 | 2.3 AER error output | 64 | 2.3 AER error output |
65 | When a PCI-E AER error is captured, an error message will be outputed to | 65 | When a PCI-E AER error is captured, an error message will be outputed to |
66 | console. If it's a correctable error, it is outputed as a warning. | 66 | console. If it's a correctable error, it is outputed as a warning. |
67 | Otherwise, it is printed as an error. So users could choose different | 67 | Otherwise, it is printed as an error. So users could choose different |
68 | log level to filter out correctable error messages. | 68 | log level to filter out correctable error messages. |
69 | 69 | ||
70 | Below shows an example. | 70 | Below shows an example. |
71 | +------ PCI-Express Device Error -----+ | 71 | +------ PCI-Express Device Error -----+ |
72 | Error Severity : Uncorrected (Fatal) | 72 | Error Severity : Uncorrected (Fatal) |
73 | PCIE Bus Error type : Transaction Layer | 73 | PCIE Bus Error type : Transaction Layer |
74 | Unsupported Request : First | 74 | Unsupported Request : First |
75 | Requester ID : 0500 | 75 | Requester ID : 0500 |
76 | VendorID=8086h, DeviceID=0329h, Bus=05h, Device=00h, Function=00h | 76 | VendorID=8086h, DeviceID=0329h, Bus=05h, Device=00h, Function=00h |
77 | TLB Header: | 77 | TLB Header: |
78 | 04000001 00200a03 05010000 00050100 | 78 | 04000001 00200a03 05010000 00050100 |
79 | 79 | ||
80 | In the example, 'Requester ID' means the ID of the device who sends | 80 | In the example, 'Requester ID' means the ID of the device who sends |
81 | the error message to root port. Pls. refer to pci express specs for | 81 | the error message to root port. Pls. refer to pci express specs for |
82 | other fields. | 82 | other fields. |
83 | 83 | ||
84 | 84 | ||
85 | 3. Developer Guide | 85 | 3. Developer Guide |
86 | 86 | ||
87 | To enable AER aware support requires a software driver to configure | 87 | To enable AER aware support requires a software driver to configure |
88 | the AER capability structure within its device and to provide callbacks. | 88 | the AER capability structure within its device and to provide callbacks. |
89 | 89 | ||
90 | To support AER better, developers need understand how AER does work | 90 | To support AER better, developers need understand how AER does work |
91 | firstly. | 91 | firstly. |
92 | 92 | ||
93 | PCI Express errors are classified into two types: correctable errors | 93 | PCI Express errors are classified into two types: correctable errors |
94 | and uncorrectable errors. This classification is based on the impacts | 94 | and uncorrectable errors. This classification is based on the impacts |
95 | of those errors, which may result in degraded performance or function | 95 | of those errors, which may result in degraded performance or function |
96 | failure. | 96 | failure. |
97 | 97 | ||
98 | Correctable errors pose no impacts on the functionality of the | 98 | Correctable errors pose no impacts on the functionality of the |
99 | interface. The PCI Express protocol can recover without any software | 99 | interface. The PCI Express protocol can recover without any software |
100 | intervention or any loss of data. These errors are detected and | 100 | intervention or any loss of data. These errors are detected and |
101 | corrected by hardware. Unlike correctable errors, uncorrectable | 101 | corrected by hardware. Unlike correctable errors, uncorrectable |
102 | errors impact functionality of the interface. Uncorrectable errors | 102 | errors impact functionality of the interface. Uncorrectable errors |
103 | can cause a particular transaction or a particular PCI Express link | 103 | can cause a particular transaction or a particular PCI Express link |
104 | to be unreliable. Depending on those error conditions, uncorrectable | 104 | to be unreliable. Depending on those error conditions, uncorrectable |
105 | errors are further classified into non-fatal errors and fatal errors. | 105 | errors are further classified into non-fatal errors and fatal errors. |
106 | Non-fatal errors cause the particular transaction to be unreliable, | 106 | Non-fatal errors cause the particular transaction to be unreliable, |
107 | but the PCI Express link itself is fully functional. Fatal errors, on | 107 | but the PCI Express link itself is fully functional. Fatal errors, on |
108 | the other hand, cause the link to be unreliable. | 108 | the other hand, cause the link to be unreliable. |
109 | 109 | ||
110 | When AER is enabled, a PCI Express device will automatically send an | 110 | When AER is enabled, a PCI Express device will automatically send an |
111 | error message to the PCIE root port above it when the device captures | 111 | error message to the PCIE root port above it when the device captures |
112 | an error. The Root Port, upon receiving an error reporting message, | 112 | an error. The Root Port, upon receiving an error reporting message, |
113 | internally processes and logs the error message in its PCI Express | 113 | internally processes and logs the error message in its PCI Express |
114 | capability structure. Error information being logged includes storing | 114 | capability structure. Error information being logged includes storing |
115 | the error reporting agent's requestor ID into the Error Source | 115 | the error reporting agent's requestor ID into the Error Source |
116 | Identification Registers and setting the error bits of the Root Error | 116 | Identification Registers and setting the error bits of the Root Error |
117 | Status Register accordingly. If AER error reporting is enabled in Root | 117 | Status Register accordingly. If AER error reporting is enabled in Root |
118 | Error Command Register, the Root Port generates an interrupt if an | 118 | Error Command Register, the Root Port generates an interrupt if an |
119 | error is detected. | 119 | error is detected. |
120 | 120 | ||
121 | Note that the errors as described above are related to the PCI Express | 121 | Note that the errors as described above are related to the PCI Express |
122 | hierarchy and links. These errors do not include any device specific | 122 | hierarchy and links. These errors do not include any device specific |
123 | errors because device specific errors will still get sent directly to | 123 | errors because device specific errors will still get sent directly to |
124 | the device driver. | 124 | the device driver. |
125 | 125 | ||
126 | 3.1 Configure the AER capability structure | 126 | 3.1 Configure the AER capability structure |
127 | 127 | ||
128 | AER aware drivers of PCI Express component need change the device | 128 | AER aware drivers of PCI Express component need change the device |
129 | control registers to enable AER. They also could change AER registers, | 129 | control registers to enable AER. They also could change AER registers, |
130 | including mask and severity registers. Helper function | 130 | including mask and severity registers. Helper function |
131 | pci_enable_pcie_error_reporting could be used to enable AER. See | 131 | pci_enable_pcie_error_reporting could be used to enable AER. See |
132 | section 3.3. | 132 | section 3.3. |
133 | 133 | ||
134 | 3.2. Provide callbacks | 134 | 3.2. Provide callbacks |
135 | 135 | ||
136 | 3.2.1 callback reset_link to reset pci express link | 136 | 3.2.1 callback reset_link to reset pci express link |
137 | 137 | ||
138 | This callback is used to reset the pci express physical link when a | 138 | This callback is used to reset the pci express physical link when a |
139 | fatal error happens. The root port aer service driver provides a | 139 | fatal error happens. The root port aer service driver provides a |
140 | default reset_link function, but different upstream ports might | 140 | default reset_link function, but different upstream ports might |
141 | have different specifications to reset pci express link, so all | 141 | have different specifications to reset pci express link, so all |
142 | upstream ports should provide their own reset_link functions. | 142 | upstream ports should provide their own reset_link functions. |
143 | 143 | ||
144 | In struct pcie_port_service_driver, a new pointer, reset_link, is | 144 | In struct pcie_port_service_driver, a new pointer, reset_link, is |
145 | added. | 145 | added. |
146 | 146 | ||
147 | pci_ers_result_t (*reset_link) (struct pci_dev *dev); | 147 | pci_ers_result_t (*reset_link) (struct pci_dev *dev); |
148 | 148 | ||
149 | Section 3.2.2.2 provides more detailed info on when to call | 149 | Section 3.2.2.2 provides more detailed info on when to call |
150 | reset_link. | 150 | reset_link. |
151 | 151 | ||
152 | 3.2.2 PCI error-recovery callbacks | 152 | 3.2.2 PCI error-recovery callbacks |
153 | 153 | ||
154 | The PCI Express AER Root driver uses error callbacks to coordinate | 154 | The PCI Express AER Root driver uses error callbacks to coordinate |
155 | with downstream device drivers associated with a hierarchy in question | 155 | with downstream device drivers associated with a hierarchy in question |
156 | when performing error recovery actions. | 156 | when performing error recovery actions. |
157 | 157 | ||
158 | Data struct pci_driver has a pointer, err_handler, to point to | 158 | Data struct pci_driver has a pointer, err_handler, to point to |
159 | pci_error_handlers who consists of a couple of callback function | 159 | pci_error_handlers who consists of a couple of callback function |
160 | pointers. AER driver follows the rules defined in | 160 | pointers. AER driver follows the rules defined in |
161 | pci-error-recovery.txt except pci express specific parts (e.g. | 161 | pci-error-recovery.txt except pci express specific parts (e.g. |
162 | reset_link). Pls. refer to pci-error-recovery.txt for detailed | 162 | reset_link). Pls. refer to pci-error-recovery.txt for detailed |
163 | definitions of the callbacks. | 163 | definitions of the callbacks. |
164 | 164 | ||
165 | Below sections specify when to call the error callback functions. | 165 | Below sections specify when to call the error callback functions. |
166 | 166 | ||
167 | 3.2.2.1 Correctable errors | 167 | 3.2.2.1 Correctable errors |
168 | 168 | ||
169 | Correctable errors pose no impacts on the functionality of | 169 | Correctable errors pose no impacts on the functionality of |
170 | the interface. The PCI Express protocol can recover without any | 170 | the interface. The PCI Express protocol can recover without any |
171 | software intervention or any loss of data. These errors do not | 171 | software intervention or any loss of data. These errors do not |
172 | require any recovery actions. The AER driver clears the device's | 172 | require any recovery actions. The AER driver clears the device's |
173 | correctable error status register accordingly and logs these errors. | 173 | correctable error status register accordingly and logs these errors. |
174 | 174 | ||
175 | 3.2.2.2 Non-correctable (non-fatal and fatal) errors | 175 | 3.2.2.2 Non-correctable (non-fatal and fatal) errors |
176 | 176 | ||
177 | If an error message indicates a non-fatal error, performing link reset | 177 | If an error message indicates a non-fatal error, performing link reset |
178 | at upstream is not required. The AER driver calls error_detected(dev, | 178 | at upstream is not required. The AER driver calls error_detected(dev, |
179 | pci_channel_io_normal) to all drivers associated within a hierarchy in | 179 | pci_channel_io_normal) to all drivers associated within a hierarchy in |
180 | question. for example, | 180 | question. for example, |
181 | EndPoint<==>DownstreamPort B<==>UpstreamPort A<==>RootPort. | 181 | EndPoint<==>DownstreamPort B<==>UpstreamPort A<==>RootPort. |
182 | If Upstream port A captures an AER error, the hierarchy consists of | 182 | If Upstream port A captures an AER error, the hierarchy consists of |
183 | Downstream port B and EndPoint. | 183 | Downstream port B and EndPoint. |
184 | 184 | ||
185 | A driver may return PCI_ERS_RESULT_CAN_RECOVER, | 185 | A driver may return PCI_ERS_RESULT_CAN_RECOVER, |
186 | PCI_ERS_RESULT_DISCONNECT, or PCI_ERS_RESULT_NEED_RESET, depending on | 186 | PCI_ERS_RESULT_DISCONNECT, or PCI_ERS_RESULT_NEED_RESET, depending on |
187 | whether it can recover or the AER driver calls mmio_enabled as next. | 187 | whether it can recover or the AER driver calls mmio_enabled as next. |
188 | 188 | ||
189 | If an error message indicates a fatal error, kernel will broadcast | 189 | If an error message indicates a fatal error, kernel will broadcast |
190 | error_detected(dev, pci_channel_io_frozen) to all drivers within | 190 | error_detected(dev, pci_channel_io_frozen) to all drivers within |
191 | a hierarchy in question. Then, performing link reset at upstream is | 191 | a hierarchy in question. Then, performing link reset at upstream is |
192 | necessary. As different kinds of devices might use different approaches | 192 | necessary. As different kinds of devices might use different approaches |
193 | to reset link, AER port service driver is required to provide the | 193 | to reset link, AER port service driver is required to provide the |
194 | function to reset link. Firstly, kernel looks for if the upstream | 194 | function to reset link. Firstly, kernel looks for if the upstream |
195 | component has an aer driver. If it has, kernel uses the reset_link | 195 | component has an aer driver. If it has, kernel uses the reset_link |
196 | callback of the aer driver. If the upstream component has no aer driver | 196 | callback of the aer driver. If the upstream component has no aer driver |
197 | and the port is downstream port, we will use the aer driver of the | 197 | and the port is downstream port, we will use the aer driver of the |
198 | root port who reports the AER error. As for upstream ports, | 198 | root port who reports the AER error. As for upstream ports, |
199 | they should provide their own aer service drivers with reset_link | 199 | they should provide their own aer service drivers with reset_link |
200 | function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and | 200 | function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and |
201 | reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes | 201 | reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes |
202 | to mmio_enabled. | 202 | to mmio_enabled. |
203 | 203 | ||
204 | 3.3 helper functions | 204 | 3.3 helper functions |
205 | 205 | ||
206 | 3.3.1 int pci_find_aer_capability(struct pci_dev *dev); | 206 | 3.3.1 int pci_enable_pcie_error_reporting(struct pci_dev *dev); |
207 | pci_find_aer_capability locates the PCI Express AER capability | ||
208 | in the device configuration space. If the device doesn't support | ||
209 | PCI-Express AER, the function returns 0. | ||
210 | |||
211 | 3.3.2 int pci_enable_pcie_error_reporting(struct pci_dev *dev); | ||
212 | pci_enable_pcie_error_reporting enables the device to send error | 207 | pci_enable_pcie_error_reporting enables the device to send error |
213 | messages to root port when an error is detected. Note that devices | 208 | messages to root port when an error is detected. Note that devices |
214 | don't enable the error reporting by default, so device drivers need | 209 | don't enable the error reporting by default, so device drivers need |
215 | call this function to enable it. | 210 | call this function to enable it. |
216 | 211 | ||
217 | 3.3.3 int pci_disable_pcie_error_reporting(struct pci_dev *dev); | 212 | 3.3.2 int pci_disable_pcie_error_reporting(struct pci_dev *dev); |
218 | pci_disable_pcie_error_reporting disables the device to send error | 213 | pci_disable_pcie_error_reporting disables the device to send error |
219 | messages to root port when an error is detected. | 214 | messages to root port when an error is detected. |
220 | 215 | ||
221 | 3.3.4 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); | 216 | 3.3.3 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); |
222 | pci_cleanup_aer_uncorrect_error_status cleanups the uncorrectable | 217 | pci_cleanup_aer_uncorrect_error_status cleanups the uncorrectable |
223 | error status register. | 218 | error status register. |
224 | 219 | ||
225 | 3.4 Frequent Asked Questions | 220 | 3.4 Frequent Asked Questions |
226 | 221 | ||
227 | Q: What happens if a PCI Express device driver does not provide an | 222 | Q: What happens if a PCI Express device driver does not provide an |
228 | error recovery handler (pci_driver->err_handler is equal to NULL)? | 223 | error recovery handler (pci_driver->err_handler is equal to NULL)? |
229 | 224 | ||
230 | A: The devices attached with the driver won't be recovered. If the | 225 | A: The devices attached with the driver won't be recovered. If the |
231 | error is fatal, kernel will print out warning messages. Please refer | 226 | error is fatal, kernel will print out warning messages. Please refer |
232 | to section 3 for more information. | 227 | to section 3 for more information. |
233 | 228 | ||
234 | Q: What happens if an upstream port service driver does not provide | 229 | Q: What happens if an upstream port service driver does not provide |
235 | callback reset_link? | 230 | callback reset_link? |
236 | 231 | ||
237 | A: Fatal error recovery will fail if the errors are reported by the | 232 | A: Fatal error recovery will fail if the errors are reported by the |
238 | upstream ports who are attached by the service driver. | 233 | upstream ports who are attached by the service driver. |
239 | 234 | ||
240 | Q: How does this infrastructure deal with driver that is not PCI | 235 | Q: How does this infrastructure deal with driver that is not PCI |
241 | Express aware? | 236 | Express aware? |
242 | 237 | ||
243 | A: This infrastructure calls the error callback functions of the | 238 | A: This infrastructure calls the error callback functions of the |
244 | driver when an error happens. But if the driver is not aware of | 239 | driver when an error happens. But if the driver is not aware of |
245 | PCI Express, the device might not report its own errors to root | 240 | PCI Express, the device might not report its own errors to root |
246 | port. | 241 | port. |
247 | 242 | ||
248 | Q: What modifications will that driver need to make it compatible | 243 | Q: What modifications will that driver need to make it compatible |
249 | with the PCI Express AER Root driver? | 244 | with the PCI Express AER Root driver? |
250 | 245 | ||
251 | A: It could call the helper functions to enable AER in devices and | 246 | A: It could call the helper functions to enable AER in devices and |
252 | cleanup uncorrectable status register. Pls. refer to section 3.3. | 247 | cleanup uncorrectable status register. Pls. refer to section 3.3. |
253 | 248 | ||
254 | 249 |
drivers/pci/pcie/aer/aerdrv_core.c
1 | /* | 1 | /* |
2 | * drivers/pci/pcie/aer/aerdrv_core.c | 2 | * drivers/pci/pcie/aer/aerdrv_core.c |
3 | * | 3 | * |
4 | * This file is subject to the terms and conditions of the GNU General Public | 4 | * This file is subject to the terms and conditions of the GNU General Public |
5 | * License. See the file "COPYING" in the main directory of this archive | 5 | * License. See the file "COPYING" in the main directory of this archive |
6 | * for more details. | 6 | * for more details. |
7 | * | 7 | * |
8 | * This file implements the core part of PCI-Express AER. When an pci-express | 8 | * This file implements the core part of PCI-Express AER. When an pci-express |
9 | * error is delivered, an error message will be collected and printed to | 9 | * error is delivered, an error message will be collected and printed to |
10 | * console, then, an error recovery procedure will be executed by following | 10 | * console, then, an error recovery procedure will be executed by following |
11 | * the pci error recovery rules. | 11 | * the pci error recovery rules. |
12 | * | 12 | * |
13 | * Copyright (C) 2006 Intel Corp. | 13 | * Copyright (C) 2006 Intel Corp. |
14 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | 14 | * Tom Long Nguyen (tom.l.nguyen@intel.com) |
15 | * Zhang Yanmin (yanmin.zhang@intel.com) | 15 | * Zhang Yanmin (yanmin.zhang@intel.com) |
16 | * | 16 | * |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/pci.h> | 20 | #include <linux/pci.h> |
21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/errno.h> | 22 | #include <linux/errno.h> |
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/suspend.h> | 24 | #include <linux/suspend.h> |
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include "aerdrv.h" | 26 | #include "aerdrv.h" |
27 | 27 | ||
28 | static int forceload; | 28 | static int forceload; |
29 | module_param(forceload, bool, 0); | 29 | module_param(forceload, bool, 0); |
30 | 30 | ||
31 | int pci_enable_pcie_error_reporting(struct pci_dev *dev) | 31 | int pci_enable_pcie_error_reporting(struct pci_dev *dev) |
32 | { | 32 | { |
33 | u16 reg16 = 0; | 33 | u16 reg16 = 0; |
34 | int pos; | 34 | int pos; |
35 | 35 | ||
36 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | 36 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
37 | if (!pos) | 37 | if (!pos) |
38 | return -EIO; | 38 | return -EIO; |
39 | 39 | ||
40 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 40 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); |
41 | if (!pos) | 41 | if (!pos) |
42 | return -EIO; | 42 | return -EIO; |
43 | 43 | ||
44 | pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); | 44 | pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); |
45 | reg16 = reg16 | | 45 | reg16 = reg16 | |
46 | PCI_EXP_DEVCTL_CERE | | 46 | PCI_EXP_DEVCTL_CERE | |
47 | PCI_EXP_DEVCTL_NFERE | | 47 | PCI_EXP_DEVCTL_NFERE | |
48 | PCI_EXP_DEVCTL_FERE | | 48 | PCI_EXP_DEVCTL_FERE | |
49 | PCI_EXP_DEVCTL_URRE; | 49 | PCI_EXP_DEVCTL_URRE; |
50 | pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, | 50 | pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, |
51 | reg16); | 51 | reg16); |
52 | return 0; | 52 | return 0; |
53 | } | 53 | } |
54 | 54 | ||
55 | int pci_disable_pcie_error_reporting(struct pci_dev *dev) | 55 | int pci_disable_pcie_error_reporting(struct pci_dev *dev) |
56 | { | 56 | { |
57 | u16 reg16 = 0; | 57 | u16 reg16 = 0; |
58 | int pos; | 58 | int pos; |
59 | 59 | ||
60 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | 60 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); |
61 | if (!pos) | 61 | if (!pos) |
62 | return -EIO; | 62 | return -EIO; |
63 | 63 | ||
64 | pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); | 64 | pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); |
65 | reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE | | 65 | reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE | |
66 | PCI_EXP_DEVCTL_NFERE | | 66 | PCI_EXP_DEVCTL_NFERE | |
67 | PCI_EXP_DEVCTL_FERE | | 67 | PCI_EXP_DEVCTL_FERE | |
68 | PCI_EXP_DEVCTL_URRE); | 68 | PCI_EXP_DEVCTL_URRE); |
69 | pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, | 69 | pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, |
70 | reg16); | 70 | reg16); |
71 | return 0; | 71 | return 0; |
72 | } | 72 | } |
73 | 73 | ||
74 | int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) | 74 | int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) |
75 | { | 75 | { |
76 | int pos; | 76 | int pos; |
77 | u32 status, mask; | 77 | u32 status, mask; |
78 | 78 | ||
79 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 79 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
80 | if (!pos) | 80 | if (!pos) |
81 | return -EIO; | 81 | return -EIO; |
82 | 82 | ||
83 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); | 83 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); |
84 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); | 84 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); |
85 | if (dev->error_state == pci_channel_io_normal) | 85 | if (dev->error_state == pci_channel_io_normal) |
86 | status &= ~mask; /* Clear corresponding nonfatal bits */ | 86 | status &= ~mask; /* Clear corresponding nonfatal bits */ |
87 | else | 87 | else |
88 | status &= mask; /* Clear corresponding fatal bits */ | 88 | status &= mask; /* Clear corresponding fatal bits */ |
89 | pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); | 89 | pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); |
90 | 90 | ||
91 | return 0; | 91 | return 0; |
92 | } | 92 | } |
93 | 93 | ||
94 | #if 0 | 94 | #if 0 |
95 | int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) | 95 | int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) |
96 | { | 96 | { |
97 | int pos; | 97 | int pos; |
98 | u32 status; | 98 | u32 status; |
99 | 99 | ||
100 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 100 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
101 | if (!pos) | 101 | if (!pos) |
102 | return -EIO; | 102 | return -EIO; |
103 | 103 | ||
104 | pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); | 104 | pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); |
105 | pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status); | 105 | pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status); |
106 | 106 | ||
107 | return 0; | 107 | return 0; |
108 | } | 108 | } |
109 | #endif /* 0 */ | 109 | #endif /* 0 */ |
110 | 110 | ||
111 | static int find_device_iter(struct device *device, void *data) | 111 | static int find_device_iter(struct device *device, void *data) |
112 | { | 112 | { |
113 | struct pci_dev *dev; | 113 | struct pci_dev *dev; |
114 | u16 id = *(unsigned long *)data; | 114 | u16 id = *(unsigned long *)data; |
115 | u8 secondary, subordinate, d_bus = id >> 8; | 115 | u8 secondary, subordinate, d_bus = id >> 8; |
116 | 116 | ||
117 | if (device->bus == &pci_bus_type) { | 117 | if (device->bus == &pci_bus_type) { |
118 | dev = to_pci_dev(device); | 118 | dev = to_pci_dev(device); |
119 | if (id == ((dev->bus->number << 8) | dev->devfn)) { | 119 | if (id == ((dev->bus->number << 8) | dev->devfn)) { |
120 | /* | 120 | /* |
121 | * Device ID match | 121 | * Device ID match |
122 | */ | 122 | */ |
123 | *(unsigned long*)data = (unsigned long)device; | 123 | *(unsigned long*)data = (unsigned long)device; |
124 | return 1; | 124 | return 1; |
125 | } | 125 | } |
126 | 126 | ||
127 | /* | 127 | /* |
128 | * If device is P2P, check if it is an upstream? | 128 | * If device is P2P, check if it is an upstream? |
129 | */ | 129 | */ |
130 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { | 130 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { |
131 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, | 131 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, |
132 | &secondary); | 132 | &secondary); |
133 | pci_read_config_byte(dev, PCI_SUBORDINATE_BUS, | 133 | pci_read_config_byte(dev, PCI_SUBORDINATE_BUS, |
134 | &subordinate); | 134 | &subordinate); |
135 | if (d_bus >= secondary && d_bus <= subordinate) { | 135 | if (d_bus >= secondary && d_bus <= subordinate) { |
136 | *(unsigned long*)data = (unsigned long)device; | 136 | *(unsigned long*)data = (unsigned long)device; |
137 | return 1; | 137 | return 1; |
138 | } | 138 | } |
139 | } | 139 | } |
140 | } | 140 | } |
141 | 141 | ||
142 | return 0; | 142 | return 0; |
143 | } | 143 | } |
144 | 144 | ||
145 | /** | 145 | /** |
146 | * find_source_device - search through device hierarchy for source device | 146 | * find_source_device - search through device hierarchy for source device |
147 | * @parent: pointer to Root Port pci_dev data structure | 147 | * @parent: pointer to Root Port pci_dev data structure |
148 | * @id: device ID of agent who sends an error message to this Root Port | 148 | * @id: device ID of agent who sends an error message to this Root Port |
149 | * | 149 | * |
150 | * Invoked when error is detected at the Root Port. | 150 | * Invoked when error is detected at the Root Port. |
151 | */ | 151 | */ |
152 | static struct device* find_source_device(struct pci_dev *parent, u16 id) | 152 | static struct device* find_source_device(struct pci_dev *parent, u16 id) |
153 | { | 153 | { |
154 | struct pci_dev *dev = parent; | 154 | struct pci_dev *dev = parent; |
155 | struct device *device; | 155 | struct device *device; |
156 | unsigned long device_addr; | 156 | unsigned long device_addr; |
157 | int status; | 157 | int status; |
158 | 158 | ||
159 | /* Is Root Port an agent that sends error message? */ | 159 | /* Is Root Port an agent that sends error message? */ |
160 | if (id == ((dev->bus->number << 8) | dev->devfn)) | 160 | if (id == ((dev->bus->number << 8) | dev->devfn)) |
161 | return &dev->dev; | 161 | return &dev->dev; |
162 | 162 | ||
163 | do { | 163 | do { |
164 | device_addr = id; | 164 | device_addr = id; |
165 | if ((status = device_for_each_child(&dev->dev, | 165 | if ((status = device_for_each_child(&dev->dev, |
166 | &device_addr, find_device_iter))) { | 166 | &device_addr, find_device_iter))) { |
167 | device = (struct device*)device_addr; | 167 | device = (struct device*)device_addr; |
168 | dev = to_pci_dev(device); | 168 | dev = to_pci_dev(device); |
169 | if (id == ((dev->bus->number << 8) | dev->devfn)) | 169 | if (id == ((dev->bus->number << 8) | dev->devfn)) |
170 | return device; | 170 | return device; |
171 | } | 171 | } |
172 | }while (status); | 172 | }while (status); |
173 | 173 | ||
174 | return NULL; | 174 | return NULL; |
175 | } | 175 | } |
176 | 176 | ||
177 | static void report_error_detected(struct pci_dev *dev, void *data) | 177 | static void report_error_detected(struct pci_dev *dev, void *data) |
178 | { | 178 | { |
179 | pci_ers_result_t vote; | 179 | pci_ers_result_t vote; |
180 | struct pci_error_handlers *err_handler; | 180 | struct pci_error_handlers *err_handler; |
181 | struct aer_broadcast_data *result_data; | 181 | struct aer_broadcast_data *result_data; |
182 | result_data = (struct aer_broadcast_data *) data; | 182 | result_data = (struct aer_broadcast_data *) data; |
183 | 183 | ||
184 | dev->error_state = result_data->state; | 184 | dev->error_state = result_data->state; |
185 | 185 | ||
186 | if (!dev->driver || | 186 | if (!dev->driver || |
187 | !dev->driver->err_handler || | 187 | !dev->driver->err_handler || |
188 | !dev->driver->err_handler->error_detected) { | 188 | !dev->driver->err_handler->error_detected) { |
189 | if (result_data->state == pci_channel_io_frozen && | 189 | if (result_data->state == pci_channel_io_frozen && |
190 | !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) { | 190 | !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) { |
191 | /* | 191 | /* |
192 | * In case of fatal recovery, if one of down- | 192 | * In case of fatal recovery, if one of down- |
193 | * stream device has no driver. We might be | 193 | * stream device has no driver. We might be |
194 | * unable to recover because a later insmod | 194 | * unable to recover because a later insmod |
195 | * of a driver for this device is unaware of | 195 | * of a driver for this device is unaware of |
196 | * its hw state. | 196 | * its hw state. |
197 | */ | 197 | */ |
198 | dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n", | 198 | dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n", |
199 | dev->driver ? | 199 | dev->driver ? |
200 | "no AER-aware driver" : "no driver"); | 200 | "no AER-aware driver" : "no driver"); |
201 | } | 201 | } |
202 | return; | 202 | return; |
203 | } | 203 | } |
204 | 204 | ||
205 | err_handler = dev->driver->err_handler; | 205 | err_handler = dev->driver->err_handler; |
206 | vote = err_handler->error_detected(dev, result_data->state); | 206 | vote = err_handler->error_detected(dev, result_data->state); |
207 | result_data->result = merge_result(result_data->result, vote); | 207 | result_data->result = merge_result(result_data->result, vote); |
208 | return; | 208 | return; |
209 | } | 209 | } |
210 | 210 | ||
211 | static void report_mmio_enabled(struct pci_dev *dev, void *data) | 211 | static void report_mmio_enabled(struct pci_dev *dev, void *data) |
212 | { | 212 | { |
213 | pci_ers_result_t vote; | 213 | pci_ers_result_t vote; |
214 | struct pci_error_handlers *err_handler; | 214 | struct pci_error_handlers *err_handler; |
215 | struct aer_broadcast_data *result_data; | 215 | struct aer_broadcast_data *result_data; |
216 | result_data = (struct aer_broadcast_data *) data; | 216 | result_data = (struct aer_broadcast_data *) data; |
217 | 217 | ||
218 | if (!dev->driver || | 218 | if (!dev->driver || |
219 | !dev->driver->err_handler || | 219 | !dev->driver->err_handler || |
220 | !dev->driver->err_handler->mmio_enabled) | 220 | !dev->driver->err_handler->mmio_enabled) |
221 | return; | 221 | return; |
222 | 222 | ||
223 | err_handler = dev->driver->err_handler; | 223 | err_handler = dev->driver->err_handler; |
224 | vote = err_handler->mmio_enabled(dev); | 224 | vote = err_handler->mmio_enabled(dev); |
225 | result_data->result = merge_result(result_data->result, vote); | 225 | result_data->result = merge_result(result_data->result, vote); |
226 | return; | 226 | return; |
227 | } | 227 | } |
228 | 228 | ||
229 | static void report_slot_reset(struct pci_dev *dev, void *data) | 229 | static void report_slot_reset(struct pci_dev *dev, void *data) |
230 | { | 230 | { |
231 | pci_ers_result_t vote; | 231 | pci_ers_result_t vote; |
232 | struct pci_error_handlers *err_handler; | 232 | struct pci_error_handlers *err_handler; |
233 | struct aer_broadcast_data *result_data; | 233 | struct aer_broadcast_data *result_data; |
234 | result_data = (struct aer_broadcast_data *) data; | 234 | result_data = (struct aer_broadcast_data *) data; |
235 | 235 | ||
236 | if (!dev->driver || | 236 | if (!dev->driver || |
237 | !dev->driver->err_handler || | 237 | !dev->driver->err_handler || |
238 | !dev->driver->err_handler->slot_reset) | 238 | !dev->driver->err_handler->slot_reset) |
239 | return; | 239 | return; |
240 | 240 | ||
241 | err_handler = dev->driver->err_handler; | 241 | err_handler = dev->driver->err_handler; |
242 | vote = err_handler->slot_reset(dev); | 242 | vote = err_handler->slot_reset(dev); |
243 | result_data->result = merge_result(result_data->result, vote); | 243 | result_data->result = merge_result(result_data->result, vote); |
244 | return; | 244 | return; |
245 | } | 245 | } |
246 | 246 | ||
247 | static void report_resume(struct pci_dev *dev, void *data) | 247 | static void report_resume(struct pci_dev *dev, void *data) |
248 | { | 248 | { |
249 | struct pci_error_handlers *err_handler; | 249 | struct pci_error_handlers *err_handler; |
250 | 250 | ||
251 | dev->error_state = pci_channel_io_normal; | 251 | dev->error_state = pci_channel_io_normal; |
252 | 252 | ||
253 | if (!dev->driver || | 253 | if (!dev->driver || |
254 | !dev->driver->err_handler || | 254 | !dev->driver->err_handler || |
255 | !dev->driver->err_handler->slot_reset) | 255 | !dev->driver->err_handler->slot_reset) |
256 | return; | 256 | return; |
257 | 257 | ||
258 | err_handler = dev->driver->err_handler; | 258 | err_handler = dev->driver->err_handler; |
259 | err_handler->resume(dev); | 259 | err_handler->resume(dev); |
260 | return; | 260 | return; |
261 | } | 261 | } |
262 | 262 | ||
263 | /** | 263 | /** |
264 | * broadcast_error_message - handle message broadcast to downstream drivers | 264 | * broadcast_error_message - handle message broadcast to downstream drivers |
265 | * @dev: pointer to from where in a hierarchy message is broadcasted down | 265 | * @dev: pointer to from where in a hierarchy message is broadcasted down |
266 | * @state: error state | 266 | * @state: error state |
267 | * @error_mesg: message to print | 267 | * @error_mesg: message to print |
268 | * @cb: callback to be broadcasted | 268 | * @cb: callback to be broadcasted |
269 | * | 269 | * |
270 | * Invoked during error recovery process. Once being invoked, the content | 270 | * Invoked during error recovery process. Once being invoked, the content |
271 | * of error severity will be broadcasted to all downstream drivers in a | 271 | * of error severity will be broadcasted to all downstream drivers in a |
272 | * hierarchy in question. | 272 | * hierarchy in question. |
273 | */ | 273 | */ |
274 | static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, | 274 | static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, |
275 | enum pci_channel_state state, | 275 | enum pci_channel_state state, |
276 | char *error_mesg, | 276 | char *error_mesg, |
277 | void (*cb)(struct pci_dev *, void *)) | 277 | void (*cb)(struct pci_dev *, void *)) |
278 | { | 278 | { |
279 | struct aer_broadcast_data result_data; | 279 | struct aer_broadcast_data result_data; |
280 | 280 | ||
281 | dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg); | 281 | dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg); |
282 | result_data.state = state; | 282 | result_data.state = state; |
283 | if (cb == report_error_detected) | 283 | if (cb == report_error_detected) |
284 | result_data.result = PCI_ERS_RESULT_CAN_RECOVER; | 284 | result_data.result = PCI_ERS_RESULT_CAN_RECOVER; |
285 | else | 285 | else |
286 | result_data.result = PCI_ERS_RESULT_RECOVERED; | 286 | result_data.result = PCI_ERS_RESULT_RECOVERED; |
287 | 287 | ||
288 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { | 288 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { |
289 | /* | 289 | /* |
290 | * If the error is reported by a bridge, we think this error | 290 | * If the error is reported by a bridge, we think this error |
291 | * is related to the downstream link of the bridge, so we | 291 | * is related to the downstream link of the bridge, so we |
292 | * do error recovery on all subordinates of the bridge instead | 292 | * do error recovery on all subordinates of the bridge instead |
293 | * of the bridge and clear the error status of the bridge. | 293 | * of the bridge and clear the error status of the bridge. |
294 | */ | 294 | */ |
295 | if (cb == report_error_detected) | 295 | if (cb == report_error_detected) |
296 | dev->error_state = state; | 296 | dev->error_state = state; |
297 | pci_walk_bus(dev->subordinate, cb, &result_data); | 297 | pci_walk_bus(dev->subordinate, cb, &result_data); |
298 | if (cb == report_resume) { | 298 | if (cb == report_resume) { |
299 | pci_cleanup_aer_uncorrect_error_status(dev); | 299 | pci_cleanup_aer_uncorrect_error_status(dev); |
300 | dev->error_state = pci_channel_io_normal; | 300 | dev->error_state = pci_channel_io_normal; |
301 | } | 301 | } |
302 | } | 302 | } |
303 | else { | 303 | else { |
304 | /* | 304 | /* |
305 | * If the error is reported by an end point, we think this | 305 | * If the error is reported by an end point, we think this |
306 | * error is related to the upstream link of the end point. | 306 | * error is related to the upstream link of the end point. |
307 | */ | 307 | */ |
308 | pci_walk_bus(dev->bus, cb, &result_data); | 308 | pci_walk_bus(dev->bus, cb, &result_data); |
309 | } | 309 | } |
310 | 310 | ||
311 | return result_data.result; | 311 | return result_data.result; |
312 | } | 312 | } |
313 | 313 | ||
314 | struct find_aer_service_data { | 314 | struct find_aer_service_data { |
315 | struct pcie_port_service_driver *aer_driver; | 315 | struct pcie_port_service_driver *aer_driver; |
316 | int is_downstream; | 316 | int is_downstream; |
317 | }; | 317 | }; |
318 | 318 | ||
319 | static int find_aer_service_iter(struct device *device, void *data) | 319 | static int find_aer_service_iter(struct device *device, void *data) |
320 | { | 320 | { |
321 | struct device_driver *driver; | 321 | struct device_driver *driver; |
322 | struct pcie_port_service_driver *service_driver; | 322 | struct pcie_port_service_driver *service_driver; |
323 | struct pcie_device *pcie_dev; | 323 | struct pcie_device *pcie_dev; |
324 | struct find_aer_service_data *result; | 324 | struct find_aer_service_data *result; |
325 | 325 | ||
326 | result = (struct find_aer_service_data *) data; | 326 | result = (struct find_aer_service_data *) data; |
327 | 327 | ||
328 | if (device->bus == &pcie_port_bus_type) { | 328 | if (device->bus == &pcie_port_bus_type) { |
329 | pcie_dev = to_pcie_device(device); | 329 | pcie_dev = to_pcie_device(device); |
330 | if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT) | 330 | if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT) |
331 | result->is_downstream = 1; | 331 | result->is_downstream = 1; |
332 | 332 | ||
333 | driver = device->driver; | 333 | driver = device->driver; |
334 | if (driver) { | 334 | if (driver) { |
335 | service_driver = to_service_driver(driver); | 335 | service_driver = to_service_driver(driver); |
336 | if (service_driver->id_table->service_type == | 336 | if (service_driver->id_table->service_type == |
337 | PCIE_PORT_SERVICE_AER) { | 337 | PCIE_PORT_SERVICE_AER) { |
338 | result->aer_driver = service_driver; | 338 | result->aer_driver = service_driver; |
339 | return 1; | 339 | return 1; |
340 | } | 340 | } |
341 | } | 341 | } |
342 | } | 342 | } |
343 | 343 | ||
344 | return 0; | 344 | return 0; |
345 | } | 345 | } |
346 | 346 | ||
347 | static void find_aer_service(struct pci_dev *dev, | 347 | static void find_aer_service(struct pci_dev *dev, |
348 | struct find_aer_service_data *data) | 348 | struct find_aer_service_data *data) |
349 | { | 349 | { |
350 | int retval; | 350 | int retval; |
351 | retval = device_for_each_child(&dev->dev, data, find_aer_service_iter); | 351 | retval = device_for_each_child(&dev->dev, data, find_aer_service_iter); |
352 | } | 352 | } |
353 | 353 | ||
354 | static pci_ers_result_t reset_link(struct pcie_device *aerdev, | 354 | static pci_ers_result_t reset_link(struct pcie_device *aerdev, |
355 | struct pci_dev *dev) | 355 | struct pci_dev *dev) |
356 | { | 356 | { |
357 | struct pci_dev *udev; | 357 | struct pci_dev *udev; |
358 | pci_ers_result_t status; | 358 | pci_ers_result_t status; |
359 | struct find_aer_service_data data; | 359 | struct find_aer_service_data data; |
360 | 360 | ||
361 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) | 361 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) |
362 | udev = dev; | 362 | udev = dev; |
363 | else | 363 | else |
364 | udev= dev->bus->self; | 364 | udev= dev->bus->self; |
365 | 365 | ||
366 | data.is_downstream = 0; | 366 | data.is_downstream = 0; |
367 | data.aer_driver = NULL; | 367 | data.aer_driver = NULL; |
368 | find_aer_service(udev, &data); | 368 | find_aer_service(udev, &data); |
369 | 369 | ||
370 | /* | 370 | /* |
371 | * Use the aer driver of the error agent firstly. | 371 | * Use the aer driver of the error agent firstly. |
372 | * If it hasn't the aer driver, use the root port's | 372 | * If it hasn't the aer driver, use the root port's |
373 | */ | 373 | */ |
374 | if (!data.aer_driver || !data.aer_driver->reset_link) { | 374 | if (!data.aer_driver || !data.aer_driver->reset_link) { |
375 | if (data.is_downstream && | 375 | if (data.is_downstream && |
376 | aerdev->device.driver && | 376 | aerdev->device.driver && |
377 | to_service_driver(aerdev->device.driver)->reset_link) { | 377 | to_service_driver(aerdev->device.driver)->reset_link) { |
378 | data.aer_driver = | 378 | data.aer_driver = |
379 | to_service_driver(aerdev->device.driver); | 379 | to_service_driver(aerdev->device.driver); |
380 | } else { | 380 | } else { |
381 | dev_printk(KERN_DEBUG, &dev->dev, "no link-reset " | 381 | dev_printk(KERN_DEBUG, &dev->dev, "no link-reset " |
382 | "support\n"); | 382 | "support\n"); |
383 | return PCI_ERS_RESULT_DISCONNECT; | 383 | return PCI_ERS_RESULT_DISCONNECT; |
384 | } | 384 | } |
385 | } | 385 | } |
386 | 386 | ||
387 | status = data.aer_driver->reset_link(udev); | 387 | status = data.aer_driver->reset_link(udev); |
388 | if (status != PCI_ERS_RESULT_RECOVERED) { | 388 | if (status != PCI_ERS_RESULT_RECOVERED) { |
389 | dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream " | 389 | dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream " |
390 | "device %s failed\n", pci_name(udev)); | 390 | "device %s failed\n", pci_name(udev)); |
391 | return PCI_ERS_RESULT_DISCONNECT; | 391 | return PCI_ERS_RESULT_DISCONNECT; |
392 | } | 392 | } |
393 | 393 | ||
394 | return status; | 394 | return status; |
395 | } | 395 | } |
396 | 396 | ||
397 | /** | 397 | /** |
398 | * do_recovery - handle nonfatal/fatal error recovery process | 398 | * do_recovery - handle nonfatal/fatal error recovery process |
399 | * @aerdev: pointer to a pcie_device data structure of root port | 399 | * @aerdev: pointer to a pcie_device data structure of root port |
400 | * @dev: pointer to a pci_dev data structure of agent detecting an error | 400 | * @dev: pointer to a pci_dev data structure of agent detecting an error |
401 | * @severity: error severity type | 401 | * @severity: error severity type |
402 | * | 402 | * |
403 | * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast | 403 | * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast |
404 | * error detected message to all downstream drivers within a hierarchy in | 404 | * error detected message to all downstream drivers within a hierarchy in |
405 | * question and return the returned code. | 405 | * question and return the returned code. |
406 | */ | 406 | */ |
407 | static pci_ers_result_t do_recovery(struct pcie_device *aerdev, | 407 | static pci_ers_result_t do_recovery(struct pcie_device *aerdev, |
408 | struct pci_dev *dev, | 408 | struct pci_dev *dev, |
409 | int severity) | 409 | int severity) |
410 | { | 410 | { |
411 | pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED; | 411 | pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED; |
412 | enum pci_channel_state state; | 412 | enum pci_channel_state state; |
413 | 413 | ||
414 | if (severity == AER_FATAL) | 414 | if (severity == AER_FATAL) |
415 | state = pci_channel_io_frozen; | 415 | state = pci_channel_io_frozen; |
416 | else | 416 | else |
417 | state = pci_channel_io_normal; | 417 | state = pci_channel_io_normal; |
418 | 418 | ||
419 | status = broadcast_error_message(dev, | 419 | status = broadcast_error_message(dev, |
420 | state, | 420 | state, |
421 | "error_detected", | 421 | "error_detected", |
422 | report_error_detected); | 422 | report_error_detected); |
423 | 423 | ||
424 | if (severity == AER_FATAL) { | 424 | if (severity == AER_FATAL) { |
425 | result = reset_link(aerdev, dev); | 425 | result = reset_link(aerdev, dev); |
426 | if (result != PCI_ERS_RESULT_RECOVERED) { | 426 | if (result != PCI_ERS_RESULT_RECOVERED) { |
427 | /* TODO: Should panic here? */ | 427 | /* TODO: Should panic here? */ |
428 | return result; | 428 | return result; |
429 | } | 429 | } |
430 | } | 430 | } |
431 | 431 | ||
432 | if (status == PCI_ERS_RESULT_CAN_RECOVER) | 432 | if (status == PCI_ERS_RESULT_CAN_RECOVER) |
433 | status = broadcast_error_message(dev, | 433 | status = broadcast_error_message(dev, |
434 | state, | 434 | state, |
435 | "mmio_enabled", | 435 | "mmio_enabled", |
436 | report_mmio_enabled); | 436 | report_mmio_enabled); |
437 | 437 | ||
438 | if (status == PCI_ERS_RESULT_NEED_RESET) { | 438 | if (status == PCI_ERS_RESULT_NEED_RESET) { |
439 | /* | 439 | /* |
440 | * TODO: Should call platform-specific | 440 | * TODO: Should call platform-specific |
441 | * functions to reset slot before calling | 441 | * functions to reset slot before calling |
442 | * drivers' slot_reset callbacks? | 442 | * drivers' slot_reset callbacks? |
443 | */ | 443 | */ |
444 | status = broadcast_error_message(dev, | 444 | status = broadcast_error_message(dev, |
445 | state, | 445 | state, |
446 | "slot_reset", | 446 | "slot_reset", |
447 | report_slot_reset); | 447 | report_slot_reset); |
448 | } | 448 | } |
449 | 449 | ||
450 | if (status == PCI_ERS_RESULT_RECOVERED) | 450 | if (status == PCI_ERS_RESULT_RECOVERED) |
451 | broadcast_error_message(dev, | 451 | broadcast_error_message(dev, |
452 | state, | 452 | state, |
453 | "resume", | 453 | "resume", |
454 | report_resume); | 454 | report_resume); |
455 | 455 | ||
456 | return status; | 456 | return status; |
457 | } | 457 | } |
458 | 458 | ||
459 | /** | 459 | /** |
460 | * handle_error_source - handle logging error into an event log | 460 | * handle_error_source - handle logging error into an event log |
461 | * @aerdev: pointer to pcie_device data structure of the root port | 461 | * @aerdev: pointer to pcie_device data structure of the root port |
462 | * @dev: pointer to pci_dev data structure of error source device | 462 | * @dev: pointer to pci_dev data structure of error source device |
463 | * @info: comprehensive error information | 463 | * @info: comprehensive error information |
464 | * | 464 | * |
465 | * Invoked when an error being detected by Root Port. | 465 | * Invoked when an error being detected by Root Port. |
466 | */ | 466 | */ |
467 | static void handle_error_source(struct pcie_device * aerdev, | 467 | static void handle_error_source(struct pcie_device * aerdev, |
468 | struct pci_dev *dev, | 468 | struct pci_dev *dev, |
469 | struct aer_err_info info) | 469 | struct aer_err_info info) |
470 | { | 470 | { |
471 | pci_ers_result_t status = 0; | 471 | pci_ers_result_t status = 0; |
472 | int pos; | 472 | int pos; |
473 | 473 | ||
474 | if (info.severity == AER_CORRECTABLE) { | 474 | if (info.severity == AER_CORRECTABLE) { |
475 | /* | 475 | /* |
476 | * Correctable error does not need software intevention. | 476 | * Correctable error does not need software intevention. |
477 | * No need to go through error recovery process. | 477 | * No need to go through error recovery process. |
478 | */ | 478 | */ |
479 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 479 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
480 | if (pos) | 480 | if (pos) |
481 | pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, | 481 | pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, |
482 | info.status); | 482 | info.status); |
483 | } else { | 483 | } else { |
484 | status = do_recovery(aerdev, dev, info.severity); | 484 | status = do_recovery(aerdev, dev, info.severity); |
485 | if (status == PCI_ERS_RESULT_RECOVERED) { | 485 | if (status == PCI_ERS_RESULT_RECOVERED) { |
486 | dev_printk(KERN_DEBUG, &dev->dev, "AER driver " | 486 | dev_printk(KERN_DEBUG, &dev->dev, "AER driver " |
487 | "successfully recovered\n"); | 487 | "successfully recovered\n"); |
488 | } else { | 488 | } else { |
489 | /* TODO: Should kernel panic here? */ | 489 | /* TODO: Should kernel panic here? */ |
490 | dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't " | 490 | dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't " |
491 | "recover\n"); | 491 | "recover\n"); |
492 | } | 492 | } |
493 | } | 493 | } |
494 | } | 494 | } |
495 | 495 | ||
496 | /** | 496 | /** |
497 | * aer_enable_rootport - enable Root Port's interrupts when receiving messages | 497 | * aer_enable_rootport - enable Root Port's interrupts when receiving messages |
498 | * @rpc: pointer to a Root Port data structure | 498 | * @rpc: pointer to a Root Port data structure |
499 | * | 499 | * |
500 | * Invoked when PCIE bus loads AER service driver. | 500 | * Invoked when PCIE bus loads AER service driver. |
501 | */ | 501 | */ |
502 | void aer_enable_rootport(struct aer_rpc *rpc) | 502 | void aer_enable_rootport(struct aer_rpc *rpc) |
503 | { | 503 | { |
504 | struct pci_dev *pdev = rpc->rpd->port; | 504 | struct pci_dev *pdev = rpc->rpd->port; |
505 | int pos, aer_pos; | 505 | int pos, aer_pos; |
506 | u16 reg16; | 506 | u16 reg16; |
507 | u32 reg32; | 507 | u32 reg32; |
508 | 508 | ||
509 | pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); | 509 | pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); |
510 | /* Clear PCIE Capability's Device Status */ | 510 | /* Clear PCIE Capability's Device Status */ |
511 | pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); | 511 | pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); |
512 | pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); | 512 | pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); |
513 | 513 | ||
514 | /* Disable system error generation in response to error messages */ | 514 | /* Disable system error generation in response to error messages */ |
515 | pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); | 515 | pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); |
516 | reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); | 516 | reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); |
517 | pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); | 517 | pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); |
518 | 518 | ||
519 | aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); | 519 | aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); |
520 | /* Clear error status */ | 520 | /* Clear error status */ |
521 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); | 521 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); |
522 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); | 522 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); |
523 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); | 523 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); |
524 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); | 524 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); |
525 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); | 525 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); |
526 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); | 526 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); |
527 | 527 | ||
528 | /* Enable Root Port device reporting error itself */ | 528 | /* Enable Root Port device reporting error itself */ |
529 | pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, ®16); | 529 | pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, ®16); |
530 | reg16 = reg16 | | 530 | reg16 = reg16 | |
531 | PCI_EXP_DEVCTL_CERE | | 531 | PCI_EXP_DEVCTL_CERE | |
532 | PCI_EXP_DEVCTL_NFERE | | 532 | PCI_EXP_DEVCTL_NFERE | |
533 | PCI_EXP_DEVCTL_FERE | | 533 | PCI_EXP_DEVCTL_FERE | |
534 | PCI_EXP_DEVCTL_URRE; | 534 | PCI_EXP_DEVCTL_URRE; |
535 | pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL, | 535 | pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL, |
536 | reg16); | 536 | reg16); |
537 | 537 | ||
538 | /* Enable Root Port's interrupt in response to error messages */ | 538 | /* Enable Root Port's interrupt in response to error messages */ |
539 | pci_write_config_dword(pdev, | 539 | pci_write_config_dword(pdev, |
540 | aer_pos + PCI_ERR_ROOT_COMMAND, | 540 | aer_pos + PCI_ERR_ROOT_COMMAND, |
541 | ROOT_PORT_INTR_ON_MESG_MASK); | 541 | ROOT_PORT_INTR_ON_MESG_MASK); |
542 | } | 542 | } |
543 | 543 | ||
544 | /** | 544 | /** |
545 | * disable_root_aer - disable Root Port's interrupts when receiving messages | 545 | * disable_root_aer - disable Root Port's interrupts when receiving messages |
546 | * @rpc: pointer to a Root Port data structure | 546 | * @rpc: pointer to a Root Port data structure |
547 | * | 547 | * |
548 | * Invoked when PCIE bus unloads AER service driver. | 548 | * Invoked when PCIE bus unloads AER service driver. |
549 | */ | 549 | */ |
550 | static void disable_root_aer(struct aer_rpc *rpc) | 550 | static void disable_root_aer(struct aer_rpc *rpc) |
551 | { | 551 | { |
552 | struct pci_dev *pdev = rpc->rpd->port; | 552 | struct pci_dev *pdev = rpc->rpd->port; |
553 | u32 reg32; | 553 | u32 reg32; |
554 | int pos; | 554 | int pos; |
555 | 555 | ||
556 | pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); | 556 | pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); |
557 | /* Disable Root's interrupt in response to error messages */ | 557 | /* Disable Root's interrupt in response to error messages */ |
558 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); | 558 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); |
559 | 559 | ||
560 | /* Clear Root's error status reg */ | 560 | /* Clear Root's error status reg */ |
561 | pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); | 561 | pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); |
562 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); | 562 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); |
563 | } | 563 | } |
564 | 564 | ||
565 | /** | 565 | /** |
566 | * get_e_source - retrieve an error source | 566 | * get_e_source - retrieve an error source |
567 | * @rpc: pointer to the root port which holds an error | 567 | * @rpc: pointer to the root port which holds an error |
568 | * | 568 | * |
569 | * Invoked by DPC handler to consume an error. | 569 | * Invoked by DPC handler to consume an error. |
570 | */ | 570 | */ |
571 | static struct aer_err_source* get_e_source(struct aer_rpc *rpc) | 571 | static struct aer_err_source* get_e_source(struct aer_rpc *rpc) |
572 | { | 572 | { |
573 | struct aer_err_source *e_source; | 573 | struct aer_err_source *e_source; |
574 | unsigned long flags; | 574 | unsigned long flags; |
575 | 575 | ||
576 | /* Lock access to Root error producer/consumer index */ | 576 | /* Lock access to Root error producer/consumer index */ |
577 | spin_lock_irqsave(&rpc->e_lock, flags); | 577 | spin_lock_irqsave(&rpc->e_lock, flags); |
578 | if (rpc->prod_idx == rpc->cons_idx) { | 578 | if (rpc->prod_idx == rpc->cons_idx) { |
579 | spin_unlock_irqrestore(&rpc->e_lock, flags); | 579 | spin_unlock_irqrestore(&rpc->e_lock, flags); |
580 | return NULL; | 580 | return NULL; |
581 | } | 581 | } |
582 | e_source = &rpc->e_sources[rpc->cons_idx]; | 582 | e_source = &rpc->e_sources[rpc->cons_idx]; |
583 | rpc->cons_idx++; | 583 | rpc->cons_idx++; |
584 | if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) | 584 | if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) |
585 | rpc->cons_idx = 0; | 585 | rpc->cons_idx = 0; |
586 | spin_unlock_irqrestore(&rpc->e_lock, flags); | 586 | spin_unlock_irqrestore(&rpc->e_lock, flags); |
587 | 587 | ||
588 | return e_source; | 588 | return e_source; |
589 | } | 589 | } |
590 | 590 | ||
591 | static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) | 591 | static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) |
592 | { | 592 | { |
593 | int pos; | 593 | int pos; |
594 | 594 | ||
595 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 595 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
596 | 596 | ||
597 | /* The device might not support AER */ | 597 | /* The device might not support AER */ |
598 | if (!pos) | 598 | if (!pos) |
599 | return AER_SUCCESS; | 599 | return AER_SUCCESS; |
600 | 600 | ||
601 | if (info->severity == AER_CORRECTABLE) { | 601 | if (info->severity == AER_CORRECTABLE) { |
602 | pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, | 602 | pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, |
603 | &info->status); | 603 | &info->status); |
604 | if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) | 604 | if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) |
605 | return AER_UNSUCCESS; | 605 | return AER_UNSUCCESS; |
606 | } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || | 606 | } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || |
607 | info->severity == AER_NONFATAL) { | 607 | info->severity == AER_NONFATAL) { |
608 | 608 | ||
609 | /* Link is still healthy for IO reads */ | 609 | /* Link is still healthy for IO reads */ |
610 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, | 610 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, |
611 | &info->status); | 611 | &info->status); |
612 | if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) | 612 | if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) |
613 | return AER_UNSUCCESS; | 613 | return AER_UNSUCCESS; |
614 | 614 | ||
615 | if (info->status & AER_LOG_TLP_MASKS) { | 615 | if (info->status & AER_LOG_TLP_MASKS) { |
616 | info->flags |= AER_TLP_HEADER_VALID_FLAG; | 616 | info->flags |= AER_TLP_HEADER_VALID_FLAG; |
617 | pci_read_config_dword(dev, | 617 | pci_read_config_dword(dev, |
618 | pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); | 618 | pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); |
619 | pci_read_config_dword(dev, | 619 | pci_read_config_dword(dev, |
620 | pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); | 620 | pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); |
621 | pci_read_config_dword(dev, | 621 | pci_read_config_dword(dev, |
622 | pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); | 622 | pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); |
623 | pci_read_config_dword(dev, | 623 | pci_read_config_dword(dev, |
624 | pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); | 624 | pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); |
625 | } | 625 | } |
626 | } | 626 | } |
627 | 627 | ||
628 | return AER_SUCCESS; | 628 | return AER_SUCCESS; |
629 | } | 629 | } |
630 | 630 | ||
631 | /** | 631 | /** |
632 | * aer_isr_one_error - consume an error detected by root port | 632 | * aer_isr_one_error - consume an error detected by root port |
633 | * @p_device: pointer to error root port service device | 633 | * @p_device: pointer to error root port service device |
634 | * @e_src: pointer to an error source | 634 | * @e_src: pointer to an error source |
635 | */ | 635 | */ |
636 | static void aer_isr_one_error(struct pcie_device *p_device, | 636 | static void aer_isr_one_error(struct pcie_device *p_device, |
637 | struct aer_err_source *e_src) | 637 | struct aer_err_source *e_src) |
638 | { | 638 | { |
639 | struct device *s_device; | 639 | struct device *s_device; |
640 | struct aer_err_info e_info = {0, 0, 0,}; | 640 | struct aer_err_info e_info = {0, 0, 0,}; |
641 | int i; | 641 | int i; |
642 | u16 id; | 642 | u16 id; |
643 | 643 | ||
644 | /* | 644 | /* |
645 | * There is a possibility that both correctable error and | 645 | * There is a possibility that both correctable error and |
646 | * uncorrectable error being logged. Report correctable error first. | 646 | * uncorrectable error being logged. Report correctable error first. |
647 | */ | 647 | */ |
648 | for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) { | 648 | for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) { |
649 | if (i > 4) | 649 | if (i > 4) |
650 | break; | 650 | break; |
651 | if (!(e_src->status & i)) | 651 | if (!(e_src->status & i)) |
652 | continue; | 652 | continue; |
653 | 653 | ||
654 | /* Init comprehensive error information */ | 654 | /* Init comprehensive error information */ |
655 | if (i & PCI_ERR_ROOT_COR_RCV) { | 655 | if (i & PCI_ERR_ROOT_COR_RCV) { |
656 | id = ERR_COR_ID(e_src->id); | 656 | id = ERR_COR_ID(e_src->id); |
657 | e_info.severity = AER_CORRECTABLE; | 657 | e_info.severity = AER_CORRECTABLE; |
658 | } else { | 658 | } else { |
659 | id = ERR_UNCOR_ID(e_src->id); | 659 | id = ERR_UNCOR_ID(e_src->id); |
660 | e_info.severity = ((e_src->status >> 6) & 1); | 660 | e_info.severity = ((e_src->status >> 6) & 1); |
661 | } | 661 | } |
662 | if (e_src->status & | 662 | if (e_src->status & |
663 | (PCI_ERR_ROOT_MULTI_COR_RCV | | 663 | (PCI_ERR_ROOT_MULTI_COR_RCV | |
664 | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) | 664 | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) |
665 | e_info.flags |= AER_MULTI_ERROR_VALID_FLAG; | 665 | e_info.flags |= AER_MULTI_ERROR_VALID_FLAG; |
666 | if (!(s_device = find_source_device(p_device->port, id))) { | 666 | if (!(s_device = find_source_device(p_device->port, id))) { |
667 | printk(KERN_DEBUG "%s->can't find device of ID%04x\n", | 667 | printk(KERN_DEBUG "%s->can't find device of ID%04x\n", |
668 | __func__, id); | 668 | __func__, id); |
669 | continue; | 669 | continue; |
670 | } | 670 | } |
671 | if (get_device_error_info(to_pci_dev(s_device), &e_info) == | 671 | if (get_device_error_info(to_pci_dev(s_device), &e_info) == |
672 | AER_SUCCESS) { | 672 | AER_SUCCESS) { |
673 | aer_print_error(to_pci_dev(s_device), &e_info); | 673 | aer_print_error(to_pci_dev(s_device), &e_info); |
674 | handle_error_source(p_device, | 674 | handle_error_source(p_device, |
675 | to_pci_dev(s_device), | 675 | to_pci_dev(s_device), |
676 | e_info); | 676 | e_info); |
677 | } | 677 | } |
678 | } | 678 | } |
679 | } | 679 | } |
680 | 680 | ||
681 | /** | 681 | /** |
682 | * aer_isr - consume errors detected by root port | 682 | * aer_isr - consume errors detected by root port |
683 | * @work: definition of this work item | 683 | * @work: definition of this work item |
684 | * | 684 | * |
685 | * Invoked, as DPC, when root port records new detected error | 685 | * Invoked, as DPC, when root port records new detected error |
686 | */ | 686 | */ |
687 | void aer_isr(struct work_struct *work) | 687 | void aer_isr(struct work_struct *work) |
688 | { | 688 | { |
689 | struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); | 689 | struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); |
690 | struct pcie_device *p_device = rpc->rpd; | 690 | struct pcie_device *p_device = rpc->rpd; |
691 | struct aer_err_source *e_src; | 691 | struct aer_err_source *e_src; |
692 | 692 | ||
693 | mutex_lock(&rpc->rpc_mutex); | 693 | mutex_lock(&rpc->rpc_mutex); |
694 | e_src = get_e_source(rpc); | 694 | e_src = get_e_source(rpc); |
695 | while (e_src) { | 695 | while (e_src) { |
696 | aer_isr_one_error(p_device, e_src); | 696 | aer_isr_one_error(p_device, e_src); |
697 | e_src = get_e_source(rpc); | 697 | e_src = get_e_source(rpc); |
698 | } | 698 | } |
699 | mutex_unlock(&rpc->rpc_mutex); | 699 | mutex_unlock(&rpc->rpc_mutex); |
700 | 700 | ||
701 | wake_up(&rpc->wait_release); | 701 | wake_up(&rpc->wait_release); |
702 | } | 702 | } |
703 | 703 | ||
704 | /** | 704 | /** |
705 | * aer_delete_rootport - disable root port aer and delete service data | 705 | * aer_delete_rootport - disable root port aer and delete service data |
706 | * @rpc: pointer to a root port device being deleted | 706 | * @rpc: pointer to a root port device being deleted |
707 | * | 707 | * |
708 | * Invoked when AER service unloaded on a specific Root Port | 708 | * Invoked when AER service unloaded on a specific Root Port |
709 | */ | 709 | */ |
710 | void aer_delete_rootport(struct aer_rpc *rpc) | 710 | void aer_delete_rootport(struct aer_rpc *rpc) |
711 | { | 711 | { |
712 | /* Disable root port AER itself */ | 712 | /* Disable root port AER itself */ |
713 | disable_root_aer(rpc); | 713 | disable_root_aer(rpc); |
714 | 714 | ||
715 | kfree(rpc); | 715 | kfree(rpc); |
716 | } | 716 | } |
717 | 717 | ||
718 | /** | 718 | /** |
719 | * aer_init - provide AER initialization | 719 | * aer_init - provide AER initialization |
720 | * @dev: pointer to AER pcie device | 720 | * @dev: pointer to AER pcie device |
721 | * | 721 | * |
722 | * Invoked when AER service driver is loaded. | 722 | * Invoked when AER service driver is loaded. |
723 | */ | 723 | */ |
724 | int aer_init(struct pcie_device *dev) | 724 | int aer_init(struct pcie_device *dev) |
725 | { | 725 | { |
726 | if (aer_osc_setup(dev) && !forceload) | 726 | if (aer_osc_setup(dev) && !forceload) |
727 | return -ENXIO; | 727 | return -ENXIO; |
728 | 728 | ||
729 | return AER_SUCCESS; | 729 | return AER_SUCCESS; |
730 | } | 730 | } |
731 | 731 | ||
732 | EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); | 732 | EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); |
733 | EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); | 733 | EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); |
734 | EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); | 734 | EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); |
735 | 735 | ||
736 | 736 |
drivers/pci/pcie/portdrv.h
1 | /* | 1 | /* |
2 | * File: portdrv.h | 2 | * File: portdrv.h |
3 | * Purpose: PCI Express Port Bus Driver's Internal Data Structures | 3 | * Purpose: PCI Express Port Bus Driver's Internal Data Structures |
4 | * | 4 | * |
5 | * Copyright (C) 2004 Intel | 5 | * Copyright (C) 2004 Intel |
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | 6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #ifndef _PORTDRV_H_ | 9 | #ifndef _PORTDRV_H_ |
10 | #define _PORTDRV_H_ | 10 | #define _PORTDRV_H_ |
11 | 11 | ||
12 | #include <linux/compiler.h> | 12 | #include <linux/compiler.h> |
13 | 13 | ||
14 | #if !defined(PCI_CAP_ID_PME) | 14 | #if !defined(PCI_CAP_ID_PME) |
15 | #define PCI_CAP_ID_PME 1 | 15 | #define PCI_CAP_ID_PME 1 |
16 | #endif | 16 | #endif |
17 | 17 | ||
18 | #if !defined(PCI_CAP_ID_EXP) | 18 | #if !defined(PCI_CAP_ID_EXP) |
19 | #define PCI_CAP_ID_EXP 0x10 | 19 | #define PCI_CAP_ID_EXP 0x10 |
20 | #endif | 20 | #endif |
21 | 21 | ||
22 | #define PORT_TYPE_MASK 0xf | 22 | #define PORT_TYPE_MASK 0xf |
23 | #define PORT_TO_SLOT_MASK 0x100 | 23 | #define PORT_TO_SLOT_MASK 0x100 |
24 | #define SLOT_HP_CAPABLE_MASK 0x40 | 24 | #define SLOT_HP_CAPABLE_MASK 0x40 |
25 | #define PCIE_CAPABILITIES_REG 0x2 | 25 | #define PCIE_CAPABILITIES_REG 0x2 |
26 | #define PCIE_SLOT_CAPABILITIES_REG 0x14 | 26 | #define PCIE_SLOT_CAPABILITIES_REG 0x14 |
27 | #define PCIE_PORT_DEVICE_MAXSERVICES 4 | 27 | #define PCIE_PORT_DEVICE_MAXSERVICES 4 |
28 | #define PCI_CFG_SPACE_SIZE 256 | ||
29 | 28 | ||
30 | #define get_descriptor_id(type, service) (((type - 4) << 4) | service) | 29 | #define get_descriptor_id(type, service) (((type - 4) << 4) | service) |
31 | 30 | ||
32 | struct pcie_port_device_ext { | 31 | struct pcie_port_device_ext { |
33 | int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ | 32 | int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */ |
34 | }; | 33 | }; |
35 | 34 | ||
36 | extern struct bus_type pcie_port_bus_type; | 35 | extern struct bus_type pcie_port_bus_type; |
37 | extern int pcie_port_device_probe(struct pci_dev *dev); | 36 | extern int pcie_port_device_probe(struct pci_dev *dev); |
38 | extern int pcie_port_device_register(struct pci_dev *dev); | 37 | extern int pcie_port_device_register(struct pci_dev *dev); |
39 | #ifdef CONFIG_PM | 38 | #ifdef CONFIG_PM |
40 | extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state); | 39 | extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state); |
41 | extern int pcie_port_device_resume(struct pci_dev *dev); | 40 | extern int pcie_port_device_resume(struct pci_dev *dev); |
42 | #endif | 41 | #endif |
43 | extern void pcie_port_device_remove(struct pci_dev *dev); | 42 | extern void pcie_port_device_remove(struct pci_dev *dev); |
44 | extern int __must_check pcie_port_bus_register(void); | 43 | extern int __must_check pcie_port_bus_register(void); |
45 | extern void pcie_port_bus_unregister(void); | 44 | extern void pcie_port_bus_unregister(void); |
46 | 45 | ||
47 | #endif /* _PORTDRV_H_ */ | 46 | #endif /* _PORTDRV_H_ */ |
48 | 47 |
include/linux/aer.h
1 | /* | 1 | /* |
2 | * Copyright (C) 2006 Intel Corp. | 2 | * Copyright (C) 2006 Intel Corp. |
3 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | 3 | * Tom Long Nguyen (tom.l.nguyen@intel.com) |
4 | * Zhang Yanmin (yanmin.zhang@intel.com) | 4 | * Zhang Yanmin (yanmin.zhang@intel.com) |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #ifndef _AER_H_ | 7 | #ifndef _AER_H_ |
8 | #define _AER_H_ | 8 | #define _AER_H_ |
9 | 9 | ||
10 | #if defined(CONFIG_PCIEAER) | 10 | #if defined(CONFIG_PCIEAER) |
11 | /* pci-e port driver needs this function to enable aer */ | 11 | /* pci-e port driver needs this function to enable aer */ |
12 | extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); | 12 | extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); |
13 | extern int pci_find_aer_capability(struct pci_dev *dev); | ||
14 | extern int pci_disable_pcie_error_reporting(struct pci_dev *dev); | 13 | extern int pci_disable_pcie_error_reporting(struct pci_dev *dev); |
15 | extern int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); | 14 | extern int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); |
16 | #else | 15 | #else |
17 | static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev) | 16 | static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev) |
18 | { | 17 | { |
19 | return -EINVAL; | 18 | return -EINVAL; |
20 | } | 19 | } |
21 | static inline int pci_disable_pcie_error_reporting(struct pci_dev *dev) | 20 | static inline int pci_disable_pcie_error_reporting(struct pci_dev *dev) |
22 | { | 21 | { |
23 | return -EINVAL; | 22 | return -EINVAL; |
24 | } | 23 | } |
25 | static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) | 24 | static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) |
26 | { | 25 | { |
27 | return -EINVAL; | 26 | return -EINVAL; |
28 | } | 27 | } |
29 | #endif | 28 | #endif |
30 | 29 | ||
31 | #endif //_AER_H_ | 30 | #endif //_AER_H_ |
32 | 31 | ||
33 | 32 |