Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 299811 Details for
Bug 438471
SMI: RedHat need SMI free solution for kernel
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
AMD EDAC driver (needs the refactor patch)
patch-amd-edac-rh-v1 (text/plain), 83.74 KB, created by
IBM Bug Proxy
on 2008-04-01 02:48:49 UTC
(
hide
)
Description:
AMD EDAC driver (needs the refactor patch)
Filename:
MIME Type:
Creator:
IBM Bug Proxy
Created:
2008-04-01 02:48:49 UTC
Size:
83.74 KB
patch
obsolete
>diff -urN linux-2.6.24.3.x86_64/configs/kernel-2.6.24.3-x86_64-rt.config kernel-2.6.24.3-work/configs/kernel-2.6.24.3-x86_64-rt.config >--- linux-2.6.24.3.x86_64/configs/kernel-2.6.24.3-x86_64-rt.config 2008-03-15 16:21:13.000000000 -0400 >+++ kernel-2.6.24.3-work/configs/kernel-2.6.24.3-x86_64-rt.config 2008-03-15 19:22:37.000000000 -0400 >@@ -2943,6 +2942,7 @@ > CONFIG_EDAC_MM_EDAC=m > CONFIG_EDAC_E752X=m > CONFIG_EDAC_I82975X=m >+CONFIG_EDAC_K8=m > CONFIG_EDAC_I5000=m > CONFIG_RTC_LIB=m > CONFIG_RTC_CLASS=m >diff -urN linux-2.6.24.3.x86_64/drivers/edac/k8_edac.c kernel-2.6.24.3-work/drivers/edac/k8_edac.c >--- linux-2.6.24.3.x86_64/drivers/edac/k8_edac.c 1969-12-31 19:00:00.000000000 -0500 >+++ kernel-2.6.24.3-work/drivers/edac/k8_edac.c 2008-03-15 19:22:01.000000000 -0400 >@@ -0,0 +1,2682 @@ >+/* >+ * AMD K8 class Memory Controller kernel module >+ * >+ * This file may be distributed under the terms of the >+ * GNU General Public License. >+ * >+ * Written by Thayne Harbaugh Linux Networx (http://lnxi.com) >+ * >+ * Changes by Douglas "norsk" Thompson <norsk5@xmission.com>: >+ * - K8 CPU Revision D and greater support >+ * >+ * Changes by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>: >+ * - Module largely rewritten, with new (and hopefully correct) >+ * code for dealing with node and chip select interleaving, various >+ * code cleanup, and bug fixes >+ * - Added support for memory hoisting using DRAM hole address >+ * register >+ * >+ * Changes by Douglas "norsk" Thompson <norsk5@xmission.com>: >+ * -K8 Rev (1207) revision supported added, required Revision >+ * specific mini-driver code to support Rev F as well as >+ * prior revisions >+ * >+ * This module is based on the following documents >+ * (available from http://www.amd.com/): >+ * >+ * Title: BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD >+ * Opteron Processors >+ * AMD publication #: 26094 >+ * Revision: 3.26 >+ * >+ * Title: BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh >+ * Processors >+ * AMD publication #: 32559 >+ * Revisio: 3.00 >+ * Issue Date: May 2006 >+ * >+ * Sections in these two documents are no longer in sync >+ * Therefore, comments that refer to a section might be off by one chapter >+ */ >+ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.h> >+#include <linux/pci_ids.h> >+#include <linux/slab.h> >+#include <linux/mmzone.h> >+#include <linux/edac.h> >+#include "edac_core.h" >+ >+#define k8_printk(level, fmt, arg...) \ >+ edac_printk(level, "k8", fmt, ##arg) >+ >+#define k8_mc_printk(mci, level, fmt, arg...) \ >+ edac_mc_chipset_printk(mci, level, "k8", fmt, ##arg) >+ >+/* Throughout the comments in this code, the following terms are used: >+ * >+ * SysAddr, DramAddr, and InputAddr >+ * >+ * These terms come directly from the k8 documentation >+ * (AMD publication #26094). They are defined as follows: >+ * >+ * SysAddr: >+ * This is a physical address generated by a CPU core or a device >+ * doing DMA. If generated by a CPU core, a SysAddr is the result of >+ * a virtual to physical address translation by the CPU core's address >+ * translation mechanism (MMU). >+ * >+ * DramAddr: >+ * A DramAddr is derived from a SysAddr by subtracting an offset that >+ * depends on which node the SysAddr maps to and whether the SysAddr >+ * is within a range affected by memory hoisting. The DRAM Base >+ * (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers >+ * determine which node a SysAddr maps to. >+ * >+ * If the DRAM Hole Address Register (DHAR) is enabled and the SysAddr >+ * is within the range of addresses specified by this register, then >+ * a value x from the DHAR is subtracted from the SysAddr to produce a >+ * DramAddr. Here, x represents the base address for the node that >+ * the SysAddr maps to plus an offset due to memory hoisting. See >+ * section 3.4.8 and the comments in get_dram_hole_info() and >+ * sys_addr_to_dram_addr() below for more information. >+ * >+ * If the SysAddr is not affected by the DHAR then a value y is >+ * subtracted from the SysAddr to produce a DramAddr. Here, y is the >+ * base address for the node that the SysAddr maps to. See section >+ * 3.4.4 and the comments in sys_addr_to_dram_addr() below for more >+ * information. >+ * >+ * InputAddr: >+ * A DramAddr is translated to an InputAddr before being passed to the >+ * memory controller for the node that the DramAddr is associated >+ * with. The memory controller then maps the InputAddr to a csrow. >+ * If node interleaving is not in use, then the InputAddr has the same >+ * value as the DramAddr. Otherwise, the InputAddr is produced by >+ * discarding the bits used for node interleaving from the DramAddr. >+ * See section 3.4.4 for more information. >+ * >+ * The memory controller for a given node uses its DRAM CS Base and >+ * DRAM CS Mask registers to map an InputAddr to a csrow. See >+ * sections 3.5.4 and 3.5.5 for more information. >+ */ >+ >+/* >+ * Alter this version for the K8 module when modifications are made >+ */ >+#define EDAC_K8_VERSION " Ver: 2.0.2 " __DATE__ >+#define EDAC_MOD_STR "k8_edac" >+ >+/* PCI Device IDs we look for */ >+ >+#ifndef PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP >+#define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 >+#endif /* PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP */ >+ >+#ifndef PCI_DEVICE_ID_AMD_K8_NB_MEMCTL >+#define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 >+#endif /* PCI_DEVICE_ID_AMD_K8_NB_MEMCTL */ >+ >+/* Extended Model from CPUID, for CPU Revision numbers */ >+#define OPTERON_CPU_LE_REV_C 0 >+#define OPTERON_CPU_REV_D 1 >+#define OPTERON_CPU_REV_E 2 >+ >+/* Unknown Extended Model value */ >+#define OPTERON_CPU_REV_X 3 >+ >+/* NPT processors have the following Extended Models */ >+#define OPTERON_CPU_REV_F 4 >+#define OPTERON_CPU_REV_FA 5 >+ >+/* Hardware limit on ChipSelect rows per MC >+ * and Processors per system >+ */ >+#define K8_NR_CSROWS 8 >+#define MAX_K8_NODES 8 >+ >+/* K8 register addresses - device 0 Function 1 - Address Map */ >+#define K8_DBR 0x40 /* Function 1: DRAM Base Register (8 x 32b >+ * interlaced with K8_DLR) >+ * >+ * 31:16 DRAM Base addr 39:24 >+ * 15:11 reserved >+ * 10:8 interleave enable >+ * 7:2 reserved >+ * 1 write enable >+ * 0 read enable >+ * >+ */ >+ >+#define K8_DLR 0x44 /* Function 1: DRAM Limit Register (8 x 32b >+ * interlaced with K8_DBR) >+ * >+ * 31:16 DRAM Limit addr 32:24 >+ * 15:11 reserved >+ * 10:8 interleave select >+ * 7:3 reserved >+ * 2:0 destination node ID >+ */ >+ >+#define K8_DHAR 0xf0 /* Function 1: DRAM Hole Address Register >+ * >+ * 31:24 DramHoleBase >+ * 23:16 reserved >+ * 15:8 DramHoleOffset >+ * 7:1 reserved >+ * 0 DramHoleValid >+ */ >+#define K8_DHAR_VALID 0x1 >+#define K8_DHAR_BASE_MASK 0xff000000 >+#define K8_DHAR_OFFSET_MASK 0x0000ff00 >+ >+/* K8 register addresses - device 0 Function 2 - DRAM controller */ >+#define K8_DCSB 0x40 /* Function 2: DRAM Chip-Select Base (8 x 32b) >+ * >+ * For Rev E and prior >+ * 31:21 Base addr high 35:25 >+ * 20:16 reserved >+ * 15:9 Base addr low 19:13 (interlvd) >+ * 8:1 reserved >+ * 0 chip-select bank enable >+ * >+ * For Rev F (NPT) and later >+ * 31:29 reserved >+ * 28:19 Base address (36:27) >+ * 18:14 reserved >+ * 13:5 Base address (21:13) >+ * 4:3 reserved >+ * 2 TestFail >+ * 1 Spare Rank >+ * 0 CESenable >+ * >+ */ >+#define K8_DCSB_CS_ENABLE 0x1 >+#define K8_DCSB_NPT_SPARE 0x2 >+#define K8_DCSB_NPT_TESTFAIL 0x4 >+ >+/* REV E: selects bits 31-21 and 15-9 from DCSB >+ * and the shift amount to form address >+ */ >+#define REV_E_DCSB_BASE_BITS (0xFFE0FE00ULL) >+#define REV_E_DCS_SHIFT 4 >+#define REV_E_DCSM_SHIFT 0 >+#define REF_E_DCSM_COUNT 4 >+ >+/* REV F: selects bits 28-19 and 13-5 from DCSB >+ * and the shift amount to form address >+ */ >+#define REV_F_DCSB_BASE_BITS (0x1FF83FE0ULL) >+#define REV_F_DCS_SHIFT 8 >+#define REV_F_DCSM_SHIFT 1 >+#define REF_F_DCSM_COUNT 8 >+ >+#define K8_DCSM 0x60 /* Function 2: DRAM Chip-Select Mask (8 x 32b) >+ * >+ * 31:30 reserved >+ * 29:21 addr mask high 33:25 >+ * 20:16 reserved >+ * 15:9 addr mask low 19:13 >+ * 8:0 reserved >+ */ >+ >+/* REV E: selects bits 29-21 and 15-9 from DCSM */ >+#define REV_E_DCSM_MASK_BITS 0x3FE0FE00 >+/* represents unused bits [24-20] and [12-0] */ >+#define REV_E_DCS_NOTUSED_BITS 0x1f01fff >+ >+/* REV F: selects bits 28-19 and 13-5 from DCSM */ >+#define REV_F_DCSM_MASK_BITS 0x1FF83FC0 >+/* represents unused bits [26-22] and [12-0] */ >+#define REV_F_DCS_NOTUSED_BITS 0x03c1fff >+ >+#define K8_DBAM 0x80 /* Function 2: DRAM Base Addr Mapping (32b) */ >+ >+#define K8_DCL 0x90 /* Function 2: DRAM configuration low reg (32b) >+ * >+ * Rev E and earlier CPUS: >+ * >+ * 31:28 reserved >+ * 27:25 Bypass Max: 000b=respect >+ * 24 Dissable receivers - no sockets >+ * 23:20 x4 DIMMS >+ * 19 32byte chunks >+ * 18 Unbuffered >+ * 17 ECC enabled >+ * 16 128/64 bit (dual/single chan) >+ * 15:14 R/W Queue bypass count >+ * 13 Self refresh >+ * 12 exit self refresh >+ * 11 mem clear status >+ * 10 DRAM enable >+ * 9 reserved >+ * 8 DRAM init >+ * 7:4 reserved >+ * 3 dis DQS hysteresis >+ * 2 QFC enabled >+ * 1 DRAM drive strength >+ * 0 Digital Locked Loop disable >+ * >+ * Rev F and later CPUs: >+ * >+ * 31:20 reserved >+ * 19 DIMM ECC Enable >+ * 18:17 reserved >+ * 16 Unbuffered DIMM >+ * 15:12 x4 DIMMs >+ * 11 Width128 bits >+ * 10 burstLength32 >+ * 9 SelRefRateEn >+ * 8 ParEn >+ * 7 DramDrvWeak >+ * 6 reserved >+ * 5:4 DramTerm >+ * 3:2 reserved >+ * 1 ExitSelfRef >+ * 0 InitDram >+ */ >+ >+/* K8 register addresses - device 0 Function 3 - Misc Control */ >+#define K8_NBCTL 0x40 /* Function 3: MCA NB Control (32b) >+ * >+ * 1 MCA UE Reporting >+ * 0 MCA CE Reporting >+ */ >+ >+#define K8_NBCFG 0x44 /* Function 3: MCA NB Config (32b) >+ * >+ * 23 Chip-kill x4 ECC enable >+ * 22 ECC enable >+ */ >+#define K8_NBCFG_CHIPKILL 23 >+#define K8_NBCFG_ECC_ENABLE 22 >+ >+#define K8_NBSL 0x48 /* Function 3: MCA NB Status Low (32b) >+ * >+ * 31:24 Syndrome 15:8 chip-kill x4 >+ * 23:20 reserved >+ * 19:16 Extended err code >+ * 15:0 Err code >+ */ >+ >+#define K8_NBSH 0x4C /* Function 3: MCA NB Status High (32b) >+ * >+ * 31 Err valid >+ * 30 Err overflow >+ * 29 Uncorrected err >+ * 28 Err enable >+ * 27 Misc err reg valid >+ * 26 Err addr valid >+ * 25 proc context corrupt >+ * 24:23 reserved >+ * 22:15 Syndrome 7:0 >+ * 14 CE >+ * 13 UE >+ * 12:9 reserved >+ * 8 err found by scrubber >+ * 7 reserved >+ * 6:4 Hyper-transport link number >+ * 3:2 reserved >+ * 1 Err CPU 1 >+ * 0 Err CPU 0 >+ */ >+ >+#define K8_NBSH_VALID_BIT BIT(31) >+ >+#define K8_NBEAL 0x50 /* Function 3: MCA NB err addr low (32b) >+ * >+ * 31:3 Err addr low 31:3 >+ * 2:0 reserved >+ */ >+ >+#define K8_NBEAH 0x54 /* Function 3: MCA NB err addr high (32b) >+ * >+ * 31:8 reserved >+ * 7:0 Err addr high 39:32 >+ */ >+ >+#define K8_SCRCTRL 0x58 /* Function 3: Memory scrub control register. >+ * >+ * 30:21 reserved >+ * 20:16 dcache scrub >+ * 15:13 reserved >+ * 12:8 L2Scrub >+ * 7:5 reserved >+ * 4:0 dramscrub >+ * >+ */ >+ >+#define K8_NBCAP 0xE8 /* Function 3: MCA NB capabilities (32b) >+ * >+ * 31:9 reserved >+ * 4 ChipKill S4ECD4ED capable >+ * 3 SECDED capable >+ */ >+#define K8_NBCAP_CHIPKILL 4 >+#define K8_NBCAP_SECDED 3 >+ >+ /* MSR's */ >+ >+ /* >+ * K8_MSR_MCxCTL (64b) >+ * (0x400,404,408,40C,410) >+ * 63 Enable reporting source 63 >+ * . >+ * . >+ * . >+ * 2 Enable error source 2 >+ * 1 Enable error source 1 >+ * 0 Enable error source 0 >+ */ >+ >+ /* >+ * K8_MSR_MCxSTAT (64b) >+ * (0x401,405,409,40D,411) >+ * 63 Error valid >+ * 62 Status overflow >+ * 61 UE >+ * 60 Enabled error condition >+ * 59 Misc register valid (not used) >+ * 58 Err addr register valid >+ * 57 Processor context corrupt >+ * 56:32 Other information >+ * 31:16 Model specific error code >+ * 15:0 MCA err code >+ */ >+ >+ /* >+ * K8_MSR_MCxADDR (64b) >+ * (0x402,406,40A,40E,412) >+ * 63:48 reserved >+ * 47:0 Address >+ */ >+ >+ /* >+ * K8_MSR_MCxMISC (64b) >+ * (0x403,407,40B,40F,413) >+ * Unused on Athlon64 and K8 >+ */ >+ >+#define K8_MSR_MCGCTL 0x017b /* Machine Chk Global report ctl (64b) >+ * >+ * 31:5 reserved >+ * 4 North Bridge >+ * 3 Load/Store >+ * 2 Bus Unit >+ * 1 Instruction Cache >+ * 0 Data Cache >+ */ >+ >+#define K8_MSR_MC4CTL 0x0410 /* North Bridge Check report ctl (64b) */ >+#define K8_MSR_MC4STAT 0x0411 /* North Bridge status (64b) */ >+#define K8_MSR_MC4ADDR 0x0412 /* North Bridge Address (64b) */ >+ >+#define K8_MSR_TOP_MEM 0xC001001A /* TOP_MEM */ >+#define K8_MSR_TOP_MEM2 0xC001001D /* TOP_MEM2 */ >+ >+static struct edac_pci_ctl_info *k8_ctl_pci; >+ >+/* >+ * AMD sets the first MC device at device ID 0x18. Subsequent ones go up >+ * from there. >+ */ >+static inline int get_mc_node_id_from_pdev(struct pci_dev *pdev) >+{ >+ /* Take whatever SLOT we are and subtract 0x18 to get this node ID */ >+ return PCI_SLOT(pdev->devfn) - 0x18; >+} >+ >+/* Ugly hack that allows module to compile when built as part of a 32-bit >+ * kernel. Just in case anyone wants to run a 32-bit kernel on their Opteron. >+ */ >+#ifndef MAXNODE >+#define MAXNODE 8 >+#endif >+ >+/* Each entry holds the CPU revision of all CPU cores for the given node. */ >+static int k8_node_revision_table[MAXNODE] = { 0 }; >+ >+static inline int node_rev(int node_id) >+{ >+ return k8_node_revision_table[node_id]; >+} >+ >+static void store_node_revision(void *param) >+{ >+ int node_id, revision; >+ >+ /* Multiple CPU cores on the same node will all write their revision >+ * number to the same array entry. This is ok. For a given node, all >+ * CPU cores are on the same piece of silicon and share the same >+ * revision number. >+ */ >+ node_id = (cpuid_ebx(1) >> 24) & 0x07; >+ revision = (cpuid_eax(1) >> 16) & 0x0f; >+ k8_node_revision_table[node_id] = revision; >+} >+ >+/* Initialize k8_node_revision_table. */ >+static void build_node_revision_table(void) >+{ >+ static int initialized; >+ >+ if (initialized) >+ return; >+ >+ on_each_cpu(store_node_revision, NULL, 1, 1); >+ initialized = 1; >+} >+ >+/* Currently only one type of K8 CPUs, as defined by the PCI DEVICE IDs */ >+enum k8_chips { >+ K8_CPUS = 0, >+}; >+ >+/* >+ * Private to this module: data and register state >+ */ >+struct k8_pvt { >+ struct pci_dev *addr_map; >+ struct pci_dev *misc_ctl; >+ >+ int mc_node_id; /* MC index of this MC node */ >+ int ext_model; /* extended model value of this node */ >+ >+ u32 dcl; /* DRAM Configuration Low reg */ >+ u32 dbr[MAX_K8_NODES]; /* DRAM Base Reg */ >+ u32 dlr[MAX_K8_NODES]; /* DRAM Limit Reg */ >+ u32 nbcap; /* North Bridge Capabilities */ >+ u32 nbcfg; /* North Bridge Configuration */ >+ >+ u32 dcsb[K8_NR_CSROWS]; /* DRAM CS Base Registers */ >+ u32 dcsm[K8_NR_CSROWS]; /* DRAM CS Mask Registers */ >+ >+ /* The following 3 fields are set at run time, after Revision has >+ * been determine, since the dcsb and dcsm registers vary >+ * by CPU Revsion >+ */ >+ u32 dcsb_mask; /* DCSB mask bits */ >+ u32 dcsm_mask; /* DCSM mask bits */ >+ u32 num_dcsm; /* Number of DCSM registers */ >+ u32 dcs_mask_notused; /* DCSM notused mask bits */ >+ u32 dcs_shift; /* DCSB and DCSM shift value */ >+ >+ /* On Rev E there are 8 DCSM registers, >+ * On Rev F there are 4 DCSM registers. >+ * This field is set as a 0 (Rev E) or 1 (Rev F) to indicate >+ * number of bits to shift the index for DCSM array look ups >+ */ >+ u32 dcsm_shift_bit; >+ >+ u32 dhar; >+ u32 dbam; /* DRAM Base Address Mapping reg */ >+ >+ u64 top_mem; >+ u64 top_mem2; >+}; >+ >+/* >+ * Structure to hold dynamicly read status and error address registers >+ */ >+struct k8_error_info_regs { >+ u32 nbsh; >+ u32 nbsl; >+ u32 nbeah; >+ u32 nbeal; >+}; >+ >+struct k8_error_info { >+ struct k8_error_info_regs error_info; >+ int race_condition_detected; >+}; >+ >+/* >+ * There currently is just one type of MC device for K8s >+ * That is the Athlon64 and Opteron CPUs. They all have the >+ * same PCI DEVICE ID, even though there is differences between >+ * the different Revisions (CG,D,E,F,future). >+ * When the DEVICE IDs change, then a new entry will be needed >+ * in the following k8_mc_type[] arrays. >+ */ >+struct k8_dev_type_info { >+ const char *ctl_name; >+ u16 addr_map; >+ u16 misc_ctl; >+}; >+ >+static const struct k8_dev_type_info k8_mc_types[] = { >+ [K8_CPUS] = { >+ .ctl_name = "Athlon64/Opteron", >+ .addr_map = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP, >+ .misc_ctl = PCI_DEVICE_ID_AMD_K8_NB_MISC}, >+}; >+ >+/* Valid scrub rates for the K8 hardware memory scrubber. We map >+ the scrubbing bandwidth to a valid bit pattern. The 'set' >+ operation finds the 'matching- or higher value'. >+ >+ FIXME: Produce a better mapping/linearisation. >+*/ >+ >+# define SDRATE_EOD 0xFFFFFFFF >+ >+static struct scrubrate { >+ u32 scrubval; /* bit pattern for scrub rate */ >+ u32 bandwidth; /* bandwidth consumed (bytes/sec) */ >+} scrubrates[] = { >+ { >+ 0x00, 0UL}, /* Scrubbing Off */ >+ { >+ 0x16, 761UL}, /* Slowest Rate */ >+ { >+ 0x15, 1523UL}, { >+ 0x14, 3051UL}, { >+ 0x13, 6101UL}, { >+ 0x12, 12213UL}, { >+ 0x11, 24427UL}, { >+ 0x10, 48854UL}, { >+ 0x0F, 97650UL}, { >+ 0x0E, 195300UL}, { >+ 0x0D, 390720UL}, { >+ 0x0C, 781440UL}, { >+ 0x0B, 1560975UL}, { >+ 0x0A, 3121951UL}, { >+ 0x09, 6274509UL}, { >+ 0x08, 12284069UL}, { >+ 0x07, 25000000UL}, { >+ 0x06, 50000000UL}, { >+ 0x05, 100000000UL}, { >+ 0x04, 200000000UL}, { >+ 0x03, 400000000UL}, { >+ 0x02, 800000000UL}, { >+ 0x01, 1600000000UL}, { >+ 0x00, SDRATE_EOD} /* End Of Data */ >+}; >+ >+/* >+ * Iterate over devices to find AMD related devices >+ */ >+static struct pci_dev *pci_get_related_function(unsigned int vendor, >+ unsigned int device, >+ struct pci_dev *related) >+{ >+ struct pci_dev *dev; >+ >+ dev = NULL; >+ >+ dev = pci_get_device(vendor, device, dev); >+ while (dev != NULL) { >+ if ((dev->bus->number == related->bus->number) && >+ (PCI_SLOT(dev->devfn) == PCI_SLOT(related->devfn))) >+ break; >+ dev = pci_get_device(vendor, device, dev); >+ } >+ >+ return dev; >+} >+ >+/* Memory scrubber control interface. For the K8 memory scrubbing is >+ handled by hardware and can involve the cache and the dcache as >+ well as the main memory. This causes the "units" for the scrubbing >+ speed to vary from 64 byte blocks (sdram) over cache lines (cache) >+ to bits (dcache). >+ >+ This is nasty, so we will use bandwidth in bytes/sec for the setting. >+ >+ Currently, we only do scrubbing of sdram - the caches are assumed >+ to be excercised always by running code and if the scrubber is done >+ in software on other archs, we might not have access to the >+ caches directly. >+*/ >+ >+static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 * bw) >+{ >+ struct k8_pvt *pvt; >+ u32 scrubval; >+ int status = -1; >+ int i; >+ >+ pvt = (struct k8_pvt *)mci->pvt_info; >+ >+ /* translate the configured rate to a value specific to >+ the K8 memory controller and apply. >+ search for the bandwidth that is greater or equal than the >+ setting and program that. >+ */ >+ for (i = 0; scrubrates[i].bandwidth != SDRATE_EOD; i++) { >+ >+ if (scrubrates[i].bandwidth >= *bw) { >+ scrubval = scrubrates[i].scrubval; >+ pci_write_bits32(pvt->misc_ctl, K8_SCRCTRL, >+ scrubval, 0x001F); >+ status = 0; >+ break; >+ } >+ } >+ return status; >+} >+ >+static int get_sdram_scrub_rate(struct mem_ctl_info *mci, u32 * bw) >+{ >+ struct k8_pvt *pvt; >+ u32 scrubval = 0; >+ int status = -1; >+ int i; >+ >+ pvt = (struct k8_pvt *)mci->pvt_info; >+ >+ /* find the bandwidth matching the memory scrubber configuration >+ and return that value in *bw. >+ */ >+ pci_read_config_dword(pvt->misc_ctl, K8_SCRCTRL, &scrubval); >+ scrubval = scrubval & 0x001F; >+ >+ edac_printk(KERN_DEBUG, EDAC_MC, >+ "pci-read, sdram scrub control value: %d \n", scrubval); >+ >+ for (i = 0; scrubrates[i].bandwidth != SDRATE_EOD; i++) { >+ >+ if (scrubrates[i].scrubval == scrubval) { >+ *bw = scrubrates[i].bandwidth; >+ status = 0; >+ break; >+ } >+ } >+ >+ /* the bit pattern is invalid - we might fix it >+ by applying the slowest scrub rate as this is >+ closest to the valid value, but we do not! >+ */ >+ >+ if (scrubrates[i].bandwidth == SDRATE_EOD) { >+ edac_printk(KERN_WARNING, EDAC_MC, >+ "Invalid sdram scrub control value: %d\n", >+ scrubval); >+ status = -1; >+ } >+ return status; >+} >+ >+/* stolen from msr.c - the calls in msr.c could be exported */ >+struct msr_command { >+ int cpu; >+ int err; >+ u32 reg; >+ u32 data[2]; >+}; >+ >+static void smp_wrmsr(void *cmd_block) >+{ >+ struct msr_command *cmd = cmd_block; >+ wrmsr(cmd->reg, cmd->data[0], cmd->data[1]); >+} >+ >+static void smp_rdmsr(void *cmd_block) >+{ >+ struct msr_command *cmd = cmd_block; >+ rdmsr(cmd->reg, cmd->data[0], cmd->data[1]); >+} >+ >+static void do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) >+{ >+ struct msr_command cmd; >+ >+ cmd.cpu = raw_smp_processor_id(); >+ cmd.reg = reg; >+ cmd.data[0] = eax; >+ cmd.data[1] = edx; >+ on_each_cpu(smp_wrmsr, &cmd, 1, 1); >+} >+ >+static void do_rdmsr(int cpu, u32 reg, u32 * eax, u32 * edx) >+{ >+ struct msr_command cmd; >+ >+ cmd.cpu = raw_smp_processor_id(); >+ cmd.reg = reg; >+ on_each_cpu(smp_rdmsr, &cmd, 1, 1); >+ *eax = cmd.data[0]; >+ *edx = cmd.data[1]; >+} >+ >+/* >+ * FIXME - This is a large chunk of memory to suck up just to decode the >+ * syndrome. It would be nice to discover a pattern in the syndromes that >+ * could be used to quickly identify the channel. The big problems with >+ * this table is memory usage, lookup speed (could sort and binary search), >+ * correctness (there could be a transcription error). A zero in any nibble >+ * for a syndrom is always channel 0, but that only decodes some of the >+ * syndromes. Can anyone find any other patterns? >+ * >+ * The comment in the left column is the nibble that is in error. The least >+ * significant nibble of the syndrome is the mask for the bits that are >+ * in error (need to be toggled) for the particular nibble. >+ */ >+#define SYNDROME_TABLE_SIZE 270 >+static const unsigned long syndromes_chan0[SYNDROME_TABLE_SIZE] = { >+ /*0 */ 0xe821, 0x7c32, 0x9413, 0xbb44, 0x5365, 0xc776, 0x2f57, >+ 0xdd88, 0x35a9, 0xa1ba, 0x499b, 0x66cc, 0x8eed, 0x1afe, 0xf2df, >+ /*1 */ 0x5d31, 0xa612, 0xfb23, 0x9584, 0xc8b5, 0x3396, 0x6ea7, >+ 0xeac8, 0xb7f9, 0x4cda, 0x11eb, 0x7f4c, 0x227d, 0xd95e, 0x846f, >+ /*2 */ 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, >+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, >+ /*3 */ 0x2021, 0x3032, 0x1013, 0x4044, 0x6065, 0x7076, 0x5057, >+ 0x8088, 0xa0a9, 0xb0ba, 0x909b, 0xc0cc, 0xe0ed, 0xf0fe, 0xd0df, >+ /*4 */ 0x5041, 0xa082, 0xf0c3, 0x9054, 0xc015, 0x30d6, 0x6097, >+ 0xe0a8, 0xb0e9, 0x402a, 0x106b, 0x70fc, 0x20bd, 0xd07e, 0x803f, >+ /*5 */ 0xbe21, 0xd732, 0x6913, 0x2144, 0x9f65, 0xf676, 0x4857, >+ 0x3288, 0x8ca9, 0xe5ba, 0x5b9b, 0x13cc, 0xaded, 0xc4fe, 0x7adf, >+ /*6 */ 0x4951, 0x8ea2, 0xc7f3, 0x5394, 0x1ac5, 0xdd36, 0x9467, >+ 0xa1e8, 0xe8b9, 0x2f4a, 0x661b, 0xf27c, 0xbb2d, 0x7cde, 0x358f, >+ /*7 */ 0x74e1, 0x9872, 0xec93, 0xd6b4, 0xa255, 0x4ec6, 0x3a27, >+ 0x6bd8, 0x1f39, 0xf3aa, 0x874b, 0xbd6c, 0xc98d, 0x251e, 0x51ff, >+ /*8 */ 0x15c1, 0x2a42, 0x3f83, 0xcef4, 0xdb35, 0xe4b6, 0xf177, >+ 0x4758, 0x5299, 0x6d1a, 0x78db, 0x89ac, 0x9c6d, 0xa3ee, 0xb62f, >+ /*9 */ 0x3d01, 0x1602, 0x2b03, 0x8504, 0xb805, 0x9306, 0xae07, >+ 0xca08, 0xf709, 0xdc0a, 0xe10b, 0x4f0c, 0x720d, 0x590e, 0x640f, >+ /*a */ 0x9801, 0xec02, 0x7403, 0x6b04, 0xf305, 0x8706, 0x1f07, >+ 0xbd08, 0x2509, 0x510a, 0xc90b, 0xd60c, 0x4e0d, 0x3a0e, 0xa20f, >+ /*b */ 0xd131, 0x6212, 0xb323, 0x3884, 0xe9b5, 0x5a96, 0x8ba7, >+ 0x1cc8, 0xcdf9, 0x7eda, 0xafeb, 0x244c, 0xf57d, 0x465e, 0x976f, >+ /*c */ 0xe1d1, 0x7262, 0x93b3, 0xb834, 0x59e5, 0xca56, 0x2b87, >+ 0xdc18, 0x3dc9, 0xae7a, 0x4fab, 0x542c, 0x85fd, 0x164e, 0xf79f, >+ /*d */ 0x6051, 0xb0a2, 0xd0f3, 0x1094, 0x70c5, 0xa036, 0xc067, >+ 0x20e8, 0x40b9, 0x904a, 0x601b, 0x307c, 0x502d, 0x80de, 0xe08f, >+ /*e */ 0xa4c1, 0xf842, 0x5c83, 0xe6f4, 0x4235, 0x1eb6, 0xba77, >+ 0x7b58, 0xdf99, 0x831a, 0x27db, 0x9dac, 0x396d, 0x65ee, 0xc12f, >+ /*f */ 0x11c1, 0x2242, 0x3383, 0xc8f4, 0xd935, 0xeab6, 0xfb77, >+ 0x4c58, 0x5d99, 0x6e1a, 0x7fdb, 0x84ac, 0x9562, 0xa6ee, 0xb72f, >+ >+ /*20 */ 0xbe01, 0xd702, 0x6903, 0x2104, 0x9f05, 0xf606, 0x4807, >+ 0x3208, 0x8c09, 0xe50a, 0x5b0b, 0x130c, 0xad0d, 0xc40e, 0x7a0f, >+ /*21 */ 0x4101, 0x8202, 0xc303, 0x5804, 0x1905, 0xda06, 0x9b07, >+ 0xac08, 0xed09, 0x2e0a, 0x6f0b, 0x640c, 0xb50d, 0x760e, 0x370f >+}; >+ >+static const unsigned long syndromes_chan1[SYNDROME_TABLE_SIZE] = { >+ /*10 */ 0x45d1, 0x8a62, 0xcfb3, 0x5e34, 0x1be5, 0xd456, 0x9187, >+ 0xa718, 0xe2c9, 0x2d7a, 0x68ab, 0xf92c, 0xbcfd, 0x734e, 0x369f, >+ /*11 */ 0x63e1, 0xb172, 0xd293, 0x14b4, 0x7755, 0xa5c6, 0xc627, >+ 0x28d8, 0x4b39, 0x99aa, 0xfa4b, 0x3c6c, 0x5f8d, 0x8d1e, 0xeeff, >+ /*12 */ 0xb741, 0xd982, 0x6ec3, 0x2254, 0x9515, 0xfbd6, 0x4c97, >+ 0x33a8, 0x84e9, 0xea2a, 0x5d6b, 0x11fc, 0xa6bd, 0xc87e, 0x7f3f, >+ /*13 */ 0xdd41, 0x6682, 0xbbc3, 0x3554, 0xe815, 0x53d6, 0xce97, >+ 0x1aa8, 0xc7e9, 0x7c2a, 0xa1fb, 0x2ffc, 0xf2bd, 0x497e, 0x943f, >+ /*14 */ 0x2bd1, 0x3d62, 0x16b3, 0x4f34, 0x64e5, 0x7256, 0x5987, >+ 0x8518, 0xaec9, 0xb87a, 0x93ab, 0xca2c, 0xe1fd, 0xf74e, 0xdc9f, >+ /*15 */ 0x83c1, 0xc142, 0x4283, 0xa4f4, 0x2735, 0x65b6, 0xe677, >+ 0xf858, 0x7b99, 0x391a, 0xbadb, 0x5cac, 0xdf6d, 0x9dee, 0x1e2f, >+ /*16 */ 0x8fd1, 0xc562, 0x4ab3, 0xa934, 0x26e5, 0x6c56, 0xe387, >+ 0xfe18, 0x71c9, 0x3b7a, 0xb4ab, 0x572c, 0xd8fd, 0x924e, 0x1d9f, >+ /*17 */ 0x4791, 0x89e2, 0xce73, 0x5264, 0x15f5, 0xdb86, 0x9c17, >+ 0xa3b8, 0xe429, 0x2a5a, 0x6dcb, 0xf1dc, 0xb64d, 0x783e, 0x3faf, >+ /*18 */ 0x5781, 0xa9c2, 0xfe43, 0x92a4, 0xc525, 0x3b66, 0x6ce7, >+ 0xe3f8, 0xb479, 0x4a3a, 0x1dbb, 0x715c, 0x26dd, 0xd89e, 0x8f1f, >+ /*19 */ 0xbf41, 0xd582, 0x6ac3, 0x2954, 0x9615, 0xfcd6, 0x4397, >+ 0x3ea8, 0x81e9, 0xeb2a, 0x546b, 0x17fc, 0xa8bd, 0xc27e, 0x7d3f, >+ /*1a */ 0x9891, 0xe1e2, 0x7273, 0x6464, 0xf7f5, 0x8586, 0x1617, >+ 0xb8b8, 0x2b29, 0x595a, 0xcacb, 0xdcdc, 0x4f4d, 0x3d3e, 0xaeaf, >+ /*1b */ 0xcce1, 0x4472, 0x8893, 0xfdb4, 0x3f55, 0xb9c6, 0x7527, >+ 0x56d8, 0x9a39, 0x12aa, 0xde4b, 0xab6c, 0x678d, 0xef1e, 0x23ff, >+ /*1c */ 0xa761, 0xf9b2, 0x5ed3, 0xe214, 0x4575, 0x1ba6, 0xbcc7, >+ 0x7328, 0xd449, 0x8a9a, 0x2dfb, 0x913c, 0x365d, 0x688e, 0xcfef, >+ /*1d */ 0xff61, 0x55b2, 0xaad3, 0x7914, 0x8675, 0x2ca6, 0xd3c7, >+ 0x9e28, 0x6149, 0xcb9a, 0x34fb, 0xe73c, 0x185d, 0xb28e, 0x4def, >+ /*1e */ 0x5451, 0xa8a2, 0xfcf3, 0x9694, 0xc2c5, 0x3e36, 0x6a67, >+ 0xebe8, 0xbfb9, 0x434a, 0x171b, 0x7d7c, 0x292d, 0xd5de, 0x818f, >+ /*1f */ 0x6fc1, 0xb542, 0xda83, 0x19f4, 0x7635, 0xacb6, 0xc377, >+ 0x2e58, 0x4199, 0x9b1a, 0xf4db, 0x37ac, 0x586d, 0x82ee, 0xed2f, >+ >+ /*22 */ 0xc441, 0x4882, 0x8cc3, 0xf654, 0x3215, 0xbed6, 0x7a97, >+ 0x5ba8, 0x9fe9, 0x132a, 0xd76b, 0xadfc, 0x69bd, 0xe57e, 0x213f, >+ /*23 */ 0x7621, 0x9b32, 0xed13, 0xda44, 0xac65, 0x4176, 0x3757, >+ 0x6f88, 0x19a9, 0xf4ba, 0x829b, 0xb5cc, 0xc3ed, 0x2efe, 0x58df >+}; >+ >+static int chan_from_chipkill_syndrome(unsigned long syndrome) >+{ >+ int i; >+ >+ debugf0("%s()\n", __func__); >+ >+ for (i = 0; i < SYNDROME_TABLE_SIZE; i++) { >+ if (syndromes_chan0[i] == syndrome) >+ return 0; >+ if (syndromes_chan1[i] == syndrome) >+ return 1; >+ } >+ >+ debugf0("%s(): syndrome(%lx) not found\n", __func__, syndrome); >+ return -1; >+} >+ >+static const char *tt_msgs[] = { /* transaction type */ >+ "inst", >+ "data", >+ "generic", >+ "reserved" >+}; >+ >+static const char *ll_msgs[] = { /* cache level */ >+ "0", >+ "1", >+ "2", >+ "generic" >+}; >+ >+static const char *memtt_msgs[] = { >+ "generic", >+ "generic read", >+ "generic write", >+ "data read", >+ "data write", >+ "inst fetch", >+ "prefetch", >+ "evict", >+ "snoop", >+ "unknown error 9", >+ "unknown error 10", >+ "unknown error 11", >+ "unknown error 12", >+ "unknown error 13", >+ "unknown error 14", >+ "unknown error 15" >+}; >+ >+static const char *pp_msgs[] = { /* participating processor */ >+ "local node origin", >+ "local node response", >+ "local node observed", >+ "generic" >+}; >+ >+static const char *to_msgs[] = { >+ "no timeout", >+ "timed out" >+}; >+ >+static const char *ii_msgs[] = { /* memory or i/o */ >+ "mem access", >+ "reserved", >+ "i/o access", >+ "generic" >+}; >+ >+static const char *ext_msgs[] = { /* extended error */ >+ "ECC error", >+ "CRC error", >+ "sync error", >+ "mst abort", >+ "tgt abort", >+ "GART error", >+ "RMW error", >+ "watchdog error", >+ "ECC chipkill x4 error", >+ "unknown error 9", >+ "unknown error 10", >+ "unknown error 11", >+ "unknown error 12", >+ "unknown error 13", >+ "unknown error 14", >+ "unknown error 15" >+}; >+ >+static const char *htlink_msgs[] = { >+ "none", >+ "1", >+ "2", >+ "1 2", >+ "3", >+ "1 3", >+ "2 3", >+ "1 2 3" >+}; >+ >+/* >+ * The DCSB and DCSM registers differ between Rev E and Rev F CPUs >+ * The following several functions intialize and extract information >+ * from this registers >+ */ >+ >+/* >+ * set_dcsb_dcsm_rev_specific(pvt) >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * Set the DCSB and DCSM mask values depending on the >+ * CPU revision value. >+ * Also set the shift factor for the DCSB and DCSM values >+ * >+ * member dcs_mask_notused, REV E: >+ * >+ * To find the max InputAddr for the csrow, start with the base >+ * address and set all bits that are "don't care" bits in the test at >+ * the start of section 3.5.4 (p. 84). >+ * >+ * The "don't care" bits are all set bits in the mask and >+ * all bits in the gaps between bit ranges [35-25] and [19-13]. >+ * The value REV_E_DCS_NOTUSED_BITS represents bits [24-20] and [12-0], >+ * which are all bits in the above-mentioned gaps. >+ * >+ * member dcs_mask_notused, REV F: >+ * >+ * To find the max InputAddr for the csrow, start with the base >+ * address and set all bits that are "don't care" bits in the test at >+ * the start of NPT section 4.5.4 (p. 87). >+ * >+ * The "don't care" bits are all set bits in the mask and >+ * all bits in the gaps between bit ranges [36-27] and [21-13]. >+ * The value REV_F_DCS_NOTUSED_BITS represents bits [26-22] and [12-0], >+ * which are all bits in the above-mentioned gaps. >+ */ >+static void set_dcsb_dcsm_rev_specific(struct k8_pvt *pvt) >+{ >+ if (pvt->ext_model >= OPTERON_CPU_REV_F) { >+ pvt->dcsb_mask = REV_F_DCSB_BASE_BITS; >+ pvt->dcsm_mask = REV_F_DCSM_MASK_BITS; >+ pvt->dcs_mask_notused = REV_F_DCS_NOTUSED_BITS; >+ pvt->dcs_shift = REV_F_DCS_SHIFT; >+ pvt->dcsm_shift_bit = REV_F_DCSM_SHIFT; >+ pvt->num_dcsm = REF_F_DCSM_COUNT; >+ } else { >+ pvt->dcsb_mask = REV_E_DCSB_BASE_BITS; >+ pvt->dcsm_mask = REV_E_DCSM_MASK_BITS; >+ pvt->dcs_mask_notused = REV_E_DCS_NOTUSED_BITS; >+ pvt->dcs_shift = REV_E_DCS_SHIFT; >+ pvt->dcsm_shift_bit = REV_E_DCSM_SHIFT; >+ pvt->num_dcsm = REF_E_DCSM_COUNT; >+ } >+} >+ >+/* >+ * get_dcsb() >+ * >+ * getter function to return the 'base' address the i'th CS entry. >+ */ >+static u32 get_dcsb(struct k8_pvt *pvt, int csrow) >+{ >+ return pvt->dcsb[csrow]; >+} >+ >+/* >+ * get_dcsm() >+ * >+ * getter function to return the 'mask' address the i'th CS entry. >+ * This getter function is needed because there different number >+ * of DCSM registers on Rev E and prior vs Rev F and later >+ */ >+static u32 get_dcsm(struct k8_pvt *pvt, int csrow) >+{ >+ return pvt->dcsm[csrow >> pvt->dcsm_shift_bit]; >+} >+ >+/* >+ * base_from_dcsb >+ * >+ * Extract the DRAM CS base address from selected csrow register >+ */ >+static u64 base_from_dcsb(struct k8_pvt *pvt, int csrow) >+{ >+ return ((u64) (get_dcsb(pvt, csrow) & pvt->dcsb_mask)) << >+ pvt->dcs_shift; >+} >+ >+/* >+ * mask_from_dcsm >+ * >+ * Extract the Mask from the dcsb[csrow] entry >+ * Depends on CPU Revision on how to extract this information >+ */ >+static u64 mask_from_dcsm(struct k8_pvt *pvt, int csrow) >+{ >+ u64 dcsm_bits, other_bits; >+ u64 mask; >+ >+ /* Extract bits bits 29-21 and 15-9 from DCSM (section 3.5.5). */ >+ dcsm_bits = get_dcsm(pvt, csrow) & pvt->dcsm_mask; >+ >+ /* Set all bits except bits 33-25 and 19-13. */ >+ other_bits = pvt->dcsm_mask; >+ other_bits = ~(other_bits << pvt->dcs_shift); >+ >+ /* The extracted bits from DCSM belong in the spaces represented by >+ * the cleared bits in other_bits. >+ */ >+ mask = (dcsm_bits << pvt->dcs_shift) | other_bits; >+ >+ return mask; >+} >+ >+/* >+ * setup_dcsb_dcsm() >+ * >+ * Setup the DCSB and DCSM arrays from hardware >+ */ >+static void setup_dcsb_dcsm(struct k8_pvt *pvt, struct pci_dev *pdev) >+{ >+ int i; >+ >+ /* Set the dcsb and dcsm mask bits and their shift value */ >+ set_dcsb_dcsm_rev_specific(pvt); >+ >+ /* Retrieve the DRAM CS Base Address Registers from hardware >+ */ >+ for (i = 0; i < K8_NR_CSROWS; i++) { >+ pci_read_config_dword(pdev, K8_DCSB + (i * 4), &pvt->dcsb[i]); >+ >+ } >+ >+ /* The number of DCSMs differents at the Rev E/Rev F boundary >+ * so we retrieve the number of registers defined for this processor >+ */ >+ for (i = 0; i < pvt->num_dcsm; i++) { >+ pci_read_config_dword(pdev, K8_DCSM + (i * 4), &pvt->dcsm[i]); >+ } >+ >+ /* Debug dump only of DCSB and DCSM registers */ >+ for (i = 0; i < K8_NR_CSROWS; i++) { >+ debugf1(" dcsb[%d]: 0x%8.8x dcsm[%d]: 0x%x\n", >+ i, get_dcsb(pvt, i), >+ i >> pvt->dcsm_shift_bit, get_dcsm(pvt, i)); >+ } >+} >+ >+/* >+ * get_base_and_limit() >+ * >+ * In *base and *limit, pass back the full 40-bit base and limit physical >+ * addresses for the node given by node_id. This information is obtained from >+ DRAM Base (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers. The >+ * base and limit addresses are of type SysAddr, as defined at the start of >+ * section 3.4.4 (p. 70). They are the lowest and highest physical addresses >+ * in the address range they represent. >+ */ >+static void get_base_and_limit(struct k8_pvt *pvt, int node_id, >+ u64 * base, u64 * limit) >+{ >+ *base = ((u64) (pvt->dbr[node_id] & 0xffff0000)) << 8; >+ >+ /* Since the limit represents the highest address in the range, we >+ * must set its lowest 24 bits to 1. >+ */ >+ *limit = (((u64) (pvt->dlr[node_id] & 0xffff0000)) << 8) | 0xffffff; >+} >+ >+/* Return 1 if the SysAddr given by sys_addr matches the base/limit associated >+ * with node_id >+ */ >+static int base_limit_match(struct k8_pvt *pvt, u64 sys_addr, int node_id) >+{ >+ u64 base, limit, addr; >+ >+ get_base_and_limit(pvt, node_id, &base, &limit); >+ >+ /* The k8 treats this as a 40-bit value. However, bits 63-40 will be >+ * all ones if the most significant implemented address bit is 1. >+ * Here we discard bits 63-40. See section 3.4.2 of AMD publication >+ * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 >+ * Application Programming. >+ */ >+ addr = sys_addr & 0x000000ffffffffffull; >+ >+ return (addr >= base) && (addr <= limit); >+} >+ >+/* Attempt to map a SysAddr to a node. On success, return a pointer to the >+ * mem_ctl_info structure for the node that the SysAddr maps to. On failure, >+ * return NULL. >+ */ >+static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, >+ u64 sys_addr) >+{ >+ struct k8_pvt *pvt; >+ int node_id; >+ u32 intlv_en, bits; >+ >+ /* Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section >+ * 3.4.4.2) registers to map the SysAddr to a node ID. >+ */ >+ >+ pvt = mci->pvt_info; >+ >+ /* The value of this field should be the same for all DRAM Base >+ * registers. Therefore we arbitrarily choose to read it from the >+ * register for node 0. >+ */ >+ intlv_en = pvt->dbr[0] & (0x07 << 8); >+ >+ if (intlv_en == 0) { /* node interleaving is disabled */ >+ debugf2("%s(): node interleaving disabled\n", __func__); >+ for (node_id = 0; ; ) { >+ if (base_limit_match(pvt, sys_addr, node_id)) >+ break; >+ >+ if (++node_id >= MAX_K8_NODES) { >+ debugf2("%s(): sys_addr 0x%lx " >+ "does not match any node\n", __func__, >+ (unsigned long)sys_addr); >+ return NULL; >+ } >+ } >+ >+ goto found; >+ } >+ >+ if (unlikely((intlv_en != (0x01 << 8)) && >+ (intlv_en != (0x03 << 8)) && (intlv_en != (0x07 << 8)))) { >+ k8_printk(KERN_WARNING, >+ "%s(): junk value of 0x%x extracted from IntlvEn " >+ "field of DRAM Base Register for node 0: This " >+ "probably indicates a BIOS bug.\n", __func__, >+ intlv_en); >+ return NULL; >+ } >+ >+ /* If we get this far, node interleaving is enabled. */ >+ debugf2("%s(): node interleaving enabled\n", __func__); >+ bits = (((u32) sys_addr) >> 12) & intlv_en; >+ >+ for (node_id = 0; ; ) { >+ if ((pvt->dlr[node_id] & intlv_en) == bits) >+ break; /* intlv_sel field matches */ >+ >+ if (++node_id >= MAX_K8_NODES) { >+ debugf2("%s(): sys_addr 0x%lx does not match any " >+ "node\n", __func__, (unsigned long)sys_addr); >+ return NULL; >+ } >+ } >+ >+ /* sanity test for sys_addr */ >+ if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { >+ k8_printk(KERN_WARNING, >+ "%s(): sys_addr 0x%lx falls outside base/limit " >+ "address range for node %d with node interleaving " >+ "enabled.\n", __func__, (unsigned long)sys_addr, >+ node_id); >+ return NULL; >+ } >+ >+found: >+ debugf2("%s(): sys_addr 0x%lx matches node %d\n", __func__, >+ (unsigned long)sys_addr, node_id); >+ return edac_mc_find(node_id); >+} >+ >+/* Return the base value defined by the DRAM Base register for the node >+ * represented by mci. This function returns the full 40-bit value despite >+ * the fact that the register only stores bits 39-24 of the value. See >+ * section 3.4.4.1. >+ */ >+static inline u64 get_dram_base(struct mem_ctl_info *mci) >+{ >+ struct k8_pvt *pvt; >+ >+ pvt = mci->pvt_info; >+ return ((u64) (pvt->dbr[pvt->mc_node_id] & 0xffff0000)) << 8; >+} >+ >+/* Obtain info from the DRAM Hole Address Register (section 3.4.8) for the >+ * node represented by mci. Info is passed back in *hole_base, *hole_offset, >+ * and *hole_size. Function returns 0 if info is valid or 1 if info is >+ * invalid. Info may be invalid for either of the following reasons: >+ * >+ * - The revision of the node is not E or greater. In this case, the DRAM >+ * Hole Address Register does not exist. >+ * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, >+ * indicating that its contents are not valid. >+ * >+ * The values passed back in *hole_base, *hole_offset, and *hole_size are >+ * complete 32-bit values despite the fact that the bitfields in the DHAR >+ * only represent bits 31-24 of the base and offset values. >+ */ >+static int get_dram_hole_info(struct mem_ctl_info *mci, u64 * hole_base, >+ u64 * hole_offset, u64 * hole_size) >+{ >+ struct k8_pvt *pvt; >+ u64 base; >+ >+ pvt = mci->pvt_info; >+ >+ /* >+ * Only Rev E and later have the DRAM Hole Address Register >+ */ >+ if (pvt->ext_model < OPTERON_CPU_REV_E) { >+ debugf1(" revision %d for node %d does not support DHAR\n", >+ pvt->ext_model, pvt->mc_node_id); >+ return 1; >+ } >+ >+ /* >+ * If the DRAM Hole Address Register is OFF, return that status >+ */ >+ if ((pvt->dhar & K8_DHAR_VALID) == 0) { >+ debugf1(" DramHoleValid bit cleared in DHAR for node %d\n", >+ pvt->mc_node_id); >+ return 1; /* DramHoleValid bit is cleared */ >+ } >+ >+ /* +------------------+--------------------+--------------------+----- >+ * | memory | DRAM hole | relocated | >+ * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | >+ * | | | DRAM hole | >+ * | | | [0x100000000, | >+ * | | | (0x100000000+ | >+ * | | | (0xffffffff-x))] | >+ * +------------------+--------------------+--------------------+----- >+ * >+ * Above is a diagram of physical memory showing the DRAM hole and the >+ * relocated addresses from the DRAM hole. As shown, the DRAM hole >+ * starts at address x (the base address) and extends through address >+ * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the >+ * addresses in the hole so that they start at 0x100000000. >+ */ >+ >+ base = pvt->dhar & K8_DHAR_BASE_MASK; >+ >+ *hole_base = base; >+ *hole_offset = (pvt->dhar & K8_DHAR_OFFSET_MASK) << 16; >+ *hole_size = (0x1ull << 32) - base; >+ >+ debugf1(" DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", >+ pvt->mc_node_id, (unsigned long)*hole_base, >+ (unsigned long)*hole_offset, (unsigned long)*hole_size); >+ >+ return 0; >+} >+ >+/* Return the DramAddr that the SysAddr given by sys_addr maps to. It is >+ * assumed that sys_addr maps to the node given by mci. >+ */ >+static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) >+{ >+ u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; >+ >+ /* The first part of section 3.4.4 (p. 70) shows how the DRAM Base >+ * (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are >+ * used to translate a SysAddr to a DramAddr. If the DRAM Hole >+ * Address Register (DHAR) is enabled, then it is also involved in >+ * translating a SysAddr to a DramAddr. Sections 3.4.8 and 3.5.8.2 >+ * describe the DHAR and how it is used for memory hoisting. These >+ * parts of the documentation are unclear. I interpret them as >+ * follows: >+ * >+ * When node n receives a SysAddr, it processes the SysAddr as >+ * follows: >+ * >+ * 1. It extracts the DRAMBase and DRAMLimit values from the >+ * DRAM Base and DRAM Limit registers for node n. If the >+ * SysAddr is not within the range specified by the base >+ * and limit values, then node n ignores the Sysaddr >+ * (since it does not map to node n). Otherwise continue >+ * to step 2 below. >+ * >+ * 2. If the DramHoleValid bit of the DHAR for node n is >+ * clear, the DHAR is disabled so skip to step 3 below. >+ * Otherwise see if the SysAddr is within the range of >+ * relocated addresses (starting at 0x100000000) from the >+ * DRAM hole. If not, skip to step 3 below. Else get the >+ * value of the DramHoleOffset field from the DHAR. To >+ * obtain the DramAddr, subtract the offset defined by >+ * this value from the SysAddr. >+ * >+ * 3. Obtain the base address for node n from the DRAMBase >+ * field of the DRAM Base register for node n. To obtain >+ * the DramAddr, subtract the base address from the >+ * SysAddr, as shown near the start of section 3.4.4 >+ * (p. 70). >+ */ >+ >+ dram_base = get_dram_base(mci); >+ >+ if (!get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size)) { >+ if ((sys_addr >= (1ull << 32)) && >+ (sys_addr < ((1ull << 32) + hole_size))) { >+ /* use DHAR to translate SysAddr to DramAddr */ >+ dram_addr = sys_addr - hole_offset; >+ debugf2("using DHAR to translate SysAddr 0x%lx to " >+ "DramAddr 0x%lx\n", >+ (unsigned long)sys_addr, >+ (unsigned long)dram_addr); >+ return dram_addr; >+ } >+ } >+ >+ /* Translate the SysAddr to a DramAddr as shown near the start of >+ * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 >+ * only deals with 40-bit values. Therefore we discard bits 63-40 of >+ * sys_addr below. If bit 39 of sys_addr is 1 then the bits we >+ * discard are all 1s. Otherwise the bits we discard are all 0s. See >+ * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture >+ * Programmer's Manual Volume 1 Application Programming. >+ */ >+ dram_addr = (sys_addr & 0xffffffffffull) - dram_base; >+ >+ debugf2("using DRAM Base register to translate SysAddr 0x%lx to " >+ "DramAddr 0x%lx\n", (unsigned long)sys_addr, >+ (unsigned long)dram_addr); >+ return dram_addr; >+} >+ >+/* Parameter intlv_en is the value of the IntlvEn field from a DRAM Base >+ * register (section 3.4.4.1). Return the number of bits from a SysAddr that >+ * are used for node interleaving. >+ */ >+static int num_node_interleave_bits(unsigned intlv_en) >+{ >+ static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; >+ int n; >+ >+ BUG_ON(intlv_en > 7); >+ n = intlv_shift_table[intlv_en]; >+ debugf2("using %d bits for node interleave\n", n); >+ return n; >+} >+ >+/* Translate the DramAddr given by dram_addr to an InputAddr and return the >+ * result. >+ */ >+static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) >+{ >+ struct k8_pvt *pvt; >+ int intlv_shift; >+ u64 input_addr; >+ >+ pvt = mci->pvt_info; >+ >+ /* Near the start of section 3.4.4 (p. 70), the k8 documentation gives >+ * instructions for translating a DramAddr to an InputAddr. Here we >+ * are following these instructions. >+ */ >+ intlv_shift = num_node_interleave_bits((pvt->dbr[0] >> 8) & 0x07); >+ input_addr = ((dram_addr >> intlv_shift) & 0xffffff000ull) + >+ (dram_addr & 0xfff); >+ >+ debugf2("DramAddr 0x%lx translates to InputAddr 0x%lx\n", >+ (unsigned long)dram_addr, (unsigned long)input_addr); >+ return input_addr; >+} >+ >+/* Translate the SysAddr represented by sys_addr to an InputAddr and return >+ * the result. It is assumed that sys_addr maps to the node given by mci. >+ */ >+static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) >+{ >+ u64 input_addr; >+ >+ input_addr = >+ dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); >+ debugf2("%s(): SysAdddr 0x%lx translates to InputAddr 0x%lx\n", >+ __func__, (unsigned long)sys_addr, (unsigned long)input_addr); >+ return input_addr; >+} >+ >+/* input_addr is an InputAddr associated with the node given by mci. Return >+ * the csrow that input_addr maps to, or -1 on failure (no csrow claims >+ * input_addr). >+ */ >+static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) >+{ >+ struct k8_pvt *pvt; >+ int csrow; >+ u64 base, mask; >+ >+ pvt = mci->pvt_info; >+ >+ /* Here we use the DRAM CS Base (section 3.5.4) and DRAM CS Mask >+ * (section 3.5.5) registers. For each CS base/mask register pair, >+ * test the condition shown near the start of section 3.5.4 (p. 84). >+ */ >+ >+ for (csrow = 0; csrow < K8_NR_CSROWS; csrow++) { >+ >+ if ((pvt->dcsb[csrow] & K8_DCSB_CS_ENABLE) == 0) { >+ debugf2("input_addr_to_csrow: CSBE bit is cleared " >+ "for csrow %d (node %d)\n", >+ csrow, pvt->mc_node_id); >+ continue; /* CSBE bit is cleared, no csrow */ >+ } >+ >+ /* Get the base addr and the mask values for this csrow */ >+ base = base_from_dcsb(pvt, csrow); >+ mask = ~mask_from_dcsm(pvt, csrow); >+ >+ if ((input_addr & mask) == (base & mask)) { >+ debugf2("InputAddr 0x%lx matches csrow %d " >+ "(MC node %d)\n", >+ (unsigned long)input_addr, >+ csrow, pvt->mc_node_id); >+ return csrow; /* success: csrow matches */ >+ } >+ } >+ >+ debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n", >+ (unsigned long)input_addr, pvt->mc_node_id); >+ return -1; /* failed to find matching csrow */ >+} >+ >+/* input_addr is an InputAddr associated with the node represented by mci. >+ * Translate input_addr to a DramAddr and return the result. >+ */ >+static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr) >+{ >+ struct k8_pvt *pvt; >+ int node_id, intlv_shift; >+ u64 bits, dram_addr; >+ u32 intlv_sel; >+ >+ /* Near the start of section 3.4.4 (p. 70), the k8 documentation shows >+ * how to translate a DramAddr to an InputAddr. Here we reverse this >+ * procedure. When translating from a DramAddr to an InputAddr, the >+ * bits used for node interleaving are discarded. Here we recover >+ * these bits from the IntlvSel field of the DRAM Limit register >+ * (section 3.4.4.2) for the node that input_addr is associated with. >+ */ >+ >+ pvt = mci->pvt_info; >+ node_id = pvt->mc_node_id; >+ BUG_ON((node_id < 0) || (node_id > 7)); >+ intlv_shift = num_node_interleave_bits((pvt->dbr[0] >> 8) & 0x07); >+ >+ if (intlv_shift == 0) { >+ debugf1(" node interleaving disabled:\n"); >+ debugf1(" InputAddr 0x%lx translates " >+ "to DramAddr of same value\n", >+ (unsigned long)input_addr); >+ return input_addr; >+ } >+ >+ bits = ((input_addr & 0xffffff000ull) << intlv_shift) + >+ (input_addr & 0xfff); >+ intlv_sel = pvt->dlr[node_id] & (((1 << intlv_shift) - 1) << 8); >+ dram_addr = bits + (intlv_sel << 4); >+ debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx " >+ "(%d node interleave bits)\n", (unsigned long)input_addr, >+ (unsigned long)dram_addr, intlv_shift); >+ return dram_addr; >+} >+ >+/* dram_addr is a DramAddr that maps to the node represented by mci. Convert >+ * dram_addr to a SysAddr and return the result. >+ */ >+static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr) >+{ >+ struct k8_pvt *pvt; >+ u64 hole_base, hole_offset, hole_size, base, limit, sys_addr; >+ >+ pvt = mci->pvt_info; >+ >+ if (!get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size)) { >+ if ((dram_addr >= hole_base) && >+ (dram_addr < (hole_base + hole_size))) { >+ /* use DHAR to translate DramAddr to SysAddr */ >+ sys_addr = dram_addr + hole_offset; >+ debugf1("using DHAR to translate DramAddr 0x%lx to " >+ "SysAddr 0x%lx\n", (unsigned long)dram_addr, >+ (unsigned long)sys_addr); >+ return sys_addr; >+ } >+ } >+ >+ get_base_and_limit(pvt, pvt->mc_node_id, &base, &limit); >+ sys_addr = dram_addr + base; >+ >+ /* The sys_addr we have computed up to this point is a 40-bit value >+ * because the k8 deals with 40-bit values. However, the value we are >+ * supposed to return is a full 64-bit physical address. The AMD >+ * x86-64 architecture specifies that the most significant implemented >+ * address bit through bit 63 of a physical address must be either all >+ * 0s or all 1s. Therefore we sign-extend the 40-bit sys_addr to a >+ * 64-bit value below. See section 3.4.2 of AMD publication 24592: >+ * AMD x86-64 Architecture Programmer's Manual Volume 1 Application >+ * Programming. >+ */ >+ sys_addr |= ~((sys_addr & (1ull << 39)) - 1); >+ >+ debugf1(" Using DRAM Base reg on node %d to translate\n", >+ pvt->mc_node_id); >+ debugf1(" DramAddr 0x%lx to SysAddr 0x%lx\n", >+ (unsigned long)dram_addr, (unsigned long)sys_addr); >+ return sys_addr; >+} >+ >+/* input_addr is an InputAddr associated with the node given by mci. >+ * Translate input_addr to a SysAddr and return the result. >+ */ >+static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci, >+ u64 input_addr) >+{ >+ return dram_addr_to_sys_addr(mci, >+ input_addr_to_dram_addr(mci, input_addr)); >+} >+ >+/* Find the minimum and maximum InputAddr values that map to the given csrow. >+ * Pass back these values in *input_addr_min and *input_addr_max. >+ */ >+static void find_csrow_limits(struct mem_ctl_info *mci, >+ int csrow, >+ u64 * input_addr_min, u64 * input_addr_max) >+{ >+ struct k8_pvt *pvt; >+ u64 base, mask; >+ >+ pvt = mci->pvt_info; >+ BUG_ON((csrow < 0) || (csrow >= K8_NR_CSROWS)); >+ >+ base = base_from_dcsb(pvt, csrow); >+ mask = mask_from_dcsm(pvt, csrow); >+ >+ *input_addr_min = base & ~mask; >+ *input_addr_max = base | mask | pvt->dcs_mask_notused; >+} >+ >+/* Extract error address from MCA NB Address Low (section 3.6.4.5) and >+ * MCA NB Address High (section 3.6.4.6) register values and return the >+ * result. >+ */ >+static inline u64 error_address_from_k8_error_info(struct k8_error_info *info) >+{ >+ return (((u64) (info->error_info.nbeah & 0xff)) << 32) + >+ (info->error_info.nbeal & ~0x03); >+} >+ >+static inline void error_address_to_page_and_offset(u64 error_address, >+ u32 * page, u32 * offset) >+{ >+ *page = (u32) (error_address >> PAGE_SHIFT); >+ *offset = ((u32) error_address) & ~PAGE_MASK; >+} >+ >+/* Return 1 if registers contain valid error information. Else return 0. */ >+static inline int k8_error_info_valid(struct k8_error_info_regs *regs) >+{ >+ return ((regs->nbsh & K8_NBSH_VALID_BIT) != 0); >+} >+ >+/* return 0 if regs contains valid error info; else return 1 */ >+static int k8_get_error_info_regs(struct mem_ctl_info *mci, >+ struct k8_error_info_regs *regs) >+{ >+ struct k8_pvt *pvt; >+ >+ pvt = mci->pvt_info; >+ pci_read_config_dword(pvt->misc_ctl, K8_NBSH, ®s->nbsh); >+ >+ if (!k8_error_info_valid(regs)) >+ return 1; >+ >+ pci_read_config_dword(pvt->misc_ctl, K8_NBSL, ®s->nbsl); >+ pci_read_config_dword(pvt->misc_ctl, K8_NBEAH, ®s->nbeah); >+ pci_read_config_dword(pvt->misc_ctl, K8_NBEAL, ®s->nbeal); >+ return 0; >+} >+ >+/* >+ * k8_get_error_info >+ * >+ * this function is called to retrieve the data from hardware >+ * and store it in the info structure and the nbcfg in the pvt >+ * area. >+ */ >+static void k8_get_error_info(struct mem_ctl_info *mci, >+ struct k8_error_info *info) >+{ >+ struct k8_pvt *pvt; >+ struct k8_error_info_regs regs; >+ >+ pvt = mci->pvt_info; >+ info->race_condition_detected = 0; >+ >+ if (k8_get_error_info_regs(mci, &info->error_info)) >+ return; >+ >+ /* >+ * Here's the problem with the K8's EDAC reporting: >+ * There are four registers which report pieces of error >+ * information. These four registers are shared between >+ * CEs and UEs. Furthermore, contrary to what is stated in >+ * the OBKG, the overflow bit is never used! Every error >+ * always updates the reporting registers. >+ * >+ * Can you see the race condition? All four error reporting >+ * registers must be read before a new error updates them! >+ * There is no way to read all four registers atomically. The >+ * best than can be done is to detect that a race has occured >+ * and then report the error without any kind of precision. >+ * >+ * What is still positive is that errors are >+ * still reported and thus problems can still be detected - >+ * just not localized because the syndrome and address are >+ * spread out across registers. >+ * >+ * Grrrrr!!!!! Here's hoping that AMD fixes this in some >+ * future K8 rev. UEs and CEs should have separate >+ * register sets with proper overflow bits that are used! >+ * At very least the problem can be fixed by honoring the >+ * ErrValid bit in nbsh and not updating registers - just >+ * set the overflow bit - unless the current error is CE >+ * and the new error is UE which would be the only situation >+ * for overwriting the current values. >+ */ >+ >+ regs = info->error_info; >+ >+ /* Use info from the second read - most current */ >+ if (unlikely(k8_get_error_info_regs(mci, &info->error_info))) >+ return; >+ >+ /* clear the error */ >+ pci_write_bits32(pvt->misc_ctl, K8_NBSH, 0, K8_NBSH_VALID_BIT); >+ >+ /* Get the North Bridge Configuration register, store in the >+ * private area >+ */ >+ pci_read_config_dword(pvt->misc_ctl, K8_NBCFG, &pvt->nbcfg); >+ >+ /* Check for the possible race condition */ >+ info->race_condition_detected = >+ ((regs.nbsh != info->error_info.nbsh) || >+ (regs.nbsl != info->error_info.nbsl) || >+ (regs.nbeah != info->error_info.nbeah) || >+ (regs.nbeal != info->error_info.nbeal)); >+} >+ >+static inline void decode_gart_tlb_error(struct mem_ctl_info *mci, >+ struct k8_error_info *info) >+{ >+ u32 err_code; >+ u32 ec_tt; /* error code transaction type (2b) */ >+ u32 ec_ll; /* error code cache level (2b) */ >+ >+ err_code = info->error_info.nbsl & 0xffffUL; >+ ec_tt = (err_code >> 2) & 0x03UL; >+ ec_ll = (err_code >> 0) & 0x03UL; >+ k8_mc_printk(mci, KERN_ERR, >+ "GART TLB errorr: transaction type(%s), " >+ "cache level(%s)\n", tt_msgs[ec_tt], ll_msgs[ec_ll]); >+} >+ >+static inline void decode_cache_error(struct mem_ctl_info *mci, >+ struct k8_error_info *info) >+{ >+ u32 err_code; >+ u32 ec_rrrr; /* error code memory transaction (4b) */ >+ u32 ec_tt; /* error code transaction type (2b) */ >+ u32 ec_ll; /* error code cache level (2b) */ >+ >+ err_code = info->error_info.nbsl & 0xffffUL; >+ ec_rrrr = (err_code >> 4) & 0x0fUL; >+ ec_tt = (err_code >> 2) & 0x03UL; >+ ec_ll = (err_code >> 0) & 0x03UL; >+ k8_mc_printk(mci, KERN_ERR, >+ "cache heirarchy error: memory transaction type(%s), " >+ "transaction type(%s), cache level(%s)\n", >+ memtt_msgs[ec_rrrr], tt_msgs[ec_tt], ll_msgs[ec_ll]); >+} >+ >+/* sys_addr is an error address (a SysAddr) extracted from the MCA NB Address >+ * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers >+ * of a node that detected an ECC memory error. mci represents the node that >+ * the error address maps to (possibly different from the node that detected >+ * the error). Return the number of the csrow that sys_addr maps to, or -1 on >+ * error. >+ */ >+static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) >+{ >+ int csrow; >+ >+ csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); >+ >+ if (csrow == -1) >+ k8_mc_printk(mci, KERN_ERR, >+ "Failed to translate InputAddr to csrow for " >+ "address 0x%lx\n", (unsigned long)sys_addr); >+ >+ return csrow; >+} >+ >+/* >+ * k8_handle_ce >+ * >+ * this routine is called to handle any Correctable Errors (CEs) >+ */ >+static void k8_handle_ce(struct mem_ctl_info *mci, struct k8_error_info *info) >+{ >+ struct k8_pvt *pvt; >+ unsigned syndrome; >+ u64 error_address; >+ u32 page, offset; >+ int channel, csrow; >+ struct mem_ctl_info *log_mci, *src_mci; >+ >+ log_mci = mci; >+ pvt = mci->pvt_info; >+ >+ if ((info->error_info.nbsh & BIT(26)) == 0) { >+ edac_mc_handle_ce_no_info(log_mci, EDAC_MOD_STR); >+ return; >+ } >+ >+ error_address = error_address_from_k8_error_info(info); >+ syndrome = ((info->error_info.nbsh >> 15) & 0xff); >+ >+ if (pvt->nbcfg & BIT(K8_NBCFG_CHIPKILL)) { >+ /* chipkill ecc mode */ >+ syndrome += (info->error_info.nbsl >> 16) & 0xff00; >+ channel = chan_from_chipkill_syndrome(syndrome); >+ >+ if (channel < 0) { >+ /* If the syndrome couldn't be found then the race >+ * condition for error reporting registers likely >+ * occurred. There's alot more in doubt than just the >+ * channel. Might as well just log the error without >+ * any info. >+ */ >+ k8_mc_printk(mci, KERN_WARNING, >+ "unknown syndrome 0x%x - possible error " >+ "reporting race\n", syndrome); >+ edac_mc_handle_ce_no_info(log_mci, EDAC_MOD_STR); >+ return; >+ } >+ } else { >+ /* non-chipkill ecc mode >+ * >+ * The k8 documentation is unclear about how to determine the >+ * channel number when using non-chipkill memory. This method >+ * was obtained from email communication with someone at AMD. >+ */ >+ channel = ((error_address & BIT(3)) != 0); >+ } >+ >+ /* Find out which node the error address belongs to. This may be >+ * different from the node that detected the error. >+ */ >+ src_mci = find_mc_by_sys_addr(mci, error_address); >+ if (src_mci == NULL) { >+ k8_mc_printk(mci, KERN_ERR, >+ "failed to map error address 0x%lx to a node\n", >+ (unsigned long)error_address); >+ edac_mc_handle_ce_no_info(log_mci, EDAC_MOD_STR); >+ return; >+ } >+ >+ log_mci = src_mci; >+ >+ csrow = sys_addr_to_csrow(log_mci, error_address); >+ if (csrow < 0) { >+ edac_mc_handle_ce_no_info(log_mci, EDAC_MOD_STR); >+ } else { >+ error_address_to_page_and_offset(error_address, &page, &offset); >+ edac_mc_handle_ce(log_mci, page, offset, syndrome, csrow, >+ channel, EDAC_MOD_STR); >+ } >+} >+ >+static void k8_handle_ue(struct mem_ctl_info *mci, struct k8_error_info *info) >+{ >+ int csrow; >+ u64 error_address; >+ u32 page, offset; >+ struct mem_ctl_info *log_mci, *src_mci; >+ >+ log_mci = mci; >+ >+ if ((info->error_info.nbsh & BIT(26)) == 0) { >+ edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR); >+ } >+ >+ error_address = error_address_from_k8_error_info(info); >+ >+ /* Find out which node the error address belongs to. This may be >+ * different from the node that detected the error. >+ */ >+ src_mci = find_mc_by_sys_addr(mci, error_address); >+ if (src_mci == NULL) { >+ k8_mc_printk(mci, KERN_ERR, >+ "failed to map error address 0x%lx to a node\n", >+ (unsigned long)error_address); >+ edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR); >+ return; >+ } >+ >+ log_mci = src_mci; >+ >+ csrow = sys_addr_to_csrow(log_mci, error_address); >+ if (csrow < 0) { >+ edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR); >+ } else { >+ error_address_to_page_and_offset(error_address, &page, &offset); >+ edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR); >+ } >+} >+ >+static void decode_bus_error(struct mem_ctl_info *mci, >+ struct k8_error_info *info) >+{ >+ u32 err_code, ext_ec; >+ u32 ec_pp; /* error code participating processor (2p) */ >+ u32 ec_to; /* error code timed out (1b) */ >+ u32 ec_rrrr; /* error code memory transaction (4b) */ >+ u32 ec_ii; /* error code memory or I/O (2b) */ >+ u32 ec_ll; /* error code cache level (2b) */ >+ >+ debugf0("MC%d: %s()\n", mci->mc_idx, __func__); >+ err_code = info->error_info.nbsl & 0xffffUL; >+ ec_pp = (err_code >> 9) & 0x03UL; >+ ec_to = (err_code >> 8) & 0x01UL; >+ ec_rrrr = (err_code >> 4) & 0x0fUL; >+ ec_ii = (err_code >> 2) & 0x03UL; >+ ec_ll = (err_code >> 0) & 0x03UL; >+ ext_ec = (info->error_info.nbsl >> 16) & 0xfUL; >+ >+ /* FIXME - these should report through EDAC channels */ >+ k8_mc_printk(mci, KERN_ERR, "general bus error: participating " >+ "processor(%s), time-out(%s) memory transaction " >+ "type(%s), mem or i/o(%s), cache level(%s)\n", >+ pp_msgs[ec_pp], to_msgs[ec_to], memtt_msgs[ec_rrrr], >+ ii_msgs[ec_ii], ll_msgs[ec_ll]); >+ >+ if (ec_pp & 0x02) >+ return; /* We aren't the node involved */ >+ >+ /* FIXME - other errors should have other error handling mechanisms */ >+ if (ext_ec && (ext_ec != 0x8)) { >+ k8_mc_printk(mci, KERN_ERR, >+ "no special error handling for this error\n"); >+ return; >+ } >+ >+ if (info->error_info.nbsh & BIT(14)) >+ k8_handle_ce(mci, info); >+ else if (info->error_info.nbsh & BIT(13)) >+ k8_handle_ue(mci, info); >+ >+ /* If main error is CE then overflow must be CE. If main error is UE >+ * then overflow is unknown. We'll call the overflow a CE - if >+ * panic_on_ue is set then we're already panic'ed and won't arrive >+ * here. If panic_on_ue is not set then apparently someone doesn't >+ * think that UE's are catastrophic. >+ */ >+ if (info->error_info.nbsh & BIT(30)) >+ edac_mc_handle_ce_no_info(mci, >+ EDAC_MOD_STR " Error Overflow set"); >+} >+ >+/* return 1 if error found or 0 if error not found */ >+static int k8_process_error_info(struct mem_ctl_info *mci, >+ struct k8_error_info *info, int handle_errors) >+{ >+ struct k8_pvt *pvt; >+ struct k8_error_info_regs *regs; >+ u32 err_code, ext_ec; >+ int gart_tlb_error; >+ >+ pvt = mci->pvt_info; >+ >+ /* check for an error */ >+ if (!k8_error_info_valid(&info->error_info)) >+ return 0; >+ >+ if (!handle_errors) >+ return 1; >+ >+ if (info->race_condition_detected) >+ k8_mc_printk(mci, KERN_WARNING, "race condition detected!\n"); >+ >+ gart_tlb_error = 0; >+ regs = &info->error_info; >+ err_code = info->error_info.nbsl & 0xffffUL; >+ ext_ec = (info->error_info.nbsl >> 16) & 0x0fUL; >+ debugf1("NorthBridge ERROR: mci(0x%p) MC node(%d) " >+ "ErrAddr(0x%.8x-%.8x) nbsh(0x%.8x) nbsl(0x%.8x)\n", >+ mci, pvt->mc_node_id, >+ regs->nbeah, regs->nbeal, regs->nbsh, regs->nbsl); >+ >+ if ((err_code & 0xfff0UL) == 0x0010UL) { >+ debugf1("GART TLB error\n"); >+ gart_tlb_error = 1; >+ decode_gart_tlb_error(mci, info); >+ } else if ((err_code & 0xff00UL) == 0x0100UL) { >+ debugf1("Cache error\n"); >+ decode_cache_error(mci, info); >+ } else if ((err_code & 0xf800UL) == 0x0800UL) { >+ debugf1("Bus error\n"); >+ decode_bus_error(mci, info); >+ } else >+ /* shouldn't reach here! */ >+ k8_mc_printk(mci, KERN_WARNING, >+ "%s(): unknown MCE error 0x%x\n", __func__, >+ err_code); >+ >+ k8_mc_printk(mci, KERN_ERR, "extended error code: %s\n", >+ ext_msgs[ext_ec]); >+ >+ if (((ext_ec >= 1 && ext_ec <= 4) || (ext_ec == 6)) && >+ ((info->error_info.nbsh >> 4) & 0x07UL)) >+ k8_mc_printk(mci, KERN_ERR, >+ "Error on hypertransport link: %s\n", >+ htlink_msgs[(info->error_info. >+ nbsh >> 4) & 0x07UL]); >+ >+ /* GART errors are benign as per AMD, do not panic on them */ >+ if (!gart_tlb_error && (regs->nbsh & BIT(29))) { >+ k8_mc_printk(mci, KERN_CRIT, "uncorrected error\n"); >+ edac_mc_handle_ue_no_info(mci, "UE bit is set\n"); >+ } >+ >+ if (regs->nbsh & BIT(25)) >+ k8_mc_printk(mci, KERN_CRIT, "processor context corrupt\n"); >+ >+ return 1; >+} >+ >+/* The main 'check' function to be called by the helper thread >+ * for this hardware. Get the error information, then process any errors >+ */ >+static void k8_check(struct mem_ctl_info *mci) >+{ >+ struct k8_error_info info; >+ >+ debugf3("%s()\n", __func__); >+ k8_get_error_info(mci, &info); >+ k8_process_error_info(mci, &info, 1); >+} >+ >+/* 'get' (reserve) the various PCI devices composing the memory controller >+ * which we need to retrieve data from in the processing of errors >+ */ >+static int k8_get_devs(struct mem_ctl_info *mci, int mc_type_index) >+{ >+ const struct k8_dev_type_info *k8_dev = &k8_mc_types[mc_type_index]; >+ struct k8_pvt *pvt; >+ struct pci_dev *pdev; >+ >+ pdev = to_pci_dev(mci->dev); >+ pvt = mci->pvt_info; >+ >+ /* The address mapping device provides a table that indicates which >+ * physical address ranges are owned by which node. Each node's >+ * memory controller has memory controller addresses that begin at >+ * 0x0. >+ */ >+ pvt->addr_map = pci_get_related_function(pdev->vendor, >+ k8_dev->addr_map, pdev); >+ >+ if (pvt->addr_map == NULL) { >+ k8_printk(KERN_ERR, "error address map device not found: " >+ "vendor %x device 0x%x (broken BIOS?)\n", >+ PCI_VENDOR_ID_AMD, k8_dev->addr_map); >+ return 1; >+ } >+ >+ debugf1("Addr Map device PCI Bus ID:\t%s\n", pci_name(pvt->addr_map)); >+ >+ pvt->misc_ctl = pci_get_related_function(pdev->vendor, >+ k8_dev->misc_ctl, pdev); >+ >+ if (pvt->misc_ctl == NULL) { >+ pci_dev_put(pvt->addr_map); >+ pvt->addr_map = NULL; >+ k8_printk(KERN_ERR, "error miscellaneous device not found: " >+ "vendor %x device 0x%x (broken BIOS?)\n", >+ PCI_VENDOR_ID_AMD, k8_dev->misc_ctl); >+ return 1; >+ } >+ >+ debugf1("Misc device PCI Bus ID:\t%s\n", pci_name(pvt->misc_ctl)); >+ >+ return 0; >+} >+ >+/* >+ * k8_get_mc_regs() >+ * >+ * Retrieve the hardware registers of the memory controller >+ * (this includes the 'Address Map' and 'Misc' device regs) >+ * and cache that data in the private data area for this instance >+ */ >+static void k8_get_mc_regs(struct mem_ctl_info *mci) >+{ >+ struct k8_pvt *pvt = mci->pvt_info; >+ struct pci_dev *pdev = to_pci_dev(mci->dev); >+ int i; >+ >+ debugf1("%s(MC node-id=%d): (ExtModel=%d) %s\n", >+ __func__, pvt->mc_node_id, pvt->ext_model, >+ (pvt->ext_model >= OPTERON_CPU_REV_F) ? >+ "Rev F or later" : "Rev E or earlier"); >+ >+ /* Retrieve the DRAM Base i'th and Limit i'th Registers */ >+ for (i = 0; i < MAX_K8_NODES; i++) { >+ pci_read_config_dword(pvt->addr_map, K8_DBR + (i * 8), >+ &pvt->dbr[i]); >+ pci_read_config_dword(pvt->addr_map, K8_DLR + (i * 8), >+ &pvt->dlr[i]); >+ >+ /* Only print out debug info on rows with both R and W >+ * Enabled. Normal processing, compiler should optimize >+ * this whole debug output block away >+ */ >+ if ((pvt->dbr[i] & 0x3) != 0) { >+ debugf1(" dbr[%d]: 0x%8.8x " >+ "BASE = 0x%10.10llx IntlvEn = %d %s %s\n", >+ i, pvt->dbr[i], >+ ((u64) (pvt->dbr[i] & 0xFFFF0000) << 8), >+ ((pvt->dbr[i] >> 7) & 0x7), >+ (pvt->dbr[i] & 0x2) ? "W" : "!W", >+ (pvt->dbr[i] & 0x1) ? "R" : "!R"); >+ debugf1(" dlr[%d]: 0x%8.8x " >+ "LIMIT= 0x%10.10llx IntlvSel= %d DstNode= %d\n", >+ i, pvt->dlr[i], >+ ((u64) (pvt->dlr[i] & 0xFFFF0000) << 8), >+ ((pvt->dlr[i] >> 7) & 0x7), pvt->dlr[i] & 0x3); >+ } >+ } >+ >+ /* Setup the DCSB and DCSM arrays from hardware */ >+ setup_dcsb_dcsm(pvt, pdev); >+ >+ /* Read in some misc, but important, registers */ >+ pci_read_config_dword(pvt->addr_map, K8_DHAR, &pvt->dhar); >+ pci_read_config_dword(pdev, K8_DBAM, &pvt->dbam); >+ pci_read_config_dword(pvt->misc_ctl, K8_NBCAP, &pvt->nbcap); >+ >+ debugf1(" dhar: 0x%x\n", pvt->dhar); >+ debugf1(" dbam: 0x%x\n", pvt->dbam); >+ debugf1(" dcl: 0x%x\n", pvt->dcl); >+ debugf1(" nbcap:0x%x\n", pvt->nbcap); >+} >+ >+/* >+ * dual_channel_active() >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * the DCL - DRAM Configuration Low Register contains various >+ * configuration bits on memory. >+ * >+ * BUT it is different between CG, D & E revs and the later >+ * Rev F memory controllers (DDR vs DDR2) >+ * >+ * Return: >+ * 1 if dual channel mode is active. >+ * 0 if single channel mode is active. >+ */ >+static int dual_channel_active(u32 dcl, int mc_device_index) >+{ >+ int flag; >+ int ext_model = node_rev(mc_device_index); >+ >+ if (ext_model >= OPTERON_CPU_REV_F) { >+ /* Rev F (NPT) and later */ >+ flag = (dcl >> 11) & 0x1; >+ } else { >+ /* Rev E and earlier */ >+ flag = (dcl >> 16) & 0x1; >+ } >+ >+ return flag; >+} >+ >+/* >+ * csrow_nr_pages() >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * Input: >+ * csrow_nr ChipSelect Row Number (0..K8_NR_CSROWS-1) >+ * k8 private pointer to --> >+ * DRAM Bank Address mapping register >+ * node_id >+ * DCL register where dual_channel_active is >+ * >+ * The DBAM register consists of 4 sets of 4 bits each definitions: >+ * >+ * Bits: CSROWs >+ * 0-3 CSROWs 0 and 1 >+ * 4-7 CSROWs 2 and 3 >+ * 8-11 CSROWs 4 and 5 >+ * 12-15 CSROWs 6 and 7 >+ * >+ * Values range from: 0 to 15 >+ * The meanings of the values depends on CPU REV and dual-channel state >+ * >+ * Various CPU revisions have different size definitions for this >+ * 4 bit values. >+ * Therefore, we need to examine which CPU we are running on to >+ * extract meaning for these bits: >+ * >+ * REV CG and earlier have a given DIMM size definition >+ * REV D & REV E share another set of size definitions >+ * >+ * REV CG, D and E all have a common beginning set of size definitions, >+ * but change the meanings midway into the table. >+ * >+ * REV F has yet another set of size definitions, which has a totally >+ * different starting point. >+ * >+ * REV ? future revs? We won't know till we get the specs >+ * >+ * The memory controller provides for total of only 8 CSROWs in its >+ * current architecture. Each "pair" of CSROWs normally represents >+ * just one (1) * DIMM in single channel or just two (2) DIMMs >+ * in dual channel mode. >+ * >+ * The following code logic collapses the various tables for CSROW >+ * based on CPU Rev number. >+ * See the tables/algorithms in the respective BKDG manuals. >+ * >+ * return: >+ * The number of PAGE_SIZE pages on the specified CSROW number >+ * This number incompasses >+ * >+ */ >+static u32 csrow_nr_pages(int csrow_nr, struct k8_pvt *pvt) >+{ >+ u32 shift; >+ u32 nr_pages; >+ int ext_model = pvt->ext_model; >+ >+ /* The math on this doesn't look right on the surface because x/2*4 >+ * can be simplified to x*2 but this expression makes use of the fact >+ * that it is integral math where 1/2=0. This intermediate value >+ * becomes the number of bits to shift the DBAM register to extract >+ * the proper CSROW field. >+ */ >+ shift = (pvt->dbam >> ((csrow_nr / 2) * 4)) & 0xF; /* PG88 */ >+ >+ debugf0(" %s(csrow=%d) DBAM index= %d\n", __func__, csrow_nr, shift); >+ >+ /* First step is to calc the number of bits to shift a value of 1 >+ * left to indicate show many pages. Start with the DBAM value >+ * as the starting bits, then proceed to adjust those shift >+ * bits, based on CPU REV and the table. See BKDG on the DBAM >+ */ >+ if (ext_model >= OPTERON_CPU_REV_F) { >+ /* 27 shift, is 128Mib minimum DIMM size in REV F and later >+ * upto 8 Gb, in a step function progression >+ */ >+ static u32 rev_f_shift[] = { 27, 28, 29, 29, 29, 30, 30, 31, >+ 31, 32, 32, 33, 0, 0, 0, 0 >+ }; >+ >+ /* REV F and greater section */ >+ nr_pages = 1 << (rev_f_shift[shift] - PAGE_SHIFT); >+ >+ } else { >+ /* REV E and less section This line is tricky. >+ * It collapses the table used by revision D and later to one >+ * that matches revision CG and earlier >+ */ >+ shift -= (ext_model >= OPTERON_CPU_REV_D) ? >+ (shift > 8 ? 4 : (shift > 5 ? 3 : (shift > 2 ? 1 : 0))) : 0; >+ >+ /* 25 shift, is 32MiB minimum DIMM size in REV E and prior >+ */ >+ nr_pages = 1 << (shift + 25 - PAGE_SHIFT); >+ } >+ >+ /* If dual channel then double the memory size of single channel */ >+ nr_pages <<= dual_channel_active(pvt->dcl, pvt->mc_node_id); >+ >+ debugf0(" nr_pages= %u dual_channel_active = %d\n", >+ nr_pages, dual_channel_active(pvt->dcl, pvt->mc_node_id)); >+ >+ return nr_pages; >+} >+ >+/* >+ * determine_parity_enabled() >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * determine if Parity is Enabled >+ */ >+static int determine_parity_enabled(struct k8_pvt *pvt) >+{ >+ int rc = 0; >+ >+ if (pvt->ext_model >= OPTERON_CPU_REV_F) { >+ if (pvt->dcl & BIT(8)) >+ rc = 1; >+ } >+ >+ return rc; >+} >+ >+/* >+ * determine_memory_type() >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * determine the memory type in operation on this controller >+ */ >+static enum mem_type determine_memory_type(struct k8_pvt *pvt) >+{ >+ enum mem_type type; >+ >+ if (pvt->ext_model >= OPTERON_CPU_REV_F) { >+ /* Rev F and later */ >+ type = ((pvt->dcl >> 16) & 0x1) ? MEM_DDR2 : MEM_RDDR2; >+ } else { >+ /* Rev E and earlier */ >+ type = ((pvt->dcl >> 18) & 0x1) ? MEM_DDR : MEM_RDDR; >+ } >+ >+ debugf1(" Memory type is: %s\n", >+ (type == MEM_DDR2) ? "MEM_DDR2" : >+ (type == MEM_RDDR2) ? "MEM_RDDR2" : >+ (type == MEM_DDR) ? "MEM_DDR" : "MEM_RDDR"); >+ >+ return type; >+} >+ >+/* >+ * determine_dram_type() >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * determine the DRAM type in operation >+ * There are K8_NR_CSROWS (8) and 2 CSROWS per DIMM, therefore >+ * there are 4 Logical DIMMs possible, thus 4 bits in the >+ * configuration register indicating whether there are >+ * X4 or X8 devices, one per logical DIMM >+ */ >+static enum dev_type determine_dram_type(struct k8_pvt *pvt, int row) >+{ >+ int bit; >+ enum dev_type type; >+ >+ /* the starting bit depends on Revision value */ >+ bit = (pvt->ext_model >= OPTERON_CPU_REV_F) ? 12 : 20; >+ type = ((pvt->dcl >> (bit + (row / 2))) & 0x01) ? DEV_X4 : DEV_X8; >+ >+ debugf1(" DRAM type is: %s\n", (type == DEV_X4) ? "DEV-x4" : "DEV-x8"); >+ >+ return type; >+} >+ >+/* >+ * determine_edac_cap() >+ * >+ * NOTE: CPU Revision Dependent code >+ * >+ * determine if the DIMMs have ECC enabled >+ * ECC is enabled ONLY if all the DIMMs are ECC capable >+ */ >+static enum edac_type determine_edac_cap(struct k8_pvt *pvt) >+{ >+ int bit; >+ enum dev_type edac_cap = EDAC_NONE; >+ >+ bit = (pvt->ext_model >= OPTERON_CPU_REV_F) ? 19 : 17; >+ if ((pvt->dcl >> bit) & 0x1) { >+ debugf1(" edac_type is: EDAC_FLAG_SECDED\n"); >+ edac_cap = EDAC_FLAG_SECDED; >+ } >+ >+ return edac_cap; >+} >+ >+/* >+ * k8_init_csrows >+ * >+ * perform initialization on the array of csrow attribute instances >+ */ >+static int k8_init_csrows(struct mem_ctl_info *mci) >+{ >+ struct csrow_info *csrow; >+ struct k8_pvt *pvt; >+ int i; >+ int empty = 1; /* start out assume the rows are empty */ >+ u64 input_addr_min, input_addr_max, sys_addr; >+ >+ pvt = mci->pvt_info; >+ >+ /* read the North Bridge Configuration reg */ >+ pci_read_config_dword(pvt->misc_ctl, K8_NBCFG, &pvt->nbcfg); >+ >+ /* Iterate over the rows, looking for a valid set */ >+ for (i = 0; i < K8_NR_CSROWS; i++) { >+ csrow = &mci->csrows[i]; >+ >+ /* Check the 'CS Enable' bit of this instance */ >+ if ((pvt->dcsb[i] & K8_DCSB_CS_ENABLE) == 0) { >+ debugf1("csrow %d empty for node %d\n", i, >+ pvt->mc_node_id); >+ continue; /* empty */ >+ } >+ >+ debugf1("csrow %d valid for MC node %d\n", i, pvt->mc_node_id); >+ >+ empty = 0; >+ csrow->nr_pages = csrow_nr_pages(i, pvt); >+ find_csrow_limits(mci, i, &input_addr_min, &input_addr_max); >+ sys_addr = input_addr_to_sys_addr(mci, input_addr_min); >+ csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT); >+ sys_addr = input_addr_to_sys_addr(mci, input_addr_max); >+ csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT); >+ csrow->page_mask = ~mask_from_dcsm(pvt, i); >+ csrow->grain = 8; /* 8 bytes of resolution */ >+ >+ csrow->mtype = determine_memory_type(pvt); >+ csrow->dtype = determine_dram_type(pvt, i); >+ >+ debugf1(" for MC node %d csrow %d:\n", pvt->mc_node_id, i); >+ debugf1(" input_addr_min: 0x%lx input_addr_max: 0x%lx\n", >+ (unsigned long)input_addr_min, >+ (unsigned long)input_addr_max); >+ debugf1(" sys_addr: 0x%lx page_mask: 0x%lx\n", >+ (unsigned long)sys_addr, csrow->page_mask); >+ debugf1(" nr_pages: %u first_page: 0x%lx " >+ "last_page: 0x%lx\n", >+ (unsigned)csrow->nr_pages, >+ csrow->first_page, csrow->last_page); >+ >+ if (pvt->nbcfg & BIT(K8_NBCFG_ECC_ENABLE)) >+ csrow->edac_mode = >+ (pvt->nbcfg & BIT(K8_NBCFG_CHIPKILL)) ? >+ EDAC_S4ECD4ED : EDAC_SECDED; >+ else >+ csrow->edac_mode = EDAC_NONE; >+ } >+ >+ return empty; >+} >+ >+/* >+ * k8_enable_error_reporting() >+ * >+ * enable the hardware to generated and detect error reporting >+ */ >+static void k8_enable_error_reporting(struct mem_ctl_info *mci) >+{ >+ struct k8_pvt *pvt; >+ u32 mc4ctl_l = 0, mc4ctl_h = 0, mcgctl_l = 0, mcgctl_h = 0; >+ >+ pvt = mci->pvt_info; >+ pci_write_bits32(pvt->misc_ctl, K8_NBCTL, 0x3UL, 0x3UL); >+ do_rdmsr(pvt->mc_node_id, K8_MSR_MC4CTL, &mc4ctl_l, &mc4ctl_h); >+ mc4ctl_l |= BIT(0) | BIT(1); >+ do_wrmsr(pvt->mc_node_id, K8_MSR_MC4CTL, mc4ctl_l, mc4ctl_h); >+ do_rdmsr(pvt->mc_node_id, K8_MSR_MC4CTL, &mc4ctl_l, &mc4ctl_h); >+ do_rdmsr(pvt->mc_node_id, K8_MSR_MCGCTL, &mcgctl_l, &mcgctl_h); >+ mcgctl_l |= BIT(4); >+ do_wrmsr(pvt->mc_node_id, K8_MSR_MCGCTL, mcgctl_l, mcgctl_h); >+ do_rdmsr(pvt->mc_node_id, K8_MSR_MCGCTL, &mcgctl_l, &mcgctl_h); >+} >+ >+/* >+ * get_top_mem_regs() >+ */ >+static void get_top_mem_regs(struct k8_pvt *pvt) >+{ >+ u32 low, high; >+ >+ debugf0("%s()\n", __func__); >+ do_rdmsr(pvt->mc_node_id, K8_MSR_TOP_MEM, &low, &high); >+ pvt->top_mem = ((u64) high << 32) | low; >+ debugf0(" TOP_MEM= 0x%08x-%08x\n", high, low); >+ >+ do_rdmsr(pvt->mc_node_id, K8_MSR_TOP_MEM2, &low, &high); >+ pvt->top_mem2 = ((u64) high << 32) | low; >+ debugf0(" TOP_MEM2= 0x%08x-%08x\n", high, low); >+} >+ >+/* >+ * k8_probe1() >+ * >+ * probe function to determine if there is a DRAM Controller >+ * device is there and to construct data tables >+ * and read in data from those HW regs >+ */ >+static int k8_probe1(struct pci_dev *pdev, int mc_type_index) >+{ >+ struct mem_ctl_info *mci; >+ struct k8_pvt *pvt; >+ u32 dcl, dual_channel; >+ int parity_enable; >+ int node_id; >+ >+ /* get the index of this MC */ >+ node_id = get_mc_node_id_from_pdev(pdev); >+ >+ debugf0("%s(mc_type_index=%d)\n", __func__, mc_type_index); >+ debugf0("DRAM MEM-CTL PCI Bus ID: 0000:%02u:%02x.%x\n", >+ pdev->bus->number, >+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); >+ >+ /* Determine CPU Rev of each CPU. On >+ * Rev E and earlier we have to do the CPUID instruction. >+ * Rev F and later we could just do a dword read from >+ * Function 3 Offset FCh >+ * TODO: Fix this function to do just that somehow >+ */ >+ build_node_revision_table(); >+ >+ /* We need to determine if we are single or dual channel operation >+ * and use that information for calculating the size of the dynamic >+ * instance tables in the 'mci' structure >+ */ >+ pci_read_config_dword(pdev, K8_DCL, &dcl); >+ dual_channel = dual_channel_active(dcl, node_id); >+ >+ mci = edac_mc_alloc(sizeof(*pvt), K8_NR_CSROWS, >+ dual_channel + 1, node_id); >+ if (mci == NULL) >+ return -ENOMEM; >+ >+ /* start filling in the mci data */ >+ mci->dev = &pdev->dev; >+ >+ /* fill in the private data as well */ >+ pvt = mci->pvt_info; >+ pvt->dcl = dcl; >+ pvt->mc_node_id = node_id; >+ pvt->ext_model = node_rev(pvt->mc_node_id); >+ >+ /* Reserved the other NB devices we use */ >+ if (k8_get_devs(mci, mc_type_index)) >+ goto fail0; >+ >+ /* Retrieve TOP_MEM and TOP_MEM2 */ >+ get_top_mem_regs(pvt); >+ >+ /* Retrieve the hardware registers, and cache them in the private area, >+ * for this instance of memory controller >+ */ >+ k8_get_mc_regs(mci); >+ >+ /* Initialize state */ >+ mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; >+ mci->edac_ctl_cap = EDAC_FLAG_NONE; >+ mci->edac_cap = EDAC_FLAG_NONE; >+ >+ /* Exam the capabilities of the northbridge */ >+ if (pvt->nbcap & BIT(K8_NBCAP_SECDED)) >+ mci->edac_ctl_cap |= EDAC_FLAG_SECDED; >+ >+ if (pvt->nbcap & BIT(K8_NBCAP_CHIPKILL)) >+ mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; >+ >+ mci->edac_cap = determine_edac_cap(pvt); >+ if (mci->edac_cap & EDAC_FLAG_SECDED) { >+ >+/* >+ * NOT Correct: >+ * TODO: Fix >+ if (dual_channel) { >+ debugf1(" DUAL-Channel: setting EDAC_FLAG_S4ECD4ED\n"); >+ mci->edac_cap |= EDAC_FLAG_S4ECD4ED; >+ } >+ */ >+ } >+ >+ parity_enable = determine_parity_enabled(pvt); >+ debugf1(" Parity is %s\n", parity_enable ? "Enabled" : "Disabled"); >+ >+ mci->mod_name = EDAC_MOD_STR; >+ mci->mod_ver = EDAC_K8_VERSION; >+ mci->ctl_name = k8_mc_types[mc_type_index].ctl_name; >+ mci->dev_name = pci_name(pdev); >+ mci->edac_check = k8_check; >+ mci->ctl_page_to_phys = NULL; >+ >+ /* memory scrubber interface */ >+ mci->set_sdram_scrub_rate = set_sdram_scrub_rate; >+ mci->get_sdram_scrub_rate = get_sdram_scrub_rate; >+ >+ /* Now initialize control structures for all CSROWs present */ >+ if (k8_init_csrows(mci)) { >+ debugf1("Setting mci->edac_cap to EDAC_FLAG_NONE because\n"); >+ debugf1(" k8_init_csrows() returned NO csrows found\n"); >+ mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */ >+ } else { >+ /* Enable the ECC reporting hardware */ >+ k8_enable_error_reporting(mci); >+ } >+ >+ /* Finalize the registration with the EDAC Helper CORE */ >+ if (edac_mc_add_mc(mci)) { >+ debugf1("%s(): failed edac_mc_add_mc()\n", __func__); >+ /* FIXME: perhaps some code should go here that disables error >+ * reporting if we just enabled it >+ */ >+ goto fail1; >+ } >+ >+ /* allocating ONE generic PCI control info */ >+ if (!k8_ctl_pci) { >+ k8_ctl_pci = edac_pci_create_generic_ctl(&pdev->dev, >+ EDAC_MOD_STR); >+ if (!k8_ctl_pci) { >+ printk(KERN_WARNING >+ "%s(): Unable to create PCI control\n", >+ __func__); >+ printk(KERN_WARNING >+ "%s(): PCI error report via EDAC not setup\n", >+ __func__); >+ } >+ } >+ >+ debugf1("%s(): success\n", __func__); >+ return 0; >+ >+fail1: >+ pci_dev_put(pvt->addr_map); >+ pci_dev_put(pvt->misc_ctl); >+ >+fail0: >+ edac_mc_free(mci); >+ return -ENODEV; >+} >+ >+/* >+ * k8_init_one >+ * initialize just one device >+ * >+ * returns count (>= 0), or negative on error >+ */ >+static int __devinit k8_init_one(struct pci_dev *pdev, >+ const struct pci_device_id *mc_type) >+{ >+ debugf0("%s(MC node=%d,mc_type='%s')\n", >+ __func__, >+ get_mc_node_id_from_pdev(pdev), >+ k8_mc_types[mc_type->driver_data].ctl_name); >+ >+ /* wake up and enable device */ >+ return pci_enable_device(pdev) ? -EIO : >+ k8_probe1(pdev, mc_type->driver_data); >+} >+ >+/* >+ * k8_remove_one >+ * >+ * remove just one device >+ */ >+static void __devexit k8_remove_one(struct pci_dev *pdev) >+{ >+ struct mem_ctl_info *mci; >+ struct k8_pvt *pvt; >+ >+ debugf0("%s()\n", __func__); >+ >+ mci = edac_mc_del_mc(&pdev->dev); >+ if (mci == NULL) >+ return; >+ >+ pvt = mci->pvt_info; >+ pci_dev_put(pvt->addr_map); >+ pci_dev_put(pvt->misc_ctl); >+ edac_mc_free(mci); >+} >+ >+/* >+ * The 'pci_device_id' table. >+ * The PCI help functions walk this table and call the >+ * '.probe' function of the 'pci_driver' table, for each >+ * instance in this table >+ */ >+static const struct pci_device_id k8_pci_tbl[] __devinitdata = { >+ {PCI_VEND_DEV(AMD, K8_NB_MEMCTL), PCI_ANY_ID, PCI_ANY_ID, >+ 0, 0, K8_CPUS}, >+ {0, } /* 0 terminated list. */ >+}; >+ >+MODULE_DEVICE_TABLE(pci, k8_pci_tbl); >+ >+/* >+ * The 'pci_driver' structure to define the name, probe and removal >+ * functions >+ */ >+static struct pci_driver k8_driver = { >+ .name = EDAC_MOD_STR, >+ .probe = k8_init_one, >+ .remove = __devexit_p(k8_remove_one), >+ .id_table = k8_pci_tbl, >+}; >+ >+/* >+ * edac_opstate_setup() >+ * ensure OP state is valid >+ */ >+static void edac_opstate_setup(void) >+{ >+ /* make sure error reporting method is sane */ >+ switch (edac_op_state) { >+ case EDAC_OPSTATE_POLL: >+ case EDAC_OPSTATE_NMI: >+ break; >+ default: >+ edac_op_state = EDAC_OPSTATE_POLL; >+ break; >+ } >+} >+ >+static int __init k8_init(void) >+{ >+ /* setup the OPSTATE */ >+ edac_opstate_setup(); >+ >+ return pci_register_driver(&k8_driver); >+} >+ >+static void __exit k8_exit(void) >+{ >+ /* release the pci control structure */ >+ if (k8_ctl_pci) >+ edac_pci_release_generic_ctl(k8_ctl_pci); >+ >+ pci_unregister_driver(&k8_driver); >+} >+ >+module_init(k8_init); >+module_exit(k8_exit); >+ >+MODULE_LICENSE("GPL"); >+MODULE_AUTHOR("Linux Networx (http://lnxi.com) " >+ "Thayne Harbaugh, Doug Thompson, Dave Peterson"); >+MODULE_DESCRIPTION("MC support for AMD K8 memory controllers - " >+ EDAC_K8_VERSION); >diff -urN linux-2.6.24.3.x86_64/drivers/edac/Kconfig kernel-2.6.24.3-work/drivers/edac/Kconfig >--- linux-2.6.24.3.x86_64/drivers/edac/Kconfig 2008-01-24 17:58:37.000000000 -0500 >+++ kernel-2.6.24.3-work/drivers/edac/Kconfig 2008-03-15 19:22:01.000000000 -0400 >@@ -109,6 +109,13 @@ > Support for error detection and correction on the Intel > 82860 chipset. > >+config EDAC_K8 >+ tristate "AMD K8 (Opteron, Athlon64)" >+ depends on EDAC_MM_EDAC && X86 && PCI >+ help >+ Support for error detection and correction on the AMD >+ K8 Memory Controller >+ > config EDAC_R82600 > tristate "Radisys 82600 embedded chipset" > depends on EDAC_MM_EDAC && PCI && X86_32 >diff -urN linux-2.6.24.3.x86_64/drivers/edac/Makefile kernel-2.6.24.3-work/drivers/edac/Makefile >--- linux-2.6.24.3.x86_64/drivers/edac/Makefile 2008-01-24 17:58:37.000000000 -0500 >+++ kernel-2.6.24.3-work/drivers/edac/Makefile 2008-03-15 19:22:01.000000000 -0400 >@@ -26,6 +26,7 @@ > obj-$(CONFIG_EDAC_I82975X) += i82975x_edac.o > obj-$(CONFIG_EDAC_I3000) += i3000_edac.o > obj-$(CONFIG_EDAC_I82860) += i82860_edac.o >+obj-$(CONFIG_EDAC_K8) += k8_edac.o > obj-$(CONFIG_EDAC_R82600) += r82600_edac.o > obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 438471
:
299808
|
299809
|
299810
| 299811 |
299812