diff options
author | Doug Thompson <dougthompson@xmission.com> | 2009-04-27 13:41:25 -0400 |
---|---|---|
committer | Borislav Petkov <borislav.petkov@amd.com> | 2009-06-10 06:19:00 -0400 |
commit | 0ec449ee95b20245fef4aa9fa2486456f1540514 (patch) | |
tree | d4a7951fccc5a45cb7c0c82edb05ef9e9107bfbd /drivers/edac/amd64_edac.c | |
parent | d27bf6fa369ca0272df10558d2f290d6fc72e675 (diff) |
amd64_edac: add EDAC core-related initializers
Borislav:
- add a amd64_free_mc_sibling_devices() helper instead of opencoding the
release-path.
- fix/cleanup comments
- fix function return value patterns
- cleanup debug calls
Reviewed-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'drivers/edac/amd64_edac.c')
-rw-r--r-- | drivers/edac/amd64_edac.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 09991c8a6ee3..5a6e714b115e 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -2455,4 +2455,319 @@ int amd64_process_error_info(struct mem_ctl_info *mci, | |||
2455 | } | 2455 | } |
2456 | EXPORT_SYMBOL_GPL(amd64_process_error_info); | 2456 | EXPORT_SYMBOL_GPL(amd64_process_error_info); |
2457 | 2457 | ||
2458 | /* | ||
2459 | * The main polling 'check' function, called FROM the edac core to perform the | ||
2460 | * error checking and if an error is encountered, error processing. | ||
2461 | */ | ||
2462 | static void amd64_check(struct mem_ctl_info *mci) | ||
2463 | { | ||
2464 | struct amd64_error_info_regs info; | ||
2465 | |||
2466 | if (amd64_get_error_info(mci, &info)) | ||
2467 | amd64_process_error_info(mci, &info, 1); | ||
2468 | } | ||
2469 | |||
2470 | /* | ||
2471 | * Input: | ||
2472 | * 1) struct amd64_pvt which contains pvt->dram_f2_ctl pointer | ||
2473 | * 2) AMD Family index value | ||
2474 | * | ||
2475 | * Ouput: | ||
2476 | * Upon return of 0, the following filled in: | ||
2477 | * | ||
2478 | * struct pvt->addr_f1_ctl | ||
2479 | * struct pvt->misc_f3_ctl | ||
2480 | * | ||
2481 | * Filled in with related device funcitions of 'dram_f2_ctl' | ||
2482 | * These devices are "reserved" via the pci_get_device() | ||
2483 | * | ||
2484 | * Upon return of 1 (error status): | ||
2485 | * | ||
2486 | * Nothing reserved | ||
2487 | */ | ||
2488 | static int amd64_reserve_mc_sibling_devices(struct amd64_pvt *pvt, int mc_idx) | ||
2489 | { | ||
2490 | const struct amd64_family_type *amd64_dev = &amd64_family_types[mc_idx]; | ||
2491 | |||
2492 | /* Reserve the ADDRESS MAP Device */ | ||
2493 | pvt->addr_f1_ctl = pci_get_related_function(pvt->dram_f2_ctl->vendor, | ||
2494 | amd64_dev->addr_f1_ctl, | ||
2495 | pvt->dram_f2_ctl); | ||
2496 | |||
2497 | if (!pvt->addr_f1_ctl) { | ||
2498 | amd64_printk(KERN_ERR, "error address map device not found: " | ||
2499 | "vendor %x device 0x%x (broken BIOS?)\n", | ||
2500 | PCI_VENDOR_ID_AMD, amd64_dev->addr_f1_ctl); | ||
2501 | return 1; | ||
2502 | } | ||
2503 | |||
2504 | /* Reserve the MISC Device */ | ||
2505 | pvt->misc_f3_ctl = pci_get_related_function(pvt->dram_f2_ctl->vendor, | ||
2506 | amd64_dev->misc_f3_ctl, | ||
2507 | pvt->dram_f2_ctl); | ||
2508 | |||
2509 | if (!pvt->misc_f3_ctl) { | ||
2510 | pci_dev_put(pvt->addr_f1_ctl); | ||
2511 | pvt->addr_f1_ctl = NULL; | ||
2512 | |||
2513 | amd64_printk(KERN_ERR, "error miscellaneous device not found: " | ||
2514 | "vendor %x device 0x%x (broken BIOS?)\n", | ||
2515 | PCI_VENDOR_ID_AMD, amd64_dev->misc_f3_ctl); | ||
2516 | return 1; | ||
2517 | } | ||
2518 | |||
2519 | debugf1(" Addr Map device PCI Bus ID:\t%s\n", | ||
2520 | pci_name(pvt->addr_f1_ctl)); | ||
2521 | debugf1(" DRAM MEM-CTL PCI Bus ID:\t%s\n", | ||
2522 | pci_name(pvt->dram_f2_ctl)); | ||
2523 | debugf1(" Misc device PCI Bus ID:\t%s\n", | ||
2524 | pci_name(pvt->misc_f3_ctl)); | ||
2525 | |||
2526 | return 0; | ||
2527 | } | ||
2528 | |||
2529 | static void amd64_free_mc_sibling_devices(struct amd64_pvt *pvt) | ||
2530 | { | ||
2531 | pci_dev_put(pvt->addr_f1_ctl); | ||
2532 | pci_dev_put(pvt->misc_f3_ctl); | ||
2533 | } | ||
2534 | |||
2535 | /* | ||
2536 | * Retrieve the hardware registers of the memory controller (this includes the | ||
2537 | * 'Address Map' and 'Misc' device regs) | ||
2538 | */ | ||
2539 | static void amd64_read_mc_registers(struct amd64_pvt *pvt) | ||
2540 | { | ||
2541 | u64 msr_val; | ||
2542 | int dram, err = 0; | ||
2543 | |||
2544 | /* | ||
2545 | * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since | ||
2546 | * those are Read-As-Zero | ||
2547 | */ | ||
2548 | rdmsrl(MSR_K8_TOP_MEM1, msr_val); | ||
2549 | pvt->top_mem = msr_val >> 23; | ||
2550 | debugf0(" TOP_MEM=0x%08llx\n", pvt->top_mem); | ||
2551 | |||
2552 | /* check first whether TOP_MEM2 is enabled */ | ||
2553 | rdmsrl(MSR_K8_SYSCFG, msr_val); | ||
2554 | if (msr_val & (1U << 21)) { | ||
2555 | rdmsrl(MSR_K8_TOP_MEM2, msr_val); | ||
2556 | pvt->top_mem2 = msr_val >> 23; | ||
2557 | debugf0(" TOP_MEM2=0x%08llx\n", pvt->top_mem2); | ||
2558 | } else | ||
2559 | debugf0(" TOP_MEM2 disabled.\n"); | ||
2560 | |||
2561 | amd64_cpu_display_info(pvt); | ||
2562 | |||
2563 | err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCAP, &pvt->nbcap); | ||
2564 | if (err) | ||
2565 | goto err_reg; | ||
2566 | |||
2567 | if (pvt->ops->read_dram_ctl_register) | ||
2568 | pvt->ops->read_dram_ctl_register(pvt); | ||
2569 | |||
2570 | for (dram = 0; dram < DRAM_REG_COUNT; dram++) { | ||
2571 | /* | ||
2572 | * Call CPU specific READ function to get the DRAM Base and | ||
2573 | * Limit values from the DCT. | ||
2574 | */ | ||
2575 | pvt->ops->read_dram_base_limit(pvt, dram); | ||
2576 | |||
2577 | /* | ||
2578 | * Only print out debug info on rows with both R and W Enabled. | ||
2579 | * Normal processing, compiler should optimize this whole 'if' | ||
2580 | * debug output block away. | ||
2581 | */ | ||
2582 | if (pvt->dram_rw_en[dram] != 0) { | ||
2583 | debugf1(" DRAM_BASE[%d]: 0x%8.08x-%8.08x " | ||
2584 | "DRAM_LIMIT: 0x%8.08x-%8.08x\n", | ||
2585 | dram, | ||
2586 | (u32)(pvt->dram_base[dram] >> 32), | ||
2587 | (u32)(pvt->dram_base[dram] & 0xFFFFFFFF), | ||
2588 | (u32)(pvt->dram_limit[dram] >> 32), | ||
2589 | (u32)(pvt->dram_limit[dram] & 0xFFFFFFFF)); | ||
2590 | debugf1(" IntlvEn=%s %s %s " | ||
2591 | "IntlvSel=%d DstNode=%d\n", | ||
2592 | pvt->dram_IntlvEn[dram] ? | ||
2593 | "Enabled" : "Disabled", | ||
2594 | (pvt->dram_rw_en[dram] & 0x2) ? "W" : "!W", | ||
2595 | (pvt->dram_rw_en[dram] & 0x1) ? "R" : "!R", | ||
2596 | pvt->dram_IntlvSel[dram], | ||
2597 | pvt->dram_DstNode[dram]); | ||
2598 | } | ||
2599 | } | ||
2600 | |||
2601 | amd64_read_dct_base_mask(pvt); | ||
2602 | |||
2603 | err = pci_read_config_dword(pvt->addr_f1_ctl, K8_DHAR, &pvt->dhar); | ||
2604 | if (err) | ||
2605 | goto err_reg; | ||
2606 | |||
2607 | amd64_read_dbam_reg(pvt); | ||
2608 | |||
2609 | err = pci_read_config_dword(pvt->misc_f3_ctl, | ||
2610 | F10_ONLINE_SPARE, &pvt->online_spare); | ||
2611 | if (err) | ||
2612 | goto err_reg; | ||
2613 | |||
2614 | err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0); | ||
2615 | if (err) | ||
2616 | goto err_reg; | ||
2617 | |||
2618 | err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_0, &pvt->dchr0); | ||
2619 | if (err) | ||
2620 | goto err_reg; | ||
2621 | |||
2622 | if (!dct_ganging_enabled(pvt)) { | ||
2623 | err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1, | ||
2624 | &pvt->dclr1); | ||
2625 | if (err) | ||
2626 | goto err_reg; | ||
2627 | |||
2628 | err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_1, | ||
2629 | &pvt->dchr1); | ||
2630 | if (err) | ||
2631 | goto err_reg; | ||
2632 | } | ||
2633 | |||
2634 | amd64_dump_misc_regs(pvt); | ||
2635 | |||
2636 | err_reg: | ||
2637 | debugf0("Reading an MC register failed\n"); | ||
2638 | |||
2639 | } | ||
2640 | |||
2641 | /* | ||
2642 | * NOTE: CPU Revision Dependent code | ||
2643 | * | ||
2644 | * Input: | ||
2645 | * @csrow_nr ChipSelect Row Number (0..CHIPSELECT_COUNT-1) | ||
2646 | * k8 private pointer to --> | ||
2647 | * DRAM Bank Address mapping register | ||
2648 | * node_id | ||
2649 | * DCL register where dual_channel_active is | ||
2650 | * | ||
2651 | * The DBAM register consists of 4 sets of 4 bits each definitions: | ||
2652 | * | ||
2653 | * Bits: CSROWs | ||
2654 | * 0-3 CSROWs 0 and 1 | ||
2655 | * 4-7 CSROWs 2 and 3 | ||
2656 | * 8-11 CSROWs 4 and 5 | ||
2657 | * 12-15 CSROWs 6 and 7 | ||
2658 | * | ||
2659 | * Values range from: 0 to 15 | ||
2660 | * The meaning of the values depends on CPU revision and dual-channel state, | ||
2661 | * see relevant BKDG more info. | ||
2662 | * | ||
2663 | * The memory controller provides for total of only 8 CSROWs in its current | ||
2664 | * architecture. Each "pair" of CSROWs normally represents just one DIMM in | ||
2665 | * single channel or two (2) DIMMs in dual channel mode. | ||
2666 | * | ||
2667 | * The following code logic collapses the various tables for CSROW based on CPU | ||
2668 | * revision. | ||
2669 | * | ||
2670 | * Returns: | ||
2671 | * The number of PAGE_SIZE pages on the specified CSROW number it | ||
2672 | * encompasses | ||
2673 | * | ||
2674 | */ | ||
2675 | static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt) | ||
2676 | { | ||
2677 | u32 dram_map, nr_pages; | ||
2678 | |||
2679 | /* | ||
2680 | * The math on this doesn't look right on the surface because x/2*4 can | ||
2681 | * be simplified to x*2 but this expression makes use of the fact that | ||
2682 | * it is integral math where 1/2=0. This intermediate value becomes the | ||
2683 | * number of bits to shift the DBAM register to extract the proper CSROW | ||
2684 | * field. | ||
2685 | */ | ||
2686 | dram_map = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF; | ||
2687 | |||
2688 | nr_pages = pvt->ops->dbam_map_to_pages(pvt, dram_map); | ||
2689 | |||
2690 | /* | ||
2691 | * If dual channel then double the memory size of single channel. | ||
2692 | * Channel count is 1 or 2 | ||
2693 | */ | ||
2694 | nr_pages <<= (pvt->channel_count - 1); | ||
2695 | |||
2696 | debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, dram_map); | ||
2697 | debugf0(" nr_pages= %u channel-count = %d\n", | ||
2698 | nr_pages, pvt->channel_count); | ||
2699 | |||
2700 | return nr_pages; | ||
2701 | } | ||
2702 | |||
2703 | /* | ||
2704 | * Initialize the array of csrow attribute instances, based on the values | ||
2705 | * from pci config hardware registers. | ||
2706 | */ | ||
2707 | static int amd64_init_csrows(struct mem_ctl_info *mci) | ||
2708 | { | ||
2709 | struct csrow_info *csrow; | ||
2710 | struct amd64_pvt *pvt; | ||
2711 | u64 input_addr_min, input_addr_max, sys_addr; | ||
2712 | int i, err = 0, empty = 1; | ||
2713 | |||
2714 | pvt = mci->pvt_info; | ||
2715 | |||
2716 | err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &pvt->nbcfg); | ||
2717 | if (err) | ||
2718 | debugf0("Reading K8_NBCFG failed\n"); | ||
2719 | |||
2720 | debugf0("NBCFG= 0x%x CHIPKILL= %s DRAM ECC= %s\n", pvt->nbcfg, | ||
2721 | (pvt->nbcfg & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled", | ||
2722 | (pvt->nbcfg & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled" | ||
2723 | ); | ||
2724 | |||
2725 | for (i = 0; i < CHIPSELECT_COUNT; i++) { | ||
2726 | csrow = &mci->csrows[i]; | ||
2727 | |||
2728 | if ((pvt->dcsb0[i] & K8_DCSB_CS_ENABLE) == 0) { | ||
2729 | debugf1("----CSROW %d EMPTY for node %d\n", i, | ||
2730 | pvt->mc_node_id); | ||
2731 | continue; | ||
2732 | } | ||
2733 | |||
2734 | debugf1("----CSROW %d VALID for MC node %d\n", | ||
2735 | i, pvt->mc_node_id); | ||
2736 | |||
2737 | empty = 0; | ||
2738 | csrow->nr_pages = amd64_csrow_nr_pages(i, pvt); | ||
2739 | find_csrow_limits(mci, i, &input_addr_min, &input_addr_max); | ||
2740 | sys_addr = input_addr_to_sys_addr(mci, input_addr_min); | ||
2741 | csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT); | ||
2742 | sys_addr = input_addr_to_sys_addr(mci, input_addr_max); | ||
2743 | csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT); | ||
2744 | csrow->page_mask = ~mask_from_dct_mask(pvt, i); | ||
2745 | /* 8 bytes of resolution */ | ||
2746 | |||
2747 | csrow->mtype = amd64_determine_memory_type(pvt); | ||
2748 | |||
2749 | debugf1(" for MC node %d csrow %d:\n", pvt->mc_node_id, i); | ||
2750 | debugf1(" input_addr_min: 0x%lx input_addr_max: 0x%lx\n", | ||
2751 | (unsigned long)input_addr_min, | ||
2752 | (unsigned long)input_addr_max); | ||
2753 | debugf1(" sys_addr: 0x%lx page_mask: 0x%lx\n", | ||
2754 | (unsigned long)sys_addr, csrow->page_mask); | ||
2755 | debugf1(" nr_pages: %u first_page: 0x%lx " | ||
2756 | "last_page: 0x%lx\n", | ||
2757 | (unsigned)csrow->nr_pages, | ||
2758 | csrow->first_page, csrow->last_page); | ||
2759 | |||
2760 | /* | ||
2761 | * determine whether CHIPKILL or JUST ECC or NO ECC is operating | ||
2762 | */ | ||
2763 | if (pvt->nbcfg & K8_NBCFG_ECC_ENABLE) | ||
2764 | csrow->edac_mode = | ||
2765 | (pvt->nbcfg & K8_NBCFG_CHIPKILL) ? | ||
2766 | EDAC_S4ECD4ED : EDAC_SECDED; | ||
2767 | else | ||
2768 | csrow->edac_mode = EDAC_NONE; | ||
2769 | } | ||
2770 | |||
2771 | return empty; | ||
2772 | } | ||
2458 | 2773 | ||