diff options
Diffstat (limited to 'drivers/edac/amd64_edac.c')
-rw-r--r-- | drivers/edac/amd64_edac.c | 52 |
1 files changed, 50 insertions, 2 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index e17de90ba6c6..9a8bebcf6b17 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -931,15 +931,63 @@ static int k8_early_channel_count(struct amd64_pvt *pvt) /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ static u64 get_error_address(struct mce *m) { + struct cpuinfo_x86 *c = &boot_cpu_data; + u64 addr; u8 start_bit = 1; u8 end_bit = 47; - if (boot_cpu_data.x86 == 0xf) { + if (c->x86 == 0xf) { start_bit = 3; end_bit = 39; } - return m->addr & GENMASK(start_bit, end_bit); + addr = m->addr & GENMASK(start_bit, end_bit); + + /* + * Erratum 637 workaround + */ + if (c->x86 == 0x15) { + struct amd64_pvt *pvt; + u64 cc6_base, tmp_addr; + u32 tmp; + u8 mce_nid, intlv_en; + + if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7) + return addr; + + mce_nid = amd_get_nb_id(m->extcpu); + pvt = mcis[mce_nid]->pvt_info; + + amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); + intlv_en = tmp >> 21 & 0x7; + + /* add [47:27] + 3 trailing bits */ + cc6_base = (tmp & GENMASK(0, 20)) << 3; + + /* reverse and add DramIntlvEn */ + cc6_base |= intlv_en ^ 0x7; + + /* pin at [47:24] */ + cc6_base <<= 24; + + if (!intlv_en) + return cc6_base | (addr & GENMASK(0, 23)); + + amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); + + /* faster log2 */ + tmp_addr = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1); + + /* OR DramIntlvSel into bits [14:12] */ + tmp_addr |= (tmp & GENMASK(21, 23)) >> 9; + + /* add remaining [11:0] bits from original MC4_ADDR */ + tmp_addr |= addr & GENMASK(0, 11); + + return cc6_base | tmp_addr; + } + + return addr; } static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) |