cregit-Linux how code gets into the kernel

Release 4.11 drivers/edac/amd64_edac.c

Directory: drivers/edac
#include "amd64_edac.h"
#include <asm/amd_nb.h>


static struct edac_pci_ctl_info *pci_ctl;


static int report_gart_errors;
module_param(report_gart_errors, int, 0644);

/*
 * Set by command line parameter. If BIOS has enabled the ECC, this override is
 * cleared to prevent re-enabling the hardware by this driver.
 */

static int ecc_enable_override;
module_param(ecc_enable_override, int, 0644);


static struct msr __percpu *msrs;

/* Per-node stuff */

static struct ecc_settings **ecc_stngs;

/*
 * 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.
 */

static const struct scrubrate {
       
u32 scrubval;           /* bit pattern for scrub rate */
       
u32 bandwidth;          /* bandwidth consumed (bytes/sec) */
} 
scrubrates[] = {
	{ 0x01, 1600000000UL},
	{ 0x02, 800000000UL},
	{ 0x03, 400000000UL},
	{ 0x04, 200000000UL},
	{ 0x05, 100000000UL},
	{ 0x06, 50000000UL},
	{ 0x07, 25000000UL},
	{ 0x08, 12284069UL},
	{ 0x09, 6274509UL},
	{ 0x0A, 3121951UL},
	{ 0x0B, 1560975UL},
	{ 0x0C, 781440UL},
	{ 0x0D, 390720UL},
	{ 0x0E, 195300UL},
	{ 0x0F, 97650UL},
	{ 0x10, 48854UL},
	{ 0x11, 24427UL},
	{ 0x12, 12213UL},
	{ 0x13, 6101UL},
	{ 0x14, 3051UL},
	{ 0x15, 1523UL},
	{ 0x16, 761UL},
	{ 0x00, 0UL},        /* scrubbing off */
};


int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, u32 *val, const char *func) { int err = 0; err = pci_read_config_dword(pdev, offset, val); if (err) amd64_warn("%s: error reading F%dx%03x.\n", func, PCI_FUNC(pdev->devfn), offset); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov61100.00%1100.00%
Total61100.00%1100.00%


int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, u32 val, const char *func) { int err = 0; err = pci_write_config_dword(pdev, offset, val); if (err) amd64_warn("%s: error writing to F%dx%03x.\n", func, PCI_FUNC(pdev->devfn), offset); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov60100.00%1100.00%
Total60100.00%1100.00%

/* * Select DCT to which PCI cfg accesses are routed */
static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct) { u32 reg = 0; amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, &reg); reg &= (pvt->model == 0x30) ? ~3 : ~1; reg |= dct; amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg); }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov4979.03%133.33%
Aravind Gopalakrishnan1320.97%266.67%
Total62100.00%3100.00%

/* * * Depending on the family, F2 DCT reads need special handling: * * K8: has a single DCT only and no address offsets >= 0x100 * * F10h: each DCT has its own set of regs * DCT0 -> F2x040.. * DCT1 -> F2x140.. * * F16h: has only 1 DCT * * F15h: we select which DCT we access using F1x10C[DctCfgSel] */
static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, int offset, u32 *val) { switch (pvt->fam) { case 0xf: if (dct || offset >= 0x100) return -EINVAL; break; case 0x10: if (dct) { /* * Note: If ganging is enabled, barring the regs * F2x[1,0]98 and F2x[1,0]9C; reads reads to F2x1xx * return 0. (cf. Section 2.8.1 F10h BKDG) */ if (dct_ganging_enabled(pvt)) return 0; offset += 0x100; } break; case 0x15: /* * F15h: F2x1xx addresses do not map explicitly to DCT1. * We should select which DCT we access using F1x10C[DctCfgSel] */ dct = (dct && pvt->model == 0x30) ? 3 : dct; f15h_select_dct(pvt, dct); break; case 0x16: if (dct) return -EINVAL; break; default: break; } return amd64_read_pci_cfg(pvt->F2, offset, val); }

Contributors

PersonTokensPropCommitsCommitProp
Aravind Gopalakrishnan8467.20%250.00%
Borislav Petkov4132.80%250.00%
Total125100.00%4100.00%

/* * Memory scrubber control interface. For K8, memory scrubbing is handled by * hardware and can involve L2 cache, dcache as well as the main memory. With * F10, this is extended to L3 cache scrubbing on CPU models sporting that * functionality. * * This causes the "units" for the scrubbing speed to vary from 64 byte blocks * (dram) over to cache lines. This is nasty, so we will use bandwidth in * bytes/sec for the setting. * * Currently, we only do dram scrubbing. If the scrubbing is done in software on * other archs, we might not have access to the caches directly. */
static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval) { /* * Fam17h supports scrub values between 0x5 and 0x14. Also, the values * are shifted down by 0x5, so scrubval 0x5 is written to the register * as 0x0, scrubval 0x6 as 0x1, etc. */ if (scrubval >= 0x5 && scrubval <= 0x14) { scrubval -= 0x5; pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF); pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1); } else { pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1); } }

