18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Intel i7 core/Nehalem Memory Controller kernel module 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This driver supports the memory controllers found on the Intel 58c2ecf20Sopenharmony_ci * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx, 68c2ecf20Sopenharmony_ci * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield 78c2ecf20Sopenharmony_ci * and Westmere-EP. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2009-2010 by: 108c2ecf20Sopenharmony_ci * Mauro Carvalho Chehab 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Red Hat Inc. https://www.redhat.com 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Forked and adapted from the i5400_edac driver 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Based on the following public Intel datasheets: 178c2ecf20Sopenharmony_ci * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor 188c2ecf20Sopenharmony_ci * Datasheet, Volume 2: 198c2ecf20Sopenharmony_ci * http://download.intel.com/design/processor/datashts/320835.pdf 208c2ecf20Sopenharmony_ci * Intel Xeon Processor 5500 Series Datasheet Volume 2 218c2ecf20Sopenharmony_ci * http://www.intel.com/Assets/PDF/datasheet/321322.pdf 228c2ecf20Sopenharmony_ci * also available at: 238c2ecf20Sopenharmony_ci * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/init.h> 288c2ecf20Sopenharmony_ci#include <linux/pci.h> 298c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci#include <linux/delay.h> 328c2ecf20Sopenharmony_ci#include <linux/dmi.h> 338c2ecf20Sopenharmony_ci#include <linux/edac.h> 348c2ecf20Sopenharmony_ci#include <linux/mmzone.h> 358c2ecf20Sopenharmony_ci#include <linux/smp.h> 368c2ecf20Sopenharmony_ci#include <asm/mce.h> 378c2ecf20Sopenharmony_ci#include <asm/processor.h> 388c2ecf20Sopenharmony_ci#include <asm/div64.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "edac_module.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Static vars */ 438c2ecf20Sopenharmony_cistatic LIST_HEAD(i7core_edac_list); 448c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(i7core_edac_lock); 458c2ecf20Sopenharmony_cistatic int probed; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int use_pci_fixup; 488c2ecf20Sopenharmony_cimodule_param(use_pci_fixup, int, 0444); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices"); 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core 528c2ecf20Sopenharmony_ci * registers start at bus 255, and are not reported by BIOS. 538c2ecf20Sopenharmony_ci * We currently find devices with only 2 sockets. In order to support more QPI 548c2ecf20Sopenharmony_ci * Quick Path Interconnect, just increment this number. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define MAX_SOCKET_BUSES 2 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * Alter this version for the module when modifications are made 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define I7CORE_REVISION " Ver: 1.0.0" 638c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "i7core_edac" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Debug macros 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci#define i7core_printk(level, fmt, arg...) \ 698c2ecf20Sopenharmony_ci edac_printk(level, "i7core", fmt, ##arg) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define i7core_mc_printk(mci, level, fmt, arg...) \ 728c2ecf20Sopenharmony_ci edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * i7core Memory Controller Registers 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* OFFSETS for Device 0 Function 0 */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define MC_CFG_CONTROL 0x90 818c2ecf20Sopenharmony_ci #define MC_CFG_UNLOCK 0x02 828c2ecf20Sopenharmony_ci #define MC_CFG_LOCK 0x00 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* OFFSETS for Device 3 Function 0 */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define MC_CONTROL 0x48 878c2ecf20Sopenharmony_ci#define MC_STATUS 0x4c 888c2ecf20Sopenharmony_ci#define MC_MAX_DOD 0x64 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * OFFSETS for Device 3 Function 4, as indicated on Xeon 5500 datasheet: 928c2ecf20Sopenharmony_ci * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define MC_TEST_ERR_RCV1 0x60 968c2ecf20Sopenharmony_ci #define DIMM2_COR_ERR(r) ((r) & 0x7fff) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define MC_TEST_ERR_RCV0 0x64 998c2ecf20Sopenharmony_ci #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff) 1008c2ecf20Sopenharmony_ci #define DIMM0_COR_ERR(r) ((r) & 0x7fff) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* OFFSETS for Device 3 Function 2, as indicated on Xeon 5500 datasheet */ 1038c2ecf20Sopenharmony_ci#define MC_SSRCONTROL 0x48 1048c2ecf20Sopenharmony_ci #define SSR_MODE_DISABLE 0x00 1058c2ecf20Sopenharmony_ci #define SSR_MODE_ENABLE 0x01 1068c2ecf20Sopenharmony_ci #define SSR_MODE_MASK 0x03 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define MC_SCRUB_CONTROL 0x4c 1098c2ecf20Sopenharmony_ci #define STARTSCRUB (1 << 24) 1108c2ecf20Sopenharmony_ci #define SCRUBINTERVAL_MASK 0xffffff 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define MC_COR_ECC_CNT_0 0x80 1138c2ecf20Sopenharmony_ci#define MC_COR_ECC_CNT_1 0x84 1148c2ecf20Sopenharmony_ci#define MC_COR_ECC_CNT_2 0x88 1158c2ecf20Sopenharmony_ci#define MC_COR_ECC_CNT_3 0x8c 1168c2ecf20Sopenharmony_ci#define MC_COR_ECC_CNT_4 0x90 1178c2ecf20Sopenharmony_ci#define MC_COR_ECC_CNT_5 0x94 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff) 1208c2ecf20Sopenharmony_ci#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* OFFSETS for Devices 4,5 and 6 Function 0 */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58 1268c2ecf20Sopenharmony_ci #define THREE_DIMMS_PRESENT (1 << 24) 1278c2ecf20Sopenharmony_ci #define SINGLE_QUAD_RANK_PRESENT (1 << 23) 1288c2ecf20Sopenharmony_ci #define QUAD_RANK_PRESENT (1 << 22) 1298c2ecf20Sopenharmony_ci #define REGISTERED_DIMM (1 << 15) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define MC_CHANNEL_MAPPER 0x60 1328c2ecf20Sopenharmony_ci #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1) 1338c2ecf20Sopenharmony_ci #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define MC_CHANNEL_RANK_PRESENT 0x7c 1368c2ecf20Sopenharmony_ci #define RANK_PRESENT_MASK 0xffff 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define MC_CHANNEL_ADDR_MATCH 0xf0 1398c2ecf20Sopenharmony_ci#define MC_CHANNEL_ERROR_MASK 0xf8 1408c2ecf20Sopenharmony_ci#define MC_CHANNEL_ERROR_INJECT 0xfc 1418c2ecf20Sopenharmony_ci #define INJECT_ADDR_PARITY 0x10 1428c2ecf20Sopenharmony_ci #define INJECT_ECC 0x08 1438c2ecf20Sopenharmony_ci #define MASK_CACHELINE 0x06 1448c2ecf20Sopenharmony_ci #define MASK_FULL_CACHELINE 0x06 1458c2ecf20Sopenharmony_ci #define MASK_MSB32_CACHELINE 0x04 1468c2ecf20Sopenharmony_ci #define MASK_LSB32_CACHELINE 0x02 1478c2ecf20Sopenharmony_ci #define NO_MASK_CACHELINE 0x00 1488c2ecf20Sopenharmony_ci #define REPEAT_EN 0x01 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* OFFSETS for Devices 4,5 and 6 Function 1 */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#define MC_DOD_CH_DIMM0 0x48 1538c2ecf20Sopenharmony_ci#define MC_DOD_CH_DIMM1 0x4c 1548c2ecf20Sopenharmony_ci#define MC_DOD_CH_DIMM2 0x50 1558c2ecf20Sopenharmony_ci #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10)) 1568c2ecf20Sopenharmony_ci #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10) 1578c2ecf20Sopenharmony_ci #define DIMM_PRESENT_MASK (1 << 9) 1588c2ecf20Sopenharmony_ci #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9) 1598c2ecf20Sopenharmony_ci #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7)) 1608c2ecf20Sopenharmony_ci #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7) 1618c2ecf20Sopenharmony_ci #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5)) 1628c2ecf20Sopenharmony_ci #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5) 1638c2ecf20Sopenharmony_ci #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2)) 1648c2ecf20Sopenharmony_ci #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2) 1658c2ecf20Sopenharmony_ci #define MC_DOD_NUMCOL_MASK 3 1668c2ecf20Sopenharmony_ci #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define MC_RANK_PRESENT 0x7c 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define MC_SAG_CH_0 0x80 1718c2ecf20Sopenharmony_ci#define MC_SAG_CH_1 0x84 1728c2ecf20Sopenharmony_ci#define MC_SAG_CH_2 0x88 1738c2ecf20Sopenharmony_ci#define MC_SAG_CH_3 0x8c 1748c2ecf20Sopenharmony_ci#define MC_SAG_CH_4 0x90 1758c2ecf20Sopenharmony_ci#define MC_SAG_CH_5 0x94 1768c2ecf20Sopenharmony_ci#define MC_SAG_CH_6 0x98 1778c2ecf20Sopenharmony_ci#define MC_SAG_CH_7 0x9c 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_0 0x40 1808c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_1 0x44 1818c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_2 0x48 1828c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_3 0x4C 1838c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_4 0x50 1848c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_5 0x54 1858c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_6 0x58 1868c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_CH_7 0x5C 1878c2ecf20Sopenharmony_ci#define MC_RIR_LIMIT_MASK ((1 << 10) - 1) 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define MC_RIR_WAY_CH 0x80 1908c2ecf20Sopenharmony_ci #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7) 1918c2ecf20Sopenharmony_ci #define MC_RIR_WAY_RANK_MASK 0x7 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * i7core structs 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#define NUM_CHANS 3 1988c2ecf20Sopenharmony_ci#define MAX_DIMMS 3 /* Max DIMMS per channel */ 1998c2ecf20Sopenharmony_ci#define MAX_MCR_FUNC 4 2008c2ecf20Sopenharmony_ci#define MAX_CHAN_FUNC 3 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistruct i7core_info { 2038c2ecf20Sopenharmony_ci u32 mc_control; 2048c2ecf20Sopenharmony_ci u32 mc_status; 2058c2ecf20Sopenharmony_ci u32 max_dod; 2068c2ecf20Sopenharmony_ci u32 ch_map; 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistruct i7core_inject { 2118c2ecf20Sopenharmony_ci int enable; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci u32 section; 2148c2ecf20Sopenharmony_ci u32 type; 2158c2ecf20Sopenharmony_ci u32 eccmask; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Error address mask */ 2188c2ecf20Sopenharmony_ci int channel, dimm, rank, bank, page, col; 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistruct i7core_channel { 2228c2ecf20Sopenharmony_ci bool is_3dimms_present; 2238c2ecf20Sopenharmony_ci bool is_single_4rank; 2248c2ecf20Sopenharmony_ci bool has_4rank; 2258c2ecf20Sopenharmony_ci u32 dimms; 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistruct pci_id_descr { 2298c2ecf20Sopenharmony_ci int dev; 2308c2ecf20Sopenharmony_ci int func; 2318c2ecf20Sopenharmony_ci int dev_id; 2328c2ecf20Sopenharmony_ci int optional; 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistruct pci_id_table { 2368c2ecf20Sopenharmony_ci const struct pci_id_descr *descr; 2378c2ecf20Sopenharmony_ci int n_devs; 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistruct i7core_dev { 2418c2ecf20Sopenharmony_ci struct list_head list; 2428c2ecf20Sopenharmony_ci u8 socket; 2438c2ecf20Sopenharmony_ci struct pci_dev **pdev; 2448c2ecf20Sopenharmony_ci int n_devs; 2458c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistruct i7core_pvt { 2498c2ecf20Sopenharmony_ci struct device *addrmatch_dev, *chancounts_dev; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci struct pci_dev *pci_noncore; 2528c2ecf20Sopenharmony_ci struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; 2538c2ecf20Sopenharmony_ci struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci struct i7core_info info; 2588c2ecf20Sopenharmony_ci struct i7core_inject inject; 2598c2ecf20Sopenharmony_ci struct i7core_channel channel[NUM_CHANS]; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci int ce_count_available; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* ECC corrected errors counts per udimm */ 2648c2ecf20Sopenharmony_ci unsigned long udimm_ce_count[MAX_DIMMS]; 2658c2ecf20Sopenharmony_ci int udimm_last_ce_count[MAX_DIMMS]; 2668c2ecf20Sopenharmony_ci /* ECC corrected errors counts per rdimm */ 2678c2ecf20Sopenharmony_ci unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS]; 2688c2ecf20Sopenharmony_ci int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS]; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci bool is_registered, enable_scrub; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* DCLK Frequency used for computing scrub rate */ 2738c2ecf20Sopenharmony_ci int dclk_freq; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Struct to control EDAC polling */ 2768c2ecf20Sopenharmony_ci struct edac_pci_ctl_info *i7core_pci; 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci#define PCI_DESCR(device, function, device_id) \ 2808c2ecf20Sopenharmony_ci .dev = (device), \ 2818c2ecf20Sopenharmony_ci .func = (function), \ 2828c2ecf20Sopenharmony_ci .dev_id = (device_id) 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { 2858c2ecf20Sopenharmony_ci /* Memory controller */ 2868c2ecf20Sopenharmony_ci { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, 2878c2ecf20Sopenharmony_ci { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, 2888c2ecf20Sopenharmony_ci /* Exists only for RDIMM */ 2898c2ecf20Sopenharmony_ci { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 }, 2908c2ecf20Sopenharmony_ci { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Channel 0 */ 2938c2ecf20Sopenharmony_ci { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) }, 2948c2ecf20Sopenharmony_ci { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) }, 2958c2ecf20Sopenharmony_ci { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) }, 2968c2ecf20Sopenharmony_ci { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) }, 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Channel 1 */ 2998c2ecf20Sopenharmony_ci { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) }, 3008c2ecf20Sopenharmony_ci { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) }, 3018c2ecf20Sopenharmony_ci { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) }, 3028c2ecf20Sopenharmony_ci { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) }, 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Channel 2 */ 3058c2ecf20Sopenharmony_ci { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) }, 3068c2ecf20Sopenharmony_ci { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) }, 3078c2ecf20Sopenharmony_ci { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) }, 3088c2ecf20Sopenharmony_ci { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) }, 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Generic Non-core registers */ 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * This is the PCI device on i7core and on Xeon 35xx (8086:2c41) 3138c2ecf20Sopenharmony_ci * On Xeon 55xx, however, it has a different id (8086:2c40). So, 3148c2ecf20Sopenharmony_ci * the probing code needs to test for the other address in case of 3158c2ecf20Sopenharmony_ci * failure of this one 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) }, 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic const struct pci_id_descr pci_dev_descr_lynnfield[] = { 3228c2ecf20Sopenharmony_ci { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) }, 3238c2ecf20Sopenharmony_ci { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) }, 3248c2ecf20Sopenharmony_ci { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) }, 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) }, 3278c2ecf20Sopenharmony_ci { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) }, 3288c2ecf20Sopenharmony_ci { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) }, 3298c2ecf20Sopenharmony_ci { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) }, 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) }, 3328c2ecf20Sopenharmony_ci { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, 3338c2ecf20Sopenharmony_ci { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, 3348c2ecf20Sopenharmony_ci { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* 3378c2ecf20Sopenharmony_ci * This is the PCI device has an alternate address on some 3388c2ecf20Sopenharmony_ci * processors like Core i7 860 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) }, 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic const struct pci_id_descr pci_dev_descr_i7core_westmere[] = { 3448c2ecf20Sopenharmony_ci /* Memory controller */ 3458c2ecf20Sopenharmony_ci { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) }, 3468c2ecf20Sopenharmony_ci { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) }, 3478c2ecf20Sopenharmony_ci /* Exists only for RDIMM */ 3488c2ecf20Sopenharmony_ci { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 }, 3498c2ecf20Sopenharmony_ci { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) }, 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Channel 0 */ 3528c2ecf20Sopenharmony_ci { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) }, 3538c2ecf20Sopenharmony_ci { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) }, 3548c2ecf20Sopenharmony_ci { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) }, 3558c2ecf20Sopenharmony_ci { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) }, 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Channel 1 */ 3588c2ecf20Sopenharmony_ci { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) }, 3598c2ecf20Sopenharmony_ci { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) }, 3608c2ecf20Sopenharmony_ci { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) }, 3618c2ecf20Sopenharmony_ci { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) }, 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Channel 2 */ 3648c2ecf20Sopenharmony_ci { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) }, 3658c2ecf20Sopenharmony_ci { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) }, 3668c2ecf20Sopenharmony_ci { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) }, 3678c2ecf20Sopenharmony_ci { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) }, 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Generic Non-core registers */ 3708c2ecf20Sopenharmony_ci { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) }, 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci}; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) } 3758c2ecf20Sopenharmony_cistatic const struct pci_id_table pci_dev_table[] = { 3768c2ecf20Sopenharmony_ci PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem), 3778c2ecf20Sopenharmony_ci PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield), 3788c2ecf20Sopenharmony_ci PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere), 3798c2ecf20Sopenharmony_ci {0,} /* 0 terminated list. */ 3808c2ecf20Sopenharmony_ci}; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * pci_device_id table for which devices we are looking for 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistatic const struct pci_device_id i7core_pci_tbl[] = { 3868c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, 3878c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)}, 3888c2ecf20Sopenharmony_ci {0,} /* 0 terminated list. */ 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/**************************************************************************** 3928c2ecf20Sopenharmony_ci Ancillary status routines 3938c2ecf20Sopenharmony_ci ****************************************************************************/ 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* MC_CONTROL bits */ 3968c2ecf20Sopenharmony_ci#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch))) 3978c2ecf20Sopenharmony_ci#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1)) 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* MC_STATUS bits */ 4008c2ecf20Sopenharmony_ci#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4)) 4018c2ecf20Sopenharmony_ci#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch)) 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* MC_MAX_DOD read functions */ 4048c2ecf20Sopenharmony_cistatic inline int numdimms(u32 dimms) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci return (dimms & 0x3) + 1; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic inline int numrank(u32 rank) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci static const int ranks[] = { 1, 2, 4, -EINVAL }; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return ranks[rank & 0x3]; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic inline int numbank(u32 bank) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci static const int banks[] = { 4, 8, 16, -EINVAL }; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return banks[bank & 0x3]; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic inline int numrow(u32 row) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci static const int rows[] = { 4268c2ecf20Sopenharmony_ci 1 << 12, 1 << 13, 1 << 14, 1 << 15, 4278c2ecf20Sopenharmony_ci 1 << 16, -EINVAL, -EINVAL, -EINVAL, 4288c2ecf20Sopenharmony_ci }; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return rows[row & 0x7]; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic inline int numcol(u32 col) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci static const int cols[] = { 4368c2ecf20Sopenharmony_ci 1 << 10, 1 << 11, 1 << 12, -EINVAL, 4378c2ecf20Sopenharmony_ci }; 4388c2ecf20Sopenharmony_ci return cols[col & 0x3]; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic struct i7core_dev *get_i7core_dev(u8 socket) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci list_for_each_entry(i7core_dev, &i7core_edac_list, list) { 4468c2ecf20Sopenharmony_ci if (i7core_dev->socket == socket) 4478c2ecf20Sopenharmony_ci return i7core_dev; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return NULL; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic struct i7core_dev *alloc_i7core_dev(u8 socket, 4548c2ecf20Sopenharmony_ci const struct pci_id_table *table) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (!i7core_dev) 4608c2ecf20Sopenharmony_ci return NULL; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci i7core_dev->pdev = kcalloc(table->n_devs, sizeof(*i7core_dev->pdev), 4638c2ecf20Sopenharmony_ci GFP_KERNEL); 4648c2ecf20Sopenharmony_ci if (!i7core_dev->pdev) { 4658c2ecf20Sopenharmony_ci kfree(i7core_dev); 4668c2ecf20Sopenharmony_ci return NULL; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci i7core_dev->socket = socket; 4708c2ecf20Sopenharmony_ci i7core_dev->n_devs = table->n_devs; 4718c2ecf20Sopenharmony_ci list_add_tail(&i7core_dev->list, &i7core_edac_list); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return i7core_dev; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void free_i7core_dev(struct i7core_dev *i7core_dev) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci list_del(&i7core_dev->list); 4798c2ecf20Sopenharmony_ci kfree(i7core_dev->pdev); 4808c2ecf20Sopenharmony_ci kfree(i7core_dev); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/**************************************************************************** 4848c2ecf20Sopenharmony_ci Memory check routines 4858c2ecf20Sopenharmony_ci ****************************************************************************/ 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int get_dimm_config(struct mem_ctl_info *mci) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 4908c2ecf20Sopenharmony_ci struct pci_dev *pdev; 4918c2ecf20Sopenharmony_ci int i, j; 4928c2ecf20Sopenharmony_ci enum edac_type mode; 4938c2ecf20Sopenharmony_ci enum mem_type mtype; 4948c2ecf20Sopenharmony_ci struct dimm_info *dimm; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Get data from the MC register, function 0 */ 4978c2ecf20Sopenharmony_ci pdev = pvt->pci_mcr[0]; 4988c2ecf20Sopenharmony_ci if (!pdev) 4998c2ecf20Sopenharmony_ci return -ENODEV; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Device 3 function 0 reads */ 5028c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control); 5038c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status); 5048c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod); 5058c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", 5088c2ecf20Sopenharmony_ci pvt->i7core_dev->socket, pvt->info.mc_control, 5098c2ecf20Sopenharmony_ci pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (ECC_ENABLED(pvt)) { 5128c2ecf20Sopenharmony_ci edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4); 5138c2ecf20Sopenharmony_ci if (ECCx8(pvt)) 5148c2ecf20Sopenharmony_ci mode = EDAC_S8ECD8ED; 5158c2ecf20Sopenharmony_ci else 5168c2ecf20Sopenharmony_ci mode = EDAC_S4ECD4ED; 5178c2ecf20Sopenharmony_ci } else { 5188c2ecf20Sopenharmony_ci edac_dbg(0, "ECC disabled\n"); 5198c2ecf20Sopenharmony_ci mode = EDAC_NONE; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* FIXME: need to handle the error codes */ 5238c2ecf20Sopenharmony_ci edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n", 5248c2ecf20Sopenharmony_ci numdimms(pvt->info.max_dod), 5258c2ecf20Sopenharmony_ci numrank(pvt->info.max_dod >> 2), 5268c2ecf20Sopenharmony_ci numbank(pvt->info.max_dod >> 4), 5278c2ecf20Sopenharmony_ci numrow(pvt->info.max_dod >> 6), 5288c2ecf20Sopenharmony_ci numcol(pvt->info.max_dod >> 9)); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci for (i = 0; i < NUM_CHANS; i++) { 5318c2ecf20Sopenharmony_ci u32 data, dimm_dod[3], value[8]; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (!pvt->pci_ch[i][0]) 5348c2ecf20Sopenharmony_ci continue; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!CH_ACTIVE(pvt, i)) { 5378c2ecf20Sopenharmony_ci edac_dbg(0, "Channel %i is not active\n", i); 5388c2ecf20Sopenharmony_ci continue; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci if (CH_DISABLED(pvt, i)) { 5418c2ecf20Sopenharmony_ci edac_dbg(0, "Channel %i is disabled\n", i); 5428c2ecf20Sopenharmony_ci continue; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Devices 4-6 function 0 */ 5468c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_ch[i][0], 5478c2ecf20Sopenharmony_ci MC_CHANNEL_DIMM_INIT_PARAMS, &data); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (data & THREE_DIMMS_PRESENT) 5518c2ecf20Sopenharmony_ci pvt->channel[i].is_3dimms_present = true; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (data & SINGLE_QUAD_RANK_PRESENT) 5548c2ecf20Sopenharmony_ci pvt->channel[i].is_single_4rank = true; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (data & QUAD_RANK_PRESENT) 5578c2ecf20Sopenharmony_ci pvt->channel[i].has_4rank = true; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (data & REGISTERED_DIMM) 5608c2ecf20Sopenharmony_ci mtype = MEM_RDDR3; 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci mtype = MEM_DDR3; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* Devices 4-6 function 1 */ 5658c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_ch[i][1], 5668c2ecf20Sopenharmony_ci MC_DOD_CH_DIMM0, &dimm_dod[0]); 5678c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_ch[i][1], 5688c2ecf20Sopenharmony_ci MC_DOD_CH_DIMM1, &dimm_dod[1]); 5698c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_ch[i][1], 5708c2ecf20Sopenharmony_ci MC_DOD_CH_DIMM2, &dimm_dod[2]); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n", 5738c2ecf20Sopenharmony_ci i, 5748c2ecf20Sopenharmony_ci RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), 5758c2ecf20Sopenharmony_ci data, 5768c2ecf20Sopenharmony_ci pvt->channel[i].is_3dimms_present ? "3DIMMS " : "", 5778c2ecf20Sopenharmony_ci pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "", 5788c2ecf20Sopenharmony_ci pvt->channel[i].has_4rank ? "HAS_4R " : "", 5798c2ecf20Sopenharmony_ci (data & REGISTERED_DIMM) ? 'R' : 'U'); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci for (j = 0; j < 3; j++) { 5828c2ecf20Sopenharmony_ci u32 banks, ranks, rows, cols; 5838c2ecf20Sopenharmony_ci u32 size, npages; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (!DIMM_PRESENT(dimm_dod[j])) 5868c2ecf20Sopenharmony_ci continue; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, i, j, 0); 5898c2ecf20Sopenharmony_ci banks = numbank(MC_DOD_NUMBANK(dimm_dod[j])); 5908c2ecf20Sopenharmony_ci ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j])); 5918c2ecf20Sopenharmony_ci rows = numrow(MC_DOD_NUMROW(dimm_dod[j])); 5928c2ecf20Sopenharmony_ci cols = numcol(MC_DOD_NUMCOL(dimm_dod[j])); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* DDR3 has 8 I/O banks */ 5958c2ecf20Sopenharmony_ci size = (rows * cols * banks * ranks) >> (20 - 3); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci edac_dbg(0, "\tdimm %d %d MiB offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n", 5988c2ecf20Sopenharmony_ci j, size, 5998c2ecf20Sopenharmony_ci RANKOFFSET(dimm_dod[j]), 6008c2ecf20Sopenharmony_ci banks, ranks, rows, cols); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci npages = MiB_TO_PAGES(size); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci dimm->nr_pages = npages; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci switch (banks) { 6078c2ecf20Sopenharmony_ci case 4: 6088c2ecf20Sopenharmony_ci dimm->dtype = DEV_X4; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case 8: 6118c2ecf20Sopenharmony_ci dimm->dtype = DEV_X8; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci case 16: 6148c2ecf20Sopenharmony_ci dimm->dtype = DEV_X16; 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci default: 6178c2ecf20Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci snprintf(dimm->label, sizeof(dimm->label), 6218c2ecf20Sopenharmony_ci "CPU#%uChannel#%u_DIMM#%u", 6228c2ecf20Sopenharmony_ci pvt->i7core_dev->socket, i, j); 6238c2ecf20Sopenharmony_ci dimm->grain = 8; 6248c2ecf20Sopenharmony_ci dimm->edac_mode = mode; 6258c2ecf20Sopenharmony_ci dimm->mtype = mtype; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); 6298c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]); 6308c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]); 6318c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]); 6328c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]); 6338c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); 6348c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); 6358c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); 6368c2ecf20Sopenharmony_ci edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); 6378c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) 6388c2ecf20Sopenharmony_ci edac_dbg(1, "\t\t%#x\t%#x\t%#x\n", 6398c2ecf20Sopenharmony_ci (value[j] >> 27) & 0x1, 6408c2ecf20Sopenharmony_ci (value[j] >> 24) & 0x7, 6418c2ecf20Sopenharmony_ci (value[j] & ((1 << 24) - 1))); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/**************************************************************************** 6488c2ecf20Sopenharmony_ci Error insertion routines 6498c2ecf20Sopenharmony_ci ****************************************************************************/ 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci#define to_mci(k) container_of(k, struct mem_ctl_info, dev) 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/* The i7core has independent error injection features per channel. 6548c2ecf20Sopenharmony_ci However, to have a simpler code, we don't allow enabling error injection 6558c2ecf20Sopenharmony_ci on more than one channel. 6568c2ecf20Sopenharmony_ci Also, since a change at an inject parameter will be applied only at enable, 6578c2ecf20Sopenharmony_ci we're disabling error injection on all write calls to the sysfs nodes that 6588c2ecf20Sopenharmony_ci controls the error code injection. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_cistatic int disable_inject(const struct mem_ctl_info *mci) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci pvt->inject.enable = 0; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (!pvt->pci_ch[pvt->inject.channel][0]) 6678c2ecf20Sopenharmony_ci return -ENODEV; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], 6708c2ecf20Sopenharmony_ci MC_CHANNEL_ERROR_INJECT, 0); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/* 6768c2ecf20Sopenharmony_ci * i7core inject inject.section 6778c2ecf20Sopenharmony_ci * 6788c2ecf20Sopenharmony_ci * accept and store error injection inject.section value 6798c2ecf20Sopenharmony_ci * bit 0 - refers to the lower 32-byte half cacheline 6808c2ecf20Sopenharmony_ci * bit 1 - refers to the upper 32-byte half cacheline 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_section_store(struct device *dev, 6838c2ecf20Sopenharmony_ci struct device_attribute *mattr, 6848c2ecf20Sopenharmony_ci const char *data, size_t count) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 6878c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 6888c2ecf20Sopenharmony_ci unsigned long value; 6898c2ecf20Sopenharmony_ci int rc; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (pvt->inject.enable) 6928c2ecf20Sopenharmony_ci disable_inject(mci); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci rc = kstrtoul(data, 10, &value); 6958c2ecf20Sopenharmony_ci if ((rc < 0) || (value > 3)) 6968c2ecf20Sopenharmony_ci return -EIO; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci pvt->inject.section = (u32) value; 6998c2ecf20Sopenharmony_ci return count; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_section_show(struct device *dev, 7038c2ecf20Sopenharmony_ci struct device_attribute *mattr, 7048c2ecf20Sopenharmony_ci char *data) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 7078c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 7088c2ecf20Sopenharmony_ci return sprintf(data, "0x%08x\n", pvt->inject.section); 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci/* 7128c2ecf20Sopenharmony_ci * i7core inject.type 7138c2ecf20Sopenharmony_ci * 7148c2ecf20Sopenharmony_ci * accept and store error injection inject.section value 7158c2ecf20Sopenharmony_ci * bit 0 - repeat enable - Enable error repetition 7168c2ecf20Sopenharmony_ci * bit 1 - inject ECC error 7178c2ecf20Sopenharmony_ci * bit 2 - inject parity error 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_type_store(struct device *dev, 7208c2ecf20Sopenharmony_ci struct device_attribute *mattr, 7218c2ecf20Sopenharmony_ci const char *data, size_t count) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 7248c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 7258c2ecf20Sopenharmony_ci unsigned long value; 7268c2ecf20Sopenharmony_ci int rc; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (pvt->inject.enable) 7298c2ecf20Sopenharmony_ci disable_inject(mci); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci rc = kstrtoul(data, 10, &value); 7328c2ecf20Sopenharmony_ci if ((rc < 0) || (value > 7)) 7338c2ecf20Sopenharmony_ci return -EIO; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci pvt->inject.type = (u32) value; 7368c2ecf20Sopenharmony_ci return count; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_type_show(struct device *dev, 7408c2ecf20Sopenharmony_ci struct device_attribute *mattr, 7418c2ecf20Sopenharmony_ci char *data) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 7448c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return sprintf(data, "0x%08x\n", pvt->inject.type); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/* 7508c2ecf20Sopenharmony_ci * i7core_inject_inject.eccmask_store 7518c2ecf20Sopenharmony_ci * 7528c2ecf20Sopenharmony_ci * The type of error (UE/CE) will depend on the inject.eccmask value: 7538c2ecf20Sopenharmony_ci * Any bits set to a 1 will flip the corresponding ECC bit 7548c2ecf20Sopenharmony_ci * Correctable errors can be injected by flipping 1 bit or the bits within 7558c2ecf20Sopenharmony_ci * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or 7568c2ecf20Sopenharmony_ci * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an 7578c2ecf20Sopenharmony_ci * uncorrectable error to be injected. 7588c2ecf20Sopenharmony_ci */ 7598c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_eccmask_store(struct device *dev, 7608c2ecf20Sopenharmony_ci struct device_attribute *mattr, 7618c2ecf20Sopenharmony_ci const char *data, size_t count) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 7648c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 7658c2ecf20Sopenharmony_ci unsigned long value; 7668c2ecf20Sopenharmony_ci int rc; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (pvt->inject.enable) 7698c2ecf20Sopenharmony_ci disable_inject(mci); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci rc = kstrtoul(data, 10, &value); 7728c2ecf20Sopenharmony_ci if (rc < 0) 7738c2ecf20Sopenharmony_ci return -EIO; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci pvt->inject.eccmask = (u32) value; 7768c2ecf20Sopenharmony_ci return count; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_eccmask_show(struct device *dev, 7808c2ecf20Sopenharmony_ci struct device_attribute *mattr, 7818c2ecf20Sopenharmony_ci char *data) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 7848c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return sprintf(data, "0x%08x\n", pvt->inject.eccmask); 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/* 7908c2ecf20Sopenharmony_ci * i7core_addrmatch 7918c2ecf20Sopenharmony_ci * 7928c2ecf20Sopenharmony_ci * The type of error (UE/CE) will depend on the inject.eccmask value: 7938c2ecf20Sopenharmony_ci * Any bits set to a 1 will flip the corresponding ECC bit 7948c2ecf20Sopenharmony_ci * Correctable errors can be injected by flipping 1 bit or the bits within 7958c2ecf20Sopenharmony_ci * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or 7968c2ecf20Sopenharmony_ci * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an 7978c2ecf20Sopenharmony_ci * uncorrectable error to be injected. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci#define DECLARE_ADDR_MATCH(param, limit) \ 8018c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_store_##param( \ 8028c2ecf20Sopenharmony_ci struct device *dev, \ 8038c2ecf20Sopenharmony_ci struct device_attribute *mattr, \ 8048c2ecf20Sopenharmony_ci const char *data, size_t count) \ 8058c2ecf20Sopenharmony_ci{ \ 8068c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_get_drvdata(dev); \ 8078c2ecf20Sopenharmony_ci struct i7core_pvt *pvt; \ 8088c2ecf20Sopenharmony_ci long value; \ 8098c2ecf20Sopenharmony_ci int rc; \ 8108c2ecf20Sopenharmony_ci \ 8118c2ecf20Sopenharmony_ci edac_dbg(1, "\n"); \ 8128c2ecf20Sopenharmony_ci pvt = mci->pvt_info; \ 8138c2ecf20Sopenharmony_ci \ 8148c2ecf20Sopenharmony_ci if (pvt->inject.enable) \ 8158c2ecf20Sopenharmony_ci disable_inject(mci); \ 8168c2ecf20Sopenharmony_ci \ 8178c2ecf20Sopenharmony_ci if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\ 8188c2ecf20Sopenharmony_ci value = -1; \ 8198c2ecf20Sopenharmony_ci else { \ 8208c2ecf20Sopenharmony_ci rc = kstrtoul(data, 10, &value); \ 8218c2ecf20Sopenharmony_ci if ((rc < 0) || (value >= limit)) \ 8228c2ecf20Sopenharmony_ci return -EIO; \ 8238c2ecf20Sopenharmony_ci } \ 8248c2ecf20Sopenharmony_ci \ 8258c2ecf20Sopenharmony_ci pvt->inject.param = value; \ 8268c2ecf20Sopenharmony_ci \ 8278c2ecf20Sopenharmony_ci return count; \ 8288c2ecf20Sopenharmony_ci} \ 8298c2ecf20Sopenharmony_ci \ 8308c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_show_##param( \ 8318c2ecf20Sopenharmony_ci struct device *dev, \ 8328c2ecf20Sopenharmony_ci struct device_attribute *mattr, \ 8338c2ecf20Sopenharmony_ci char *data) \ 8348c2ecf20Sopenharmony_ci{ \ 8358c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_get_drvdata(dev); \ 8368c2ecf20Sopenharmony_ci struct i7core_pvt *pvt; \ 8378c2ecf20Sopenharmony_ci \ 8388c2ecf20Sopenharmony_ci pvt = mci->pvt_info; \ 8398c2ecf20Sopenharmony_ci edac_dbg(1, "pvt=%p\n", pvt); \ 8408c2ecf20Sopenharmony_ci if (pvt->inject.param < 0) \ 8418c2ecf20Sopenharmony_ci return sprintf(data, "any\n"); \ 8428c2ecf20Sopenharmony_ci else \ 8438c2ecf20Sopenharmony_ci return sprintf(data, "%d\n", pvt->inject.param);\ 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci#define ATTR_ADDR_MATCH(param) \ 8478c2ecf20Sopenharmony_ci static DEVICE_ATTR(param, S_IRUGO | S_IWUSR, \ 8488c2ecf20Sopenharmony_ci i7core_inject_show_##param, \ 8498c2ecf20Sopenharmony_ci i7core_inject_store_##param) 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ciDECLARE_ADDR_MATCH(channel, 3); 8528c2ecf20Sopenharmony_ciDECLARE_ADDR_MATCH(dimm, 3); 8538c2ecf20Sopenharmony_ciDECLARE_ADDR_MATCH(rank, 4); 8548c2ecf20Sopenharmony_ciDECLARE_ADDR_MATCH(bank, 32); 8558c2ecf20Sopenharmony_ciDECLARE_ADDR_MATCH(page, 0x10000); 8568c2ecf20Sopenharmony_ciDECLARE_ADDR_MATCH(col, 0x4000); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ciATTR_ADDR_MATCH(channel); 8598c2ecf20Sopenharmony_ciATTR_ADDR_MATCH(dimm); 8608c2ecf20Sopenharmony_ciATTR_ADDR_MATCH(rank); 8618c2ecf20Sopenharmony_ciATTR_ADDR_MATCH(bank); 8628c2ecf20Sopenharmony_ciATTR_ADDR_MATCH(page); 8638c2ecf20Sopenharmony_ciATTR_ADDR_MATCH(col); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic int write_and_test(struct pci_dev *dev, const int where, const u32 val) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci u32 read; 8688c2ecf20Sopenharmony_ci int count; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci edac_dbg(0, "setting pci %02x:%02x.%x reg=%02x value=%08x\n", 8718c2ecf20Sopenharmony_ci dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), 8728c2ecf20Sopenharmony_ci where, val); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci for (count = 0; count < 10; count++) { 8758c2ecf20Sopenharmony_ci if (count) 8768c2ecf20Sopenharmony_ci msleep(100); 8778c2ecf20Sopenharmony_ci pci_write_config_dword(dev, where, val); 8788c2ecf20Sopenharmony_ci pci_read_config_dword(dev, where, &read); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (read == val) 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x " 8858c2ecf20Sopenharmony_ci "write=%08x. Read=%08x\n", 8868c2ecf20Sopenharmony_ci dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), 8878c2ecf20Sopenharmony_ci where, val, read); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci/* 8938c2ecf20Sopenharmony_ci * This routine prepares the Memory Controller for error injection. 8948c2ecf20Sopenharmony_ci * The error will be injected when some process tries to write to the 8958c2ecf20Sopenharmony_ci * memory that matches the given criteria. 8968c2ecf20Sopenharmony_ci * The criteria can be set in terms of a mask where dimm, rank, bank, page 8978c2ecf20Sopenharmony_ci * and col can be specified. 8988c2ecf20Sopenharmony_ci * A -1 value for any of the mask items will make the MCU to ignore 8998c2ecf20Sopenharmony_ci * that matching criteria for error injection. 9008c2ecf20Sopenharmony_ci * 9018c2ecf20Sopenharmony_ci * It should be noticed that the error will only happen after a write operation 9028c2ecf20Sopenharmony_ci * on a memory that matches the condition. if REPEAT_EN is not enabled at 9038c2ecf20Sopenharmony_ci * inject mask, then it will produce just one error. Otherwise, it will repeat 9048c2ecf20Sopenharmony_ci * until the injectmask would be cleaned. 9058c2ecf20Sopenharmony_ci * 9068c2ecf20Sopenharmony_ci * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD 9078c2ecf20Sopenharmony_ci * is reliable enough to check if the MC is using the 9088c2ecf20Sopenharmony_ci * three channels. However, this is not clear at the datasheet. 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_enable_store(struct device *dev, 9118c2ecf20Sopenharmony_ci struct device_attribute *mattr, 9128c2ecf20Sopenharmony_ci const char *data, size_t count) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 9158c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 9168c2ecf20Sopenharmony_ci u32 injectmask; 9178c2ecf20Sopenharmony_ci u64 mask = 0; 9188c2ecf20Sopenharmony_ci int rc; 9198c2ecf20Sopenharmony_ci long enable; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (!pvt->pci_ch[pvt->inject.channel][0]) 9228c2ecf20Sopenharmony_ci return 0; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci rc = kstrtoul(data, 10, &enable); 9258c2ecf20Sopenharmony_ci if ((rc < 0)) 9268c2ecf20Sopenharmony_ci return 0; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (enable) { 9298c2ecf20Sopenharmony_ci pvt->inject.enable = 1; 9308c2ecf20Sopenharmony_ci } else { 9318c2ecf20Sopenharmony_ci disable_inject(mci); 9328c2ecf20Sopenharmony_ci return count; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* Sets pvt->inject.dimm mask */ 9368c2ecf20Sopenharmony_ci if (pvt->inject.dimm < 0) 9378c2ecf20Sopenharmony_ci mask |= 1LL << 41; 9388c2ecf20Sopenharmony_ci else { 9398c2ecf20Sopenharmony_ci if (pvt->channel[pvt->inject.channel].dimms > 2) 9408c2ecf20Sopenharmony_ci mask |= (pvt->inject.dimm & 0x3LL) << 35; 9418c2ecf20Sopenharmony_ci else 9428c2ecf20Sopenharmony_ci mask |= (pvt->inject.dimm & 0x1LL) << 36; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Sets pvt->inject.rank mask */ 9468c2ecf20Sopenharmony_ci if (pvt->inject.rank < 0) 9478c2ecf20Sopenharmony_ci mask |= 1LL << 40; 9488c2ecf20Sopenharmony_ci else { 9498c2ecf20Sopenharmony_ci if (pvt->channel[pvt->inject.channel].dimms > 2) 9508c2ecf20Sopenharmony_ci mask |= (pvt->inject.rank & 0x1LL) << 34; 9518c2ecf20Sopenharmony_ci else 9528c2ecf20Sopenharmony_ci mask |= (pvt->inject.rank & 0x3LL) << 34; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* Sets pvt->inject.bank mask */ 9568c2ecf20Sopenharmony_ci if (pvt->inject.bank < 0) 9578c2ecf20Sopenharmony_ci mask |= 1LL << 39; 9588c2ecf20Sopenharmony_ci else 9598c2ecf20Sopenharmony_ci mask |= (pvt->inject.bank & 0x15LL) << 30; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Sets pvt->inject.page mask */ 9628c2ecf20Sopenharmony_ci if (pvt->inject.page < 0) 9638c2ecf20Sopenharmony_ci mask |= 1LL << 38; 9648c2ecf20Sopenharmony_ci else 9658c2ecf20Sopenharmony_ci mask |= (pvt->inject.page & 0xffff) << 14; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* Sets pvt->inject.column mask */ 9688c2ecf20Sopenharmony_ci if (pvt->inject.col < 0) 9698c2ecf20Sopenharmony_ci mask |= 1LL << 37; 9708c2ecf20Sopenharmony_ci else 9718c2ecf20Sopenharmony_ci mask |= (pvt->inject.col & 0x3fff); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* 9748c2ecf20Sopenharmony_ci * bit 0: REPEAT_EN 9758c2ecf20Sopenharmony_ci * bits 1-2: MASK_HALF_CACHELINE 9768c2ecf20Sopenharmony_ci * bit 3: INJECT_ECC 9778c2ecf20Sopenharmony_ci * bit 4: INJECT_ADDR_PARITY 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci injectmask = (pvt->inject.type & 1) | 9818c2ecf20Sopenharmony_ci (pvt->inject.section & 0x3) << 1 | 9828c2ecf20Sopenharmony_ci (pvt->inject.type & 0x6) << (3 - 1); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* Unlock writes to registers - this register is write only */ 9858c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_noncore, 9868c2ecf20Sopenharmony_ci MC_CFG_CONTROL, 0x2); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci write_and_test(pvt->pci_ch[pvt->inject.channel][0], 9898c2ecf20Sopenharmony_ci MC_CHANNEL_ADDR_MATCH, mask); 9908c2ecf20Sopenharmony_ci write_and_test(pvt->pci_ch[pvt->inject.channel][0], 9918c2ecf20Sopenharmony_ci MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci write_and_test(pvt->pci_ch[pvt->inject.channel][0], 9948c2ecf20Sopenharmony_ci MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci write_and_test(pvt->pci_ch[pvt->inject.channel][0], 9978c2ecf20Sopenharmony_ci MC_CHANNEL_ERROR_INJECT, injectmask); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* 10008c2ecf20Sopenharmony_ci * This is something undocumented, based on my tests 10018c2ecf20Sopenharmony_ci * Without writing 8 to this register, errors aren't injected. Not sure 10028c2ecf20Sopenharmony_ci * why. 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_noncore, 10058c2ecf20Sopenharmony_ci MC_CFG_CONTROL, 8); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci edac_dbg(0, "Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n", 10088c2ecf20Sopenharmony_ci mask, pvt->inject.eccmask, injectmask); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci return count; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic ssize_t i7core_inject_enable_show(struct device *dev, 10158c2ecf20Sopenharmony_ci struct device_attribute *mattr, 10168c2ecf20Sopenharmony_ci char *data) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 10198c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 10208c2ecf20Sopenharmony_ci u32 injectmask; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!pvt->pci_ch[pvt->inject.channel][0]) 10238c2ecf20Sopenharmony_ci return 0; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], 10268c2ecf20Sopenharmony_ci MC_CHANNEL_ERROR_INJECT, &injectmask); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci edac_dbg(0, "Inject error read: 0x%018x\n", injectmask); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (injectmask & 0x0c) 10318c2ecf20Sopenharmony_ci pvt->inject.enable = 1; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci return sprintf(data, "%d\n", pvt->inject.enable); 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci#define DECLARE_COUNTER(param) \ 10378c2ecf20Sopenharmony_cistatic ssize_t i7core_show_counter_##param( \ 10388c2ecf20Sopenharmony_ci struct device *dev, \ 10398c2ecf20Sopenharmony_ci struct device_attribute *mattr, \ 10408c2ecf20Sopenharmony_ci char *data) \ 10418c2ecf20Sopenharmony_ci{ \ 10428c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_get_drvdata(dev); \ 10438c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; \ 10448c2ecf20Sopenharmony_ci \ 10458c2ecf20Sopenharmony_ci edac_dbg(1, "\n"); \ 10468c2ecf20Sopenharmony_ci if (!pvt->ce_count_available || (pvt->is_registered)) \ 10478c2ecf20Sopenharmony_ci return sprintf(data, "data unavailable\n"); \ 10488c2ecf20Sopenharmony_ci return sprintf(data, "%lu\n", \ 10498c2ecf20Sopenharmony_ci pvt->udimm_ce_count[param]); \ 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci#define ATTR_COUNTER(param) \ 10538c2ecf20Sopenharmony_ci static DEVICE_ATTR(udimm##param, S_IRUGO | S_IWUSR, \ 10548c2ecf20Sopenharmony_ci i7core_show_counter_##param, \ 10558c2ecf20Sopenharmony_ci NULL) 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ciDECLARE_COUNTER(0); 10588c2ecf20Sopenharmony_ciDECLARE_COUNTER(1); 10598c2ecf20Sopenharmony_ciDECLARE_COUNTER(2); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ciATTR_COUNTER(0); 10628c2ecf20Sopenharmony_ciATTR_COUNTER(1); 10638c2ecf20Sopenharmony_ciATTR_COUNTER(2); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* 10668c2ecf20Sopenharmony_ci * inject_addrmatch device sysfs struct 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic struct attribute *i7core_addrmatch_attrs[] = { 10708c2ecf20Sopenharmony_ci &dev_attr_channel.attr, 10718c2ecf20Sopenharmony_ci &dev_attr_dimm.attr, 10728c2ecf20Sopenharmony_ci &dev_attr_rank.attr, 10738c2ecf20Sopenharmony_ci &dev_attr_bank.attr, 10748c2ecf20Sopenharmony_ci &dev_attr_page.attr, 10758c2ecf20Sopenharmony_ci &dev_attr_col.attr, 10768c2ecf20Sopenharmony_ci NULL 10778c2ecf20Sopenharmony_ci}; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic const struct attribute_group addrmatch_grp = { 10808c2ecf20Sopenharmony_ci .attrs = i7core_addrmatch_attrs, 10818c2ecf20Sopenharmony_ci}; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic const struct attribute_group *addrmatch_groups[] = { 10848c2ecf20Sopenharmony_ci &addrmatch_grp, 10858c2ecf20Sopenharmony_ci NULL 10868c2ecf20Sopenharmony_ci}; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic void addrmatch_release(struct device *device) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci edac_dbg(1, "Releasing device %s\n", dev_name(device)); 10918c2ecf20Sopenharmony_ci kfree(device); 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic const struct device_type addrmatch_type = { 10958c2ecf20Sopenharmony_ci .groups = addrmatch_groups, 10968c2ecf20Sopenharmony_ci .release = addrmatch_release, 10978c2ecf20Sopenharmony_ci}; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci/* 11008c2ecf20Sopenharmony_ci * all_channel_counts sysfs struct 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic struct attribute *i7core_udimm_counters_attrs[] = { 11048c2ecf20Sopenharmony_ci &dev_attr_udimm0.attr, 11058c2ecf20Sopenharmony_ci &dev_attr_udimm1.attr, 11068c2ecf20Sopenharmony_ci &dev_attr_udimm2.attr, 11078c2ecf20Sopenharmony_ci NULL 11088c2ecf20Sopenharmony_ci}; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic const struct attribute_group all_channel_counts_grp = { 11118c2ecf20Sopenharmony_ci .attrs = i7core_udimm_counters_attrs, 11128c2ecf20Sopenharmony_ci}; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic const struct attribute_group *all_channel_counts_groups[] = { 11158c2ecf20Sopenharmony_ci &all_channel_counts_grp, 11168c2ecf20Sopenharmony_ci NULL 11178c2ecf20Sopenharmony_ci}; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic void all_channel_counts_release(struct device *device) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci edac_dbg(1, "Releasing device %s\n", dev_name(device)); 11228c2ecf20Sopenharmony_ci kfree(device); 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic const struct device_type all_channel_counts_type = { 11268c2ecf20Sopenharmony_ci .groups = all_channel_counts_groups, 11278c2ecf20Sopenharmony_ci .release = all_channel_counts_release, 11288c2ecf20Sopenharmony_ci}; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/* 11318c2ecf20Sopenharmony_ci * inject sysfs attributes 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, 11358c2ecf20Sopenharmony_ci i7core_inject_section_show, i7core_inject_section_store); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR, 11388c2ecf20Sopenharmony_ci i7core_inject_type_show, i7core_inject_type_store); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR, 11428c2ecf20Sopenharmony_ci i7core_inject_eccmask_show, i7core_inject_eccmask_store); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR, 11458c2ecf20Sopenharmony_ci i7core_inject_enable_show, i7core_inject_enable_store); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic struct attribute *i7core_dev_attrs[] = { 11488c2ecf20Sopenharmony_ci &dev_attr_inject_section.attr, 11498c2ecf20Sopenharmony_ci &dev_attr_inject_type.attr, 11508c2ecf20Sopenharmony_ci &dev_attr_inject_eccmask.attr, 11518c2ecf20Sopenharmony_ci &dev_attr_inject_enable.attr, 11528c2ecf20Sopenharmony_ci NULL 11538c2ecf20Sopenharmony_ci}; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(i7core_dev); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int i7core_create_sysfs_devices(struct mem_ctl_info *mci) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 11608c2ecf20Sopenharmony_ci int rc; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL); 11638c2ecf20Sopenharmony_ci if (!pvt->addrmatch_dev) 11648c2ecf20Sopenharmony_ci return -ENOMEM; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci pvt->addrmatch_dev->type = &addrmatch_type; 11678c2ecf20Sopenharmony_ci pvt->addrmatch_dev->bus = mci->dev.bus; 11688c2ecf20Sopenharmony_ci device_initialize(pvt->addrmatch_dev); 11698c2ecf20Sopenharmony_ci pvt->addrmatch_dev->parent = &mci->dev; 11708c2ecf20Sopenharmony_ci dev_set_name(pvt->addrmatch_dev, "inject_addrmatch"); 11718c2ecf20Sopenharmony_ci dev_set_drvdata(pvt->addrmatch_dev, mci); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci edac_dbg(1, "creating %s\n", dev_name(pvt->addrmatch_dev)); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci rc = device_add(pvt->addrmatch_dev); 11768c2ecf20Sopenharmony_ci if (rc < 0) 11778c2ecf20Sopenharmony_ci goto err_put_addrmatch; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (!pvt->is_registered) { 11808c2ecf20Sopenharmony_ci pvt->chancounts_dev = kzalloc(sizeof(*pvt->chancounts_dev), 11818c2ecf20Sopenharmony_ci GFP_KERNEL); 11828c2ecf20Sopenharmony_ci if (!pvt->chancounts_dev) { 11838c2ecf20Sopenharmony_ci rc = -ENOMEM; 11848c2ecf20Sopenharmony_ci goto err_del_addrmatch; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci pvt->chancounts_dev->type = &all_channel_counts_type; 11888c2ecf20Sopenharmony_ci pvt->chancounts_dev->bus = mci->dev.bus; 11898c2ecf20Sopenharmony_ci device_initialize(pvt->chancounts_dev); 11908c2ecf20Sopenharmony_ci pvt->chancounts_dev->parent = &mci->dev; 11918c2ecf20Sopenharmony_ci dev_set_name(pvt->chancounts_dev, "all_channel_counts"); 11928c2ecf20Sopenharmony_ci dev_set_drvdata(pvt->chancounts_dev, mci); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci edac_dbg(1, "creating %s\n", dev_name(pvt->chancounts_dev)); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci rc = device_add(pvt->chancounts_dev); 11978c2ecf20Sopenharmony_ci if (rc < 0) 11988c2ecf20Sopenharmony_ci goto err_put_chancounts; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci return 0; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cierr_put_chancounts: 12038c2ecf20Sopenharmony_ci put_device(pvt->chancounts_dev); 12048c2ecf20Sopenharmony_cierr_del_addrmatch: 12058c2ecf20Sopenharmony_ci device_del(pvt->addrmatch_dev); 12068c2ecf20Sopenharmony_cierr_put_addrmatch: 12078c2ecf20Sopenharmony_ci put_device(pvt->addrmatch_dev); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci return rc; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cistatic void i7core_delete_sysfs_devices(struct mem_ctl_info *mci) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci edac_dbg(1, "\n"); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (!pvt->is_registered) { 12198c2ecf20Sopenharmony_ci device_del(pvt->chancounts_dev); 12208c2ecf20Sopenharmony_ci put_device(pvt->chancounts_dev); 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci device_del(pvt->addrmatch_dev); 12238c2ecf20Sopenharmony_ci put_device(pvt->addrmatch_dev); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci/**************************************************************************** 12278c2ecf20Sopenharmony_ci Device initialization routines: put/get, init/exit 12288c2ecf20Sopenharmony_ci ****************************************************************************/ 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/* 12318c2ecf20Sopenharmony_ci * i7core_put_all_devices 'put' all the devices that we have 12328c2ecf20Sopenharmony_ci * reserved via 'get' 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_cistatic void i7core_put_devices(struct i7core_dev *i7core_dev) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci int i; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 12398c2ecf20Sopenharmony_ci for (i = 0; i < i7core_dev->n_devs; i++) { 12408c2ecf20Sopenharmony_ci struct pci_dev *pdev = i7core_dev->pdev[i]; 12418c2ecf20Sopenharmony_ci if (!pdev) 12428c2ecf20Sopenharmony_ci continue; 12438c2ecf20Sopenharmony_ci edac_dbg(0, "Removing dev %02x:%02x.%d\n", 12448c2ecf20Sopenharmony_ci pdev->bus->number, 12458c2ecf20Sopenharmony_ci PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); 12468c2ecf20Sopenharmony_ci pci_dev_put(pdev); 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic void i7core_put_all_devices(void) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev, *tmp; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) { 12558c2ecf20Sopenharmony_ci i7core_put_devices(i7core_dev); 12568c2ecf20Sopenharmony_ci free_i7core_dev(i7core_dev); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic void __init i7core_xeon_pci_fixup(const struct pci_id_table *table) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 12638c2ecf20Sopenharmony_ci int i; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* 12668c2ecf20Sopenharmony_ci * On Xeon 55xx, the Intel Quick Path Arch Generic Non-core pci buses 12678c2ecf20Sopenharmony_ci * aren't announced by acpi. So, we need to use a legacy scan probing 12688c2ecf20Sopenharmony_ci * to detect them 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_ci while (table && table->descr) { 12718c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL); 12728c2ecf20Sopenharmony_ci if (unlikely(!pdev)) { 12738c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SOCKET_BUSES; i++) 12748c2ecf20Sopenharmony_ci pcibios_scan_specific_bus(255-i); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci pci_dev_put(pdev); 12778c2ecf20Sopenharmony_ci table++; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic unsigned i7core_pci_lastbus(void) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci int last_bus = 0, bus; 12848c2ecf20Sopenharmony_ci struct pci_bus *b = NULL; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci while ((b = pci_find_next_bus(b)) != NULL) { 12878c2ecf20Sopenharmony_ci bus = b->number; 12888c2ecf20Sopenharmony_ci edac_dbg(0, "Found bus %d\n", bus); 12898c2ecf20Sopenharmony_ci if (bus > last_bus) 12908c2ecf20Sopenharmony_ci last_bus = bus; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci edac_dbg(0, "Last bus %d\n", last_bus); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return last_bus; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci/* 12998c2ecf20Sopenharmony_ci * i7core_get_all_devices Find and perform 'get' operation on the MCH's 13008c2ecf20Sopenharmony_ci * device/functions we want to reference for this driver 13018c2ecf20Sopenharmony_ci * 13028c2ecf20Sopenharmony_ci * Need to 'get' device 16 func 1 and func 2 13038c2ecf20Sopenharmony_ci */ 13048c2ecf20Sopenharmony_cistatic int i7core_get_onedevice(struct pci_dev **prev, 13058c2ecf20Sopenharmony_ci const struct pci_id_table *table, 13068c2ecf20Sopenharmony_ci const unsigned devno, 13078c2ecf20Sopenharmony_ci const unsigned last_bus) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev; 13108c2ecf20Sopenharmony_ci const struct pci_id_descr *dev_descr = &table->descr[devno]; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 13138c2ecf20Sopenharmony_ci u8 bus = 0; 13148c2ecf20Sopenharmony_ci u8 socket = 0; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 13178c2ecf20Sopenharmony_ci dev_descr->dev_id, *prev); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* 13208c2ecf20Sopenharmony_ci * On Xeon 55xx, the Intel QuickPath Arch Generic Non-core regs 13218c2ecf20Sopenharmony_ci * is at addr 8086:2c40, instead of 8086:2c41. So, we need 13228c2ecf20Sopenharmony_ci * to probe for the alternate address in case of failure 13238c2ecf20Sopenharmony_ci */ 13248c2ecf20Sopenharmony_ci if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) { 13258c2ecf20Sopenharmony_ci pci_dev_get(*prev); /* pci_get_device will put it */ 13268c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 13278c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && 13318c2ecf20Sopenharmony_ci !pdev) { 13328c2ecf20Sopenharmony_ci pci_dev_get(*prev); /* pci_get_device will put it */ 13338c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 13348c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT, 13358c2ecf20Sopenharmony_ci *prev); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (!pdev) { 13398c2ecf20Sopenharmony_ci if (*prev) { 13408c2ecf20Sopenharmony_ci *prev = pdev; 13418c2ecf20Sopenharmony_ci return 0; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci if (dev_descr->optional) 13458c2ecf20Sopenharmony_ci return 0; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (devno == 0) 13488c2ecf20Sopenharmony_ci return -ENODEV; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci i7core_printk(KERN_INFO, 13518c2ecf20Sopenharmony_ci "Device not found: dev %02x.%d PCI ID %04x:%04x\n", 13528c2ecf20Sopenharmony_ci dev_descr->dev, dev_descr->func, 13538c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, dev_descr->dev_id); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci /* End of list, leave */ 13568c2ecf20Sopenharmony_ci return -ENODEV; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci bus = pdev->bus->number; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci socket = last_bus - bus; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci i7core_dev = get_i7core_dev(socket); 13638c2ecf20Sopenharmony_ci if (!i7core_dev) { 13648c2ecf20Sopenharmony_ci i7core_dev = alloc_i7core_dev(socket, table); 13658c2ecf20Sopenharmony_ci if (!i7core_dev) { 13668c2ecf20Sopenharmony_ci pci_dev_put(pdev); 13678c2ecf20Sopenharmony_ci return -ENOMEM; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (i7core_dev->pdev[devno]) { 13728c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, 13738c2ecf20Sopenharmony_ci "Duplicated device for " 13748c2ecf20Sopenharmony_ci "dev %02x:%02x.%d PCI ID %04x:%04x\n", 13758c2ecf20Sopenharmony_ci bus, dev_descr->dev, dev_descr->func, 13768c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, dev_descr->dev_id); 13778c2ecf20Sopenharmony_ci pci_dev_put(pdev); 13788c2ecf20Sopenharmony_ci return -ENODEV; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci i7core_dev->pdev[devno] = pdev; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci /* Sanity check */ 13848c2ecf20Sopenharmony_ci if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev || 13858c2ecf20Sopenharmony_ci PCI_FUNC(pdev->devfn) != dev_descr->func)) { 13868c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, 13878c2ecf20Sopenharmony_ci "Device PCI ID %04x:%04x " 13888c2ecf20Sopenharmony_ci "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n", 13898c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, dev_descr->dev_id, 13908c2ecf20Sopenharmony_ci bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), 13918c2ecf20Sopenharmony_ci bus, dev_descr->dev, dev_descr->func); 13928c2ecf20Sopenharmony_ci return -ENODEV; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci /* Be sure that the device is enabled */ 13968c2ecf20Sopenharmony_ci if (unlikely(pci_enable_device(pdev) < 0)) { 13978c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, 13988c2ecf20Sopenharmony_ci "Couldn't enable " 13998c2ecf20Sopenharmony_ci "dev %02x:%02x.%d PCI ID %04x:%04x\n", 14008c2ecf20Sopenharmony_ci bus, dev_descr->dev, dev_descr->func, 14018c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, dev_descr->dev_id); 14028c2ecf20Sopenharmony_ci return -ENODEV; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci edac_dbg(0, "Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n", 14068c2ecf20Sopenharmony_ci socket, bus, dev_descr->dev, 14078c2ecf20Sopenharmony_ci dev_descr->func, 14088c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, dev_descr->dev_id); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* 14118c2ecf20Sopenharmony_ci * As stated on drivers/pci/search.c, the reference count for 14128c2ecf20Sopenharmony_ci * @from is always decremented if it is not %NULL. So, as we need 14138c2ecf20Sopenharmony_ci * to get all devices up to null, we need to do a get for the device 14148c2ecf20Sopenharmony_ci */ 14158c2ecf20Sopenharmony_ci pci_dev_get(pdev); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci *prev = pdev; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci return 0; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic int i7core_get_all_devices(void) 14238c2ecf20Sopenharmony_ci{ 14248c2ecf20Sopenharmony_ci int i, rc, last_bus; 14258c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 14268c2ecf20Sopenharmony_ci const struct pci_id_table *table = pci_dev_table; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci last_bus = i7core_pci_lastbus(); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci while (table && table->descr) { 14318c2ecf20Sopenharmony_ci for (i = 0; i < table->n_devs; i++) { 14328c2ecf20Sopenharmony_ci pdev = NULL; 14338c2ecf20Sopenharmony_ci do { 14348c2ecf20Sopenharmony_ci rc = i7core_get_onedevice(&pdev, table, i, 14358c2ecf20Sopenharmony_ci last_bus); 14368c2ecf20Sopenharmony_ci if (rc < 0) { 14378c2ecf20Sopenharmony_ci if (i == 0) { 14388c2ecf20Sopenharmony_ci i = table->n_devs; 14398c2ecf20Sopenharmony_ci break; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci i7core_put_all_devices(); 14428c2ecf20Sopenharmony_ci return -ENODEV; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci } while (pdev); 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci table++; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci return 0; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic int mci_bind_devs(struct mem_ctl_info *mci, 14538c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 14568c2ecf20Sopenharmony_ci struct pci_dev *pdev; 14578c2ecf20Sopenharmony_ci int i, func, slot; 14588c2ecf20Sopenharmony_ci char *family; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci pvt->is_registered = false; 14618c2ecf20Sopenharmony_ci pvt->enable_scrub = false; 14628c2ecf20Sopenharmony_ci for (i = 0; i < i7core_dev->n_devs; i++) { 14638c2ecf20Sopenharmony_ci pdev = i7core_dev->pdev[i]; 14648c2ecf20Sopenharmony_ci if (!pdev) 14658c2ecf20Sopenharmony_ci continue; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci func = PCI_FUNC(pdev->devfn); 14688c2ecf20Sopenharmony_ci slot = PCI_SLOT(pdev->devfn); 14698c2ecf20Sopenharmony_ci if (slot == 3) { 14708c2ecf20Sopenharmony_ci if (unlikely(func > MAX_MCR_FUNC)) 14718c2ecf20Sopenharmony_ci goto error; 14728c2ecf20Sopenharmony_ci pvt->pci_mcr[func] = pdev; 14738c2ecf20Sopenharmony_ci } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { 14748c2ecf20Sopenharmony_ci if (unlikely(func > MAX_CHAN_FUNC)) 14758c2ecf20Sopenharmony_ci goto error; 14768c2ecf20Sopenharmony_ci pvt->pci_ch[slot - 4][func] = pdev; 14778c2ecf20Sopenharmony_ci } else if (!slot && !func) { 14788c2ecf20Sopenharmony_ci pvt->pci_noncore = pdev; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* Detect the processor family */ 14818c2ecf20Sopenharmony_ci switch (pdev->device) { 14828c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_I7_NONCORE: 14838c2ecf20Sopenharmony_ci family = "Xeon 35xx/ i7core"; 14848c2ecf20Sopenharmony_ci pvt->enable_scrub = false; 14858c2ecf20Sopenharmony_ci break; 14868c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT: 14878c2ecf20Sopenharmony_ci family = "i7-800/i5-700"; 14888c2ecf20Sopenharmony_ci pvt->enable_scrub = false; 14898c2ecf20Sopenharmony_ci break; 14908c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE: 14918c2ecf20Sopenharmony_ci family = "Xeon 34xx"; 14928c2ecf20Sopenharmony_ci pvt->enable_scrub = false; 14938c2ecf20Sopenharmony_ci break; 14948c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT: 14958c2ecf20Sopenharmony_ci family = "Xeon 55xx"; 14968c2ecf20Sopenharmony_ci pvt->enable_scrub = true; 14978c2ecf20Sopenharmony_ci break; 14988c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2: 14998c2ecf20Sopenharmony_ci family = "Xeon 56xx / i7-900"; 15008c2ecf20Sopenharmony_ci pvt->enable_scrub = true; 15018c2ecf20Sopenharmony_ci break; 15028c2ecf20Sopenharmony_ci default: 15038c2ecf20Sopenharmony_ci family = "unknown"; 15048c2ecf20Sopenharmony_ci pvt->enable_scrub = false; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci edac_dbg(0, "Detected a processor type %s\n", family); 15078c2ecf20Sopenharmony_ci } else 15088c2ecf20Sopenharmony_ci goto error; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci edac_dbg(0, "Associated fn %d.%d, dev = %p, socket %d\n", 15118c2ecf20Sopenharmony_ci PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), 15128c2ecf20Sopenharmony_ci pdev, i7core_dev->socket); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (PCI_SLOT(pdev->devfn) == 3 && 15158c2ecf20Sopenharmony_ci PCI_FUNC(pdev->devfn) == 2) 15168c2ecf20Sopenharmony_ci pvt->is_registered = true; 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci return 0; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cierror: 15228c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, "Device %d, function %d " 15238c2ecf20Sopenharmony_ci "is out of the expected range\n", 15248c2ecf20Sopenharmony_ci slot, func); 15258c2ecf20Sopenharmony_ci return -EINVAL; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci/**************************************************************************** 15298c2ecf20Sopenharmony_ci Error check routines 15308c2ecf20Sopenharmony_ci ****************************************************************************/ 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, 15338c2ecf20Sopenharmony_ci const int chan, 15348c2ecf20Sopenharmony_ci const int new0, 15358c2ecf20Sopenharmony_ci const int new1, 15368c2ecf20Sopenharmony_ci const int new2) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 15398c2ecf20Sopenharmony_ci int add0 = 0, add1 = 0, add2 = 0; 15408c2ecf20Sopenharmony_ci /* Updates CE counters if it is not the first time here */ 15418c2ecf20Sopenharmony_ci if (pvt->ce_count_available) { 15428c2ecf20Sopenharmony_ci /* Updates CE counters */ 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci add2 = new2 - pvt->rdimm_last_ce_count[chan][2]; 15458c2ecf20Sopenharmony_ci add1 = new1 - pvt->rdimm_last_ce_count[chan][1]; 15468c2ecf20Sopenharmony_ci add0 = new0 - pvt->rdimm_last_ce_count[chan][0]; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (add2 < 0) 15498c2ecf20Sopenharmony_ci add2 += 0x7fff; 15508c2ecf20Sopenharmony_ci pvt->rdimm_ce_count[chan][2] += add2; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (add1 < 0) 15538c2ecf20Sopenharmony_ci add1 += 0x7fff; 15548c2ecf20Sopenharmony_ci pvt->rdimm_ce_count[chan][1] += add1; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (add0 < 0) 15578c2ecf20Sopenharmony_ci add0 += 0x7fff; 15588c2ecf20Sopenharmony_ci pvt->rdimm_ce_count[chan][0] += add0; 15598c2ecf20Sopenharmony_ci } else 15608c2ecf20Sopenharmony_ci pvt->ce_count_available = 1; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* Store the new values */ 15638c2ecf20Sopenharmony_ci pvt->rdimm_last_ce_count[chan][2] = new2; 15648c2ecf20Sopenharmony_ci pvt->rdimm_last_ce_count[chan][1] = new1; 15658c2ecf20Sopenharmony_ci pvt->rdimm_last_ce_count[chan][0] = new0; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /*updated the edac core */ 15688c2ecf20Sopenharmony_ci if (add0 != 0) 15698c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add0, 15708c2ecf20Sopenharmony_ci 0, 0, 0, 15718c2ecf20Sopenharmony_ci chan, 0, -1, "error", ""); 15728c2ecf20Sopenharmony_ci if (add1 != 0) 15738c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add1, 15748c2ecf20Sopenharmony_ci 0, 0, 0, 15758c2ecf20Sopenharmony_ci chan, 1, -1, "error", ""); 15768c2ecf20Sopenharmony_ci if (add2 != 0) 15778c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add2, 15788c2ecf20Sopenharmony_ci 0, 0, 0, 15798c2ecf20Sopenharmony_ci chan, 2, -1, "error", ""); 15808c2ecf20Sopenharmony_ci} 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_cistatic void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 15858c2ecf20Sopenharmony_ci u32 rcv[3][2]; 15868c2ecf20Sopenharmony_ci int i, new0, new1, new2; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/ 15898c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0, 15908c2ecf20Sopenharmony_ci &rcv[0][0]); 15918c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1, 15928c2ecf20Sopenharmony_ci &rcv[0][1]); 15938c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2, 15948c2ecf20Sopenharmony_ci &rcv[1][0]); 15958c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3, 15968c2ecf20Sopenharmony_ci &rcv[1][1]); 15978c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4, 15988c2ecf20Sopenharmony_ci &rcv[2][0]); 15998c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5, 16008c2ecf20Sopenharmony_ci &rcv[2][1]); 16018c2ecf20Sopenharmony_ci for (i = 0 ; i < 3; i++) { 16028c2ecf20Sopenharmony_ci edac_dbg(3, "MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n", 16038c2ecf20Sopenharmony_ci (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]); 16048c2ecf20Sopenharmony_ci /*if the channel has 3 dimms*/ 16058c2ecf20Sopenharmony_ci if (pvt->channel[i].dimms > 2) { 16068c2ecf20Sopenharmony_ci new0 = DIMM_BOT_COR_ERR(rcv[i][0]); 16078c2ecf20Sopenharmony_ci new1 = DIMM_TOP_COR_ERR(rcv[i][0]); 16088c2ecf20Sopenharmony_ci new2 = DIMM_BOT_COR_ERR(rcv[i][1]); 16098c2ecf20Sopenharmony_ci } else { 16108c2ecf20Sopenharmony_ci new0 = DIMM_TOP_COR_ERR(rcv[i][0]) + 16118c2ecf20Sopenharmony_ci DIMM_BOT_COR_ERR(rcv[i][0]); 16128c2ecf20Sopenharmony_ci new1 = DIMM_TOP_COR_ERR(rcv[i][1]) + 16138c2ecf20Sopenharmony_ci DIMM_BOT_COR_ERR(rcv[i][1]); 16148c2ecf20Sopenharmony_ci new2 = 0; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci i7core_rdimm_update_ce_count(mci, i, new0, new1, new2); 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci/* This function is based on the device 3 function 4 registers as described on: 16228c2ecf20Sopenharmony_ci * Intel Xeon Processor 5500 Series Datasheet Volume 2 16238c2ecf20Sopenharmony_ci * http://www.intel.com/Assets/PDF/datasheet/321322.pdf 16248c2ecf20Sopenharmony_ci * also available at: 16258c2ecf20Sopenharmony_ci * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_cistatic void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci) 16288c2ecf20Sopenharmony_ci{ 16298c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 16308c2ecf20Sopenharmony_ci u32 rcv1, rcv0; 16318c2ecf20Sopenharmony_ci int new0, new1, new2; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci if (!pvt->pci_mcr[4]) { 16348c2ecf20Sopenharmony_ci edac_dbg(0, "MCR registers not found\n"); 16358c2ecf20Sopenharmony_ci return; 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* Corrected test errors */ 16398c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1); 16408c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0); 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* Store the new values */ 16438c2ecf20Sopenharmony_ci new2 = DIMM2_COR_ERR(rcv1); 16448c2ecf20Sopenharmony_ci new1 = DIMM1_COR_ERR(rcv0); 16458c2ecf20Sopenharmony_ci new0 = DIMM0_COR_ERR(rcv0); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* Updates CE counters if it is not the first time here */ 16488c2ecf20Sopenharmony_ci if (pvt->ce_count_available) { 16498c2ecf20Sopenharmony_ci /* Updates CE counters */ 16508c2ecf20Sopenharmony_ci int add0, add1, add2; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci add2 = new2 - pvt->udimm_last_ce_count[2]; 16538c2ecf20Sopenharmony_ci add1 = new1 - pvt->udimm_last_ce_count[1]; 16548c2ecf20Sopenharmony_ci add0 = new0 - pvt->udimm_last_ce_count[0]; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci if (add2 < 0) 16578c2ecf20Sopenharmony_ci add2 += 0x7fff; 16588c2ecf20Sopenharmony_ci pvt->udimm_ce_count[2] += add2; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if (add1 < 0) 16618c2ecf20Sopenharmony_ci add1 += 0x7fff; 16628c2ecf20Sopenharmony_ci pvt->udimm_ce_count[1] += add1; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci if (add0 < 0) 16658c2ecf20Sopenharmony_ci add0 += 0x7fff; 16668c2ecf20Sopenharmony_ci pvt->udimm_ce_count[0] += add0; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (add0 | add1 | add2) 16698c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, "New Corrected error(s): " 16708c2ecf20Sopenharmony_ci "dimm0: +%d, dimm1: +%d, dimm2 +%d\n", 16718c2ecf20Sopenharmony_ci add0, add1, add2); 16728c2ecf20Sopenharmony_ci } else 16738c2ecf20Sopenharmony_ci pvt->ce_count_available = 1; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* Store the new values */ 16768c2ecf20Sopenharmony_ci pvt->udimm_last_ce_count[2] = new2; 16778c2ecf20Sopenharmony_ci pvt->udimm_last_ce_count[1] = new1; 16788c2ecf20Sopenharmony_ci pvt->udimm_last_ce_count[0] = new0; 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci/* 16828c2ecf20Sopenharmony_ci * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32 16838c2ecf20Sopenharmony_ci * Architectures Software Developer’s Manual Volume 3B. 16848c2ecf20Sopenharmony_ci * Nehalem are defined as family 0x06, model 0x1a 16858c2ecf20Sopenharmony_ci * 16868c2ecf20Sopenharmony_ci * The MCA registers used here are the following ones: 16878c2ecf20Sopenharmony_ci * struct mce field MCA Register 16888c2ecf20Sopenharmony_ci * m->status MSR_IA32_MC8_STATUS 16898c2ecf20Sopenharmony_ci * m->addr MSR_IA32_MC8_ADDR 16908c2ecf20Sopenharmony_ci * m->misc MSR_IA32_MC8_MISC 16918c2ecf20Sopenharmony_ci * In the case of Nehalem, the error information is masked at .status and .misc 16928c2ecf20Sopenharmony_ci * fields 16938c2ecf20Sopenharmony_ci */ 16948c2ecf20Sopenharmony_cistatic void i7core_mce_output_error(struct mem_ctl_info *mci, 16958c2ecf20Sopenharmony_ci const struct mce *m) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 16988c2ecf20Sopenharmony_ci char *optype, *err; 16998c2ecf20Sopenharmony_ci enum hw_event_mc_err_type tp_event; 17008c2ecf20Sopenharmony_ci unsigned long error = m->status & 0x1ff0000l; 17018c2ecf20Sopenharmony_ci bool uncorrected_error = m->mcgstatus & 1ll << 61; 17028c2ecf20Sopenharmony_ci bool ripv = m->mcgstatus & 1; 17038c2ecf20Sopenharmony_ci u32 optypenum = (m->status >> 4) & 0x07; 17048c2ecf20Sopenharmony_ci u32 core_err_cnt = (m->status >> 38) & 0x7fff; 17058c2ecf20Sopenharmony_ci u32 dimm = (m->misc >> 16) & 0x3; 17068c2ecf20Sopenharmony_ci u32 channel = (m->misc >> 18) & 0x3; 17078c2ecf20Sopenharmony_ci u32 syndrome = m->misc >> 32; 17088c2ecf20Sopenharmony_ci u32 errnum = find_first_bit(&error, 32); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci if (uncorrected_error) { 17118c2ecf20Sopenharmony_ci core_err_cnt = 1; 17128c2ecf20Sopenharmony_ci if (ripv) 17138c2ecf20Sopenharmony_ci tp_event = HW_EVENT_ERR_UNCORRECTED; 17148c2ecf20Sopenharmony_ci else 17158c2ecf20Sopenharmony_ci tp_event = HW_EVENT_ERR_FATAL; 17168c2ecf20Sopenharmony_ci } else { 17178c2ecf20Sopenharmony_ci tp_event = HW_EVENT_ERR_CORRECTED; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci switch (optypenum) { 17218c2ecf20Sopenharmony_ci case 0: 17228c2ecf20Sopenharmony_ci optype = "generic undef request"; 17238c2ecf20Sopenharmony_ci break; 17248c2ecf20Sopenharmony_ci case 1: 17258c2ecf20Sopenharmony_ci optype = "read error"; 17268c2ecf20Sopenharmony_ci break; 17278c2ecf20Sopenharmony_ci case 2: 17288c2ecf20Sopenharmony_ci optype = "write error"; 17298c2ecf20Sopenharmony_ci break; 17308c2ecf20Sopenharmony_ci case 3: 17318c2ecf20Sopenharmony_ci optype = "addr/cmd error"; 17328c2ecf20Sopenharmony_ci break; 17338c2ecf20Sopenharmony_ci case 4: 17348c2ecf20Sopenharmony_ci optype = "scrubbing error"; 17358c2ecf20Sopenharmony_ci break; 17368c2ecf20Sopenharmony_ci default: 17378c2ecf20Sopenharmony_ci optype = "reserved"; 17388c2ecf20Sopenharmony_ci break; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci switch (errnum) { 17428c2ecf20Sopenharmony_ci case 16: 17438c2ecf20Sopenharmony_ci err = "read ECC error"; 17448c2ecf20Sopenharmony_ci break; 17458c2ecf20Sopenharmony_ci case 17: 17468c2ecf20Sopenharmony_ci err = "RAS ECC error"; 17478c2ecf20Sopenharmony_ci break; 17488c2ecf20Sopenharmony_ci case 18: 17498c2ecf20Sopenharmony_ci err = "write parity error"; 17508c2ecf20Sopenharmony_ci break; 17518c2ecf20Sopenharmony_ci case 19: 17528c2ecf20Sopenharmony_ci err = "redundancy loss"; 17538c2ecf20Sopenharmony_ci break; 17548c2ecf20Sopenharmony_ci case 20: 17558c2ecf20Sopenharmony_ci err = "reserved"; 17568c2ecf20Sopenharmony_ci break; 17578c2ecf20Sopenharmony_ci case 21: 17588c2ecf20Sopenharmony_ci err = "memory range error"; 17598c2ecf20Sopenharmony_ci break; 17608c2ecf20Sopenharmony_ci case 22: 17618c2ecf20Sopenharmony_ci err = "RTID out of range"; 17628c2ecf20Sopenharmony_ci break; 17638c2ecf20Sopenharmony_ci case 23: 17648c2ecf20Sopenharmony_ci err = "address parity error"; 17658c2ecf20Sopenharmony_ci break; 17668c2ecf20Sopenharmony_ci case 24: 17678c2ecf20Sopenharmony_ci err = "byte enable parity error"; 17688c2ecf20Sopenharmony_ci break; 17698c2ecf20Sopenharmony_ci default: 17708c2ecf20Sopenharmony_ci err = "unknown"; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci /* 17748c2ecf20Sopenharmony_ci * Call the helper to output message 17758c2ecf20Sopenharmony_ci * FIXME: what to do if core_err_cnt > 1? Currently, it generates 17768c2ecf20Sopenharmony_ci * only one event 17778c2ecf20Sopenharmony_ci */ 17788c2ecf20Sopenharmony_ci if (uncorrected_error || !pvt->is_registered) 17798c2ecf20Sopenharmony_ci edac_mc_handle_error(tp_event, mci, core_err_cnt, 17808c2ecf20Sopenharmony_ci m->addr >> PAGE_SHIFT, 17818c2ecf20Sopenharmony_ci m->addr & ~PAGE_MASK, 17828c2ecf20Sopenharmony_ci syndrome, 17838c2ecf20Sopenharmony_ci channel, dimm, -1, 17848c2ecf20Sopenharmony_ci err, optype); 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci/* 17888c2ecf20Sopenharmony_ci * i7core_check_error Retrieve and process errors reported by the 17898c2ecf20Sopenharmony_ci * hardware. Called by the Core module. 17908c2ecf20Sopenharmony_ci */ 17918c2ecf20Sopenharmony_cistatic void i7core_check_error(struct mem_ctl_info *mci, struct mce *m) 17928c2ecf20Sopenharmony_ci{ 17938c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci i7core_mce_output_error(mci, m); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci /* 17988c2ecf20Sopenharmony_ci * Now, let's increment CE error counts 17998c2ecf20Sopenharmony_ci */ 18008c2ecf20Sopenharmony_ci if (!pvt->is_registered) 18018c2ecf20Sopenharmony_ci i7core_udimm_check_mc_ecc_err(mci); 18028c2ecf20Sopenharmony_ci else 18038c2ecf20Sopenharmony_ci i7core_rdimm_check_mc_ecc_err(mci); 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci/* 18078c2ecf20Sopenharmony_ci * Check that logging is enabled and that this is the right type 18088c2ecf20Sopenharmony_ci * of error for us to handle. 18098c2ecf20Sopenharmony_ci */ 18108c2ecf20Sopenharmony_cistatic int i7core_mce_check_error(struct notifier_block *nb, unsigned long val, 18118c2ecf20Sopenharmony_ci void *data) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci struct mce *mce = (struct mce *)data; 18148c2ecf20Sopenharmony_ci struct i7core_dev *i7_dev; 18158c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci i7_dev = get_i7core_dev(mce->socketid); 18188c2ecf20Sopenharmony_ci if (!i7_dev || (mce->kflags & MCE_HANDLED_CEC)) 18198c2ecf20Sopenharmony_ci return NOTIFY_DONE; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci mci = i7_dev->mci; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci /* 18248c2ecf20Sopenharmony_ci * Just let mcelog handle it if the error is 18258c2ecf20Sopenharmony_ci * outside the memory controller 18268c2ecf20Sopenharmony_ci */ 18278c2ecf20Sopenharmony_ci if (((mce->status & 0xffff) >> 7) != 1) 18288c2ecf20Sopenharmony_ci return NOTIFY_DONE; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* Bank 8 registers are the only ones that we know how to handle */ 18318c2ecf20Sopenharmony_ci if (mce->bank != 8) 18328c2ecf20Sopenharmony_ci return NOTIFY_DONE; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci i7core_check_error(mci, mce); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci /* Advise mcelog that the errors were handled */ 18378c2ecf20Sopenharmony_ci mce->kflags |= MCE_HANDLED_EDAC; 18388c2ecf20Sopenharmony_ci return NOTIFY_OK; 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic struct notifier_block i7_mce_dec = { 18428c2ecf20Sopenharmony_ci .notifier_call = i7core_mce_check_error, 18438c2ecf20Sopenharmony_ci .priority = MCE_PRIO_EDAC, 18448c2ecf20Sopenharmony_ci}; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_cistruct memdev_dmi_entry { 18478c2ecf20Sopenharmony_ci u8 type; 18488c2ecf20Sopenharmony_ci u8 length; 18498c2ecf20Sopenharmony_ci u16 handle; 18508c2ecf20Sopenharmony_ci u16 phys_mem_array_handle; 18518c2ecf20Sopenharmony_ci u16 mem_err_info_handle; 18528c2ecf20Sopenharmony_ci u16 total_width; 18538c2ecf20Sopenharmony_ci u16 data_width; 18548c2ecf20Sopenharmony_ci u16 size; 18558c2ecf20Sopenharmony_ci u8 form; 18568c2ecf20Sopenharmony_ci u8 device_set; 18578c2ecf20Sopenharmony_ci u8 device_locator; 18588c2ecf20Sopenharmony_ci u8 bank_locator; 18598c2ecf20Sopenharmony_ci u8 memory_type; 18608c2ecf20Sopenharmony_ci u16 type_detail; 18618c2ecf20Sopenharmony_ci u16 speed; 18628c2ecf20Sopenharmony_ci u8 manufacturer; 18638c2ecf20Sopenharmony_ci u8 serial_number; 18648c2ecf20Sopenharmony_ci u8 asset_tag; 18658c2ecf20Sopenharmony_ci u8 part_number; 18668c2ecf20Sopenharmony_ci u8 attributes; 18678c2ecf20Sopenharmony_ci u32 extended_size; 18688c2ecf20Sopenharmony_ci u16 conf_mem_clk_speed; 18698c2ecf20Sopenharmony_ci} __attribute__((__packed__)); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci/* 18738c2ecf20Sopenharmony_ci * Decode the DRAM Clock Frequency, be paranoid, make sure that all 18748c2ecf20Sopenharmony_ci * memory devices show the same speed, and if they don't then consider 18758c2ecf20Sopenharmony_ci * all speeds to be invalid. 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_cistatic void decode_dclk(const struct dmi_header *dh, void *_dclk_freq) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci int *dclk_freq = _dclk_freq; 18808c2ecf20Sopenharmony_ci u16 dmi_mem_clk_speed; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci if (*dclk_freq == -1) 18838c2ecf20Sopenharmony_ci return; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if (dh->type == DMI_ENTRY_MEM_DEVICE) { 18868c2ecf20Sopenharmony_ci struct memdev_dmi_entry *memdev_dmi_entry = 18878c2ecf20Sopenharmony_ci (struct memdev_dmi_entry *)dh; 18888c2ecf20Sopenharmony_ci unsigned long conf_mem_clk_speed_offset = 18898c2ecf20Sopenharmony_ci (unsigned long)&memdev_dmi_entry->conf_mem_clk_speed - 18908c2ecf20Sopenharmony_ci (unsigned long)&memdev_dmi_entry->type; 18918c2ecf20Sopenharmony_ci unsigned long speed_offset = 18928c2ecf20Sopenharmony_ci (unsigned long)&memdev_dmi_entry->speed - 18938c2ecf20Sopenharmony_ci (unsigned long)&memdev_dmi_entry->type; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* Check that a DIMM is present */ 18968c2ecf20Sopenharmony_ci if (memdev_dmi_entry->size == 0) 18978c2ecf20Sopenharmony_ci return; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci /* 19008c2ecf20Sopenharmony_ci * Pick the configured speed if it's available, otherwise 19018c2ecf20Sopenharmony_ci * pick the DIMM speed, or we don't have a speed. 19028c2ecf20Sopenharmony_ci */ 19038c2ecf20Sopenharmony_ci if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) { 19048c2ecf20Sopenharmony_ci dmi_mem_clk_speed = 19058c2ecf20Sopenharmony_ci memdev_dmi_entry->conf_mem_clk_speed; 19068c2ecf20Sopenharmony_ci } else if (memdev_dmi_entry->length > speed_offset) { 19078c2ecf20Sopenharmony_ci dmi_mem_clk_speed = memdev_dmi_entry->speed; 19088c2ecf20Sopenharmony_ci } else { 19098c2ecf20Sopenharmony_ci *dclk_freq = -1; 19108c2ecf20Sopenharmony_ci return; 19118c2ecf20Sopenharmony_ci } 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (*dclk_freq == 0) { 19148c2ecf20Sopenharmony_ci /* First pass, speed was 0 */ 19158c2ecf20Sopenharmony_ci if (dmi_mem_clk_speed > 0) { 19168c2ecf20Sopenharmony_ci /* Set speed if a valid speed is read */ 19178c2ecf20Sopenharmony_ci *dclk_freq = dmi_mem_clk_speed; 19188c2ecf20Sopenharmony_ci } else { 19198c2ecf20Sopenharmony_ci /* Otherwise we don't have a valid speed */ 19208c2ecf20Sopenharmony_ci *dclk_freq = -1; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci } else if (*dclk_freq > 0 && 19238c2ecf20Sopenharmony_ci *dclk_freq != dmi_mem_clk_speed) { 19248c2ecf20Sopenharmony_ci /* 19258c2ecf20Sopenharmony_ci * If we have a speed, check that all DIMMS are the same 19268c2ecf20Sopenharmony_ci * speed, otherwise set the speed as invalid. 19278c2ecf20Sopenharmony_ci */ 19288c2ecf20Sopenharmony_ci *dclk_freq = -1; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci} 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci/* 19348c2ecf20Sopenharmony_ci * The default DCLK frequency is used as a fallback if we 19358c2ecf20Sopenharmony_ci * fail to find anything reliable in the DMI. The value 19368c2ecf20Sopenharmony_ci * is taken straight from the datasheet. 19378c2ecf20Sopenharmony_ci */ 19388c2ecf20Sopenharmony_ci#define DEFAULT_DCLK_FREQ 800 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_cistatic int get_dclk_freq(void) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci int dclk_freq = 0; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci dmi_walk(decode_dclk, (void *)&dclk_freq); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci if (dclk_freq < 1) 19478c2ecf20Sopenharmony_ci return DEFAULT_DCLK_FREQ; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci return dclk_freq; 19508c2ecf20Sopenharmony_ci} 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci/* 19538c2ecf20Sopenharmony_ci * set_sdram_scrub_rate This routine sets byte/sec bandwidth scrub rate 19548c2ecf20Sopenharmony_ci * to hardware according to SCRUBINTERVAL formula 19558c2ecf20Sopenharmony_ci * found in datasheet. 19568c2ecf20Sopenharmony_ci */ 19578c2ecf20Sopenharmony_cistatic int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw) 19588c2ecf20Sopenharmony_ci{ 19598c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 19608c2ecf20Sopenharmony_ci struct pci_dev *pdev; 19618c2ecf20Sopenharmony_ci u32 dw_scrub; 19628c2ecf20Sopenharmony_ci u32 dw_ssr; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci /* Get data from the MC register, function 2 */ 19658c2ecf20Sopenharmony_ci pdev = pvt->pci_mcr[2]; 19668c2ecf20Sopenharmony_ci if (!pdev) 19678c2ecf20Sopenharmony_ci return -ENODEV; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci if (new_bw == 0) { 19728c2ecf20Sopenharmony_ci /* Prepare to disable petrol scrub */ 19738c2ecf20Sopenharmony_ci dw_scrub &= ~STARTSCRUB; 19748c2ecf20Sopenharmony_ci /* Stop the patrol scrub engine */ 19758c2ecf20Sopenharmony_ci write_and_test(pdev, MC_SCRUB_CONTROL, 19768c2ecf20Sopenharmony_ci dw_scrub & ~SCRUBINTERVAL_MASK); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* Get current status of scrub rate and set bit to disable */ 19798c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr); 19808c2ecf20Sopenharmony_ci dw_ssr &= ~SSR_MODE_MASK; 19818c2ecf20Sopenharmony_ci dw_ssr |= SSR_MODE_DISABLE; 19828c2ecf20Sopenharmony_ci } else { 19838c2ecf20Sopenharmony_ci const int cache_line_size = 64; 19848c2ecf20Sopenharmony_ci const u32 freq_dclk_mhz = pvt->dclk_freq; 19858c2ecf20Sopenharmony_ci unsigned long long scrub_interval; 19868c2ecf20Sopenharmony_ci /* 19878c2ecf20Sopenharmony_ci * Translate the desired scrub rate to a register value and 19888c2ecf20Sopenharmony_ci * program the corresponding register value. 19898c2ecf20Sopenharmony_ci */ 19908c2ecf20Sopenharmony_ci scrub_interval = (unsigned long long)freq_dclk_mhz * 19918c2ecf20Sopenharmony_ci cache_line_size * 1000000; 19928c2ecf20Sopenharmony_ci do_div(scrub_interval, new_bw); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK) 19958c2ecf20Sopenharmony_ci return -EINVAL; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci dw_scrub = SCRUBINTERVAL_MASK & scrub_interval; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci /* Start the patrol scrub engine */ 20008c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, MC_SCRUB_CONTROL, 20018c2ecf20Sopenharmony_ci STARTSCRUB | dw_scrub); 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* Get current status of scrub rate and set bit to enable */ 20048c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr); 20058c2ecf20Sopenharmony_ci dw_ssr &= ~SSR_MODE_MASK; 20068c2ecf20Sopenharmony_ci dw_ssr |= SSR_MODE_ENABLE; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci /* Disable or enable scrubbing */ 20098c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci return new_bw; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci/* 20158c2ecf20Sopenharmony_ci * get_sdram_scrub_rate This routine convert current scrub rate value 20168c2ecf20Sopenharmony_ci * into byte/sec bandwidth according to 20178c2ecf20Sopenharmony_ci * SCRUBINTERVAL formula found in datasheet. 20188c2ecf20Sopenharmony_ci */ 20198c2ecf20Sopenharmony_cistatic int get_sdram_scrub_rate(struct mem_ctl_info *mci) 20208c2ecf20Sopenharmony_ci{ 20218c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 20228c2ecf20Sopenharmony_ci struct pci_dev *pdev; 20238c2ecf20Sopenharmony_ci const u32 cache_line_size = 64; 20248c2ecf20Sopenharmony_ci const u32 freq_dclk_mhz = pvt->dclk_freq; 20258c2ecf20Sopenharmony_ci unsigned long long scrub_rate; 20268c2ecf20Sopenharmony_ci u32 scrubval; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci /* Get data from the MC register, function 2 */ 20298c2ecf20Sopenharmony_ci pdev = pvt->pci_mcr[2]; 20308c2ecf20Sopenharmony_ci if (!pdev) 20318c2ecf20Sopenharmony_ci return -ENODEV; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci /* Get current scrub control data */ 20348c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci /* Mask highest 8-bits to 0 */ 20378c2ecf20Sopenharmony_ci scrubval &= SCRUBINTERVAL_MASK; 20388c2ecf20Sopenharmony_ci if (!scrubval) 20398c2ecf20Sopenharmony_ci return 0; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci /* Calculate scrub rate value into byte/sec bandwidth */ 20428c2ecf20Sopenharmony_ci scrub_rate = (unsigned long long)freq_dclk_mhz * 20438c2ecf20Sopenharmony_ci 1000000 * cache_line_size; 20448c2ecf20Sopenharmony_ci do_div(scrub_rate, scrubval); 20458c2ecf20Sopenharmony_ci return (int)scrub_rate; 20468c2ecf20Sopenharmony_ci} 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_cistatic void enable_sdram_scrub_setting(struct mem_ctl_info *mci) 20498c2ecf20Sopenharmony_ci{ 20508c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 20518c2ecf20Sopenharmony_ci u32 pci_lock; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci /* Unlock writes to pci registers */ 20548c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock); 20558c2ecf20Sopenharmony_ci pci_lock &= ~0x3; 20568c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 20578c2ecf20Sopenharmony_ci pci_lock | MC_CFG_UNLOCK); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci mci->set_sdram_scrub_rate = set_sdram_scrub_rate; 20608c2ecf20Sopenharmony_ci mci->get_sdram_scrub_rate = get_sdram_scrub_rate; 20618c2ecf20Sopenharmony_ci} 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_cistatic void disable_sdram_scrub_setting(struct mem_ctl_info *mci) 20648c2ecf20Sopenharmony_ci{ 20658c2ecf20Sopenharmony_ci struct i7core_pvt *pvt = mci->pvt_info; 20668c2ecf20Sopenharmony_ci u32 pci_lock; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci /* Lock writes to pci registers */ 20698c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock); 20708c2ecf20Sopenharmony_ci pci_lock &= ~0x3; 20718c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 20728c2ecf20Sopenharmony_ci pci_lock | MC_CFG_LOCK); 20738c2ecf20Sopenharmony_ci} 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_cistatic void i7core_pci_ctl_create(struct i7core_pvt *pvt) 20768c2ecf20Sopenharmony_ci{ 20778c2ecf20Sopenharmony_ci pvt->i7core_pci = edac_pci_create_generic_ctl( 20788c2ecf20Sopenharmony_ci &pvt->i7core_dev->pdev[0]->dev, 20798c2ecf20Sopenharmony_ci EDAC_MOD_STR); 20808c2ecf20Sopenharmony_ci if (unlikely(!pvt->i7core_pci)) 20818c2ecf20Sopenharmony_ci i7core_printk(KERN_WARNING, 20828c2ecf20Sopenharmony_ci "Unable to setup PCI error report via EDAC\n"); 20838c2ecf20Sopenharmony_ci} 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cistatic void i7core_pci_ctl_release(struct i7core_pvt *pvt) 20868c2ecf20Sopenharmony_ci{ 20878c2ecf20Sopenharmony_ci if (likely(pvt->i7core_pci)) 20888c2ecf20Sopenharmony_ci edac_pci_release_generic_ctl(pvt->i7core_pci); 20898c2ecf20Sopenharmony_ci else 20908c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, 20918c2ecf20Sopenharmony_ci "Couldn't find mem_ctl_info for socket %d\n", 20928c2ecf20Sopenharmony_ci pvt->i7core_dev->socket); 20938c2ecf20Sopenharmony_ci pvt->i7core_pci = NULL; 20948c2ecf20Sopenharmony_ci} 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_cistatic void i7core_unregister_mci(struct i7core_dev *i7core_dev) 20978c2ecf20Sopenharmony_ci{ 20988c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = i7core_dev->mci; 20998c2ecf20Sopenharmony_ci struct i7core_pvt *pvt; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci if (unlikely(!mci || !mci->pvt_info)) { 21028c2ecf20Sopenharmony_ci edac_dbg(0, "MC: dev = %p\n", &i7core_dev->pdev[0]->dev); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, "Couldn't find mci handler\n"); 21058c2ecf20Sopenharmony_ci return; 21068c2ecf20Sopenharmony_ci } 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci /* Disable scrubrate setting */ 21138c2ecf20Sopenharmony_ci if (pvt->enable_scrub) 21148c2ecf20Sopenharmony_ci disable_sdram_scrub_setting(mci); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci /* Disable EDAC polling */ 21178c2ecf20Sopenharmony_ci i7core_pci_ctl_release(pvt); 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci /* Remove MC sysfs nodes */ 21208c2ecf20Sopenharmony_ci i7core_delete_sysfs_devices(mci); 21218c2ecf20Sopenharmony_ci edac_mc_del_mc(mci->pdev); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); 21248c2ecf20Sopenharmony_ci kfree(mci->ctl_name); 21258c2ecf20Sopenharmony_ci edac_mc_free(mci); 21268c2ecf20Sopenharmony_ci i7core_dev->mci = NULL; 21278c2ecf20Sopenharmony_ci} 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_cistatic int i7core_register_mci(struct i7core_dev *i7core_dev) 21308c2ecf20Sopenharmony_ci{ 21318c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 21328c2ecf20Sopenharmony_ci struct i7core_pvt *pvt; 21338c2ecf20Sopenharmony_ci int rc; 21348c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci /* allocate a new MC control structure */ 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHANNEL; 21398c2ecf20Sopenharmony_ci layers[0].size = NUM_CHANS; 21408c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = false; 21418c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_SLOT; 21428c2ecf20Sopenharmony_ci layers[1].size = MAX_DIMMS; 21438c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = true; 21448c2ecf20Sopenharmony_ci mci = edac_mc_alloc(i7core_dev->socket, ARRAY_SIZE(layers), layers, 21458c2ecf20Sopenharmony_ci sizeof(*pvt)); 21468c2ecf20Sopenharmony_ci if (unlikely(!mci)) 21478c2ecf20Sopenharmony_ci return -ENOMEM; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 21528c2ecf20Sopenharmony_ci memset(pvt, 0, sizeof(*pvt)); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci /* Associates i7core_dev and mci for future usage */ 21558c2ecf20Sopenharmony_ci pvt->i7core_dev = i7core_dev; 21568c2ecf20Sopenharmony_ci i7core_dev->mci = mci; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci /* 21598c2ecf20Sopenharmony_ci * FIXME: how to handle RDDR3 at MCI level? It is possible to have 21608c2ecf20Sopenharmony_ci * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different 21618c2ecf20Sopenharmony_ci * memory channels 21628c2ecf20Sopenharmony_ci */ 21638c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR3; 21648c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE; 21658c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_NONE; 21668c2ecf20Sopenharmony_ci mci->mod_name = "i7core_edac.c"; 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d", i7core_dev->socket); 21698c2ecf20Sopenharmony_ci if (!mci->ctl_name) { 21708c2ecf20Sopenharmony_ci rc = -ENOMEM; 21718c2ecf20Sopenharmony_ci goto fail1; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci mci->dev_name = pci_name(i7core_dev->pdev[0]); 21758c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci /* Store pci devices at mci for faster access */ 21788c2ecf20Sopenharmony_ci rc = mci_bind_devs(mci, i7core_dev); 21798c2ecf20Sopenharmony_ci if (unlikely(rc < 0)) 21808c2ecf20Sopenharmony_ci goto fail0; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci /* Get dimm basic config */ 21848c2ecf20Sopenharmony_ci get_dimm_config(mci); 21858c2ecf20Sopenharmony_ci /* record ptr to the generic device */ 21868c2ecf20Sopenharmony_ci mci->pdev = &i7core_dev->pdev[0]->dev; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci /* Enable scrubrate setting */ 21898c2ecf20Sopenharmony_ci if (pvt->enable_scrub) 21908c2ecf20Sopenharmony_ci enable_sdram_scrub_setting(mci); 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci /* add this new MC control structure to EDAC's list of MCs */ 21938c2ecf20Sopenharmony_ci if (unlikely(edac_mc_add_mc_with_groups(mci, i7core_dev_groups))) { 21948c2ecf20Sopenharmony_ci edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); 21958c2ecf20Sopenharmony_ci /* FIXME: perhaps some code should go here that disables error 21968c2ecf20Sopenharmony_ci * reporting if we just enabled it 21978c2ecf20Sopenharmony_ci */ 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci rc = -EINVAL; 22008c2ecf20Sopenharmony_ci goto fail0; 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci if (i7core_create_sysfs_devices(mci)) { 22038c2ecf20Sopenharmony_ci edac_dbg(0, "MC: failed to create sysfs nodes\n"); 22048c2ecf20Sopenharmony_ci edac_mc_del_mc(mci->pdev); 22058c2ecf20Sopenharmony_ci rc = -EINVAL; 22068c2ecf20Sopenharmony_ci goto fail0; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci /* Default error mask is any memory */ 22108c2ecf20Sopenharmony_ci pvt->inject.channel = 0; 22118c2ecf20Sopenharmony_ci pvt->inject.dimm = -1; 22128c2ecf20Sopenharmony_ci pvt->inject.rank = -1; 22138c2ecf20Sopenharmony_ci pvt->inject.bank = -1; 22148c2ecf20Sopenharmony_ci pvt->inject.page = -1; 22158c2ecf20Sopenharmony_ci pvt->inject.col = -1; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci /* allocating generic PCI control info */ 22188c2ecf20Sopenharmony_ci i7core_pci_ctl_create(pvt); 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci /* DCLK for scrub rate setting */ 22218c2ecf20Sopenharmony_ci pvt->dclk_freq = get_dclk_freq(); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci return 0; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_cifail0: 22268c2ecf20Sopenharmony_ci kfree(mci->ctl_name); 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_cifail1: 22298c2ecf20Sopenharmony_ci edac_mc_free(mci); 22308c2ecf20Sopenharmony_ci i7core_dev->mci = NULL; 22318c2ecf20Sopenharmony_ci return rc; 22328c2ecf20Sopenharmony_ci} 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci/* 22358c2ecf20Sopenharmony_ci * i7core_probe Probe for ONE instance of device to see if it is 22368c2ecf20Sopenharmony_ci * present. 22378c2ecf20Sopenharmony_ci * return: 22388c2ecf20Sopenharmony_ci * 0 for FOUND a device 22398c2ecf20Sopenharmony_ci * < 0 for error code 22408c2ecf20Sopenharmony_ci */ 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_cistatic int i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id) 22438c2ecf20Sopenharmony_ci{ 22448c2ecf20Sopenharmony_ci int rc, count = 0; 22458c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci /* get the pci devices we want to reserve for our use */ 22488c2ecf20Sopenharmony_ci mutex_lock(&i7core_edac_lock); 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci /* 22518c2ecf20Sopenharmony_ci * All memory controllers are allocated at the first pass. 22528c2ecf20Sopenharmony_ci */ 22538c2ecf20Sopenharmony_ci if (unlikely(probed >= 1)) { 22548c2ecf20Sopenharmony_ci mutex_unlock(&i7core_edac_lock); 22558c2ecf20Sopenharmony_ci return -ENODEV; 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci probed++; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci rc = i7core_get_all_devices(); 22608c2ecf20Sopenharmony_ci if (unlikely(rc < 0)) 22618c2ecf20Sopenharmony_ci goto fail0; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci list_for_each_entry(i7core_dev, &i7core_edac_list, list) { 22648c2ecf20Sopenharmony_ci count++; 22658c2ecf20Sopenharmony_ci rc = i7core_register_mci(i7core_dev); 22668c2ecf20Sopenharmony_ci if (unlikely(rc < 0)) 22678c2ecf20Sopenharmony_ci goto fail1; 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci /* 22718c2ecf20Sopenharmony_ci * Nehalem-EX uses a different memory controller. However, as the 22728c2ecf20Sopenharmony_ci * memory controller is not visible on some Nehalem/Nehalem-EP, we 22738c2ecf20Sopenharmony_ci * need to indirectly probe via a X58 PCI device. The same devices 22748c2ecf20Sopenharmony_ci * are found on (some) Nehalem-EX. So, on those machines, the 22758c2ecf20Sopenharmony_ci * probe routine needs to return -ENODEV, as the actual Memory 22768c2ecf20Sopenharmony_ci * Controller registers won't be detected. 22778c2ecf20Sopenharmony_ci */ 22788c2ecf20Sopenharmony_ci if (!count) { 22798c2ecf20Sopenharmony_ci rc = -ENODEV; 22808c2ecf20Sopenharmony_ci goto fail1; 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci i7core_printk(KERN_INFO, 22848c2ecf20Sopenharmony_ci "Driver loaded, %d memory controller(s) found.\n", 22858c2ecf20Sopenharmony_ci count); 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci mutex_unlock(&i7core_edac_lock); 22888c2ecf20Sopenharmony_ci return 0; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_cifail1: 22918c2ecf20Sopenharmony_ci list_for_each_entry(i7core_dev, &i7core_edac_list, list) 22928c2ecf20Sopenharmony_ci i7core_unregister_mci(i7core_dev); 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci i7core_put_all_devices(); 22958c2ecf20Sopenharmony_cifail0: 22968c2ecf20Sopenharmony_ci mutex_unlock(&i7core_edac_lock); 22978c2ecf20Sopenharmony_ci return rc; 22988c2ecf20Sopenharmony_ci} 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci/* 23018c2ecf20Sopenharmony_ci * i7core_remove destructor for one instance of device 23028c2ecf20Sopenharmony_ci * 23038c2ecf20Sopenharmony_ci */ 23048c2ecf20Sopenharmony_cistatic void i7core_remove(struct pci_dev *pdev) 23058c2ecf20Sopenharmony_ci{ 23068c2ecf20Sopenharmony_ci struct i7core_dev *i7core_dev; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci /* 23118c2ecf20Sopenharmony_ci * we have a trouble here: pdev value for removal will be wrong, since 23128c2ecf20Sopenharmony_ci * it will point to the X58 register used to detect that the machine 23138c2ecf20Sopenharmony_ci * is a Nehalem or upper design. However, due to the way several PCI 23148c2ecf20Sopenharmony_ci * devices are grouped together to provide MC functionality, we need 23158c2ecf20Sopenharmony_ci * to use a different method for releasing the devices 23168c2ecf20Sopenharmony_ci */ 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci mutex_lock(&i7core_edac_lock); 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci if (unlikely(!probed)) { 23218c2ecf20Sopenharmony_ci mutex_unlock(&i7core_edac_lock); 23228c2ecf20Sopenharmony_ci return; 23238c2ecf20Sopenharmony_ci } 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci list_for_each_entry(i7core_dev, &i7core_edac_list, list) 23268c2ecf20Sopenharmony_ci i7core_unregister_mci(i7core_dev); 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci /* Release PCI resources */ 23298c2ecf20Sopenharmony_ci i7core_put_all_devices(); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci probed--; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci mutex_unlock(&i7core_edac_lock); 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i7core_pci_tbl); 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci/* 23398c2ecf20Sopenharmony_ci * i7core_driver pci_driver structure for this module 23408c2ecf20Sopenharmony_ci * 23418c2ecf20Sopenharmony_ci */ 23428c2ecf20Sopenharmony_cistatic struct pci_driver i7core_driver = { 23438c2ecf20Sopenharmony_ci .name = "i7core_edac", 23448c2ecf20Sopenharmony_ci .probe = i7core_probe, 23458c2ecf20Sopenharmony_ci .remove = i7core_remove, 23468c2ecf20Sopenharmony_ci .id_table = i7core_pci_tbl, 23478c2ecf20Sopenharmony_ci}; 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_ci/* 23508c2ecf20Sopenharmony_ci * i7core_init Module entry function 23518c2ecf20Sopenharmony_ci * Try to initialize this module for its devices 23528c2ecf20Sopenharmony_ci */ 23538c2ecf20Sopenharmony_cistatic int __init i7core_init(void) 23548c2ecf20Sopenharmony_ci{ 23558c2ecf20Sopenharmony_ci int pci_rc; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 23608c2ecf20Sopenharmony_ci opstate_init(); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci if (use_pci_fixup) 23638c2ecf20Sopenharmony_ci i7core_xeon_pci_fixup(pci_dev_table); 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci pci_rc = pci_register_driver(&i7core_driver); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci if (pci_rc >= 0) { 23688c2ecf20Sopenharmony_ci mce_register_decode_chain(&i7_mce_dec); 23698c2ecf20Sopenharmony_ci return 0; 23708c2ecf20Sopenharmony_ci } 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci i7core_printk(KERN_ERR, "Failed to register device with error %d.\n", 23738c2ecf20Sopenharmony_ci pci_rc); 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci return pci_rc; 23768c2ecf20Sopenharmony_ci} 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci/* 23798c2ecf20Sopenharmony_ci * i7core_exit() Module exit function 23808c2ecf20Sopenharmony_ci * Unregister the driver 23818c2ecf20Sopenharmony_ci */ 23828c2ecf20Sopenharmony_cistatic void __exit i7core_exit(void) 23838c2ecf20Sopenharmony_ci{ 23848c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 23858c2ecf20Sopenharmony_ci pci_unregister_driver(&i7core_driver); 23868c2ecf20Sopenharmony_ci mce_unregister_decode_chain(&i7_mce_dec); 23878c2ecf20Sopenharmony_ci} 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_cimodule_init(i7core_init); 23908c2ecf20Sopenharmony_cimodule_exit(i7core_exit); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 23938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab"); 23948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Red Hat Inc. (https://www.redhat.com)"); 23958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - " 23968c2ecf20Sopenharmony_ci I7CORE_REVISION); 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444); 23998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 2400