1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * amd8111_edac.c, AMD8111 Hyper Transport chip EDAC kernel module |
4 | * |
5 | * Copyright (c) 2008 Wind River Systems, Inc. |
6 | * |
7 | * Authors: Cao Qingtao <qingtao.cao@windriver.com> |
8 | * Benjamin Walsh <benjamin.walsh@windriver.com> |
9 | * Hu Yongqi <yongqi.hu@windriver.com> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/init.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/bitops.h> |
16 | #include <linux/edac.h> |
17 | #include <linux/pci_ids.h> |
18 | #include <asm/io.h> |
19 | |
20 | #include "edac_module.h" |
21 | #include "amd8111_edac.h" |
22 | |
23 | #define AMD8111_EDAC_REVISION " Ver: 1.0.0" |
24 | #define AMD8111_EDAC_MOD_STR "amd8111_edac" |
25 | |
26 | #define PCI_DEVICE_ID_AMD_8111_PCI 0x7460 |
27 | |
28 | enum amd8111_edac_devs { |
29 | LPC_BRIDGE = 0, |
30 | }; |
31 | |
32 | enum amd8111_edac_pcis { |
33 | PCI_BRIDGE = 0, |
34 | }; |
35 | |
36 | /* Wrapper functions for accessing PCI configuration space */ |
37 | static int edac_pci_read_dword(struct pci_dev *dev, int reg, u32 *val32) |
38 | { |
39 | int ret; |
40 | |
41 | ret = pci_read_config_dword(dev, where: reg, val: val32); |
42 | if (ret != 0) |
43 | printk(KERN_ERR AMD8111_EDAC_MOD_STR |
44 | " PCI Access Read Error at 0x%x\n" , reg); |
45 | |
46 | return ret; |
47 | } |
48 | |
49 | static void edac_pci_read_byte(struct pci_dev *dev, int reg, u8 *val8) |
50 | { |
51 | int ret; |
52 | |
53 | ret = pci_read_config_byte(dev, where: reg, val: val8); |
54 | if (ret != 0) |
55 | printk(KERN_ERR AMD8111_EDAC_MOD_STR |
56 | " PCI Access Read Error at 0x%x\n" , reg); |
57 | } |
58 | |
59 | static void edac_pci_write_dword(struct pci_dev *dev, int reg, u32 val32) |
60 | { |
61 | int ret; |
62 | |
63 | ret = pci_write_config_dword(dev, where: reg, val: val32); |
64 | if (ret != 0) |
65 | printk(KERN_ERR AMD8111_EDAC_MOD_STR |
66 | " PCI Access Write Error at 0x%x\n" , reg); |
67 | } |
68 | |
69 | static void edac_pci_write_byte(struct pci_dev *dev, int reg, u8 val8) |
70 | { |
71 | int ret; |
72 | |
73 | ret = pci_write_config_byte(dev, where: reg, val: val8); |
74 | if (ret != 0) |
75 | printk(KERN_ERR AMD8111_EDAC_MOD_STR |
76 | " PCI Access Write Error at 0x%x\n" , reg); |
77 | } |
78 | |
79 | /* |
80 | * device-specific methods for amd8111 PCI Bridge Controller |
81 | * |
82 | * Error Reporting and Handling for amd8111 chipset could be found |
83 | * in its datasheet 3.1.2 section, P37 |
84 | */ |
85 | static void amd8111_pci_bridge_init(struct amd8111_pci_info *pci_info) |
86 | { |
87 | u32 val32; |
88 | struct pci_dev *dev = pci_info->dev; |
89 | |
90 | /* First clear error detection flags on the host interface */ |
91 | |
92 | /* Clear SSE/SMA/STA flags in the global status register*/ |
93 | edac_pci_read_dword(dev, REG_PCI_STSCMD, val32: &val32); |
94 | if (val32 & PCI_STSCMD_CLEAR_MASK) |
95 | edac_pci_write_dword(dev, REG_PCI_STSCMD, val32); |
96 | |
97 | /* Clear CRC and Link Fail flags in HT Link Control reg */ |
98 | edac_pci_read_dword(dev, REG_HT_LINK, val32: &val32); |
99 | if (val32 & HT_LINK_CLEAR_MASK) |
100 | edac_pci_write_dword(dev, REG_HT_LINK, val32); |
101 | |
102 | /* Second clear all fault on the secondary interface */ |
103 | |
104 | /* Clear error flags in the memory-base limit reg. */ |
105 | edac_pci_read_dword(dev, REG_MEM_LIM, val32: &val32); |
106 | if (val32 & MEM_LIMIT_CLEAR_MASK) |
107 | edac_pci_write_dword(dev, REG_MEM_LIM, val32); |
108 | |
109 | /* Clear Discard Timer Expired flag in Interrupt/Bridge Control reg */ |
110 | edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, val32: &val32); |
111 | if (val32 & PCI_INTBRG_CTRL_CLEAR_MASK) |
112 | edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32); |
113 | |
114 | /* Last enable error detections */ |
115 | if (edac_op_state == EDAC_OPSTATE_POLL) { |
116 | /* Enable System Error reporting in global status register */ |
117 | edac_pci_read_dword(dev, REG_PCI_STSCMD, val32: &val32); |
118 | val32 |= PCI_STSCMD_SERREN; |
119 | edac_pci_write_dword(dev, REG_PCI_STSCMD, val32); |
120 | |
121 | /* Enable CRC Sync flood packets to HyperTransport Link */ |
122 | edac_pci_read_dword(dev, REG_HT_LINK, val32: &val32); |
123 | val32 |= HT_LINK_CRCFEN; |
124 | edac_pci_write_dword(dev, REG_HT_LINK, val32); |
125 | |
126 | /* Enable SSE reporting etc in Interrupt control reg */ |
127 | edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, val32: &val32); |
128 | val32 |= PCI_INTBRG_CTRL_POLL_MASK; |
129 | edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32); |
130 | } |
131 | } |
132 | |
133 | static void amd8111_pci_bridge_exit(struct amd8111_pci_info *pci_info) |
134 | { |
135 | u32 val32; |
136 | struct pci_dev *dev = pci_info->dev; |
137 | |
138 | if (edac_op_state == EDAC_OPSTATE_POLL) { |
139 | /* Disable System Error reporting */ |
140 | edac_pci_read_dword(dev, REG_PCI_STSCMD, val32: &val32); |
141 | val32 &= ~PCI_STSCMD_SERREN; |
142 | edac_pci_write_dword(dev, REG_PCI_STSCMD, val32); |
143 | |
144 | /* Disable CRC flood packets */ |
145 | edac_pci_read_dword(dev, REG_HT_LINK, val32: &val32); |
146 | val32 &= ~HT_LINK_CRCFEN; |
147 | edac_pci_write_dword(dev, REG_HT_LINK, val32); |
148 | |
149 | /* Disable DTSERREN/MARSP/SERREN in Interrupt Control reg */ |
150 | edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, val32: &val32); |
151 | val32 &= ~PCI_INTBRG_CTRL_POLL_MASK; |
152 | edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32); |
153 | } |
154 | } |
155 | |
156 | static void amd8111_pci_bridge_check(struct edac_pci_ctl_info *edac_dev) |
157 | { |
158 | struct amd8111_pci_info *pci_info = edac_dev->pvt_info; |
159 | struct pci_dev *dev = pci_info->dev; |
160 | u32 val32; |
161 | |
162 | /* Check out PCI Bridge Status and Command Register */ |
163 | edac_pci_read_dword(dev, REG_PCI_STSCMD, val32: &val32); |
164 | if (val32 & PCI_STSCMD_CLEAR_MASK) { |
165 | printk(KERN_INFO "Error(s) in PCI bridge status and command" |
166 | "register on device %s\n" , pci_info->ctl_name); |
167 | printk(KERN_INFO "SSE: %d, RMA: %d, RTA: %d\n" , |
168 | (val32 & PCI_STSCMD_SSE) != 0, |
169 | (val32 & PCI_STSCMD_RMA) != 0, |
170 | (val32 & PCI_STSCMD_RTA) != 0); |
171 | |
172 | val32 |= PCI_STSCMD_CLEAR_MASK; |
173 | edac_pci_write_dword(dev, REG_PCI_STSCMD, val32); |
174 | |
175 | edac_pci_handle_npe(pci: edac_dev, msg: edac_dev->ctl_name); |
176 | } |
177 | |
178 | /* Check out HyperTransport Link Control Register */ |
179 | edac_pci_read_dword(dev, REG_HT_LINK, val32: &val32); |
180 | if (val32 & HT_LINK_LKFAIL) { |
181 | printk(KERN_INFO "Error(s) in hypertransport link control" |
182 | "register on device %s\n" , pci_info->ctl_name); |
183 | printk(KERN_INFO "LKFAIL: %d\n" , |
184 | (val32 & HT_LINK_LKFAIL) != 0); |
185 | |
186 | val32 |= HT_LINK_LKFAIL; |
187 | edac_pci_write_dword(dev, REG_HT_LINK, val32); |
188 | |
189 | edac_pci_handle_npe(pci: edac_dev, msg: edac_dev->ctl_name); |
190 | } |
191 | |
192 | /* Check out PCI Interrupt and Bridge Control Register */ |
193 | edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, val32: &val32); |
194 | if (val32 & PCI_INTBRG_CTRL_DTSTAT) { |
195 | printk(KERN_INFO "Error(s) in PCI interrupt and bridge control" |
196 | "register on device %s\n" , pci_info->ctl_name); |
197 | printk(KERN_INFO "DTSTAT: %d\n" , |
198 | (val32 & PCI_INTBRG_CTRL_DTSTAT) != 0); |
199 | |
200 | val32 |= PCI_INTBRG_CTRL_DTSTAT; |
201 | edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32); |
202 | |
203 | edac_pci_handle_npe(pci: edac_dev, msg: edac_dev->ctl_name); |
204 | } |
205 | |
206 | /* Check out PCI Bridge Memory Base-Limit Register */ |
207 | edac_pci_read_dword(dev, REG_MEM_LIM, val32: &val32); |
208 | if (val32 & MEM_LIMIT_CLEAR_MASK) { |
209 | printk(KERN_INFO |
210 | "Error(s) in mem limit register on %s device\n" , |
211 | pci_info->ctl_name); |
212 | printk(KERN_INFO "DPE: %d, RSE: %d, RMA: %d\n" |
213 | "RTA: %d, STA: %d, MDPE: %d\n" , |
214 | (val32 & MEM_LIMIT_DPE) != 0, |
215 | (val32 & MEM_LIMIT_RSE) != 0, |
216 | (val32 & MEM_LIMIT_RMA) != 0, |
217 | (val32 & MEM_LIMIT_RTA) != 0, |
218 | (val32 & MEM_LIMIT_STA) != 0, |
219 | (val32 & MEM_LIMIT_MDPE) != 0); |
220 | |
221 | val32 |= MEM_LIMIT_CLEAR_MASK; |
222 | edac_pci_write_dword(dev, REG_MEM_LIM, val32); |
223 | |
224 | edac_pci_handle_npe(pci: edac_dev, msg: edac_dev->ctl_name); |
225 | } |
226 | } |
227 | |
228 | static struct resource *legacy_io_res; |
229 | static int at_compat_reg_broken; |
230 | #define LEGACY_NR_PORTS 1 |
231 | |
232 | /* device-specific methods for amd8111 LPC Bridge device */ |
233 | static void amd8111_lpc_bridge_init(struct amd8111_dev_info *dev_info) |
234 | { |
235 | u8 val8; |
236 | struct pci_dev *dev = dev_info->dev; |
237 | |
238 | /* First clear REG_AT_COMPAT[SERR, IOCHK] if necessary */ |
239 | legacy_io_res = request_region(REG_AT_COMPAT, LEGACY_NR_PORTS, |
240 | AMD8111_EDAC_MOD_STR); |
241 | if (!legacy_io_res) |
242 | printk(KERN_INFO "%s: failed to request legacy I/O region " |
243 | "start %d, len %d\n" , __func__, |
244 | REG_AT_COMPAT, LEGACY_NR_PORTS); |
245 | else { |
246 | val8 = __do_inb(REG_AT_COMPAT); |
247 | if (val8 == 0xff) { /* buggy port */ |
248 | printk(KERN_INFO "%s: port %d is buggy, not supported" |
249 | " by hardware?\n" , __func__, REG_AT_COMPAT); |
250 | at_compat_reg_broken = 1; |
251 | release_region(REG_AT_COMPAT, LEGACY_NR_PORTS); |
252 | legacy_io_res = NULL; |
253 | } else { |
254 | u8 out8 = 0; |
255 | if (val8 & AT_COMPAT_SERR) |
256 | out8 = AT_COMPAT_CLRSERR; |
257 | if (val8 & AT_COMPAT_IOCHK) |
258 | out8 |= AT_COMPAT_CLRIOCHK; |
259 | if (out8 > 0) |
260 | __do_outb(out8, REG_AT_COMPAT); |
261 | } |
262 | } |
263 | |
264 | /* Second clear error flags on LPC bridge */ |
265 | edac_pci_read_byte(dev, REG_IO_CTRL_1, val8: &val8); |
266 | if (val8 & IO_CTRL_1_CLEAR_MASK) |
267 | edac_pci_write_byte(dev, REG_IO_CTRL_1, val8); |
268 | } |
269 | |
270 | static void amd8111_lpc_bridge_exit(struct amd8111_dev_info *dev_info) |
271 | { |
272 | if (legacy_io_res) |
273 | release_region(REG_AT_COMPAT, LEGACY_NR_PORTS); |
274 | } |
275 | |
276 | static void amd8111_lpc_bridge_check(struct edac_device_ctl_info *edac_dev) |
277 | { |
278 | struct amd8111_dev_info *dev_info = edac_dev->pvt_info; |
279 | struct pci_dev *dev = dev_info->dev; |
280 | u8 val8; |
281 | |
282 | edac_pci_read_byte(dev, REG_IO_CTRL_1, val8: &val8); |
283 | if (val8 & IO_CTRL_1_CLEAR_MASK) { |
284 | printk(KERN_INFO |
285 | "Error(s) in IO control register on %s device\n" , |
286 | dev_info->ctl_name); |
287 | printk(KERN_INFO "LPC ERR: %d, PW2LPC: %d\n" , |
288 | (val8 & IO_CTRL_1_LPC_ERR) != 0, |
289 | (val8 & IO_CTRL_1_PW2LPC) != 0); |
290 | |
291 | val8 |= IO_CTRL_1_CLEAR_MASK; |
292 | edac_pci_write_byte(dev, REG_IO_CTRL_1, val8); |
293 | |
294 | edac_device_handle_ue(edac_dev, inst_nr: 0, block_nr: 0, msg: edac_dev->ctl_name); |
295 | } |
296 | |
297 | if (at_compat_reg_broken == 0) { |
298 | u8 out8 = 0; |
299 | val8 = __do_inb(REG_AT_COMPAT); |
300 | if (val8 & AT_COMPAT_SERR) |
301 | out8 = AT_COMPAT_CLRSERR; |
302 | if (val8 & AT_COMPAT_IOCHK) |
303 | out8 |= AT_COMPAT_CLRIOCHK; |
304 | if (out8 > 0) { |
305 | __do_outb(out8, REG_AT_COMPAT); |
306 | edac_device_handle_ue(edac_dev, inst_nr: 0, block_nr: 0, |
307 | msg: edac_dev->ctl_name); |
308 | } |
309 | } |
310 | } |
311 | |
312 | /* General devices represented by edac_device_ctl_info */ |
313 | static struct amd8111_dev_info amd8111_devices[] = { |
314 | [LPC_BRIDGE] = { |
315 | .err_dev = PCI_DEVICE_ID_AMD_8111_LPC, |
316 | .ctl_name = "lpc" , |
317 | .init = amd8111_lpc_bridge_init, |
318 | .exit = amd8111_lpc_bridge_exit, |
319 | .check = amd8111_lpc_bridge_check, |
320 | }, |
321 | {0}, |
322 | }; |
323 | |
324 | /* PCI controllers represented by edac_pci_ctl_info */ |
325 | static struct amd8111_pci_info amd8111_pcis[] = { |
326 | [PCI_BRIDGE] = { |
327 | .err_dev = PCI_DEVICE_ID_AMD_8111_PCI, |
328 | .ctl_name = "AMD8111_PCI_Controller" , |
329 | .init = amd8111_pci_bridge_init, |
330 | .exit = amd8111_pci_bridge_exit, |
331 | .check = amd8111_pci_bridge_check, |
332 | }, |
333 | {0}, |
334 | }; |
335 | |
336 | static int amd8111_dev_probe(struct pci_dev *dev, |
337 | const struct pci_device_id *id) |
338 | { |
339 | struct amd8111_dev_info *dev_info = &amd8111_devices[id->driver_data]; |
340 | int ret = -ENODEV; |
341 | |
342 | dev_info->dev = pci_get_device(PCI_VENDOR_ID_AMD, |
343 | device: dev_info->err_dev, NULL); |
344 | |
345 | if (!dev_info->dev) { |
346 | printk(KERN_ERR "EDAC device not found:" |
347 | "vendor %x, device %x, name %s\n" , |
348 | PCI_VENDOR_ID_AMD, dev_info->err_dev, |
349 | dev_info->ctl_name); |
350 | goto err; |
351 | } |
352 | |
353 | if (pci_enable_device(dev: dev_info->dev)) { |
354 | printk(KERN_ERR "failed to enable:" |
355 | "vendor %x, device %x, name %s\n" , |
356 | PCI_VENDOR_ID_AMD, dev_info->err_dev, |
357 | dev_info->ctl_name); |
358 | goto err_dev_put; |
359 | } |
360 | |
361 | /* |
362 | * we do not allocate extra private structure for |
363 | * edac_device_ctl_info, but make use of existing |
364 | * one instead. |
365 | */ |
366 | dev_info->edac_idx = edac_device_alloc_index(); |
367 | dev_info->edac_dev = |
368 | edac_device_alloc_ctl_info(sizeof_private: 0, edac_device_name: dev_info->ctl_name, nr_instances: 1, |
369 | NULL, nr_blocks: 0, offset_value: 0, |
370 | NULL, nr_attribs: 0, device_index: dev_info->edac_idx); |
371 | if (!dev_info->edac_dev) { |
372 | ret = -ENOMEM; |
373 | goto err_dev_put; |
374 | } |
375 | |
376 | dev_info->edac_dev->pvt_info = dev_info; |
377 | dev_info->edac_dev->dev = &dev_info->dev->dev; |
378 | dev_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR; |
379 | dev_info->edac_dev->ctl_name = dev_info->ctl_name; |
380 | dev_info->edac_dev->dev_name = dev_name(dev: &dev_info->dev->dev); |
381 | |
382 | if (edac_op_state == EDAC_OPSTATE_POLL) |
383 | dev_info->edac_dev->edac_check = dev_info->check; |
384 | |
385 | if (dev_info->init) |
386 | dev_info->init(dev_info); |
387 | |
388 | if (edac_device_add_device(edac_dev: dev_info->edac_dev) > 0) { |
389 | printk(KERN_ERR "failed to add edac_dev for %s\n" , |
390 | dev_info->ctl_name); |
391 | goto err_edac_free_ctl; |
392 | } |
393 | |
394 | printk(KERN_INFO "added one edac_dev on AMD8111 " |
395 | "vendor %x, device %x, name %s\n" , |
396 | PCI_VENDOR_ID_AMD, dev_info->err_dev, |
397 | dev_info->ctl_name); |
398 | |
399 | return 0; |
400 | |
401 | err_edac_free_ctl: |
402 | edac_device_free_ctl_info(ctl_info: dev_info->edac_dev); |
403 | err_dev_put: |
404 | pci_dev_put(dev: dev_info->dev); |
405 | err: |
406 | return ret; |
407 | } |
408 | |
409 | static void amd8111_dev_remove(struct pci_dev *dev) |
410 | { |
411 | struct amd8111_dev_info *dev_info; |
412 | |
413 | for (dev_info = amd8111_devices; dev_info->err_dev; dev_info++) |
414 | if (dev_info->dev->device == dev->device) |
415 | break; |
416 | |
417 | if (!dev_info->err_dev) /* should never happen */ |
418 | return; |
419 | |
420 | if (dev_info->edac_dev) { |
421 | edac_device_del_device(dev: dev_info->edac_dev->dev); |
422 | edac_device_free_ctl_info(ctl_info: dev_info->edac_dev); |
423 | } |
424 | |
425 | if (dev_info->exit) |
426 | dev_info->exit(dev_info); |
427 | |
428 | pci_dev_put(dev: dev_info->dev); |
429 | } |
430 | |
431 | static int amd8111_pci_probe(struct pci_dev *dev, |
432 | const struct pci_device_id *id) |
433 | { |
434 | struct amd8111_pci_info *pci_info = &amd8111_pcis[id->driver_data]; |
435 | int ret = -ENODEV; |
436 | |
437 | pci_info->dev = pci_get_device(PCI_VENDOR_ID_AMD, |
438 | device: pci_info->err_dev, NULL); |
439 | |
440 | if (!pci_info->dev) { |
441 | printk(KERN_ERR "EDAC device not found:" |
442 | "vendor %x, device %x, name %s\n" , |
443 | PCI_VENDOR_ID_AMD, pci_info->err_dev, |
444 | pci_info->ctl_name); |
445 | goto err; |
446 | } |
447 | |
448 | if (pci_enable_device(dev: pci_info->dev)) { |
449 | printk(KERN_ERR "failed to enable:" |
450 | "vendor %x, device %x, name %s\n" , |
451 | PCI_VENDOR_ID_AMD, pci_info->err_dev, |
452 | pci_info->ctl_name); |
453 | goto err_dev_put; |
454 | } |
455 | |
456 | /* |
457 | * we do not allocate extra private structure for |
458 | * edac_pci_ctl_info, but make use of existing |
459 | * one instead. |
460 | */ |
461 | pci_info->edac_idx = edac_pci_alloc_index(); |
462 | pci_info->edac_dev = edac_pci_alloc_ctl_info(sz_pvt: 0, edac_pci_name: pci_info->ctl_name); |
463 | if (!pci_info->edac_dev) { |
464 | ret = -ENOMEM; |
465 | goto err_dev_put; |
466 | } |
467 | |
468 | pci_info->edac_dev->pvt_info = pci_info; |
469 | pci_info->edac_dev->dev = &pci_info->dev->dev; |
470 | pci_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR; |
471 | pci_info->edac_dev->ctl_name = pci_info->ctl_name; |
472 | pci_info->edac_dev->dev_name = dev_name(dev: &pci_info->dev->dev); |
473 | |
474 | if (edac_op_state == EDAC_OPSTATE_POLL) |
475 | pci_info->edac_dev->edac_check = pci_info->check; |
476 | |
477 | if (pci_info->init) |
478 | pci_info->init(pci_info); |
479 | |
480 | if (edac_pci_add_device(pci: pci_info->edac_dev, edac_idx: pci_info->edac_idx) > 0) { |
481 | printk(KERN_ERR "failed to add edac_pci for %s\n" , |
482 | pci_info->ctl_name); |
483 | goto err_edac_free_ctl; |
484 | } |
485 | |
486 | printk(KERN_INFO "added one edac_pci on AMD8111 " |
487 | "vendor %x, device %x, name %s\n" , |
488 | PCI_VENDOR_ID_AMD, pci_info->err_dev, |
489 | pci_info->ctl_name); |
490 | |
491 | return 0; |
492 | |
493 | err_edac_free_ctl: |
494 | edac_pci_free_ctl_info(pci: pci_info->edac_dev); |
495 | err_dev_put: |
496 | pci_dev_put(dev: pci_info->dev); |
497 | err: |
498 | return ret; |
499 | } |
500 | |
501 | static void amd8111_pci_remove(struct pci_dev *dev) |
502 | { |
503 | struct amd8111_pci_info *pci_info; |
504 | |
505 | for (pci_info = amd8111_pcis; pci_info->err_dev; pci_info++) |
506 | if (pci_info->dev->device == dev->device) |
507 | break; |
508 | |
509 | if (!pci_info->err_dev) /* should never happen */ |
510 | return; |
511 | |
512 | if (pci_info->edac_dev) { |
513 | edac_pci_del_device(dev: pci_info->edac_dev->dev); |
514 | edac_pci_free_ctl_info(pci: pci_info->edac_dev); |
515 | } |
516 | |
517 | if (pci_info->exit) |
518 | pci_info->exit(pci_info); |
519 | |
520 | pci_dev_put(dev: pci_info->dev); |
521 | } |
522 | |
523 | /* PCI Device ID talbe for general EDAC device */ |
524 | static const struct pci_device_id amd8111_edac_dev_tbl[] = { |
525 | { |
526 | PCI_VEND_DEV(AMD, 8111_LPC), |
527 | .subvendor = PCI_ANY_ID, |
528 | .subdevice = PCI_ANY_ID, |
529 | .class = 0, |
530 | .class_mask = 0, |
531 | .driver_data = LPC_BRIDGE, |
532 | }, |
533 | { |
534 | 0, |
535 | } /* table is NULL-terminated */ |
536 | }; |
537 | MODULE_DEVICE_TABLE(pci, amd8111_edac_dev_tbl); |
538 | |
539 | static struct pci_driver amd8111_edac_dev_driver = { |
540 | .name = "AMD8111_EDAC_DEV" , |
541 | .probe = amd8111_dev_probe, |
542 | .remove = amd8111_dev_remove, |
543 | .id_table = amd8111_edac_dev_tbl, |
544 | }; |
545 | |
546 | /* PCI Device ID table for EDAC PCI controller */ |
547 | static const struct pci_device_id amd8111_edac_pci_tbl[] = { |
548 | { |
549 | PCI_VEND_DEV(AMD, 8111_PCI), |
550 | .subvendor = PCI_ANY_ID, |
551 | .subdevice = PCI_ANY_ID, |
552 | .class = 0, |
553 | .class_mask = 0, |
554 | .driver_data = PCI_BRIDGE, |
555 | }, |
556 | { |
557 | 0, |
558 | } /* table is NULL-terminated */ |
559 | }; |
560 | MODULE_DEVICE_TABLE(pci, amd8111_edac_pci_tbl); |
561 | |
562 | static struct pci_driver amd8111_edac_pci_driver = { |
563 | .name = "AMD8111_EDAC_PCI" , |
564 | .probe = amd8111_pci_probe, |
565 | .remove = amd8111_pci_remove, |
566 | .id_table = amd8111_edac_pci_tbl, |
567 | }; |
568 | |
569 | static int __init amd8111_edac_init(void) |
570 | { |
571 | int val; |
572 | |
573 | printk(KERN_INFO "AMD8111 EDAC driver " AMD8111_EDAC_REVISION "\n" ); |
574 | printk(KERN_INFO "\t(c) 2008 Wind River Systems, Inc.\n" ); |
575 | |
576 | /* Only POLL mode supported so far */ |
577 | edac_op_state = EDAC_OPSTATE_POLL; |
578 | |
579 | val = pci_register_driver(&amd8111_edac_dev_driver); |
580 | val |= pci_register_driver(&amd8111_edac_pci_driver); |
581 | |
582 | return val; |
583 | } |
584 | |
585 | static void __exit amd8111_edac_exit(void) |
586 | { |
587 | pci_unregister_driver(dev: &amd8111_edac_pci_driver); |
588 | pci_unregister_driver(dev: &amd8111_edac_dev_driver); |
589 | } |
590 | |
591 | |
592 | module_init(amd8111_edac_init); |
593 | module_exit(amd8111_edac_exit); |
594 | |
595 | MODULE_LICENSE("GPL" ); |
596 | MODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>" ); |
597 | MODULE_DESCRIPTION("AMD8111 HyperTransport I/O Hub EDAC kernel module" ); |
598 | |