amd64_edac: Erratum #637 workaround
Borislav Petkov [Wed, 30 Mar 2011 13:42:10 +0000 (15:42 +0200)]
F15h CPUs may report a non-DRAM address when reporting an error address
belonging to a CC6 state save area. Add a workaround to detect this
condition and compute the actual DRAM address of the error as documented
in the Revision Guide for AMD Family 15h Models 00h-0Fh Processors.

Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>

drivers/edac/amd64_edac.c
drivers/edac/amd64_edac.h

index e17de90..9a8bebc 100644 (file)
@@ -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)
index 0110930..9a666cb 100644 (file)
 
 #define DCT_CFG_SEL                    0x10C
 
+#define DRAM_LOCAL_NODE_BASE           0x120
 #define DRAM_LOCAL_NODE_LIM            0x124
 
 #define DRAM_BASE_HI                   0x140