109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27ee40b89SJason Baron /* 37ee40b89SJason Baron * Intel E3-1200 47ee40b89SJason Baron * Copyright (C) 2014 Jason Baron <jbaron@akamai.com> 57ee40b89SJason Baron * 67ee40b89SJason Baron * Support for the E3-1200 processor family. Heavily based on previous 77ee40b89SJason Baron * Intel EDAC drivers. 87ee40b89SJason Baron * 97ee40b89SJason Baron * Since the DRAM controller is on the cpu chip, we can use its PCI device 107ee40b89SJason Baron * id to identify these processors. 117ee40b89SJason Baron * 127ee40b89SJason Baron * PCI DRAM controller device ids (Taken from The PCI ID Repository - http://pci-ids.ucw.cz/) 137ee40b89SJason Baron * 147ee40b89SJason Baron * 0108: Xeon E3-1200 Processor Family DRAM Controller 157ee40b89SJason Baron * 010c: Xeon E3-1200/2nd Generation Core Processor Family DRAM Controller 167ee40b89SJason Baron * 0150: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller 177ee40b89SJason Baron * 0158: Xeon E3-1200 v2/Ivy Bridge DRAM Controller 187ee40b89SJason Baron * 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller 197ee40b89SJason Baron * 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller 207ee40b89SJason Baron * 0c08: Xeon E3-1200 v3 Processor DRAM Controller 21953dee9bSJason Baron * 1918: Xeon E3-1200 v5 Skylake Host Bridge/DRAM Registers 227103de0eSJason Baron * 5918: Xeon E3-1200 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers 23c452a9d3SMarco Elver * 3e..: 8th/9th Gen Core Processor Host Bridge/DRAM Registers 247ee40b89SJason Baron * 257ee40b89SJason Baron * Based on Intel specification: 267ee40b89SJason Baron * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf 277ee40b89SJason Baron * http://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200-family-vol-2-datasheet.html 287103de0eSJason Baron * http://www.intel.com/content/www/us/en/processors/core/7th-gen-core-family-mobile-h-processor-lines-datasheet-vol-2.html 29c452a9d3SMarco Elver * https://www.intel.com/content/www/us/en/products/docs/processors/core/8th-gen-core-family-datasheet-vol-2.html 307ee40b89SJason Baron * 317ee40b89SJason Baron * According to the above datasheet (p.16): 327ee40b89SJason Baron * " 337ee40b89SJason Baron * 6. Software must not access B0/D0/F0 32-bit memory-mapped registers with 347ee40b89SJason Baron * requests that cross a DW boundary. 357ee40b89SJason Baron * " 367ee40b89SJason Baron * 377ee40b89SJason Baron * Thus, we make use of the explicit: lo_hi_readq(), which breaks the readq into 387ee40b89SJason Baron * 2 readl() calls. This restriction may be lifted in subsequent chip releases, 397ee40b89SJason Baron * but lo_hi_readq() ensures that we are safe across all e3-1200 processors. 407ee40b89SJason Baron */ 417ee40b89SJason Baron 427ee40b89SJason Baron #include <linux/module.h> 437ee40b89SJason Baron #include <linux/init.h> 447ee40b89SJason Baron #include <linux/pci.h> 457ee40b89SJason Baron #include <linux/pci_ids.h> 467ee40b89SJason Baron #include <linux/edac.h> 477ee40b89SJason Baron 482f8e2c87SChristoph Hellwig #include <linux/io-64-nonatomic-lo-hi.h> 4978d88e8aSMauro Carvalho Chehab #include "edac_module.h" 507ee40b89SJason Baron 517ee40b89SJason Baron #define EDAC_MOD_STR "ie31200_edac" 527ee40b89SJason Baron 537ee40b89SJason Baron #define ie31200_printk(level, fmt, arg...) \ 547ee40b89SJason Baron edac_printk(level, "ie31200", fmt, ##arg) 557ee40b89SJason Baron 567ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_1 0x0108 577ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_2 0x010c 587ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_3 0x0150 597ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_4 0x0158 607ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c 617ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04 627ee40b89SJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08 63953dee9bSJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_8 0x1918 647103de0eSJason Baron #define PCI_DEVICE_ID_INTEL_IE31200_HB_9 0x5918 657ee40b89SJason Baron 66c452a9d3SMarco Elver /* Coffee Lake-S */ 67c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK 0x3e00 68c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_1 0x3e0f 69c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_2 0x3e18 70c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_3 0x3e1f 71c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_4 0x3e30 72c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_5 0x3e31 73c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_6 0x3e32 74c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_7 0x3e33 75c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_8 0x3ec2 76c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_9 0x3ec6 77c452a9d3SMarco Elver #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10 0x3eca 78c452a9d3SMarco Elver 79c452a9d3SMarco Elver /* Test if HB is for Skylake or later. */ 80c452a9d3SMarco Elver #define DEVICE_ID_SKYLAKE_OR_LATER(did) \ 81c452a9d3SMarco Elver (((did) == PCI_DEVICE_ID_INTEL_IE31200_HB_8) || \ 82c452a9d3SMarco Elver ((did) == PCI_DEVICE_ID_INTEL_IE31200_HB_9) || \ 83c452a9d3SMarco Elver (((did) & PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK) == \ 84c452a9d3SMarco Elver PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK)) 85c452a9d3SMarco Elver 867ee40b89SJason Baron #define IE31200_DIMMS 4 877ee40b89SJason Baron #define IE31200_RANKS 8 887ee40b89SJason Baron #define IE31200_RANKS_PER_CHANNEL 4 897ee40b89SJason Baron #define IE31200_DIMMS_PER_CHANNEL 2 907ee40b89SJason Baron #define IE31200_CHANNELS 2 917ee40b89SJason Baron 927ee40b89SJason Baron /* Intel IE31200 register addresses - device 0 function 0 - DRAM Controller */ 937ee40b89SJason Baron #define IE31200_MCHBAR_LOW 0x48 947ee40b89SJason Baron #define IE31200_MCHBAR_HIGH 0x4c 957ee40b89SJason Baron #define IE31200_MCHBAR_MASK GENMASK_ULL(38, 15) 967ee40b89SJason Baron #define IE31200_MMR_WINDOW_SIZE BIT(15) 977ee40b89SJason Baron 987ee40b89SJason Baron /* 997ee40b89SJason Baron * Error Status Register (16b) 1007ee40b89SJason Baron * 1017ee40b89SJason Baron * 15 reserved 1027ee40b89SJason Baron * 14 Isochronous TBWRR Run Behind FIFO Full 1037ee40b89SJason Baron * (ITCV) 1047ee40b89SJason Baron * 13 Isochronous TBWRR Run Behind FIFO Put 1057ee40b89SJason Baron * (ITSTV) 1067ee40b89SJason Baron * 12 reserved 1077ee40b89SJason Baron * 11 MCH Thermal Sensor Event 1087ee40b89SJason Baron * for SMI/SCI/SERR (GTSE) 1097ee40b89SJason Baron * 10 reserved 1107ee40b89SJason Baron * 9 LOCK to non-DRAM Memory Flag (LCKF) 1117ee40b89SJason Baron * 8 reserved 1127ee40b89SJason Baron * 7 DRAM Throttle Flag (DTF) 1137ee40b89SJason Baron * 6:2 reserved 1147ee40b89SJason Baron * 1 Multi-bit DRAM ECC Error Flag (DMERR) 1157ee40b89SJason Baron * 0 Single-bit DRAM ECC Error Flag (DSERR) 1167ee40b89SJason Baron */ 1177ee40b89SJason Baron #define IE31200_ERRSTS 0xc8 1187ee40b89SJason Baron #define IE31200_ERRSTS_UE BIT(1) 1197ee40b89SJason Baron #define IE31200_ERRSTS_CE BIT(0) 1207ee40b89SJason Baron #define IE31200_ERRSTS_BITS (IE31200_ERRSTS_UE | IE31200_ERRSTS_CE) 1217ee40b89SJason Baron 1227ee40b89SJason Baron /* 1237ee40b89SJason Baron * Channel 0 ECC Error Log (64b) 1247ee40b89SJason Baron * 1257ee40b89SJason Baron * 63:48 Error Column Address (ERRCOL) 1267ee40b89SJason Baron * 47:32 Error Row Address (ERRROW) 1277ee40b89SJason Baron * 31:29 Error Bank Address (ERRBANK) 1287ee40b89SJason Baron * 28:27 Error Rank Address (ERRRANK) 1297ee40b89SJason Baron * 26:24 reserved 1307ee40b89SJason Baron * 23:16 Error Syndrome (ERRSYND) 1317ee40b89SJason Baron * 15: 2 reserved 1327ee40b89SJason Baron * 1 Multiple Bit Error Status (MERRSTS) 1337ee40b89SJason Baron * 0 Correctable Error Status (CERRSTS) 1347ee40b89SJason Baron */ 135953dee9bSJason Baron 1367ee40b89SJason Baron #define IE31200_C0ECCERRLOG 0x40c8 1377ee40b89SJason Baron #define IE31200_C1ECCERRLOG 0x44c8 138953dee9bSJason Baron #define IE31200_C0ECCERRLOG_SKL 0x4048 139953dee9bSJason Baron #define IE31200_C1ECCERRLOG_SKL 0x4448 1407ee40b89SJason Baron #define IE31200_ECCERRLOG_CE BIT(0) 1417ee40b89SJason Baron #define IE31200_ECCERRLOG_UE BIT(1) 1427ee40b89SJason Baron #define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27) 1437ee40b89SJason Baron #define IE31200_ECCERRLOG_RANK_SHIFT 27 1447ee40b89SJason Baron #define IE31200_ECCERRLOG_SYNDROME_BITS GENMASK_ULL(23, 16) 1457ee40b89SJason Baron #define IE31200_ECCERRLOG_SYNDROME_SHIFT 16 1467ee40b89SJason Baron 1477ee40b89SJason Baron #define IE31200_ECCERRLOG_SYNDROME(log) \ 1487ee40b89SJason Baron ((log & IE31200_ECCERRLOG_SYNDROME_BITS) >> \ 1497ee40b89SJason Baron IE31200_ECCERRLOG_SYNDROME_SHIFT) 1507ee40b89SJason Baron 1517ee40b89SJason Baron #define IE31200_CAPID0 0xe4 1527ee40b89SJason Baron #define IE31200_CAPID0_PDCD BIT(4) 1537ee40b89SJason Baron #define IE31200_CAPID0_DDPCD BIT(6) 1547ee40b89SJason Baron #define IE31200_CAPID0_ECC BIT(1) 1557ee40b89SJason Baron 1567ee40b89SJason Baron #define IE31200_MAD_DIMM_0_OFFSET 0x5004 157953dee9bSJason Baron #define IE31200_MAD_DIMM_0_OFFSET_SKL 0x500C 1587ee40b89SJason Baron #define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) 1597ee40b89SJason Baron #define IE31200_MAD_DIMM_A_RANK BIT(17) 160953dee9bSJason Baron #define IE31200_MAD_DIMM_A_RANK_SHIFT 17 161953dee9bSJason Baron #define IE31200_MAD_DIMM_A_RANK_SKL BIT(10) 162953dee9bSJason Baron #define IE31200_MAD_DIMM_A_RANK_SKL_SHIFT 10 1637ee40b89SJason Baron #define IE31200_MAD_DIMM_A_WIDTH BIT(19) 164953dee9bSJason Baron #define IE31200_MAD_DIMM_A_WIDTH_SHIFT 19 165953dee9bSJason Baron #define IE31200_MAD_DIMM_A_WIDTH_SKL GENMASK_ULL(9, 8) 166953dee9bSJason Baron #define IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT 8 1677ee40b89SJason Baron 168953dee9bSJason Baron /* Skylake reports 1GB increments, everything else is 256MB */ 169953dee9bSJason Baron #define IE31200_PAGES(n, skl) \ 170953dee9bSJason Baron (n << (28 + (2 * skl) - PAGE_SHIFT)) 1717ee40b89SJason Baron 1727ee40b89SJason Baron static int nr_channels; 173709ed1bcSJason Baron static struct pci_dev *mci_pdev; 174709ed1bcSJason Baron static int ie31200_registered = 1; 1757ee40b89SJason Baron 1767ee40b89SJason Baron struct ie31200_priv { 1777ee40b89SJason Baron void __iomem *window; 178953dee9bSJason Baron void __iomem *c0errlog; 179953dee9bSJason Baron void __iomem *c1errlog; 1807ee40b89SJason Baron }; 1817ee40b89SJason Baron 1827ee40b89SJason Baron enum ie31200_chips { 1837ee40b89SJason Baron IE31200 = 0, 1847ee40b89SJason Baron }; 1857ee40b89SJason Baron 1867ee40b89SJason Baron struct ie31200_dev_info { 1877ee40b89SJason Baron const char *ctl_name; 1887ee40b89SJason Baron }; 1897ee40b89SJason Baron 1907ee40b89SJason Baron struct ie31200_error_info { 1917ee40b89SJason Baron u16 errsts; 1927ee40b89SJason Baron u16 errsts2; 1937ee40b89SJason Baron u64 eccerrlog[IE31200_CHANNELS]; 1947ee40b89SJason Baron }; 1957ee40b89SJason Baron 1967ee40b89SJason Baron static const struct ie31200_dev_info ie31200_devs[] = { 1977ee40b89SJason Baron [IE31200] = { 1987ee40b89SJason Baron .ctl_name = "IE31200" 1997ee40b89SJason Baron }, 2007ee40b89SJason Baron }; 2017ee40b89SJason Baron 2027ee40b89SJason Baron struct dimm_data { 203953dee9bSJason Baron u8 size; /* in multiples of 256MB, except Skylake is 1GB */ 2047ee40b89SJason Baron u8 dual_rank : 1, 205953dee9bSJason Baron x16_width : 2; /* 0 means x8 width */ 2067ee40b89SJason Baron }; 2077ee40b89SJason Baron 2087ee40b89SJason Baron static int how_many_channels(struct pci_dev *pdev) 2097ee40b89SJason Baron { 2107ee40b89SJason Baron int n_channels; 2117ee40b89SJason Baron unsigned char capid0_2b; /* 2nd byte of CAPID0 */ 2127ee40b89SJason Baron 2137ee40b89SJason Baron pci_read_config_byte(pdev, IE31200_CAPID0 + 1, &capid0_2b); 2147ee40b89SJason Baron 2157ee40b89SJason Baron /* check PDCD: Dual Channel Disable */ 2167ee40b89SJason Baron if (capid0_2b & IE31200_CAPID0_PDCD) { 2177ee40b89SJason Baron edac_dbg(0, "In single channel mode\n"); 2187ee40b89SJason Baron n_channels = 1; 2197ee40b89SJason Baron } else { 2207ee40b89SJason Baron edac_dbg(0, "In dual channel mode\n"); 2217ee40b89SJason Baron n_channels = 2; 2227ee40b89SJason Baron } 2237ee40b89SJason Baron 2247ee40b89SJason Baron /* check DDPCD - check if both channels are filled */ 2257ee40b89SJason Baron if (capid0_2b & IE31200_CAPID0_DDPCD) 2267ee40b89SJason Baron edac_dbg(0, "2 DIMMS per channel disabled\n"); 2277ee40b89SJason Baron else 2287ee40b89SJason Baron edac_dbg(0, "2 DIMMS per channel enabled\n"); 2297ee40b89SJason Baron 2307ee40b89SJason Baron return n_channels; 2317ee40b89SJason Baron } 2327ee40b89SJason Baron 2337ee40b89SJason Baron static bool ecc_capable(struct pci_dev *pdev) 2347ee40b89SJason Baron { 2357ee40b89SJason Baron unsigned char capid0_4b; /* 4th byte of CAPID0 */ 2367ee40b89SJason Baron 2377ee40b89SJason Baron pci_read_config_byte(pdev, IE31200_CAPID0 + 3, &capid0_4b); 2387ee40b89SJason Baron if (capid0_4b & IE31200_CAPID0_ECC) 2397ee40b89SJason Baron return false; 2407ee40b89SJason Baron return true; 2417ee40b89SJason Baron } 2427ee40b89SJason Baron 243953dee9bSJason Baron static int eccerrlog_row(u64 log) 2447ee40b89SJason Baron { 245953dee9bSJason Baron return ((log & IE31200_ECCERRLOG_RANK_BITS) >> 2467ee40b89SJason Baron IE31200_ECCERRLOG_RANK_SHIFT); 2477ee40b89SJason Baron } 2487ee40b89SJason Baron 2497ee40b89SJason Baron static void ie31200_clear_error_info(struct mem_ctl_info *mci) 2507ee40b89SJason Baron { 2517ee40b89SJason Baron /* 2527ee40b89SJason Baron * Clear any error bits. 2537ee40b89SJason Baron * (Yes, we really clear bits by writing 1 to them.) 2547ee40b89SJason Baron */ 2557ee40b89SJason Baron pci_write_bits16(to_pci_dev(mci->pdev), IE31200_ERRSTS, 2567ee40b89SJason Baron IE31200_ERRSTS_BITS, IE31200_ERRSTS_BITS); 2577ee40b89SJason Baron } 2587ee40b89SJason Baron 2597ee40b89SJason Baron static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, 2607ee40b89SJason Baron struct ie31200_error_info *info) 2617ee40b89SJason Baron { 2627ee40b89SJason Baron struct pci_dev *pdev; 2637ee40b89SJason Baron struct ie31200_priv *priv = mci->pvt_info; 2647ee40b89SJason Baron 2657ee40b89SJason Baron pdev = to_pci_dev(mci->pdev); 2667ee40b89SJason Baron 2677ee40b89SJason Baron /* 2687ee40b89SJason Baron * This is a mess because there is no atomic way to read all the 2697ee40b89SJason Baron * registers at once and the registers can transition from CE being 2707ee40b89SJason Baron * overwritten by UE. 2717ee40b89SJason Baron */ 2727ee40b89SJason Baron pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts); 2737ee40b89SJason Baron if (!(info->errsts & IE31200_ERRSTS_BITS)) 2747ee40b89SJason Baron return; 2757ee40b89SJason Baron 276953dee9bSJason Baron info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 2777ee40b89SJason Baron if (nr_channels == 2) 278953dee9bSJason Baron info->eccerrlog[1] = lo_hi_readq(priv->c1errlog); 2797ee40b89SJason Baron 2807ee40b89SJason Baron pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2); 2817ee40b89SJason Baron 2827ee40b89SJason Baron /* 2837ee40b89SJason Baron * If the error is the same for both reads then the first set 2847ee40b89SJason Baron * of reads is valid. If there is a change then there is a CE 2857ee40b89SJason Baron * with no info and the second set of reads is valid and 2867ee40b89SJason Baron * should be UE info. 2877ee40b89SJason Baron */ 2887ee40b89SJason Baron if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 289953dee9bSJason Baron info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 2907ee40b89SJason Baron if (nr_channels == 2) 2917ee40b89SJason Baron info->eccerrlog[1] = 292953dee9bSJason Baron lo_hi_readq(priv->c1errlog); 2937ee40b89SJason Baron } 2947ee40b89SJason Baron 2957ee40b89SJason Baron ie31200_clear_error_info(mci); 2967ee40b89SJason Baron } 2977ee40b89SJason Baron 2987ee40b89SJason Baron static void ie31200_process_error_info(struct mem_ctl_info *mci, 2997ee40b89SJason Baron struct ie31200_error_info *info) 3007ee40b89SJason Baron { 3017ee40b89SJason Baron int channel; 3027ee40b89SJason Baron u64 log; 3037ee40b89SJason Baron 3047ee40b89SJason Baron if (!(info->errsts & IE31200_ERRSTS_BITS)) 3057ee40b89SJason Baron return; 3067ee40b89SJason Baron 3077ee40b89SJason Baron if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 3087ee40b89SJason Baron edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 3097ee40b89SJason Baron -1, -1, -1, "UE overwrote CE", ""); 3107ee40b89SJason Baron info->errsts = info->errsts2; 3117ee40b89SJason Baron } 3127ee40b89SJason Baron 3137ee40b89SJason Baron for (channel = 0; channel < nr_channels; channel++) { 3147ee40b89SJason Baron log = info->eccerrlog[channel]; 3157ee40b89SJason Baron if (log & IE31200_ECCERRLOG_UE) { 3167ee40b89SJason Baron edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 3177ee40b89SJason Baron 0, 0, 0, 318953dee9bSJason Baron eccerrlog_row(log), 3197ee40b89SJason Baron channel, -1, 3207ee40b89SJason Baron "ie31200 UE", ""); 3217ee40b89SJason Baron } else if (log & IE31200_ECCERRLOG_CE) { 3227ee40b89SJason Baron edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 3237ee40b89SJason Baron 0, 0, 3247ee40b89SJason Baron IE31200_ECCERRLOG_SYNDROME(log), 325953dee9bSJason Baron eccerrlog_row(log), 3267ee40b89SJason Baron channel, -1, 3277ee40b89SJason Baron "ie31200 CE", ""); 3287ee40b89SJason Baron } 3297ee40b89SJason Baron } 3307ee40b89SJason Baron } 3317ee40b89SJason Baron 3327ee40b89SJason Baron static void ie31200_check(struct mem_ctl_info *mci) 3337ee40b89SJason Baron { 3347ee40b89SJason Baron struct ie31200_error_info info; 3357ee40b89SJason Baron 3367ee40b89SJason Baron edac_dbg(1, "MC%d\n", mci->mc_idx); 3377ee40b89SJason Baron ie31200_get_and_clear_error_info(mci, &info); 3387ee40b89SJason Baron ie31200_process_error_info(mci, &info); 3397ee40b89SJason Baron } 3407ee40b89SJason Baron 3417ee40b89SJason Baron static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev) 3427ee40b89SJason Baron { 3437ee40b89SJason Baron union { 3447ee40b89SJason Baron u64 mchbar; 3457ee40b89SJason Baron struct { 3467ee40b89SJason Baron u32 mchbar_low; 3477ee40b89SJason Baron u32 mchbar_high; 3487ee40b89SJason Baron }; 3497ee40b89SJason Baron } u; 3507ee40b89SJason Baron void __iomem *window; 3517ee40b89SJason Baron 3527ee40b89SJason Baron pci_read_config_dword(pdev, IE31200_MCHBAR_LOW, &u.mchbar_low); 3537ee40b89SJason Baron pci_read_config_dword(pdev, IE31200_MCHBAR_HIGH, &u.mchbar_high); 3547ee40b89SJason Baron u.mchbar &= IE31200_MCHBAR_MASK; 3557ee40b89SJason Baron 3567ee40b89SJason Baron if (u.mchbar != (resource_size_t)u.mchbar) { 3577ee40b89SJason Baron ie31200_printk(KERN_ERR, "mmio space beyond accessible range (0x%llx)\n", 3587ee40b89SJason Baron (unsigned long long)u.mchbar); 3597ee40b89SJason Baron return NULL; 3607ee40b89SJason Baron } 3617ee40b89SJason Baron 3624bdc0d67SChristoph Hellwig window = ioremap(u.mchbar, IE31200_MMR_WINDOW_SIZE); 3637ee40b89SJason Baron if (!window) 3647ee40b89SJason Baron ie31200_printk(KERN_ERR, "Cannot map mmio space at 0x%llx\n", 3657ee40b89SJason Baron (unsigned long long)u.mchbar); 3667ee40b89SJason Baron 3677ee40b89SJason Baron return window; 3687ee40b89SJason Baron } 3697ee40b89SJason Baron 370953dee9bSJason Baron static void __skl_populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 371953dee9bSJason Baron int chan) 372953dee9bSJason Baron { 373953dee9bSJason Baron dd->size = (addr_decode >> (chan << 4)) & IE31200_MAD_DIMM_SIZE; 374953dee9bSJason Baron dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK_SKL << (chan << 4))) ? 1 : 0; 375953dee9bSJason Baron dd->x16_width = ((addr_decode & (IE31200_MAD_DIMM_A_WIDTH_SKL << (chan << 4))) >> 376953dee9bSJason Baron (IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT + (chan << 4))); 377953dee9bSJason Baron } 378953dee9bSJason Baron 379953dee9bSJason Baron static void __populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 380953dee9bSJason Baron int chan) 381953dee9bSJason Baron { 382953dee9bSJason Baron dd->size = (addr_decode >> (chan << 3)) & IE31200_MAD_DIMM_SIZE; 383953dee9bSJason Baron dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK << chan)) ? 1 : 0; 384953dee9bSJason Baron dd->x16_width = (addr_decode & (IE31200_MAD_DIMM_A_WIDTH << chan)) ? 1 : 0; 385953dee9bSJason Baron } 386953dee9bSJason Baron 387953dee9bSJason Baron static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int chan, 388953dee9bSJason Baron bool skl) 389953dee9bSJason Baron { 390953dee9bSJason Baron if (skl) 391953dee9bSJason Baron __skl_populate_dimm_info(dd, addr_decode, chan); 392953dee9bSJason Baron else 393953dee9bSJason Baron __populate_dimm_info(dd, addr_decode, chan); 394953dee9bSJason Baron } 395953dee9bSJason Baron 396953dee9bSJason Baron 3977ee40b89SJason Baron static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) 3987ee40b89SJason Baron { 39978fd4d12SJason Baron int i, j, ret; 4007ee40b89SJason Baron struct mem_ctl_info *mci = NULL; 4017ee40b89SJason Baron struct edac_mc_layer layers[2]; 4027ee40b89SJason Baron struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL]; 4037ee40b89SJason Baron void __iomem *window; 4047ee40b89SJason Baron struct ie31200_priv *priv; 405953dee9bSJason Baron u32 addr_decode, mad_offset; 4067103de0eSJason Baron 4077103de0eSJason Baron /* 408c452a9d3SMarco Elver * Kaby Lake, Coffee Lake seem to work like Skylake. Please re-visit 409c452a9d3SMarco Elver * this logic when adding new CPU support. 4107103de0eSJason Baron */ 411c452a9d3SMarco Elver bool skl = DEVICE_ID_SKYLAKE_OR_LATER(pdev->device); 4127ee40b89SJason Baron 4137ee40b89SJason Baron edac_dbg(0, "MC:\n"); 4147ee40b89SJason Baron 4157ee40b89SJason Baron if (!ecc_capable(pdev)) { 4167ee40b89SJason Baron ie31200_printk(KERN_INFO, "No ECC support\n"); 4177ee40b89SJason Baron return -ENODEV; 4187ee40b89SJason Baron } 4197ee40b89SJason Baron 42078fd4d12SJason Baron nr_channels = how_many_channels(pdev); 42178fd4d12SJason Baron layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 42278fd4d12SJason Baron layers[0].size = IE31200_DIMMS; 42378fd4d12SJason Baron layers[0].is_virt_csrow = true; 42478fd4d12SJason Baron layers[1].type = EDAC_MC_LAYER_CHANNEL; 42578fd4d12SJason Baron layers[1].size = nr_channels; 42678fd4d12SJason Baron layers[1].is_virt_csrow = false; 42778fd4d12SJason Baron mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 42878fd4d12SJason Baron sizeof(struct ie31200_priv)); 42978fd4d12SJason Baron if (!mci) 43078fd4d12SJason Baron return -ENOMEM; 43178fd4d12SJason Baron 4327ee40b89SJason Baron window = ie31200_map_mchbar(pdev); 43378fd4d12SJason Baron if (!window) { 43478fd4d12SJason Baron ret = -ENODEV; 43578fd4d12SJason Baron goto fail_free; 43678fd4d12SJason Baron } 43778fd4d12SJason Baron 43878fd4d12SJason Baron edac_dbg(3, "MC: init mci\n"); 43978fd4d12SJason Baron mci->pdev = &pdev->dev; 440953dee9bSJason Baron if (skl) 441953dee9bSJason Baron mci->mtype_cap = MEM_FLAG_DDR4; 442953dee9bSJason Baron else 44378fd4d12SJason Baron mci->mtype_cap = MEM_FLAG_DDR3; 44478fd4d12SJason Baron mci->edac_ctl_cap = EDAC_FLAG_SECDED; 44578fd4d12SJason Baron mci->edac_cap = EDAC_FLAG_SECDED; 44678fd4d12SJason Baron mci->mod_name = EDAC_MOD_STR; 44778fd4d12SJason Baron mci->ctl_name = ie31200_devs[dev_idx].ctl_name; 44878fd4d12SJason Baron mci->dev_name = pci_name(pdev); 44978fd4d12SJason Baron mci->edac_check = ie31200_check; 45078fd4d12SJason Baron mci->ctl_page_to_phys = NULL; 45178fd4d12SJason Baron priv = mci->pvt_info; 45278fd4d12SJason Baron priv->window = window; 453953dee9bSJason Baron if (skl) { 454953dee9bSJason Baron priv->c0errlog = window + IE31200_C0ECCERRLOG_SKL; 455953dee9bSJason Baron priv->c1errlog = window + IE31200_C1ECCERRLOG_SKL; 456953dee9bSJason Baron mad_offset = IE31200_MAD_DIMM_0_OFFSET_SKL; 457953dee9bSJason Baron } else { 458953dee9bSJason Baron priv->c0errlog = window + IE31200_C0ECCERRLOG; 459953dee9bSJason Baron priv->c1errlog = window + IE31200_C1ECCERRLOG; 460953dee9bSJason Baron mad_offset = IE31200_MAD_DIMM_0_OFFSET; 461953dee9bSJason Baron } 4627ee40b89SJason Baron 4637ee40b89SJason Baron /* populate DIMM info */ 4647ee40b89SJason Baron for (i = 0; i < IE31200_CHANNELS; i++) { 465953dee9bSJason Baron addr_decode = readl(window + mad_offset + 4667ee40b89SJason Baron (i * 4)); 4677ee40b89SJason Baron edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); 4687ee40b89SJason Baron for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { 469953dee9bSJason Baron populate_dimm_info(&dimm_info[i][j], addr_decode, j, 470953dee9bSJason Baron skl); 4717ee40b89SJason Baron edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n", 4727ee40b89SJason Baron dimm_info[i][j].size, 4737ee40b89SJason Baron dimm_info[i][j].dual_rank, 4747ee40b89SJason Baron dimm_info[i][j].x16_width); 4757ee40b89SJason Baron } 4767ee40b89SJason Baron } 4777ee40b89SJason Baron 4787ee40b89SJason Baron /* 4797ee40b89SJason Baron * The dram rank boundary (DRB) reg values are boundary addresses 4807ee40b89SJason Baron * for each DRAM rank with a granularity of 64MB. DRB regs are 4817ee40b89SJason Baron * cumulative; the last one will contain the total memory 4827ee40b89SJason Baron * contained in all ranks. 4837ee40b89SJason Baron */ 4847ee40b89SJason Baron for (i = 0; i < IE31200_DIMMS_PER_CHANNEL; i++) { 4857ee40b89SJason Baron for (j = 0; j < IE31200_CHANNELS; j++) { 4867ee40b89SJason Baron struct dimm_info *dimm; 4877ee40b89SJason Baron unsigned long nr_pages; 4887ee40b89SJason Baron 489953dee9bSJason Baron nr_pages = IE31200_PAGES(dimm_info[j][i].size, skl); 4907ee40b89SJason Baron if (nr_pages == 0) 4917ee40b89SJason Baron continue; 4927ee40b89SJason Baron 4937ee40b89SJason Baron if (dimm_info[j][i].dual_rank) { 4947ee40b89SJason Baron nr_pages = nr_pages / 2; 495bc9ad9e4SRobert Richter dimm = edac_get_dimm(mci, (i * 2) + 1, j, 0); 4967ee40b89SJason Baron dimm->nr_pages = nr_pages; 4977ee40b89SJason Baron edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 4987ee40b89SJason Baron dimm->grain = 8; /* just a guess */ 499953dee9bSJason Baron if (skl) 500953dee9bSJason Baron dimm->mtype = MEM_DDR4; 501953dee9bSJason Baron else 5027ee40b89SJason Baron dimm->mtype = MEM_DDR3; 5037ee40b89SJason Baron dimm->dtype = DEV_UNKNOWN; 5047ee40b89SJason Baron dimm->edac_mode = EDAC_UNKNOWN; 5057ee40b89SJason Baron } 506bc9ad9e4SRobert Richter dimm = edac_get_dimm(mci, i * 2, j, 0); 5077ee40b89SJason Baron dimm->nr_pages = nr_pages; 5087ee40b89SJason Baron edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 5097ee40b89SJason Baron dimm->grain = 8; /* same guess */ 510953dee9bSJason Baron if (skl) 511953dee9bSJason Baron dimm->mtype = MEM_DDR4; 512953dee9bSJason Baron else 5137ee40b89SJason Baron dimm->mtype = MEM_DDR3; 5147ee40b89SJason Baron dimm->dtype = DEV_UNKNOWN; 5157ee40b89SJason Baron dimm->edac_mode = EDAC_UNKNOWN; 5167ee40b89SJason Baron } 5177ee40b89SJason Baron } 5187ee40b89SJason Baron 5197ee40b89SJason Baron ie31200_clear_error_info(mci); 5207ee40b89SJason Baron 5217ee40b89SJason Baron if (edac_mc_add_mc(mci)) { 5227ee40b89SJason Baron edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 52378fd4d12SJason Baron ret = -ENODEV; 52478fd4d12SJason Baron goto fail_unmap; 5257ee40b89SJason Baron } 5267ee40b89SJason Baron 5277ee40b89SJason Baron /* get this far and it's successful */ 5287ee40b89SJason Baron edac_dbg(3, "MC: success\n"); 5297ee40b89SJason Baron return 0; 5307ee40b89SJason Baron 5317ee40b89SJason Baron fail_unmap: 5327ee40b89SJason Baron iounmap(window); 5337ee40b89SJason Baron 53478fd4d12SJason Baron fail_free: 53578fd4d12SJason Baron edac_mc_free(mci); 53678fd4d12SJason Baron 53778fd4d12SJason Baron return ret; 5387ee40b89SJason Baron } 5397ee40b89SJason Baron 5407ee40b89SJason Baron static int ie31200_init_one(struct pci_dev *pdev, 5417ee40b89SJason Baron const struct pci_device_id *ent) 5427ee40b89SJason Baron { 543709ed1bcSJason Baron int rc; 5447ee40b89SJason Baron 545709ed1bcSJason Baron edac_dbg(0, "MC:\n"); 5467ee40b89SJason Baron if (pci_enable_device(pdev) < 0) 5477ee40b89SJason Baron return -EIO; 548709ed1bcSJason Baron rc = ie31200_probe1(pdev, ent->driver_data); 549709ed1bcSJason Baron if (rc == 0 && !mci_pdev) 550709ed1bcSJason Baron mci_pdev = pci_dev_get(pdev); 5517ee40b89SJason Baron 552709ed1bcSJason Baron return rc; 5537ee40b89SJason Baron } 5547ee40b89SJason Baron 5557ee40b89SJason Baron static void ie31200_remove_one(struct pci_dev *pdev) 5567ee40b89SJason Baron { 5577ee40b89SJason Baron struct mem_ctl_info *mci; 5587ee40b89SJason Baron struct ie31200_priv *priv; 5597ee40b89SJason Baron 5607ee40b89SJason Baron edac_dbg(0, "\n"); 561709ed1bcSJason Baron pci_dev_put(mci_pdev); 562709ed1bcSJason Baron mci_pdev = NULL; 5637ee40b89SJason Baron mci = edac_mc_del_mc(&pdev->dev); 5647ee40b89SJason Baron if (!mci) 5657ee40b89SJason Baron return; 5667ee40b89SJason Baron priv = mci->pvt_info; 5677ee40b89SJason Baron iounmap(priv->window); 5687ee40b89SJason Baron edac_mc_free(mci); 5697ee40b89SJason Baron } 5707ee40b89SJason Baron 5717ee40b89SJason Baron static const struct pci_device_id ie31200_pci_tbl[] = { 5724d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5734d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5744d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5754d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5764d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5774d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5784d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5794d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5804d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_9), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5814d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5824d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5834d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5844d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5854d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5864d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5874d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5884d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5894d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_9), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5904d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_10), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5914d91fde8SMarco Elver { 0, } /* 0 terminated list. */ 5927ee40b89SJason Baron }; 5937ee40b89SJason Baron MODULE_DEVICE_TABLE(pci, ie31200_pci_tbl); 5947ee40b89SJason Baron 5957ee40b89SJason Baron static struct pci_driver ie31200_driver = { 5967ee40b89SJason Baron .name = EDAC_MOD_STR, 5977ee40b89SJason Baron .probe = ie31200_init_one, 5987ee40b89SJason Baron .remove = ie31200_remove_one, 5997ee40b89SJason Baron .id_table = ie31200_pci_tbl, 6007ee40b89SJason Baron }; 6017ee40b89SJason Baron 6027ee40b89SJason Baron static int __init ie31200_init(void) 6037ee40b89SJason Baron { 604709ed1bcSJason Baron int pci_rc, i; 605709ed1bcSJason Baron 6067ee40b89SJason Baron edac_dbg(3, "MC:\n"); 6077ee40b89SJason Baron /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 6087ee40b89SJason Baron opstate_init(); 6097ee40b89SJason Baron 610709ed1bcSJason Baron pci_rc = pci_register_driver(&ie31200_driver); 611709ed1bcSJason Baron if (pci_rc < 0) 612709ed1bcSJason Baron goto fail0; 613709ed1bcSJason Baron 614709ed1bcSJason Baron if (!mci_pdev) { 615709ed1bcSJason Baron ie31200_registered = 0; 616709ed1bcSJason Baron for (i = 0; ie31200_pci_tbl[i].vendor != 0; i++) { 617709ed1bcSJason Baron mci_pdev = pci_get_device(ie31200_pci_tbl[i].vendor, 618709ed1bcSJason Baron ie31200_pci_tbl[i].device, 619709ed1bcSJason Baron NULL); 620709ed1bcSJason Baron if (mci_pdev) 621709ed1bcSJason Baron break; 622709ed1bcSJason Baron } 623709ed1bcSJason Baron if (!mci_pdev) { 624709ed1bcSJason Baron edac_dbg(0, "ie31200 pci_get_device fail\n"); 625709ed1bcSJason Baron pci_rc = -ENODEV; 626709ed1bcSJason Baron goto fail1; 627709ed1bcSJason Baron } 628709ed1bcSJason Baron pci_rc = ie31200_init_one(mci_pdev, &ie31200_pci_tbl[i]); 629709ed1bcSJason Baron if (pci_rc < 0) { 630709ed1bcSJason Baron edac_dbg(0, "ie31200 init fail\n"); 631709ed1bcSJason Baron pci_rc = -ENODEV; 632709ed1bcSJason Baron goto fail1; 633709ed1bcSJason Baron } 634709ed1bcSJason Baron } 635709ed1bcSJason Baron return 0; 636709ed1bcSJason Baron 637709ed1bcSJason Baron fail1: 638709ed1bcSJason Baron pci_unregister_driver(&ie31200_driver); 639709ed1bcSJason Baron fail0: 640709ed1bcSJason Baron pci_dev_put(mci_pdev); 641709ed1bcSJason Baron 642709ed1bcSJason Baron return pci_rc; 6437ee40b89SJason Baron } 6447ee40b89SJason Baron 6457ee40b89SJason Baron static void __exit ie31200_exit(void) 6467ee40b89SJason Baron { 6477ee40b89SJason Baron edac_dbg(3, "MC:\n"); 6487ee40b89SJason Baron pci_unregister_driver(&ie31200_driver); 649709ed1bcSJason Baron if (!ie31200_registered) 650709ed1bcSJason Baron ie31200_remove_one(mci_pdev); 6517ee40b89SJason Baron } 6527ee40b89SJason Baron 6537ee40b89SJason Baron module_init(ie31200_init); 6547ee40b89SJason Baron module_exit(ie31200_exit); 6557ee40b89SJason Baron 6567ee40b89SJason Baron MODULE_LICENSE("GPL"); 6577ee40b89SJason Baron MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>"); 6587ee40b89SJason Baron MODULE_DESCRIPTION("MC support for Intel Processor E31200 memory hub controllers"); 659