]>
Commit | Line | Data |
---|---|---|
6c2b374d ZY |
1 | /* |
2 | * drivers/pci/pcie/aer/aerdrv.c | |
3 | * | |
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 | |
6 | * for more details. | |
7 | * | |
8 | * This file implements the AER root port service driver. The driver will | |
9 | * register an irq handler. When root port triggers an AER interrupt, the irq | |
10 | * handler will collect root port status and schedule a work. | |
11 | * | |
12 | * Copyright (C) 2006 Intel Corp. | |
13 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | |
14 | * Zhang Yanmin (yanmin.zhang@intel.com) | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/pci.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/errno.h> | |
22 | #include <linux/pm.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/interrupt.h> | |
25 | #include <linux/delay.h> | |
26 | #include <linux/pcieport_if.h> | |
27 | ||
28 | #include "aerdrv.h" | |
29 | ||
30 | /* | |
31 | * Version Information | |
32 | */ | |
33 | #define DRIVER_VERSION "v1.0" | |
34 | #define DRIVER_AUTHOR "tom.l.nguyen@intel.com" | |
35 | #define DRIVER_DESC "Root Port Advanced Error Reporting Driver" | |
36 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
37 | MODULE_DESCRIPTION(DRIVER_DESC); | |
38 | MODULE_LICENSE("GPL"); | |
39 | ||
40 | static int __devinit aer_probe (struct pcie_device *dev, | |
41 | const struct pcie_port_service_id *id ); | |
42 | static void aer_remove(struct pcie_device *dev); | |
43 | static int aer_suspend(struct pcie_device *dev, pm_message_t state) | |
44 | {return 0;} | |
45 | static int aer_resume(struct pcie_device *dev) {return 0;} | |
46 | static pci_ers_result_t aer_error_detected(struct pci_dev *dev, | |
47 | enum pci_channel_state error); | |
48 | static void aer_error_resume(struct pci_dev *dev); | |
49 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev); | |
50 | ||
51 | /* | |
52 | * PCI Express bus's AER Root service driver data structure | |
53 | */ | |
54 | static struct pcie_port_service_id aer_id[] = { | |
55 | { | |
56 | .vendor = PCI_ANY_ID, | |
57 | .device = PCI_ANY_ID, | |
58 | .port_type = PCIE_RC_PORT, | |
59 | .service_type = PCIE_PORT_SERVICE_AER, | |
60 | }, | |
61 | { /* end: all zeroes */ } | |
62 | }; | |
63 | ||
64 | static struct pci_error_handlers aer_error_handlers = { | |
65 | .error_detected = aer_error_detected, | |
66 | .resume = aer_error_resume, | |
67 | }; | |
68 | ||
69 | static struct pcie_port_service_driver aerdrv = { | |
70 | .name = "aer", | |
71 | .id_table = &aer_id[0], | |
72 | ||
73 | .probe = aer_probe, | |
74 | .remove = aer_remove, | |
75 | ||
76 | .suspend = aer_suspend, | |
77 | .resume = aer_resume, | |
78 | ||
79 | .err_handler = &aer_error_handlers, | |
80 | ||
81 | .reset_link = aer_root_reset, | |
82 | }; | |
83 | ||
84 | /** | |
85 | * aer_irq - Root Port's ISR | |
86 | * @irq: IRQ assigned to Root Port | |
87 | * @context: pointer to Root Port data structure | |
6c2b374d ZY |
88 | * |
89 | * Invoked when Root Port detects AER messages. | |
90 | **/ | |
7d12e780 | 91 | static irqreturn_t aer_irq(int irq, void *context) |
6c2b374d ZY |
92 | { |
93 | unsigned int status, id; | |
94 | struct pcie_device *pdev = (struct pcie_device *)context; | |
95 | struct aer_rpc *rpc = get_service_data(pdev); | |
96 | int next_prod_idx; | |
97 | unsigned long flags; | |
98 | int pos; | |
99 | ||
100 | pos = pci_find_aer_capability(pdev->port); | |
101 | /* | |
102 | * Must lock access to Root Error Status Reg, Root Error ID Reg, | |
103 | * and Root error producer/consumer index | |
104 | */ | |
105 | spin_lock_irqsave(&rpc->e_lock, flags); | |
106 | ||
107 | /* Read error status */ | |
108 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); | |
109 | if (!(status & ROOT_ERR_STATUS_MASKS)) { | |
110 | spin_unlock_irqrestore(&rpc->e_lock, flags); | |
111 | return IRQ_NONE; | |
112 | } | |
113 | ||
114 | /* Read error source and clear error status */ | |
115 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id); | |
116 | pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); | |
117 | ||
118 | /* Store error source for later DPC handler */ | |
119 | next_prod_idx = rpc->prod_idx + 1; | |
120 | if (next_prod_idx == AER_ERROR_SOURCES_MAX) | |
121 | next_prod_idx = 0; | |
122 | if (next_prod_idx == rpc->cons_idx) { | |
123 | /* | |
124 | * Error Storm Condition - possibly the same error occurred. | |
125 | * Drop the error. | |
126 | */ | |
127 | spin_unlock_irqrestore(&rpc->e_lock, flags); | |
128 | return IRQ_HANDLED; | |
129 | } | |
130 | rpc->e_sources[rpc->prod_idx].status = status; | |
131 | rpc->e_sources[rpc->prod_idx].id = id; | |
132 | rpc->prod_idx = next_prod_idx; | |
133 | spin_unlock_irqrestore(&rpc->e_lock, flags); | |
134 | ||
135 | /* Invoke DPC handler */ | |
136 | schedule_work(&rpc->dpc_handler); | |
137 | ||
138 | return IRQ_HANDLED; | |
139 | } | |
140 | ||
141 | /** | |
142 | * aer_alloc_rpc - allocate Root Port data structure | |
143 | * @dev: pointer to the pcie_dev data structure | |
144 | * | |
145 | * Invoked when Root Port's AER service is loaded. | |
146 | **/ | |
147 | static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev) | |
148 | { | |
149 | struct aer_rpc *rpc; | |
150 | ||
5cbded58 | 151 | if (!(rpc = kmalloc(sizeof(struct aer_rpc), |
6c2b374d ZY |
152 | GFP_KERNEL))) |
153 | return NULL; | |
154 | ||
155 | memset(rpc, 0, sizeof(struct aer_rpc)); | |
156 | /* | |
157 | * Initialize Root lock access, e_lock, to Root Error Status Reg, | |
158 | * Root Error ID Reg, and Root error producer/consumer index. | |
159 | */ | |
160 | rpc->e_lock = SPIN_LOCK_UNLOCKED; | |
161 | ||
162 | rpc->rpd = dev; | |
65f27f38 | 163 | INIT_WORK(&rpc->dpc_handler, aer_isr); |
6c2b374d ZY |
164 | rpc->prod_idx = rpc->cons_idx = 0; |
165 | mutex_init(&rpc->rpc_mutex); | |
166 | init_waitqueue_head(&rpc->wait_release); | |
167 | ||
168 | /* Use PCIE bus function to store rpc into PCIE device */ | |
169 | set_service_data(dev, rpc); | |
170 | ||
171 | return rpc; | |
172 | } | |
173 | ||
174 | /** | |
175 | * aer_remove - clean up resources | |
176 | * @dev: pointer to the pcie_dev data structure | |
177 | * | |
178 | * Invoked when PCI Express bus unloads or AER probe fails. | |
179 | **/ | |
180 | static void aer_remove(struct pcie_device *dev) | |
181 | { | |
182 | struct aer_rpc *rpc = get_service_data(dev); | |
183 | ||
184 | if (rpc) { | |
185 | /* If register interrupt service, it must be free. */ | |
186 | if (rpc->isr) | |
187 | free_irq(dev->irq, dev); | |
188 | ||
189 | wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); | |
190 | ||
191 | aer_delete_rootport(rpc); | |
192 | set_service_data(dev, NULL); | |
193 | } | |
194 | } | |
195 | ||
196 | /** | |
197 | * aer_probe - initialize resources | |
198 | * @dev: pointer to the pcie_dev data structure | |
199 | * @id: pointer to the service id data structure | |
200 | * | |
201 | * Invoked when PCI Express bus loads AER service driver. | |
202 | **/ | |
203 | static int __devinit aer_probe (struct pcie_device *dev, | |
204 | const struct pcie_port_service_id *id ) | |
205 | { | |
206 | int status; | |
207 | struct aer_rpc *rpc; | |
208 | struct device *device = &dev->device; | |
209 | ||
210 | /* Init */ | |
211 | if ((status = aer_init(dev))) | |
212 | return status; | |
213 | ||
214 | /* Alloc rpc data structure */ | |
215 | if (!(rpc = aer_alloc_rpc(dev))) { | |
216 | printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n", | |
217 | __FUNCTION__, device->bus_id); | |
218 | aer_remove(dev); | |
219 | return -ENOMEM; | |
220 | } | |
221 | ||
222 | /* Request IRQ ISR */ | |
223 | if ((status = request_irq(dev->irq, aer_irq, SA_SHIRQ, "aerdrv", | |
224 | dev))) { | |
225 | printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n", | |
226 | __FUNCTION__, device->bus_id); | |
227 | aer_remove(dev); | |
228 | return status; | |
229 | } | |
230 | ||
231 | rpc->isr = 1; | |
232 | ||
233 | aer_enable_rootport(rpc); | |
234 | ||
235 | return status; | |
236 | } | |
237 | ||
238 | /** | |
239 | * aer_root_reset - reset link on Root Port | |
240 | * @dev: pointer to Root Port's pci_dev data structure | |
241 | * | |
242 | * Invoked by Port Bus driver when performing link reset at Root Port. | |
243 | **/ | |
244 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev) | |
245 | { | |
246 | u16 p2p_ctrl; | |
247 | u32 status; | |
248 | int pos; | |
249 | ||
250 | pos = pci_find_aer_capability(dev); | |
251 | ||
252 | /* Disable Root's interrupt in response to error messages */ | |
253 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0); | |
254 | ||
255 | /* Assert Secondary Bus Reset */ | |
256 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); | |
257 | p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET; | |
258 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | |
259 | ||
260 | /* De-assert Secondary Bus Reset */ | |
261 | p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET; | |
262 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | |
263 | ||
264 | /* | |
265 | * System software must wait for at least 100ms from the end | |
266 | * of a reset of one or more device before it is permitted | |
267 | * to issue Configuration Requests to those devices. | |
268 | */ | |
269 | msleep(200); | |
270 | printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id); | |
271 | ||
272 | /* Enable Root Port's interrupt in response to error messages */ | |
273 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); | |
274 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); | |
275 | pci_write_config_dword(dev, | |
276 | pos + PCI_ERR_ROOT_COMMAND, | |
277 | ROOT_PORT_INTR_ON_MESG_MASK); | |
278 | ||
279 | return PCI_ERS_RESULT_RECOVERED; | |
280 | } | |
281 | ||
282 | /** | |
283 | * aer_error_detected - update severity status | |
284 | * @dev: pointer to Root Port's pci_dev data structure | |
285 | * @error: error severity being notified by port bus | |
286 | * | |
287 | * Invoked by Port Bus driver during error recovery. | |
288 | **/ | |
289 | static pci_ers_result_t aer_error_detected(struct pci_dev *dev, | |
290 | enum pci_channel_state error) | |
291 | { | |
292 | /* Root Port has no impact. Always recovers. */ | |
293 | return PCI_ERS_RESULT_CAN_RECOVER; | |
294 | } | |
295 | ||
296 | /** | |
297 | * aer_error_resume - clean up corresponding error status bits | |
298 | * @dev: pointer to Root Port's pci_dev data structure | |
299 | * | |
300 | * Invoked by Port Bus driver during nonfatal recovery. | |
301 | **/ | |
302 | static void aer_error_resume(struct pci_dev *dev) | |
303 | { | |
304 | int pos; | |
305 | u32 status, mask; | |
306 | u16 reg16; | |
307 | ||
308 | /* Clean up Root device status */ | |
309 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | |
310 | pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, ®16); | |
311 | pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16); | |
312 | ||
313 | /* Clean AER Root Error Status */ | |
314 | pos = pci_find_aer_capability(dev); | |
315 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); | |
316 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); | |
317 | if (dev->error_state == pci_channel_io_normal) | |
318 | status &= ~mask; /* Clear corresponding nonfatal bits */ | |
319 | else | |
320 | status &= mask; /* Clear corresponding fatal bits */ | |
321 | pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); | |
322 | } | |
323 | ||
324 | /** | |
325 | * aer_service_init - register AER root service driver | |
326 | * | |
327 | * Invoked when AER root service driver is loaded. | |
328 | **/ | |
329 | static int __init aer_service_init(void) | |
330 | { | |
331 | return pcie_port_service_register(&aerdrv); | |
332 | } | |
333 | ||
334 | /** | |
335 | * aer_service_exit - unregister AER root service driver | |
336 | * | |
337 | * Invoked when AER root service driver is unloaded. | |
338 | **/ | |
339 | static void __exit aer_service_exit(void) | |
340 | { | |
341 | pcie_port_service_unregister(&aerdrv); | |
342 | } | |
343 | ||
344 | module_init(aer_service_init); | |
345 | module_exit(aer_service_exit); |