Contributors

PersonTokensPropCommitsCommitProp
Yazen Ghannam74100.00%1100.00%
Total74100.00%1100.00%

/* * Scan the scrub rate mapping table for a close or matching bandwidth value to * issue. If requested is too big, then use last maximum value found. */
static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) { u32 scrubval; int i; /* * map the configured rate (new_bw) to a value specific to the AMD64 * memory controller and apply to register. Search for the first * bandwidth entry that is greater or equal than the setting requested * and program that. If at last entry, turn off DRAM scrubbing. * * If no suitable bandwidth is found, turn off DRAM scrubbing entirely * by falling back to the last element in scrubrates[]. */ for (i = 0; i < ARRAY_SIZE(scrubrates) - 1; i++) { /* * skip scrub rates which aren't recommended * (see F10 BKDG, F3x58) */ if (scrubrates[i].scrubval < min_rate) continue; if (scrubrates[i].bandwidth <= new_bw) break; } scrubval = scrubrates[i].scrubval; if (pvt->fam == 0x17) { __f17h_set_scrubval(pvt, scrubval); } else if (pvt->fam == 0x15 && pvt->model == 0x60) { f15h_select_dct(pvt, 0); pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); f15h_select_dct(pvt, 1); pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); } else { pci_write_bits32(pvt->F3, SCRCTRL, scrubval, 0x001F); } if (scrubval) return scrubrates[i].bandwidth; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson8244.81%112.50%
Aravind Gopalakrishnan6434.97%112.50%
Yazen Ghannam189.84%112.50%
Borislav Petkov168.74%450.00%
Andrew Morton31.64%112.50%
Total183100.00%8100.00%


static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) { struct amd64_pvt *pvt = mci->pvt_info; u32 min_scrubrate = 0x5; if (pvt->fam == 0xf) min_scrubrate = 0x0; if (pvt->fam == 0x15) { /* Erratum #505 */ if (pvt->model < 0x10) f15h_select_dct(pvt, 0); if (pvt->model == 0x60) min_scrubrate = 0x6; } return __set_scrub_rate(pvt, bw, min_scrubrate); }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov4146.59%770.00%
Doug Thompson2831.82%110.00%
Aravind Gopalakrishnan1921.59%220.00%
Total88100.00%10100.00%


static int get_scrub_rate(struct mem_ctl_info *mci) { struct amd64_pvt *pvt = mci->pvt_info; int i, retval = -EINVAL; u32 scrubval = 0; switch (pvt->fam) { case 0x15: /* Erratum #505 */ if (pvt->model < 0x10) f15h_select_dct(pvt, 0); if (pvt->model == 0x60) amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); break; case 0x17: amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval); if (scrubval & BIT(0)) { amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval); scrubval &= 0xF; scrubval += 0x5; } else { scrubval = 0; } break; default: amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); break; } scrubval = scrubval & 0x001F; for (i = 0; i < ARRAY_SIZE(scrubrates); i++) { if (scrubrates[i].scrubval == scrubval) { retval = scrubrates[i].bandwidth; break; } } return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson7839.20%17.69%
Yazen Ghannam6532.66%17.69%
Aravind Gopalakrishnan2814.07%215.38%
Borislav Petkov2613.07%861.54%
Roel Kluin21.01%17.69%
Total199100.00%13100.00%

/* * returns true if the SysAddr given by sys_addr matches the * DRAM base/limit associated with node_id */
static bool base_limit_match(struct amd64_pvt *pvt, u64 sys_addr, u8 nid) { u64 addr; /* 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 >= get_dram_base(pvt, nid)) && (addr <= get_dram_limit(pvt, nid))); }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson3465.38%125.00%
Borislav Petkov1732.69%250.00%
Daniel J Blueman11.92%125.00%
Total52100.00%4100.00%

/* * 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 amd64_pvt *pvt; u8 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 = dram_intlv_en(pvt, 0); if (intlv_en == 0) { for (node_id = 0; node_id < DRAM_RANGES; node_id++) { if (base_limit_match(pvt, sys_addr, node_id)) goto found; } goto err_no_match; } if (unlikely((intlv_en != 0x01) && (intlv_en != 0x03) && (intlv_en != 0x07))) { amd64_warn("DRAM Base[IntlvEn] junk value: 0x%x, BIOS bug?\n", intlv_en); return NULL; } bits = (((u32) sys_addr) >> 12) & intlv_en; for (node_id = 0; ; ) { if ((dram_intlv_sel(pvt, node_id) & intlv_en) == bits) break; /* intlv_sel field matches */ if (++node_id >= DRAM_RANGES) goto err_no_match; } /* sanity test for sys_addr */ if (unlikely(!base_limit_match(pvt, sys_addr, node_id))) { amd64_warn("%s: sys_addr 0x%llx falls outside base/limit address" "range for node %d with node interleaving enabled.\n", __func__, sys_addr, node_id); return NULL; } found: return edac_mc_find((int)node_id); err_no_match: edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n", (unsigned long)sys_addr); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson20587.23%112.50%
Borislav Petkov2611.06%562.50%
Joe Perches31.28%112.50%
Daniel J Blueman10.43%112.50%
Total235100.00%8100.00%

