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; 1737ee40b89SJason Baron 1747ee40b89SJason Baron struct ie31200_priv { 1757ee40b89SJason Baron void __iomem *window; 176953dee9bSJason Baron void __iomem *c0errlog; 177953dee9bSJason Baron void __iomem *c1errlog; 1787ee40b89SJason Baron }; 1797ee40b89SJason Baron 1807ee40b89SJason Baron enum ie31200_chips { 1817ee40b89SJason Baron IE31200 = 0, 1827ee40b89SJason Baron }; 1837ee40b89SJason Baron 1847ee40b89SJason Baron struct ie31200_dev_info { 1857ee40b89SJason Baron const char *ctl_name; 1867ee40b89SJason Baron }; 1877ee40b89SJason Baron 1887ee40b89SJason Baron struct ie31200_error_info { 1897ee40b89SJason Baron u16 errsts; 1907ee40b89SJason Baron u16 errsts2; 1917ee40b89SJason Baron u64 eccerrlog[IE31200_CHANNELS]; 1927ee40b89SJason Baron }; 1937ee40b89SJason Baron 1947ee40b89SJason Baron static const struct ie31200_dev_info ie31200_devs[] = { 1957ee40b89SJason Baron [IE31200] = { 1967ee40b89SJason Baron .ctl_name = "IE31200" 1977ee40b89SJason Baron }, 1987ee40b89SJason Baron }; 1997ee40b89SJason Baron 2007ee40b89SJason Baron struct dimm_data { 201953dee9bSJason Baron u8 size; /* in multiples of 256MB, except Skylake is 1GB */ 2027ee40b89SJason Baron u8 dual_rank : 1, 203953dee9bSJason Baron x16_width : 2; /* 0 means x8 width */ 2047ee40b89SJason Baron }; 2057ee40b89SJason Baron 2067ee40b89SJason Baron static int how_many_channels(struct pci_dev *pdev) 2077ee40b89SJason Baron { 2087ee40b89SJason Baron int n_channels; 2097ee40b89SJason Baron unsigned char capid0_2b; /* 2nd byte of CAPID0 */ 2107ee40b89SJason Baron 2117ee40b89SJason Baron pci_read_config_byte(pdev, IE31200_CAPID0 + 1, &capid0_2b); 2127ee40b89SJason Baron 2137ee40b89SJason Baron /* check PDCD: Dual Channel Disable */ 2147ee40b89SJason Baron if (capid0_2b & IE31200_CAPID0_PDCD) { 2157ee40b89SJason Baron edac_dbg(0, "In single channel mode\n"); 2167ee40b89SJason Baron n_channels = 1; 2177ee40b89SJason Baron } else { 2187ee40b89SJason Baron edac_dbg(0, "In dual channel mode\n"); 2197ee40b89SJason Baron n_channels = 2; 2207ee40b89SJason Baron } 2217ee40b89SJason Baron 2227ee40b89SJason Baron /* check DDPCD - check if both channels are filled */ 2237ee40b89SJason Baron if (capid0_2b & IE31200_CAPID0_DDPCD) 2247ee40b89SJason Baron edac_dbg(0, "2 DIMMS per channel disabled\n"); 2257ee40b89SJason Baron else 2267ee40b89SJason Baron edac_dbg(0, "2 DIMMS per channel enabled\n"); 2277ee40b89SJason Baron 2287ee40b89SJason Baron return n_channels; 2297ee40b89SJason Baron } 2307ee40b89SJason Baron 2317ee40b89SJason Baron static bool ecc_capable(struct pci_dev *pdev) 2327ee40b89SJason Baron { 2337ee40b89SJason Baron unsigned char capid0_4b; /* 4th byte of CAPID0 */ 2347ee40b89SJason Baron 2357ee40b89SJason Baron pci_read_config_byte(pdev, IE31200_CAPID0 + 3, &capid0_4b); 2367ee40b89SJason Baron if (capid0_4b & IE31200_CAPID0_ECC) 2377ee40b89SJason Baron return false; 2387ee40b89SJason Baron return true; 2397ee40b89SJason Baron } 2407ee40b89SJason Baron 241953dee9bSJason Baron static int eccerrlog_row(u64 log) 2427ee40b89SJason Baron { 243953dee9bSJason Baron return ((log & IE31200_ECCERRLOG_RANK_BITS) >> 2447ee40b89SJason Baron IE31200_ECCERRLOG_RANK_SHIFT); 2457ee40b89SJason Baron } 2467ee40b89SJason Baron 2477ee40b89SJason Baron static void ie31200_clear_error_info(struct mem_ctl_info *mci) 2487ee40b89SJason Baron { 2497ee40b89SJason Baron /* 2507ee40b89SJason Baron * Clear any error bits. 2517ee40b89SJason Baron * (Yes, we really clear bits by writing 1 to them.) 2527ee40b89SJason Baron */ 2537ee40b89SJason Baron pci_write_bits16(to_pci_dev(mci->pdev), IE31200_ERRSTS, 2547ee40b89SJason Baron IE31200_ERRSTS_BITS, IE31200_ERRSTS_BITS); 2557ee40b89SJason Baron } 2567ee40b89SJason Baron 2577ee40b89SJason Baron static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, 2587ee40b89SJason Baron struct ie31200_error_info *info) 2597ee40b89SJason Baron { 2607ee40b89SJason Baron struct pci_dev *pdev; 2617ee40b89SJason Baron struct ie31200_priv *priv = mci->pvt_info; 2627ee40b89SJason Baron 2637ee40b89SJason Baron pdev = to_pci_dev(mci->pdev); 2647ee40b89SJason Baron 2657ee40b89SJason Baron /* 2667ee40b89SJason Baron * This is a mess because there is no atomic way to read all the 2677ee40b89SJason Baron * registers at once and the registers can transition from CE being 2687ee40b89SJason Baron * overwritten by UE. 2697ee40b89SJason Baron */ 2707ee40b89SJason Baron pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts); 2717ee40b89SJason Baron if (!(info->errsts & IE31200_ERRSTS_BITS)) 2727ee40b89SJason Baron return; 2737ee40b89SJason Baron 274953dee9bSJason Baron info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 2757ee40b89SJason Baron if (nr_channels == 2) 276953dee9bSJason Baron info->eccerrlog[1] = lo_hi_readq(priv->c1errlog); 2777ee40b89SJason Baron 2787ee40b89SJason Baron pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2); 2797ee40b89SJason Baron 2807ee40b89SJason Baron /* 2817ee40b89SJason Baron * If the error is the same for both reads then the first set 2827ee40b89SJason Baron * of reads is valid. If there is a change then there is a CE 2837ee40b89SJason Baron * with no info and the second set of reads is valid and 2847ee40b89SJason Baron * should be UE info. 2857ee40b89SJason Baron */ 2867ee40b89SJason Baron if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 287953dee9bSJason Baron info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 2887ee40b89SJason Baron if (nr_channels == 2) 2897ee40b89SJason Baron info->eccerrlog[1] = 290953dee9bSJason Baron lo_hi_readq(priv->c1errlog); 2917ee40b89SJason Baron } 2927ee40b89SJason Baron 2937ee40b89SJason Baron ie31200_clear_error_info(mci); 2947ee40b89SJason Baron } 2957ee40b89SJason Baron 2967ee40b89SJason Baron static void ie31200_process_error_info(struct mem_ctl_info *mci, 2977ee40b89SJason Baron struct ie31200_error_info *info) 2987ee40b89SJason Baron { 2997ee40b89SJason Baron int channel; 3007ee40b89SJason Baron u64 log; 3017ee40b89SJason Baron 3027ee40b89SJason Baron if (!(info->errsts & IE31200_ERRSTS_BITS)) 3037ee40b89SJason Baron return; 3047ee40b89SJason Baron 3057ee40b89SJason Baron if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 3067ee40b89SJason Baron edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 3077ee40b89SJason Baron -1, -1, -1, "UE overwrote CE", ""); 3087ee40b89SJason Baron info->errsts = info->errsts2; 3097ee40b89SJason Baron } 3107ee40b89SJason Baron 3117ee40b89SJason Baron for (channel = 0; channel < nr_channels; channel++) { 3127ee40b89SJason Baron log = info->eccerrlog[channel]; 3137ee40b89SJason Baron if (log & IE31200_ECCERRLOG_UE) { 3147ee40b89SJason Baron edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 3157ee40b89SJason Baron 0, 0, 0, 316953dee9bSJason Baron eccerrlog_row(log), 3177ee40b89SJason Baron channel, -1, 3187ee40b89SJason Baron "ie31200 UE", ""); 3197ee40b89SJason Baron } else if (log & IE31200_ECCERRLOG_CE) { 3207ee40b89SJason Baron edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 3217ee40b89SJason Baron 0, 0, 3227ee40b89SJason Baron IE31200_ECCERRLOG_SYNDROME(log), 323953dee9bSJason Baron eccerrlog_row(log), 3247ee40b89SJason Baron channel, -1, 3257ee40b89SJason Baron "ie31200 CE", ""); 3267ee40b89SJason Baron } 3277ee40b89SJason Baron } 3287ee40b89SJason Baron } 3297ee40b89SJason Baron 3307ee40b89SJason Baron static void ie31200_check(struct mem_ctl_info *mci) 3317ee40b89SJason Baron { 3327ee40b89SJason Baron struct ie31200_error_info info; 3337ee40b89SJason Baron 3347ee40b89SJason Baron edac_dbg(1, "MC%d\n", mci->mc_idx); 3357ee40b89SJason Baron ie31200_get_and_clear_error_info(mci, &info); 3367ee40b89SJason Baron ie31200_process_error_info(mci, &info); 3377ee40b89SJason Baron } 3387ee40b89SJason Baron 3397ee40b89SJason Baron static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev) 3407ee40b89SJason Baron { 3417ee40b89SJason Baron union { 3427ee40b89SJason Baron u64 mchbar; 3437ee40b89SJason Baron struct { 3447ee40b89SJason Baron u32 mchbar_low; 3457ee40b89SJason Baron u32 mchbar_high; 3467ee40b89SJason Baron }; 3477ee40b89SJason Baron } u; 3487ee40b89SJason Baron void __iomem *window; 3497ee40b89SJason Baron 3507ee40b89SJason Baron pci_read_config_dword(pdev, IE31200_MCHBAR_LOW, &u.mchbar_low); 3517ee40b89SJason Baron pci_read_config_dword(pdev, IE31200_MCHBAR_HIGH, &u.mchbar_high); 3527ee40b89SJason Baron u.mchbar &= IE31200_MCHBAR_MASK; 3537ee40b89SJason Baron 3547ee40b89SJason Baron if (u.mchbar != (resource_size_t)u.mchbar) { 3557ee40b89SJason Baron ie31200_printk(KERN_ERR, "mmio space beyond accessible range (0x%llx)\n", 3567ee40b89SJason Baron (unsigned long long)u.mchbar); 3577ee40b89SJason Baron return NULL; 3587ee40b89SJason Baron } 3597ee40b89SJason Baron 3604bdc0d67SChristoph Hellwig window = ioremap(u.mchbar, IE31200_MMR_WINDOW_SIZE); 3617ee40b89SJason Baron if (!window) 3627ee40b89SJason Baron ie31200_printk(KERN_ERR, "Cannot map mmio space at 0x%llx\n", 3637ee40b89SJason Baron (unsigned long long)u.mchbar); 3647ee40b89SJason Baron 3657ee40b89SJason Baron return window; 3667ee40b89SJason Baron } 3677ee40b89SJason Baron 368953dee9bSJason Baron static void __skl_populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 369953dee9bSJason Baron int chan) 370953dee9bSJason Baron { 371953dee9bSJason Baron dd->size = (addr_decode >> (chan << 4)) & IE31200_MAD_DIMM_SIZE; 372953dee9bSJason Baron dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK_SKL << (chan << 4))) ? 1 : 0; 373953dee9bSJason Baron dd->x16_width = ((addr_decode & (IE31200_MAD_DIMM_A_WIDTH_SKL << (chan << 4))) >> 374953dee9bSJason Baron (IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT + (chan << 4))); 375953dee9bSJason Baron } 376953dee9bSJason Baron 377953dee9bSJason Baron static void __populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 378953dee9bSJason Baron int chan) 379953dee9bSJason Baron { 380953dee9bSJason Baron dd->size = (addr_decode >> (chan << 3)) & IE31200_MAD_DIMM_SIZE; 381953dee9bSJason Baron dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK << chan)) ? 1 : 0; 382953dee9bSJason Baron dd->x16_width = (addr_decode & (IE31200_MAD_DIMM_A_WIDTH << chan)) ? 1 : 0; 383953dee9bSJason Baron } 384953dee9bSJason Baron 385953dee9bSJason Baron static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int chan, 386953dee9bSJason Baron bool skl) 387953dee9bSJason Baron { 388953dee9bSJason Baron if (skl) 389953dee9bSJason Baron __skl_populate_dimm_info(dd, addr_decode, chan); 390953dee9bSJason Baron else 391953dee9bSJason Baron __populate_dimm_info(dd, addr_decode, chan); 392953dee9bSJason Baron } 393953dee9bSJason Baron 394953dee9bSJason Baron 3957ee40b89SJason Baron static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) 3967ee40b89SJason Baron { 39778fd4d12SJason Baron int i, j, ret; 3987ee40b89SJason Baron struct mem_ctl_info *mci = NULL; 3997ee40b89SJason Baron struct edac_mc_layer layers[2]; 4007ee40b89SJason Baron struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL]; 4017ee40b89SJason Baron void __iomem *window; 4027ee40b89SJason Baron struct ie31200_priv *priv; 403953dee9bSJason Baron u32 addr_decode, mad_offset; 4047103de0eSJason Baron 4057103de0eSJason Baron /* 406c452a9d3SMarco Elver * Kaby Lake, Coffee Lake seem to work like Skylake. Please re-visit 407c452a9d3SMarco Elver * this logic when adding new CPU support. 4087103de0eSJason Baron */ 409c452a9d3SMarco Elver bool skl = DEVICE_ID_SKYLAKE_OR_LATER(pdev->device); 4107ee40b89SJason Baron 4117ee40b89SJason Baron edac_dbg(0, "MC:\n"); 4127ee40b89SJason Baron 4137ee40b89SJason Baron if (!ecc_capable(pdev)) { 4147ee40b89SJason Baron ie31200_printk(KERN_INFO, "No ECC support\n"); 4157ee40b89SJason Baron return -ENODEV; 4167ee40b89SJason Baron } 4177ee40b89SJason Baron 41878fd4d12SJason Baron nr_channels = how_many_channels(pdev); 41978fd4d12SJason Baron layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 42078fd4d12SJason Baron layers[0].size = IE31200_DIMMS; 42178fd4d12SJason Baron layers[0].is_virt_csrow = true; 42278fd4d12SJason Baron layers[1].type = EDAC_MC_LAYER_CHANNEL; 42378fd4d12SJason Baron layers[1].size = nr_channels; 42478fd4d12SJason Baron layers[1].is_virt_csrow = false; 42578fd4d12SJason Baron mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 42678fd4d12SJason Baron sizeof(struct ie31200_priv)); 42778fd4d12SJason Baron if (!mci) 42878fd4d12SJason Baron return -ENOMEM; 42978fd4d12SJason Baron 4307ee40b89SJason Baron window = ie31200_map_mchbar(pdev); 43178fd4d12SJason Baron if (!window) { 43278fd4d12SJason Baron ret = -ENODEV; 43378fd4d12SJason Baron goto fail_free; 43478fd4d12SJason Baron } 43578fd4d12SJason Baron 43678fd4d12SJason Baron edac_dbg(3, "MC: init mci\n"); 43778fd4d12SJason Baron mci->pdev = &pdev->dev; 438953dee9bSJason Baron if (skl) 439953dee9bSJason Baron mci->mtype_cap = MEM_FLAG_DDR4; 440953dee9bSJason Baron else 44178fd4d12SJason Baron mci->mtype_cap = MEM_FLAG_DDR3; 44278fd4d12SJason Baron mci->edac_ctl_cap = EDAC_FLAG_SECDED; 44378fd4d12SJason Baron mci->edac_cap = EDAC_FLAG_SECDED; 44478fd4d12SJason Baron mci->mod_name = EDAC_MOD_STR; 44578fd4d12SJason Baron mci->ctl_name = ie31200_devs[dev_idx].ctl_name; 44678fd4d12SJason Baron mci->dev_name = pci_name(pdev); 44778fd4d12SJason Baron mci->edac_check = ie31200_check; 44878fd4d12SJason Baron mci->ctl_page_to_phys = NULL; 44978fd4d12SJason Baron priv = mci->pvt_info; 45078fd4d12SJason Baron priv->window = window; 451953dee9bSJason Baron if (skl) { 452953dee9bSJason Baron priv->c0errlog = window + IE31200_C0ECCERRLOG_SKL; 453953dee9bSJason Baron priv->c1errlog = window + IE31200_C1ECCERRLOG_SKL; 454953dee9bSJason Baron mad_offset = IE31200_MAD_DIMM_0_OFFSET_SKL; 455953dee9bSJason Baron } else { 456953dee9bSJason Baron priv->c0errlog = window + IE31200_C0ECCERRLOG; 457953dee9bSJason Baron priv->c1errlog = window + IE31200_C1ECCERRLOG; 458953dee9bSJason Baron mad_offset = IE31200_MAD_DIMM_0_OFFSET; 459953dee9bSJason Baron } 4607ee40b89SJason Baron 4617ee40b89SJason Baron /* populate DIMM info */ 4627ee40b89SJason Baron for (i = 0; i < IE31200_CHANNELS; i++) { 463953dee9bSJason Baron addr_decode = readl(window + mad_offset + 4647ee40b89SJason Baron (i * 4)); 4657ee40b89SJason Baron edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); 4667ee40b89SJason Baron for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { 467953dee9bSJason Baron populate_dimm_info(&dimm_info[i][j], addr_decode, j, 468953dee9bSJason Baron skl); 4697ee40b89SJason Baron edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n", 4707ee40b89SJason Baron dimm_info[i][j].size, 4717ee40b89SJason Baron dimm_info[i][j].dual_rank, 4727ee40b89SJason Baron dimm_info[i][j].x16_width); 4737ee40b89SJason Baron } 4747ee40b89SJason Baron } 4757ee40b89SJason Baron 4767ee40b89SJason Baron /* 4777ee40b89SJason Baron * The dram rank boundary (DRB) reg values are boundary addresses 4787ee40b89SJason Baron * for each DRAM rank with a granularity of 64MB. DRB regs are 4797ee40b89SJason Baron * cumulative; the last one will contain the total memory 4807ee40b89SJason Baron * contained in all ranks. 4817ee40b89SJason Baron */ 4827ee40b89SJason Baron for (i = 0; i < IE31200_DIMMS_PER_CHANNEL; i++) { 4837ee40b89SJason Baron for (j = 0; j < IE31200_CHANNELS; j++) { 4847ee40b89SJason Baron struct dimm_info *dimm; 4857ee40b89SJason Baron unsigned long nr_pages; 4867ee40b89SJason Baron 487953dee9bSJason Baron nr_pages = IE31200_PAGES(dimm_info[j][i].size, skl); 4887ee40b89SJason Baron if (nr_pages == 0) 4897ee40b89SJason Baron continue; 4907ee40b89SJason Baron 4917ee40b89SJason Baron if (dimm_info[j][i].dual_rank) { 4927ee40b89SJason Baron nr_pages = nr_pages / 2; 493bc9ad9e4SRobert Richter dimm = edac_get_dimm(mci, (i * 2) + 1, j, 0); 4947ee40b89SJason Baron dimm->nr_pages = nr_pages; 4957ee40b89SJason Baron edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 4967ee40b89SJason Baron dimm->grain = 8; /* just a guess */ 497953dee9bSJason Baron if (skl) 498953dee9bSJason Baron dimm->mtype = MEM_DDR4; 499953dee9bSJason Baron else 5007ee40b89SJason Baron dimm->mtype = MEM_DDR3; 5017ee40b89SJason Baron dimm->dtype = DEV_UNKNOWN; 5027ee40b89SJason Baron dimm->edac_mode = EDAC_UNKNOWN; 5037ee40b89SJason Baron } 504bc9ad9e4SRobert Richter dimm = edac_get_dimm(mci, i * 2, j, 0); 5057ee40b89SJason Baron dimm->nr_pages = nr_pages; 5067ee40b89SJason Baron edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 5077ee40b89SJason Baron dimm->grain = 8; /* same guess */ 508953dee9bSJason Baron if (skl) 509953dee9bSJason Baron dimm->mtype = MEM_DDR4; 510953dee9bSJason Baron else 5117ee40b89SJason Baron dimm->mtype = MEM_DDR3; 5127ee40b89SJason Baron dimm->dtype = DEV_UNKNOWN; 5137ee40b89SJason Baron dimm->edac_mode = EDAC_UNKNOWN; 5147ee40b89SJason Baron } 5157ee40b89SJason Baron } 5167ee40b89SJason Baron 5177ee40b89SJason Baron ie31200_clear_error_info(mci); 5187ee40b89SJason Baron 5197ee40b89SJason Baron if (edac_mc_add_mc(mci)) { 5207ee40b89SJason Baron edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 52178fd4d12SJason Baron ret = -ENODEV; 52278fd4d12SJason Baron goto fail_unmap; 5237ee40b89SJason Baron } 5247ee40b89SJason Baron 5257ee40b89SJason Baron /* get this far and it's successful */ 5267ee40b89SJason Baron edac_dbg(3, "MC: success\n"); 5277ee40b89SJason Baron return 0; 5287ee40b89SJason Baron 5297ee40b89SJason Baron fail_unmap: 5307ee40b89SJason Baron iounmap(window); 5317ee40b89SJason Baron 53278fd4d12SJason Baron fail_free: 53378fd4d12SJason Baron edac_mc_free(mci); 53478fd4d12SJason Baron 53578fd4d12SJason Baron return ret; 5367ee40b89SJason Baron } 5377ee40b89SJason Baron 5387ee40b89SJason Baron static int ie31200_init_one(struct pci_dev *pdev, 5397ee40b89SJason Baron const struct pci_device_id *ent) 5407ee40b89SJason Baron { 5417ee40b89SJason Baron edac_dbg(0, "MC:\n"); 5427ee40b89SJason Baron 5437ee40b89SJason Baron if (pci_enable_device(pdev) < 0) 5447ee40b89SJason Baron return -EIO; 5457ee40b89SJason Baron 5467ee40b89SJason Baron return ie31200_probe1(pdev, ent->driver_data); 5477ee40b89SJason Baron } 5487ee40b89SJason Baron 5497ee40b89SJason Baron static void ie31200_remove_one(struct pci_dev *pdev) 5507ee40b89SJason Baron { 5517ee40b89SJason Baron struct mem_ctl_info *mci; 5527ee40b89SJason Baron struct ie31200_priv *priv; 5537ee40b89SJason Baron 5547ee40b89SJason Baron edac_dbg(0, "\n"); 5557ee40b89SJason Baron mci = edac_mc_del_mc(&pdev->dev); 5567ee40b89SJason Baron if (!mci) 5577ee40b89SJason Baron return; 5587ee40b89SJason Baron priv = mci->pvt_info; 5597ee40b89SJason Baron iounmap(priv->window); 5607ee40b89SJason Baron edac_mc_free(mci); 5617ee40b89SJason Baron } 5627ee40b89SJason Baron 5637ee40b89SJason Baron static const struct pci_device_id ie31200_pci_tbl[] = { 5644d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5654d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5664d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5674d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5684d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5694d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5704d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5714d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5724d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_9), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5734d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5744d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5754d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5764d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5774d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5784d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5794d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5804d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5814d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_9), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5824d91fde8SMarco Elver { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_10), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5834d91fde8SMarco Elver { 0, } /* 0 terminated list. */ 5847ee40b89SJason Baron }; 5857ee40b89SJason Baron MODULE_DEVICE_TABLE(pci, ie31200_pci_tbl); 5867ee40b89SJason Baron 5877ee40b89SJason Baron static struct pci_driver ie31200_driver = { 5887ee40b89SJason Baron .name = EDAC_MOD_STR, 5897ee40b89SJason Baron .probe = ie31200_init_one, 5907ee40b89SJason Baron .remove = ie31200_remove_one, 5917ee40b89SJason Baron .id_table = ie31200_pci_tbl, 5927ee40b89SJason Baron }; 5937ee40b89SJason Baron 5947ee40b89SJason Baron static int __init ie31200_init(void) 5957ee40b89SJason Baron { 5967ee40b89SJason Baron edac_dbg(3, "MC:\n"); 5977ee40b89SJason Baron /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 5987ee40b89SJason Baron opstate_init(); 5997ee40b89SJason Baron 6007ee40b89SJason Baron return pci_register_driver(&ie31200_driver); 6017ee40b89SJason Baron } 6027ee40b89SJason Baron 6037ee40b89SJason Baron static void __exit ie31200_exit(void) 6047ee40b89SJason Baron { 6057ee40b89SJason Baron edac_dbg(3, "MC:\n"); 6067ee40b89SJason Baron pci_unregister_driver(&ie31200_driver); 6077ee40b89SJason Baron } 6087ee40b89SJason Baron 6097ee40b89SJason Baron module_init(ie31200_init); 6107ee40b89SJason Baron module_exit(ie31200_exit); 6117ee40b89SJason Baron 6127ee40b89SJason Baron MODULE_LICENSE("GPL"); 6137ee40b89SJason Baron MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>"); 6147ee40b89SJason Baron MODULE_DESCRIPTION("MC support for Intel Processor E31200 memory hub controllers"); 615