diff options
Diffstat (limited to 'drivers/edac/i82875p_edac.c')
-rw-r--r-- | drivers/edac/i82875p_edac.c | 188 |
1 files changed, 109 insertions, 79 deletions
diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 2be18ca96408..6787403463a1 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c | |||
@@ -265,116 +265,109 @@ static void i82875p_check(struct mem_ctl_info *mci) | |||
265 | extern int pci_proc_attach_device(struct pci_dev *); | 265 | extern int pci_proc_attach_device(struct pci_dev *); |
266 | #endif | 266 | #endif |
267 | 267 | ||
268 | static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) | 268 | /* Return 0 on success or 1 on failure. */ |
269 | static int i82875p_setup_overfl_dev(struct pci_dev *pdev, | ||
270 | struct pci_dev **ovrfl_pdev, void __iomem **ovrfl_window) | ||
269 | { | 271 | { |
270 | int rc = -ENODEV; | 272 | struct pci_dev *dev; |
271 | int index; | 273 | void __iomem *window; |
272 | struct mem_ctl_info *mci = NULL; | ||
273 | struct i82875p_pvt *pvt = NULL; | ||
274 | unsigned long last_cumul_size; | ||
275 | struct pci_dev *ovrfl_pdev; | ||
276 | void __iomem *ovrfl_window = NULL; | ||
277 | u32 drc; | ||
278 | u32 drc_chan; /* Number of channels 0=1chan,1=2chan */ | ||
279 | u32 nr_chans; | ||
280 | u32 drc_ddim; /* DRAM Data Integrity Mode 0=none,2=edac */ | ||
281 | struct i82875p_error_info discard; | ||
282 | 274 | ||
283 | debugf0("%s()\n", __func__); | 275 | *ovrfl_pdev = NULL; |
284 | ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL); | 276 | *ovrfl_window = NULL; |
277 | dev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL); | ||
285 | 278 | ||
286 | if (!ovrfl_pdev) { | 279 | if (dev == NULL) { |
287 | /* | 280 | /* Intel tells BIOS developers to hide device 6 which |
288 | * Intel tells BIOS developers to hide device 6 which | ||
289 | * configures the overflow device access containing | 281 | * configures the overflow device access containing |
290 | * the DRBs - this is where we expose device 6. | 282 | * the DRBs - this is where we expose device 6. |
291 | * http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm | 283 | * http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm |
292 | */ | 284 | */ |
293 | pci_write_bits8(pdev, 0xf4, 0x2, 0x2); | 285 | pci_write_bits8(pdev, 0xf4, 0x2, 0x2); |
294 | ovrfl_pdev = | 286 | dev = pci_scan_single_device(pdev->bus, PCI_DEVFN(6, 0)); |
295 | pci_scan_single_device(pdev->bus, PCI_DEVFN(6, 0)); | ||
296 | 287 | ||
297 | if (!ovrfl_pdev) | 288 | if (dev == NULL) |
298 | return -ENODEV; | 289 | return 1; |
299 | } | 290 | } |
300 | 291 | ||
292 | *ovrfl_pdev = dev; | ||
293 | |||
301 | #ifdef CONFIG_PROC_FS | 294 | #ifdef CONFIG_PROC_FS |
302 | if (!ovrfl_pdev->procent && pci_proc_attach_device(ovrfl_pdev)) { | 295 | if ((dev->procent == NULL) && pci_proc_attach_device(dev)) { |
303 | i82875p_printk(KERN_ERR, | 296 | i82875p_printk(KERN_ERR, "%s(): Failed to attach overflow " |
304 | "%s(): Failed to attach overflow device\n", __func__); | 297 | "device\n", __func__); |
305 | return -ENODEV; | 298 | return 1; |
306 | } | 299 | } |
307 | #endif | 300 | #endif /* CONFIG_PROC_FS */ |
308 | /* CONFIG_PROC_FS */ | 301 | if (pci_enable_device(dev)) { |
309 | if (pci_enable_device(ovrfl_pdev)) { | 302 | i82875p_printk(KERN_ERR, "%s(): Failed to enable overflow " |
310 | i82875p_printk(KERN_ERR, | 303 | "device\n", __func__); |
311 | "%s(): Failed to enable overflow device\n", __func__); | 304 | return 1; |
312 | return -ENODEV; | ||
313 | } | 305 | } |
314 | 306 | ||
315 | if (pci_request_regions(ovrfl_pdev, pci_name(ovrfl_pdev))) { | 307 | if (pci_request_regions(dev, pci_name(dev))) { |
316 | #ifdef CORRECT_BIOS | 308 | #ifdef CORRECT_BIOS |
317 | goto fail0; | 309 | goto fail0; |
318 | #endif | 310 | #endif |
319 | } | 311 | } |
320 | 312 | ||
321 | /* cache is irrelevant for PCI bus reads/writes */ | 313 | /* cache is irrelevant for PCI bus reads/writes */ |
322 | ovrfl_window = ioremap_nocache(pci_resource_start(ovrfl_pdev, 0), | 314 | window = ioremap_nocache(pci_resource_start(dev, 0), |
323 | pci_resource_len(ovrfl_pdev, 0)); | 315 | pci_resource_len(dev, 0)); |
324 | 316 | ||
325 | if (!ovrfl_window) { | 317 | if (window == NULL) { |
326 | i82875p_printk(KERN_ERR, "%s(): Failed to ioremap bar6\n", | 318 | i82875p_printk(KERN_ERR, "%s(): Failed to ioremap bar6\n", |
327 | __func__); | 319 | __func__); |
328 | goto fail1; | 320 | goto fail1; |
329 | } | 321 | } |
330 | 322 | ||
331 | /* need to find out the number of channels */ | 323 | *ovrfl_window = window; |
332 | drc = readl(ovrfl_window + I82875P_DRC); | 324 | return 0; |
333 | drc_chan = ((drc >> 21) & 0x1); | ||
334 | nr_chans = drc_chan + 1; | ||
335 | 325 | ||
336 | drc_ddim = (drc >> 18) & 0x1; | 326 | fail1: |
337 | mci = edac_mc_alloc(sizeof(*pvt), I82875P_NR_CSROWS(nr_chans), | 327 | pci_release_regions(dev); |
338 | nr_chans); | ||
339 | 328 | ||
340 | if (!mci) { | 329 | #ifdef CORRECT_BIOS |
341 | rc = -ENOMEM; | 330 | fail0: |
342 | goto fail2; | 331 | pci_disable_device(dev); |
343 | } | 332 | #endif |
333 | /* NOTE: the ovrfl proc entry and pci_dev are intentionally left */ | ||
334 | return 1; | ||
335 | } | ||
344 | 336 | ||
345 | debugf3("%s(): init mci\n", __func__); | ||
346 | mci->dev = &pdev->dev; | ||
347 | mci->mtype_cap = MEM_FLAG_DDR; | ||
348 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | ||
349 | mci->edac_cap = EDAC_FLAG_UNKNOWN; | ||
350 | /* adjust FLAGS */ | ||
351 | 337 | ||
352 | mci->mod_name = EDAC_MOD_STR; | 338 | /* Return 1 if dual channel mode is active. Else return 0. */ |
353 | mci->mod_ver = I82875P_REVISION; | 339 | static inline int dual_channel_active(u32 drc) |
354 | mci->ctl_name = i82875p_devs[dev_idx].ctl_name; | 340 | { |
355 | mci->edac_check = i82875p_check; | 341 | return (drc >> 21) & 0x1; |
356 | mci->ctl_page_to_phys = NULL; | 342 | } |
357 | debugf3("%s(): init pvt\n", __func__); | ||
358 | pvt = (struct i82875p_pvt *) mci->pvt_info; | ||
359 | pvt->ovrfl_pdev = ovrfl_pdev; | ||
360 | pvt->ovrfl_window = ovrfl_window; | ||
361 | 343 | ||
362 | /* | 344 | |
363 | * The dram row boundary (DRB) reg values are boundary address | 345 | static void i82875p_init_csrows(struct mem_ctl_info *mci, |
346 | struct pci_dev *pdev, void __iomem *ovrfl_window, u32 drc) | ||
347 | { | ||
348 | struct csrow_info *csrow; | ||
349 | unsigned long last_cumul_size; | ||
350 | u8 value; | ||
351 | u32 drc_ddim; /* DRAM Data Integrity Mode 0=none,2=edac */ | ||
352 | u32 cumul_size; | ||
353 | int index; | ||
354 | |||
355 | drc_ddim = (drc >> 18) & 0x1; | ||
356 | last_cumul_size = 0; | ||
357 | |||
358 | /* The dram row boundary (DRB) reg values are boundary address | ||
364 | * for each DRAM row with a granularity of 32 or 64MB (single/dual | 359 | * for each DRAM row with a granularity of 32 or 64MB (single/dual |
365 | * channel operation). DRB regs are cumulative; therefore DRB7 will | 360 | * channel operation). DRB regs are cumulative; therefore DRB7 will |
366 | * contain the total memory contained in all eight rows. | 361 | * contain the total memory contained in all eight rows. |
367 | */ | 362 | */ |
368 | for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) { | 363 | |
369 | u8 value; | 364 | for (index = 0; index < mci->nr_csrows; index++) { |
370 | u32 cumul_size; | 365 | csrow = &mci->csrows[index]; |
371 | struct csrow_info *csrow = &mci->csrows[index]; | ||
372 | 366 | ||
373 | value = readb(ovrfl_window + I82875P_DRB + index); | 367 | value = readb(ovrfl_window + I82875P_DRB + index); |
374 | cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT); | 368 | cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT); |
375 | debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, | 369 | debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index, |
376 | cumul_size); | 370 | cumul_size); |
377 | |||
378 | if (cumul_size == last_cumul_size) | 371 | if (cumul_size == last_cumul_size) |
379 | continue; /* not populated */ | 372 | continue; /* not populated */ |
380 | 373 | ||
@@ -382,12 +375,54 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) | |||
382 | csrow->last_page = cumul_size - 1; | 375 | csrow->last_page = cumul_size - 1; |
383 | csrow->nr_pages = cumul_size - last_cumul_size; | 376 | csrow->nr_pages = cumul_size - last_cumul_size; |
384 | last_cumul_size = cumul_size; | 377 | last_cumul_size = cumul_size; |
385 | csrow->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */ | 378 | csrow->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */ |
386 | csrow->mtype = MEM_DDR; | 379 | csrow->mtype = MEM_DDR; |
387 | csrow->dtype = DEV_UNKNOWN; | 380 | csrow->dtype = DEV_UNKNOWN; |
388 | csrow->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE; | 381 | csrow->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE; |
389 | } | 382 | } |
383 | } | ||
384 | |||
385 | static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) | ||
386 | { | ||
387 | int rc = -ENODEV; | ||
388 | struct mem_ctl_info *mci; | ||
389 | struct i82875p_pvt *pvt; | ||
390 | struct pci_dev *ovrfl_pdev; | ||
391 | void __iomem *ovrfl_window; | ||
392 | u32 drc; | ||
393 | u32 nr_chans; | ||
394 | struct i82875p_error_info discard; | ||
395 | |||
396 | debugf0("%s()\n", __func__); | ||
397 | ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL); | ||
398 | |||
399 | if (i82875p_setup_overfl_dev(pdev, &ovrfl_pdev, &ovrfl_window)) | ||
400 | return -ENODEV; | ||
401 | drc = readl(ovrfl_window + I82875P_DRC); | ||
402 | nr_chans = dual_channel_active(drc) + 1; | ||
403 | mci = edac_mc_alloc(sizeof(*pvt), I82875P_NR_CSROWS(nr_chans), | ||
404 | nr_chans); | ||
405 | |||
406 | if (!mci) { | ||
407 | rc = -ENOMEM; | ||
408 | goto fail0; | ||
409 | } | ||
390 | 410 | ||
411 | debugf3("%s(): init mci\n", __func__); | ||
412 | mci->dev = &pdev->dev; | ||
413 | mci->mtype_cap = MEM_FLAG_DDR; | ||
414 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | ||
415 | mci->edac_cap = EDAC_FLAG_UNKNOWN; | ||
416 | mci->mod_name = EDAC_MOD_STR; | ||
417 | mci->mod_ver = I82875P_REVISION; | ||
418 | mci->ctl_name = i82875p_devs[dev_idx].ctl_name; | ||
419 | mci->edac_check = i82875p_check; | ||
420 | mci->ctl_page_to_phys = NULL; | ||
421 | debugf3("%s(): init pvt\n", __func__); | ||
422 | pvt = (struct i82875p_pvt *) mci->pvt_info; | ||
423 | pvt->ovrfl_pdev = ovrfl_pdev; | ||
424 | pvt->ovrfl_window = ovrfl_window; | ||
425 | i82875p_init_csrows(mci, pdev, ovrfl_window, drc); | ||
391 | i82875p_get_error_info(mci, &discard); /* clear counters */ | 426 | i82875p_get_error_info(mci, &discard); /* clear counters */ |
392 | 427 | ||
393 | /* Here we assume that we will never see multiple instances of this | 428 | /* Here we assume that we will never see multiple instances of this |
@@ -395,25 +430,20 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) | |||
395 | */ | 430 | */ |
396 | if (edac_mc_add_mc(mci,0)) { | 431 | if (edac_mc_add_mc(mci,0)) { |
397 | debugf3("%s(): failed edac_mc_add_mc()\n", __func__); | 432 | debugf3("%s(): failed edac_mc_add_mc()\n", __func__); |
398 | goto fail3; | 433 | goto fail1; |
399 | } | 434 | } |
400 | 435 | ||
401 | /* get this far and it's successful */ | 436 | /* get this far and it's successful */ |
402 | debugf3("%s(): success\n", __func__); | 437 | debugf3("%s(): success\n", __func__); |
403 | return 0; | 438 | return 0; |
404 | 439 | ||
405 | fail3: | 440 | fail1: |
406 | edac_mc_free(mci); | 441 | edac_mc_free(mci); |
407 | 442 | ||
408 | fail2: | 443 | fail0: |
409 | iounmap(ovrfl_window); | 444 | iounmap(ovrfl_window); |
410 | |||
411 | fail1: | ||
412 | pci_release_regions(ovrfl_pdev); | 445 | pci_release_regions(ovrfl_pdev); |
413 | 446 | ||
414 | #ifdef CORRECT_BIOS | ||
415 | fail0: | ||
416 | #endif | ||
417 | pci_disable_device(ovrfl_pdev); | 447 | pci_disable_device(ovrfl_pdev); |
418 | /* NOTE: the ovrfl proc entry and pci_dev are intentionally left */ | 448 | /* NOTE: the ovrfl proc entry and pci_dev are intentionally left */ |
419 | return rc; | 449 | return rc; |