/* * compute the CS base address of the @csrow on the DRAM controller @dct. * For details see F2x[5C:40] in the processor's BKDG */
static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, u64 *base, u64 *mask) { u64 csbase, csmask, base_bits, mask_bits; u8 addr_shift; if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { csbase = pvt->csels[dct].csbases[csrow]; csmask = pvt->csels[dct].csmasks[csrow]; base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); addr_shift = 4; /* * F16h and F15h, models 30h and later need two addr_shift values: * 8 for high and 6 for low (cf. F16h BKDG). */ } else if (pvt->fam == 0x16 || (pvt->fam == 0x15 && pvt->model >= 0x30)) { csbase = pvt->csels[dct].csbases[csrow]; csmask = pvt->csels[dct].csmasks[csrow >> 1]; *base = (csbase & GENMASK_ULL(15, 5)) << 6; *base |= (csbase & GENMASK_ULL(30, 19)) << 8; *mask = ~0ULL; /* poke holes for the csmask */ *mask &= ~((GENMASK_ULL(15, 5) << 6) | (GENMASK_ULL(30, 19) << 8)); *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; return; } else { csbase = pvt->csels[dct].csbases[csrow]; csmask = pvt->csels[dct].csmasks[csrow >> 1]; addr_shift = 8; if (pvt->fam == 0x15) base_bits = mask_bits = GENMASK_ULL(30,19) | GENMASK_ULL(13,5); else base_bits = mask_bits = GENMASK_ULL(28,19) | GENMASK_ULL(13,5); } *base = (csbase & base_bits) << addr_shift; *mask = ~0ULL; /* poke holes for the csmask */ *mask &= ~(mask_bits << addr_shift); /* OR them in */ *mask |= (csmask & mask_bits) << addr_shift; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov17243.54%233.33%
Aravind Gopalakrishnan14135.70%233.33%
Chen Gong4210.63%116.67%
Doug Thompson4010.13%116.67%
Total395100.00%6100.00%

#define for_each_chip_select(i, dct, pvt) \ for (i = 0; i < pvt->csels[dct].b_cnt; i++) #define chip_select_base(i, dct, pvt) \ pvt->csels[dct].csbases[i] #define for_each_chip_select_mask(i, dct, pvt) \ for (i = 0; i < pvt->csels[dct].m_cnt; i++) /* * @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 amd64_pvt *pvt; int csrow; u64 base, mask; pvt = mci->pvt_info; for_each_chip_select(csrow, 0, pvt) { if (!csrow_enabled(csrow, 0, pvt)) continue; get_cs_base_and_mask(pvt, csrow, 0, &base, &mask); mask = ~mask; if ((input_addr & mask) == (base & mask)) { edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n", (unsigned long)input_addr, csrow, pvt->mc_node_id); return csrow; } } edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n", (unsigned long)input_addr, pvt->mc_node_id); return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson10577.78%133.33%
Borislav Petkov2417.78%133.33%
Joe Perches64.44%133.33%
Total135100.00%3100.00%

/* * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) * 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. */
int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, u64 *hole_offset, u64 *hole_size) { struct amd64_pvt *pvt = mci->pvt_info; /* only revE and later have the DRAM Hole Address Register */ if (pvt->fam == 0xf && pvt->ext_model < K8_REV_E) { edac_dbg(1, " revision %d for node %d does not support DHAR\n", pvt->ext_model, pvt->mc_node_id); return 1; } /* valid for Fam10h and above */ if (pvt->fam >= 0x10 && !dhar_mem_hoist_valid(pvt)) { edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n"); return 1; } if (!dhar_valid(pvt)) { edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n", pvt->mc_node_id); return 1; } /* This node has Memory Hoisting */ /* +------------------+--------------------+--------------------+----- * | 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. */ *hole_base = dhar_base(pvt); *hole_size = (1ULL << 32) - *hole_base; *hole_offset = (pvt->fam > 0xf) ? f10_dhar_offset(pvt) : k8_dhar_offset(pvt); edac_dbg(1, " 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; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson15277.95%114.29%
Borislav Petkov3115.90%571.43%
Joe Perches126.15%114.29%
Total195100.00%7100.00%

EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); /* * 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. * * 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). */
static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) { struct amd64_pvt *pvt = mci->pvt_info; u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; int ret; dram_base = get_dram_base(pvt, pvt->mc_node_id); ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); if (!ret) { if ((sys_addr >= (1ULL << 32)) && (sys_addr < ((1ULL << 32) + hole_size))) { /* use DHAR to translate SysAddr to DramAddr */ dram_addr = sys_addr - hole_offset; edac_dbg(2, "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 & GENMASK_ULL(39, 0)) - dram_base; edac_dbg(2, "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; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson13681.93%116.67%
Borislav Petkov1911.45%350.00%
Joe Perches84.82%116.67%
Chen Gong31.81%116.67%
Total166100.00%6100.00%

/* * @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]; return n; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson53100.00%1100.00%
Total53100.00%1100.00%

/* Translate the DramAddr given by @dram_addr to an InputAddr. */
static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) { struct amd64_pvt *pvt; int intlv_shift; u64 input_addr; pvt = mci->pvt_info; /* * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) * concerning translating a DramAddr to an InputAddr. */ intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + (dram_addr & 0xfff); edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", intlv_shift, (unsigned long)dram_addr, (unsigned long)input_addr); return input_addr; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson7885.71%120.00%
Borislav Petkov77.69%240.00%
Chen Gong33.30%120.00%
Joe Perches33.30%120.00%
Total91100.00%5100.00%

/* * Translate the SysAddr represented by @sys_addr to an InputAddr. 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)); edac_dbg(2, "SysAddr 0x%lx translates to InputAddr 0x%lx\n", (unsigned long)sys_addr, (unsigned long)input_addr); return input_addr; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson4992.45%133.33%
Joe Perches35.66%133.33%
Masanari Iida11.89%133.33%
Total53100.00%3100.00%

/* Map the Error address to a PAGE and PAGE OFFSET. */
static inline void error_address_to_page_and_offset(u64 error_address, struct err_info *err) { err->page = (u32) (error_address >> PAGE_SHIFT); err->offset = ((u32) error_address) & ~PAGE_MASK; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson3583.33%150.00%
Borislav Petkov716.67%150.00%
Total42100.00%2100.00%

/* * @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) amd64_mc_err(mci, "Failed to translate InputAddr to csrow for " "address 0x%lx\n", (unsigned long)sys_addr); return csrow; }

Contributors

PersonTokensPropCommitsCommitProp
Doug Thompson5498.18%150.00%
Borislav Petkov11.82%150.00%
Total55100.00%2100.00%

static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); /* * Determine if the DIMMs have ECC enabled. ECC is enabled ONLY if all the DIMMs * are ECC capable. */
static unsigned long determine_edac_cap(struct amd64_pvt *pvt) { unsigned long edac_cap = EDAC_FLAG_NONE; u8 bit; if (pvt->umc) { u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0; for (i = 0; i < NUM_UMCS; i++) { if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT)) continue; umc_en_mask |= BIT(i); /* UMC Configuration bit 12 (DimmEccEn) */ if (pvt->umc[i].umc_cfg & BIT(12)) dimm_ecc_en_mask |= BIT(i); } if (umc_en_mask == dimm_ecc_en_mask) edac_cap = EDAC_FLAG_SECDED; } else { bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) ? 19 : 17; if (pvt->dclr0 & BIT(bit)) edac_cap = EDAC_FLAG_SECDED; } return edac_cap; }

Contributors

PersonTokensPropCommitsCommitProp
Yazen Ghannam9863.64%114.29%
Doug Thompson4529.22%114.29%
Borislav Petkov74.55%457.14%
Dan Carpenter42.60%114.29%
Total154100.00%7100.00%

static void debug_display_dimm_sizes(struct amd64_pvt *, u8);
static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) { edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr); if (pvt->dram_type == MEM_LRDDR3) { u32 dcsm = pvt->csels[chan].csmasks[0]; /* * It's assumed all LRDIMMs in a DCT are going to be of * same 'type' until proven otherwise. So, use a cs * value of '0' here to get dcsm value. */ edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3)); } edac_dbg(1, "All DIMMs support ECC:%s\n", (dclr & BIT(19)) ? "yes" : "no"); edac_dbg(1, " PAR/ERR parity: %s\n", (dclr & BIT(8)) ? "enabled" : "disabled"); if (pvt->fam == 0x10) edac_dbg(1, " DCT 128bit mode width: %s\n", (dclr & BIT(11)) ? "128b" : "64b"); edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n", (dclr & BIT(12)) ? "yes" : "no", (dclr & BIT(13)) ? "yes" : "no", (dclr & BIT(14)) ? "yes" : "no", (