18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel E3-1200 48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Jason Baron <jbaron@akamai.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Support for the E3-1200 processor family. Heavily based on previous 78c2ecf20Sopenharmony_ci * Intel EDAC drivers. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Since the DRAM controller is on the cpu chip, we can use its PCI device 108c2ecf20Sopenharmony_ci * id to identify these processors. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * PCI DRAM controller device ids (Taken from The PCI ID Repository - https://pci-ids.ucw.cz/) 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * 0108: Xeon E3-1200 Processor Family DRAM Controller 158c2ecf20Sopenharmony_ci * 010c: Xeon E3-1200/2nd Generation Core Processor Family DRAM Controller 168c2ecf20Sopenharmony_ci * 0150: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller 178c2ecf20Sopenharmony_ci * 0158: Xeon E3-1200 v2/Ivy Bridge DRAM Controller 188c2ecf20Sopenharmony_ci * 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller 198c2ecf20Sopenharmony_ci * 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller 208c2ecf20Sopenharmony_ci * 0c08: Xeon E3-1200 v3 Processor DRAM Controller 218c2ecf20Sopenharmony_ci * 1918: Xeon E3-1200 v5 Skylake Host Bridge/DRAM Registers 228c2ecf20Sopenharmony_ci * 5918: Xeon E3-1200 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers 238c2ecf20Sopenharmony_ci * 3e..: 8th/9th Gen Core Processor Host Bridge/DRAM Registers 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Based on Intel specification: 268c2ecf20Sopenharmony_ci * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf 278c2ecf20Sopenharmony_ci * http://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200-family-vol-2-datasheet.html 288c2ecf20Sopenharmony_ci * https://www.intel.com/content/www/us/en/processors/core/7th-gen-core-family-mobile-h-processor-lines-datasheet-vol-2.html 298c2ecf20Sopenharmony_ci * https://www.intel.com/content/www/us/en/products/docs/processors/core/8th-gen-core-family-datasheet-vol-2.html 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * According to the above datasheet (p.16): 328c2ecf20Sopenharmony_ci * " 338c2ecf20Sopenharmony_ci * 6. Software must not access B0/D0/F0 32-bit memory-mapped registers with 348c2ecf20Sopenharmony_ci * requests that cross a DW boundary. 358c2ecf20Sopenharmony_ci * " 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Thus, we make use of the explicit: lo_hi_readq(), which breaks the readq into 388c2ecf20Sopenharmony_ci * 2 readl() calls. This restriction may be lifted in subsequent chip releases, 398c2ecf20Sopenharmony_ci * but lo_hi_readq() ensures that we are safe across all e3-1200 processors. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <linux/module.h> 438c2ecf20Sopenharmony_ci#include <linux/init.h> 448c2ecf20Sopenharmony_ci#include <linux/pci.h> 458c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 468c2ecf20Sopenharmony_ci#include <linux/edac.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 498c2ecf20Sopenharmony_ci#include "edac_module.h" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "ie31200_edac" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define ie31200_printk(level, fmt, arg...) \ 548c2ecf20Sopenharmony_ci edac_printk(level, "ie31200", fmt, ##arg) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_1 0x0108 578c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_2 0x010c 588c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_3 0x0150 598c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_4 0x0158 608c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c 618c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04 628c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08 638c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_8 0x1918 648c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_9 0x5918 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Coffee Lake-S */ 678c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK 0x3e00 688c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_1 0x3e0f 698c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_2 0x3e18 708c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_3 0x3e1f 718c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_4 0x3e30 728c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_5 0x3e31 738c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_6 0x3e32 748c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_7 0x3e33 758c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_8 0x3ec2 768c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_9 0x3ec6 778c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10 0x3eca 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Test if HB is for Skylake or later. */ 808c2ecf20Sopenharmony_ci#define DEVICE_ID_SKYLAKE_OR_LATER(did) \ 818c2ecf20Sopenharmony_ci (((did) == PCI_DEVICE_ID_INTEL_IE31200_HB_8) || \ 828c2ecf20Sopenharmony_ci ((did) == PCI_DEVICE_ID_INTEL_IE31200_HB_9) || \ 838c2ecf20Sopenharmony_ci (((did) & PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK) == \ 848c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK)) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define IE31200_DIMMS 4 878c2ecf20Sopenharmony_ci#define IE31200_RANKS 8 888c2ecf20Sopenharmony_ci#define IE31200_RANKS_PER_CHANNEL 4 898c2ecf20Sopenharmony_ci#define IE31200_DIMMS_PER_CHANNEL 2 908c2ecf20Sopenharmony_ci#define IE31200_CHANNELS 2 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Intel IE31200 register addresses - device 0 function 0 - DRAM Controller */ 938c2ecf20Sopenharmony_ci#define IE31200_MCHBAR_LOW 0x48 948c2ecf20Sopenharmony_ci#define IE31200_MCHBAR_HIGH 0x4c 958c2ecf20Sopenharmony_ci#define IE31200_MCHBAR_MASK GENMASK_ULL(38, 15) 968c2ecf20Sopenharmony_ci#define IE31200_MMR_WINDOW_SIZE BIT(15) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * Error Status Register (16b) 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * 15 reserved 1028c2ecf20Sopenharmony_ci * 14 Isochronous TBWRR Run Behind FIFO Full 1038c2ecf20Sopenharmony_ci * (ITCV) 1048c2ecf20Sopenharmony_ci * 13 Isochronous TBWRR Run Behind FIFO Put 1058c2ecf20Sopenharmony_ci * (ITSTV) 1068c2ecf20Sopenharmony_ci * 12 reserved 1078c2ecf20Sopenharmony_ci * 11 MCH Thermal Sensor Event 1088c2ecf20Sopenharmony_ci * for SMI/SCI/SERR (GTSE) 1098c2ecf20Sopenharmony_ci * 10 reserved 1108c2ecf20Sopenharmony_ci * 9 LOCK to non-DRAM Memory Flag (LCKF) 1118c2ecf20Sopenharmony_ci * 8 reserved 1128c2ecf20Sopenharmony_ci * 7 DRAM Throttle Flag (DTF) 1138c2ecf20Sopenharmony_ci * 6:2 reserved 1148c2ecf20Sopenharmony_ci * 1 Multi-bit DRAM ECC Error Flag (DMERR) 1158c2ecf20Sopenharmony_ci * 0 Single-bit DRAM ECC Error Flag (DSERR) 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci#define IE31200_ERRSTS 0xc8 1188c2ecf20Sopenharmony_ci#define IE31200_ERRSTS_UE BIT(1) 1198c2ecf20Sopenharmony_ci#define IE31200_ERRSTS_CE BIT(0) 1208c2ecf20Sopenharmony_ci#define IE31200_ERRSTS_BITS (IE31200_ERRSTS_UE | IE31200_ERRSTS_CE) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * Channel 0 ECC Error Log (64b) 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * 63:48 Error Column Address (ERRCOL) 1268c2ecf20Sopenharmony_ci * 47:32 Error Row Address (ERRROW) 1278c2ecf20Sopenharmony_ci * 31:29 Error Bank Address (ERRBANK) 1288c2ecf20Sopenharmony_ci * 28:27 Error Rank Address (ERRRANK) 1298c2ecf20Sopenharmony_ci * 26:24 reserved 1308c2ecf20Sopenharmony_ci * 23:16 Error Syndrome (ERRSYND) 1318c2ecf20Sopenharmony_ci * 15: 2 reserved 1328c2ecf20Sopenharmony_ci * 1 Multiple Bit Error Status (MERRSTS) 1338c2ecf20Sopenharmony_ci * 0 Correctable Error Status (CERRSTS) 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define IE31200_C0ECCERRLOG 0x40c8 1378c2ecf20Sopenharmony_ci#define IE31200_C1ECCERRLOG 0x44c8 1388c2ecf20Sopenharmony_ci#define IE31200_C0ECCERRLOG_SKL 0x4048 1398c2ecf20Sopenharmony_ci#define IE31200_C1ECCERRLOG_SKL 0x4448 1408c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_CE BIT(0) 1418c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_UE BIT(1) 1428c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27) 1438c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_RANK_SHIFT 27 1448c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_SYNDROME_BITS GENMASK_ULL(23, 16) 1458c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_SYNDROME_SHIFT 16 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define IE31200_ECCERRLOG_SYNDROME(log) \ 1488c2ecf20Sopenharmony_ci ((log & IE31200_ECCERRLOG_SYNDROME_BITS) >> \ 1498c2ecf20Sopenharmony_ci IE31200_ECCERRLOG_SYNDROME_SHIFT) 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci#define IE31200_CAPID0 0xe4 1528c2ecf20Sopenharmony_ci#define IE31200_CAPID0_PDCD BIT(4) 1538c2ecf20Sopenharmony_ci#define IE31200_CAPID0_DDPCD BIT(6) 1548c2ecf20Sopenharmony_ci#define IE31200_CAPID0_ECC BIT(1) 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_0_OFFSET 0x5004 1578c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_0_OFFSET_SKL 0x500C 1588c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) 1598c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_RANK BIT(17) 1608c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_RANK_SHIFT 17 1618c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_RANK_SKL BIT(10) 1628c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_RANK_SKL_SHIFT 10 1638c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_WIDTH BIT(19) 1648c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_WIDTH_SHIFT 19 1658c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_WIDTH_SKL GENMASK_ULL(9, 8) 1668c2ecf20Sopenharmony_ci#define IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT 8 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* Skylake reports 1GB increments, everything else is 256MB */ 1698c2ecf20Sopenharmony_ci#define IE31200_PAGES(n, skl) \ 1708c2ecf20Sopenharmony_ci (n << (28 + (2 * skl) - PAGE_SHIFT)) 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int nr_channels; 1738c2ecf20Sopenharmony_cistatic struct pci_dev *mci_pdev; 1748c2ecf20Sopenharmony_cistatic int ie31200_registered = 1; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistruct ie31200_priv { 1778c2ecf20Sopenharmony_ci void __iomem *window; 1788c2ecf20Sopenharmony_ci void __iomem *c0errlog; 1798c2ecf20Sopenharmony_ci void __iomem *c1errlog; 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cienum ie31200_chips { 1838c2ecf20Sopenharmony_ci IE31200 = 0, 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistruct ie31200_dev_info { 1878c2ecf20Sopenharmony_ci const char *ctl_name; 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistruct ie31200_error_info { 1918c2ecf20Sopenharmony_ci u16 errsts; 1928c2ecf20Sopenharmony_ci u16 errsts2; 1938c2ecf20Sopenharmony_ci u64 eccerrlog[IE31200_CHANNELS]; 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const struct ie31200_dev_info ie31200_devs[] = { 1978c2ecf20Sopenharmony_ci [IE31200] = { 1988c2ecf20Sopenharmony_ci .ctl_name = "IE31200" 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistruct dimm_data { 2038c2ecf20Sopenharmony_ci u8 size; /* in multiples of 256MB, except Skylake is 1GB */ 2048c2ecf20Sopenharmony_ci u8 dual_rank : 1, 2058c2ecf20Sopenharmony_ci x16_width : 2; /* 0 means x8 width */ 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int how_many_channels(struct pci_dev *pdev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int n_channels; 2118c2ecf20Sopenharmony_ci unsigned char capid0_2b; /* 2nd byte of CAPID0 */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, IE31200_CAPID0 + 1, &capid0_2b); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* check PDCD: Dual Channel Disable */ 2168c2ecf20Sopenharmony_ci if (capid0_2b & IE31200_CAPID0_PDCD) { 2178c2ecf20Sopenharmony_ci edac_dbg(0, "In single channel mode\n"); 2188c2ecf20Sopenharmony_ci n_channels = 1; 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci edac_dbg(0, "In dual channel mode\n"); 2218c2ecf20Sopenharmony_ci n_channels = 2; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* check DDPCD - check if both channels are filled */ 2258c2ecf20Sopenharmony_ci if (capid0_2b & IE31200_CAPID0_DDPCD) 2268c2ecf20Sopenharmony_ci edac_dbg(0, "2 DIMMS per channel disabled\n"); 2278c2ecf20Sopenharmony_ci else 2288c2ecf20Sopenharmony_ci edac_dbg(0, "2 DIMMS per channel enabled\n"); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return n_channels; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic bool ecc_capable(struct pci_dev *pdev) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci unsigned char capid0_4b; /* 4th byte of CAPID0 */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, IE31200_CAPID0 + 3, &capid0_4b); 2388c2ecf20Sopenharmony_ci if (capid0_4b & IE31200_CAPID0_ECC) 2398c2ecf20Sopenharmony_ci return false; 2408c2ecf20Sopenharmony_ci return true; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int eccerrlog_row(u64 log) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return ((log & IE31200_ECCERRLOG_RANK_BITS) >> 2468c2ecf20Sopenharmony_ci IE31200_ECCERRLOG_RANK_SHIFT); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void ie31200_clear_error_info(struct mem_ctl_info *mci) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * Clear any error bits. 2538c2ecf20Sopenharmony_ci * (Yes, we really clear bits by writing 1 to them.) 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci pci_write_bits16(to_pci_dev(mci->pdev), IE31200_ERRSTS, 2568c2ecf20Sopenharmony_ci IE31200_ERRSTS_BITS, IE31200_ERRSTS_BITS); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, 2608c2ecf20Sopenharmony_ci struct ie31200_error_info *info) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2638c2ecf20Sopenharmony_ci struct ie31200_priv *priv = mci->pvt_info; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * This is a mess because there is no atomic way to read all the 2698c2ecf20Sopenharmony_ci * registers at once and the registers can transition from CE being 2708c2ecf20Sopenharmony_ci * overwritten by UE. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts); 2738c2ecf20Sopenharmony_ci if (!(info->errsts & IE31200_ERRSTS_BITS)) 2748c2ecf20Sopenharmony_ci return; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 2778c2ecf20Sopenharmony_ci if (nr_channels == 2) 2788c2ecf20Sopenharmony_ci info->eccerrlog[1] = lo_hi_readq(priv->c1errlog); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * If the error is the same for both reads then the first set 2848c2ecf20Sopenharmony_ci * of reads is valid. If there is a change then there is a CE 2858c2ecf20Sopenharmony_ci * with no info and the second set of reads is valid and 2868c2ecf20Sopenharmony_ci * should be UE info. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 2898c2ecf20Sopenharmony_ci info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); 2908c2ecf20Sopenharmony_ci if (nr_channels == 2) 2918c2ecf20Sopenharmony_ci info->eccerrlog[1] = 2928c2ecf20Sopenharmony_ci lo_hi_readq(priv->c1errlog); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ie31200_clear_error_info(mci); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void ie31200_process_error_info(struct mem_ctl_info *mci, 2998c2ecf20Sopenharmony_ci struct ie31200_error_info *info) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci int channel; 3028c2ecf20Sopenharmony_ci u64 log; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!(info->errsts & IE31200_ERRSTS_BITS)) 3058c2ecf20Sopenharmony_ci return; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { 3088c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 3098c2ecf20Sopenharmony_ci -1, -1, -1, "UE overwrote CE", ""); 3108c2ecf20Sopenharmony_ci info->errsts = info->errsts2; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci for (channel = 0; channel < nr_channels; channel++) { 3148c2ecf20Sopenharmony_ci log = info->eccerrlog[channel]; 3158c2ecf20Sopenharmony_ci if (log & IE31200_ECCERRLOG_UE) { 3168c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 3178c2ecf20Sopenharmony_ci 0, 0, 0, 3188c2ecf20Sopenharmony_ci eccerrlog_row(log), 3198c2ecf20Sopenharmony_ci channel, -1, 3208c2ecf20Sopenharmony_ci "ie31200 UE", ""); 3218c2ecf20Sopenharmony_ci } else if (log & IE31200_ECCERRLOG_CE) { 3228c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 3238c2ecf20Sopenharmony_ci 0, 0, 3248c2ecf20Sopenharmony_ci IE31200_ECCERRLOG_SYNDROME(log), 3258c2ecf20Sopenharmony_ci eccerrlog_row(log), 3268c2ecf20Sopenharmony_ci channel, -1, 3278c2ecf20Sopenharmony_ci "ie31200 CE", ""); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void ie31200_check(struct mem_ctl_info *mci) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct ie31200_error_info info; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci edac_dbg(1, "MC%d\n", mci->mc_idx); 3378c2ecf20Sopenharmony_ci ie31200_get_and_clear_error_info(mci, &info); 3388c2ecf20Sopenharmony_ci ie31200_process_error_info(mci, &info); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void __iomem *ie31200_map_mchbar(struct pci_dev *pdev) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci union { 3448c2ecf20Sopenharmony_ci u64 mchbar; 3458c2ecf20Sopenharmony_ci struct { 3468c2ecf20Sopenharmony_ci u32 mchbar_low; 3478c2ecf20Sopenharmony_ci u32 mchbar_high; 3488c2ecf20Sopenharmony_ci }; 3498c2ecf20Sopenharmony_ci } u; 3508c2ecf20Sopenharmony_ci void __iomem *window; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, IE31200_MCHBAR_LOW, &u.mchbar_low); 3538c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, IE31200_MCHBAR_HIGH, &u.mchbar_high); 3548c2ecf20Sopenharmony_ci u.mchbar &= IE31200_MCHBAR_MASK; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (u.mchbar != (resource_size_t)u.mchbar) { 3578c2ecf20Sopenharmony_ci ie31200_printk(KERN_ERR, "mmio space beyond accessible range (0x%llx)\n", 3588c2ecf20Sopenharmony_ci (unsigned long long)u.mchbar); 3598c2ecf20Sopenharmony_ci return NULL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci window = ioremap(u.mchbar, IE31200_MMR_WINDOW_SIZE); 3638c2ecf20Sopenharmony_ci if (!window) 3648c2ecf20Sopenharmony_ci ie31200_printk(KERN_ERR, "Cannot map mmio space at 0x%llx\n", 3658c2ecf20Sopenharmony_ci (unsigned long long)u.mchbar); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return window; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void __skl_populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 3718c2ecf20Sopenharmony_ci int chan) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci dd->size = (addr_decode >> (chan << 4)) & IE31200_MAD_DIMM_SIZE; 3748c2ecf20Sopenharmony_ci dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK_SKL << (chan << 4))) ? 1 : 0; 3758c2ecf20Sopenharmony_ci dd->x16_width = ((addr_decode & (IE31200_MAD_DIMM_A_WIDTH_SKL << (chan << 4))) >> 3768c2ecf20Sopenharmony_ci (IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT + (chan << 4))); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void __populate_dimm_info(struct dimm_data *dd, u32 addr_decode, 3808c2ecf20Sopenharmony_ci int chan) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci dd->size = (addr_decode >> (chan << 3)) & IE31200_MAD_DIMM_SIZE; 3838c2ecf20Sopenharmony_ci dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK << chan)) ? 1 : 0; 3848c2ecf20Sopenharmony_ci dd->x16_width = (addr_decode & (IE31200_MAD_DIMM_A_WIDTH << chan)) ? 1 : 0; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int chan, 3888c2ecf20Sopenharmony_ci bool skl) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci if (skl) 3918c2ecf20Sopenharmony_ci __skl_populate_dimm_info(dd, addr_decode, chan); 3928c2ecf20Sopenharmony_ci else 3938c2ecf20Sopenharmony_ci __populate_dimm_info(dd, addr_decode, chan); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int ie31200_probe1(struct pci_dev *pdev, int dev_idx) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci int i, j, ret; 4008c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = NULL; 4018c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 4028c2ecf20Sopenharmony_ci struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL]; 4038c2ecf20Sopenharmony_ci void __iomem *window; 4048c2ecf20Sopenharmony_ci struct ie31200_priv *priv; 4058c2ecf20Sopenharmony_ci u32 addr_decode, mad_offset; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * Kaby Lake, Coffee Lake seem to work like Skylake. Please re-visit 4098c2ecf20Sopenharmony_ci * this logic when adding new CPU support. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci bool skl = DEVICE_ID_SKYLAKE_OR_LATER(pdev->device); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci edac_dbg(0, "MC:\n"); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (!ecc_capable(pdev)) { 4168c2ecf20Sopenharmony_ci ie31200_printk(KERN_INFO, "No ECC support\n"); 4178c2ecf20Sopenharmony_ci return -ENODEV; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci nr_channels = how_many_channels(pdev); 4218c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 4228c2ecf20Sopenharmony_ci layers[0].size = IE31200_DIMMS; 4238c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 4248c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 4258c2ecf20Sopenharmony_ci layers[1].size = nr_channels; 4268c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 4278c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 4288c2ecf20Sopenharmony_ci sizeof(struct ie31200_priv)); 4298c2ecf20Sopenharmony_ci if (!mci) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci window = ie31200_map_mchbar(pdev); 4338c2ecf20Sopenharmony_ci if (!window) { 4348c2ecf20Sopenharmony_ci ret = -ENODEV; 4358c2ecf20Sopenharmony_ci goto fail_free; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci edac_dbg(3, "MC: init mci\n"); 4398c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 4408c2ecf20Sopenharmony_ci if (skl) 4418c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR4; 4428c2ecf20Sopenharmony_ci else 4438c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR3; 4448c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_SECDED; 4458c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 4468c2ecf20Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 4478c2ecf20Sopenharmony_ci mci->ctl_name = ie31200_devs[dev_idx].ctl_name; 4488c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pdev); 4498c2ecf20Sopenharmony_ci mci->edac_check = ie31200_check; 4508c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 4518c2ecf20Sopenharmony_ci priv = mci->pvt_info; 4528c2ecf20Sopenharmony_ci priv->window = window; 4538c2ecf20Sopenharmony_ci if (skl) { 4548c2ecf20Sopenharmony_ci priv->c0errlog = window + IE31200_C0ECCERRLOG_SKL; 4558c2ecf20Sopenharmony_ci priv->c1errlog = window + IE31200_C1ECCERRLOG_SKL; 4568c2ecf20Sopenharmony_ci mad_offset = IE31200_MAD_DIMM_0_OFFSET_SKL; 4578c2ecf20Sopenharmony_ci } else { 4588c2ecf20Sopenharmony_ci priv->c0errlog = window + IE31200_C0ECCERRLOG; 4598c2ecf20Sopenharmony_ci priv->c1errlog = window + IE31200_C1ECCERRLOG; 4608c2ecf20Sopenharmony_ci mad_offset = IE31200_MAD_DIMM_0_OFFSET; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* populate DIMM info */ 4648c2ecf20Sopenharmony_ci for (i = 0; i < IE31200_CHANNELS; i++) { 4658c2ecf20Sopenharmony_ci addr_decode = readl(window + mad_offset + 4668c2ecf20Sopenharmony_ci (i * 4)); 4678c2ecf20Sopenharmony_ci edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); 4688c2ecf20Sopenharmony_ci for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { 4698c2ecf20Sopenharmony_ci populate_dimm_info(&dimm_info[i][j], addr_decode, j, 4708c2ecf20Sopenharmony_ci skl); 4718c2ecf20Sopenharmony_ci edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n", 4728c2ecf20Sopenharmony_ci dimm_info[i][j].size, 4738c2ecf20Sopenharmony_ci dimm_info[i][j].dual_rank, 4748c2ecf20Sopenharmony_ci dimm_info[i][j].x16_width); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * The dram rank boundary (DRB) reg values are boundary addresses 4808c2ecf20Sopenharmony_ci * for each DRAM rank with a granularity of 64MB. DRB regs are 4818c2ecf20Sopenharmony_ci * cumulative; the last one will contain the total memory 4828c2ecf20Sopenharmony_ci * contained in all ranks. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci for (i = 0; i < IE31200_DIMMS_PER_CHANNEL; i++) { 4858c2ecf20Sopenharmony_ci for (j = 0; j < IE31200_CHANNELS; j++) { 4868c2ecf20Sopenharmony_ci struct dimm_info *dimm; 4878c2ecf20Sopenharmony_ci unsigned long nr_pages; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci nr_pages = IE31200_PAGES(dimm_info[j][i].size, skl); 4908c2ecf20Sopenharmony_ci if (nr_pages == 0) 4918c2ecf20Sopenharmony_ci continue; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (dimm_info[j][i].dual_rank) { 4948c2ecf20Sopenharmony_ci nr_pages = nr_pages / 2; 4958c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, (i * 2) + 1, j, 0); 4968c2ecf20Sopenharmony_ci dimm->nr_pages = nr_pages; 4978c2ecf20Sopenharmony_ci edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 4988c2ecf20Sopenharmony_ci dimm->grain = 8; /* just a guess */ 4998c2ecf20Sopenharmony_ci if (skl) 5008c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR4; 5018c2ecf20Sopenharmony_ci else 5028c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR3; 5038c2ecf20Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 5048c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_UNKNOWN; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, i * 2, j, 0); 5078c2ecf20Sopenharmony_ci dimm->nr_pages = nr_pages; 5088c2ecf20Sopenharmony_ci edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); 5098c2ecf20Sopenharmony_ci dimm->grain = 8; /* same guess */ 5108c2ecf20Sopenharmony_ci if (skl) 5118c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR4; 5128c2ecf20Sopenharmony_ci else 5138c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR3; 5148c2ecf20Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 5158c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_UNKNOWN; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci ie31200_clear_error_info(mci); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 5228c2ecf20Sopenharmony_ci edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 5238c2ecf20Sopenharmony_ci ret = -ENODEV; 5248c2ecf20Sopenharmony_ci goto fail_unmap; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* get this far and it's successful */ 5288c2ecf20Sopenharmony_ci edac_dbg(3, "MC: success\n"); 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cifail_unmap: 5328c2ecf20Sopenharmony_ci iounmap(window); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cifail_free: 5358c2ecf20Sopenharmony_ci edac_mc_free(mci); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int ie31200_init_one(struct pci_dev *pdev, 5418c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci int rc; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci edac_dbg(0, "MC:\n"); 5468c2ecf20Sopenharmony_ci if (pci_enable_device(pdev) < 0) 5478c2ecf20Sopenharmony_ci return -EIO; 5488c2ecf20Sopenharmony_ci rc = ie31200_probe1(pdev, ent->driver_data); 5498c2ecf20Sopenharmony_ci if (rc == 0 && !mci_pdev) 5508c2ecf20Sopenharmony_ci mci_pdev = pci_dev_get(pdev); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return rc; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic void ie31200_remove_one(struct pci_dev *pdev) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 5588c2ecf20Sopenharmony_ci struct ie31200_priv *priv; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 5618c2ecf20Sopenharmony_ci pci_dev_put(mci_pdev); 5628c2ecf20Sopenharmony_ci mci_pdev = NULL; 5638c2ecf20Sopenharmony_ci mci = edac_mc_del_mc(&pdev->dev); 5648c2ecf20Sopenharmony_ci if (!mci) 5658c2ecf20Sopenharmony_ci return; 5668c2ecf20Sopenharmony_ci priv = mci->pvt_info; 5678c2ecf20Sopenharmony_ci iounmap(priv->window); 5688c2ecf20Sopenharmony_ci edac_mc_free(mci); 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic const struct pci_device_id ie31200_pci_tbl[] = { 5728c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5738c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5748c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5758c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5768c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5778c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5788c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5798c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5808c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_9), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5818c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_1), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5828c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_2), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5838c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_3), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5848c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_4), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5858c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_5), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5868c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_6), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5878c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5888c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5898c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_9), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5908c2ecf20Sopenharmony_ci { PCI_VEND_DEV(INTEL, IE31200_HB_CFL_10), PCI_ANY_ID, PCI_ANY_ID, 0, 0, IE31200 }, 5918c2ecf20Sopenharmony_ci { 0, } /* 0 terminated list. */ 5928c2ecf20Sopenharmony_ci}; 5938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ie31200_pci_tbl); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic struct pci_driver ie31200_driver = { 5968c2ecf20Sopenharmony_ci .name = EDAC_MOD_STR, 5978c2ecf20Sopenharmony_ci .probe = ie31200_init_one, 5988c2ecf20Sopenharmony_ci .remove = ie31200_remove_one, 5998c2ecf20Sopenharmony_ci .id_table = ie31200_pci_tbl, 6008c2ecf20Sopenharmony_ci}; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int __init ie31200_init(void) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci int pci_rc, i; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci edac_dbg(3, "MC:\n"); 6078c2ecf20Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 6088c2ecf20Sopenharmony_ci opstate_init(); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci pci_rc = pci_register_driver(&ie31200_driver); 6118c2ecf20Sopenharmony_ci if (pci_rc < 0) 6128c2ecf20Sopenharmony_ci goto fail0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (!mci_pdev) { 6158c2ecf20Sopenharmony_ci ie31200_registered = 0; 6168c2ecf20Sopenharmony_ci for (i = 0; ie31200_pci_tbl[i].vendor != 0; i++) { 6178c2ecf20Sopenharmony_ci mci_pdev = pci_get_device(ie31200_pci_tbl[i].vendor, 6188c2ecf20Sopenharmony_ci ie31200_pci_tbl[i].device, 6198c2ecf20Sopenharmony_ci NULL); 6208c2ecf20Sopenharmony_ci if (mci_pdev) 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci if (!mci_pdev) { 6248c2ecf20Sopenharmony_ci edac_dbg(0, "ie31200 pci_get_device fail\n"); 6258c2ecf20Sopenharmony_ci pci_rc = -ENODEV; 6268c2ecf20Sopenharmony_ci goto fail1; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci pci_rc = ie31200_init_one(mci_pdev, &ie31200_pci_tbl[i]); 6298c2ecf20Sopenharmony_ci if (pci_rc < 0) { 6308c2ecf20Sopenharmony_ci edac_dbg(0, "ie31200 init fail\n"); 6318c2ecf20Sopenharmony_ci pci_rc = -ENODEV; 6328c2ecf20Sopenharmony_ci goto fail1; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci return 0; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cifail1: 6388c2ecf20Sopenharmony_ci pci_unregister_driver(&ie31200_driver); 6398c2ecf20Sopenharmony_cifail0: 6408c2ecf20Sopenharmony_ci pci_dev_put(mci_pdev); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return pci_rc; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void __exit ie31200_exit(void) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci edac_dbg(3, "MC:\n"); 6488c2ecf20Sopenharmony_ci pci_unregister_driver(&ie31200_driver); 6498c2ecf20Sopenharmony_ci if (!ie31200_registered) 6508c2ecf20Sopenharmony_ci ie31200_remove_one(mci_pdev); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cimodule_init(ie31200_init); 6548c2ecf20Sopenharmony_cimodule_exit(ie31200_exit); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jason Baron <jbaron@akamai.com>"); 6588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC support for Intel Processor E31200 memory hub controllers"); 659