18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel 7300 class Memory Controllers kernel module (Clarksboro) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 by: 68c2ecf20Sopenharmony_ci * Mauro Carvalho Chehab 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Red Hat Inc. https://www.redhat.com 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Intel 7300 Chipset Memory Controller Hub (MCH) - Datasheet 118c2ecf20Sopenharmony_ci * http://www.intel.com/Assets/PDF/datasheet/318082.pdf 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * TODO: The chipset allow checking for PCI Express errors also. Currently, 148c2ecf20Sopenharmony_ci * the driver covers only memory error errors 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This driver uses "csrows" EDAC attribute to represent DIMM slot# 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/edac.h> 258c2ecf20Sopenharmony_ci#include <linux/mmzone.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "edac_module.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Alter this version for the I7300 module when modifications are made 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define I7300_REVISION " Ver: 1.0.0" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "i7300_edac" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define i7300_printk(level, fmt, arg...) \ 378c2ecf20Sopenharmony_ci edac_printk(level, "i7300", fmt, ##arg) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define i7300_mc_printk(mci, level, fmt, arg...) \ 408c2ecf20Sopenharmony_ci edac_mc_chipset_printk(mci, level, "i7300", fmt, ##arg) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/*********************************************** 438c2ecf20Sopenharmony_ci * i7300 Limit constants Structs and static vars 448c2ecf20Sopenharmony_ci ***********************************************/ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Memory topology is organized as: 488c2ecf20Sopenharmony_ci * Branch 0 - 2 channels: channels 0 and 1 (FDB0 PCI dev 21.0) 498c2ecf20Sopenharmony_ci * Branch 1 - 2 channels: channels 2 and 3 (FDB1 PCI dev 22.0) 508c2ecf20Sopenharmony_ci * Each channel can have to 8 DIMM sets (called as SLOTS) 518c2ecf20Sopenharmony_ci * Slots should generally be filled in pairs 528c2ecf20Sopenharmony_ci * Except on Single Channel mode of operation 538c2ecf20Sopenharmony_ci * just slot 0/channel0 filled on this mode 548c2ecf20Sopenharmony_ci * On normal operation mode, the two channels on a branch should be 558c2ecf20Sopenharmony_ci * filled together for the same SLOT# 568c2ecf20Sopenharmony_ci * When in mirrored mode, Branch 1 replicate memory at Branch 0, so, the four 578c2ecf20Sopenharmony_ci * channels on both branches should be filled 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Limits for i7300 */ 618c2ecf20Sopenharmony_ci#define MAX_SLOTS 8 628c2ecf20Sopenharmony_ci#define MAX_BRANCHES 2 638c2ecf20Sopenharmony_ci#define MAX_CH_PER_BRANCH 2 648c2ecf20Sopenharmony_ci#define MAX_CHANNELS (MAX_CH_PER_BRANCH * MAX_BRANCHES) 658c2ecf20Sopenharmony_ci#define MAX_MIR 3 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define to_channel(ch, branch) ((((branch)) << 1) | (ch)) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define to_csrow(slot, ch, branch) \ 708c2ecf20Sopenharmony_ci (to_channel(ch, branch) | ((slot) << 2)) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Device name and register DID (Device ID) */ 738c2ecf20Sopenharmony_cistruct i7300_dev_info { 748c2ecf20Sopenharmony_ci const char *ctl_name; /* name for this device */ 758c2ecf20Sopenharmony_ci u16 fsb_mapping_errors; /* DID for the branchmap,control */ 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Table of devices attributes supported by this driver */ 798c2ecf20Sopenharmony_cistatic const struct i7300_dev_info i7300_devs[] = { 808c2ecf20Sopenharmony_ci { 818c2ecf20Sopenharmony_ci .ctl_name = "I7300", 828c2ecf20Sopenharmony_ci .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7300_MCH_ERR, 838c2ecf20Sopenharmony_ci }, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct i7300_dimm_info { 878c2ecf20Sopenharmony_ci int megabytes; /* size, 0 means not present */ 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* driver private data structure */ 918c2ecf20Sopenharmony_cistruct i7300_pvt { 928c2ecf20Sopenharmony_ci struct pci_dev *pci_dev_16_0_fsb_ctlr; /* 16.0 */ 938c2ecf20Sopenharmony_ci struct pci_dev *pci_dev_16_1_fsb_addr_map; /* 16.1 */ 948c2ecf20Sopenharmony_ci struct pci_dev *pci_dev_16_2_fsb_err_regs; /* 16.2 */ 958c2ecf20Sopenharmony_ci struct pci_dev *pci_dev_2x_0_fbd_branch[MAX_BRANCHES]; /* 21.0 and 22.0 */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci u16 tolm; /* top of low memory */ 988c2ecf20Sopenharmony_ci u64 ambase; /* AMB BAR */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci u32 mc_settings; /* Report several settings */ 1018c2ecf20Sopenharmony_ci u32 mc_settings_a; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci u16 mir[MAX_MIR]; /* Memory Interleave Reg*/ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci u16 mtr[MAX_SLOTS][MAX_BRANCHES]; /* Memory Technlogy Reg */ 1068c2ecf20Sopenharmony_ci u16 ambpresent[MAX_CHANNELS]; /* AMB present regs */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* DIMM information matrix, allocating architecture maximums */ 1098c2ecf20Sopenharmony_ci struct i7300_dimm_info dimm_info[MAX_SLOTS][MAX_CHANNELS]; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Temporary buffer for use when preparing error messages */ 1128c2ecf20Sopenharmony_ci char *tmp_prt_buffer; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* FIXME: Why do we need to have this static? */ 1168c2ecf20Sopenharmony_cistatic struct edac_pci_ctl_info *i7300_pci; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/*************************************************** 1198c2ecf20Sopenharmony_ci * i7300 Register definitions for memory enumeration 1208c2ecf20Sopenharmony_ci ***************************************************/ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * Device 16, 1248c2ecf20Sopenharmony_ci * Function 0: System Address (not documented) 1258c2ecf20Sopenharmony_ci * Function 1: Memory Branch Map, Control, Errors Register 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* OFFSETS for Function 0 */ 1298c2ecf20Sopenharmony_ci#define AMBASE 0x48 /* AMB Mem Mapped Reg Region Base */ 1308c2ecf20Sopenharmony_ci#define MAXCH 0x56 /* Max Channel Number */ 1318c2ecf20Sopenharmony_ci#define MAXDIMMPERCH 0x57 /* Max DIMM PER Channel Number */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* OFFSETS for Function 1 */ 1348c2ecf20Sopenharmony_ci#define MC_SETTINGS 0x40 1358c2ecf20Sopenharmony_ci #define IS_MIRRORED(mc) ((mc) & (1 << 16)) 1368c2ecf20Sopenharmony_ci #define IS_ECC_ENABLED(mc) ((mc) & (1 << 5)) 1378c2ecf20Sopenharmony_ci #define IS_RETRY_ENABLED(mc) ((mc) & (1 << 31)) 1388c2ecf20Sopenharmony_ci #define IS_SCRBALGO_ENHANCED(mc) ((mc) & (1 << 8)) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#define MC_SETTINGS_A 0x58 1418c2ecf20Sopenharmony_ci #define IS_SINGLE_MODE(mca) ((mca) & (1 << 14)) 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define TOLM 0x6C 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define MIR0 0x80 1468c2ecf20Sopenharmony_ci#define MIR1 0x84 1478c2ecf20Sopenharmony_ci#define MIR2 0x88 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * Note: Other Intel EDAC drivers use AMBPRESENT to identify if the available 1518c2ecf20Sopenharmony_ci * memory. From datasheet item 7.3.1 (FB-DIMM technology & organization), it 1528c2ecf20Sopenharmony_ci * seems that we cannot use this information directly for the same usage. 1538c2ecf20Sopenharmony_ci * Each memory slot may have up to 2 AMB interfaces, one for income and another 1548c2ecf20Sopenharmony_ci * for outcome interface to the next slot. 1558c2ecf20Sopenharmony_ci * For now, the driver just stores the AMB present registers, but rely only at 1568c2ecf20Sopenharmony_ci * the MTR info to detect memory. 1578c2ecf20Sopenharmony_ci * Datasheet is also not clear about how to map each AMBPRESENT registers to 1588c2ecf20Sopenharmony_ci * one of the 4 available channels. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci#define AMBPRESENT_0 0x64 1618c2ecf20Sopenharmony_ci#define AMBPRESENT_1 0x66 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic const u16 mtr_regs[MAX_SLOTS] = { 1648c2ecf20Sopenharmony_ci 0x80, 0x84, 0x88, 0x8c, 1658c2ecf20Sopenharmony_ci 0x82, 0x86, 0x8a, 0x8e 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Defines to extract the vaious fields from the 1708c2ecf20Sopenharmony_ci * MTRx - Memory Technology Registers 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci#define MTR_DIMMS_PRESENT(mtr) ((mtr) & (1 << 8)) 1738c2ecf20Sopenharmony_ci#define MTR_DIMMS_ETHROTTLE(mtr) ((mtr) & (1 << 7)) 1748c2ecf20Sopenharmony_ci#define MTR_DRAM_WIDTH(mtr) (((mtr) & (1 << 6)) ? 8 : 4) 1758c2ecf20Sopenharmony_ci#define MTR_DRAM_BANKS(mtr) (((mtr) & (1 << 5)) ? 8 : 4) 1768c2ecf20Sopenharmony_ci#define MTR_DIMM_RANKS(mtr) (((mtr) & (1 << 4)) ? 1 : 0) 1778c2ecf20Sopenharmony_ci#define MTR_DIMM_ROWS(mtr) (((mtr) >> 2) & 0x3) 1788c2ecf20Sopenharmony_ci#define MTR_DRAM_BANKS_ADDR_BITS 2 1798c2ecf20Sopenharmony_ci#define MTR_DIMM_ROWS_ADDR_BITS(mtr) (MTR_DIMM_ROWS(mtr) + 13) 1808c2ecf20Sopenharmony_ci#define MTR_DIMM_COLS(mtr) ((mtr) & 0x3) 1818c2ecf20Sopenharmony_ci#define MTR_DIMM_COLS_ADDR_BITS(mtr) (MTR_DIMM_COLS(mtr) + 10) 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/************************************************ 1848c2ecf20Sopenharmony_ci * i7300 Register definitions for error detection 1858c2ecf20Sopenharmony_ci ************************************************/ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * Device 16.1: FBD Error Registers 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci#define FERR_FAT_FBD 0x98 1918c2ecf20Sopenharmony_cistatic const char *ferr_fat_fbd_name[] = { 1928c2ecf20Sopenharmony_ci [22] = "Non-Redundant Fast Reset Timeout", 1938c2ecf20Sopenharmony_ci [2] = ">Tmid Thermal event with intelligent throttling disabled", 1948c2ecf20Sopenharmony_ci [1] = "Memory or FBD configuration CRC read error", 1958c2ecf20Sopenharmony_ci [0] = "Memory Write error on non-redundant retry or " 1968c2ecf20Sopenharmony_ci "FBD configuration Write error on retry", 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci#define GET_FBD_FAT_IDX(fbderr) (((fbderr) >> 28) & 3) 1998c2ecf20Sopenharmony_ci#define FERR_FAT_FBD_ERR_MASK ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 22)) 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define FERR_NF_FBD 0xa0 2028c2ecf20Sopenharmony_cistatic const char *ferr_nf_fbd_name[] = { 2038c2ecf20Sopenharmony_ci [24] = "DIMM-Spare Copy Completed", 2048c2ecf20Sopenharmony_ci [23] = "DIMM-Spare Copy Initiated", 2058c2ecf20Sopenharmony_ci [22] = "Redundant Fast Reset Timeout", 2068c2ecf20Sopenharmony_ci [21] = "Memory Write error on redundant retry", 2078c2ecf20Sopenharmony_ci [18] = "SPD protocol Error", 2088c2ecf20Sopenharmony_ci [17] = "FBD Northbound parity error on FBD Sync Status", 2098c2ecf20Sopenharmony_ci [16] = "Correctable Patrol Data ECC", 2108c2ecf20Sopenharmony_ci [15] = "Correctable Resilver- or Spare-Copy Data ECC", 2118c2ecf20Sopenharmony_ci [14] = "Correctable Mirrored Demand Data ECC", 2128c2ecf20Sopenharmony_ci [13] = "Correctable Non-Mirrored Demand Data ECC", 2138c2ecf20Sopenharmony_ci [11] = "Memory or FBD configuration CRC read error", 2148c2ecf20Sopenharmony_ci [10] = "FBD Configuration Write error on first attempt", 2158c2ecf20Sopenharmony_ci [9] = "Memory Write error on first attempt", 2168c2ecf20Sopenharmony_ci [8] = "Non-Aliased Uncorrectable Patrol Data ECC", 2178c2ecf20Sopenharmony_ci [7] = "Non-Aliased Uncorrectable Resilver- or Spare-Copy Data ECC", 2188c2ecf20Sopenharmony_ci [6] = "Non-Aliased Uncorrectable Mirrored Demand Data ECC", 2198c2ecf20Sopenharmony_ci [5] = "Non-Aliased Uncorrectable Non-Mirrored Demand Data ECC", 2208c2ecf20Sopenharmony_ci [4] = "Aliased Uncorrectable Patrol Data ECC", 2218c2ecf20Sopenharmony_ci [3] = "Aliased Uncorrectable Resilver- or Spare-Copy Data ECC", 2228c2ecf20Sopenharmony_ci [2] = "Aliased Uncorrectable Mirrored Demand Data ECC", 2238c2ecf20Sopenharmony_ci [1] = "Aliased Uncorrectable Non-Mirrored Demand Data ECC", 2248c2ecf20Sopenharmony_ci [0] = "Uncorrectable Data ECC on Replay", 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci#define GET_FBD_NF_IDX(fbderr) (((fbderr) >> 28) & 3) 2278c2ecf20Sopenharmony_ci#define FERR_NF_FBD_ERR_MASK ((1 << 24) | (1 << 23) | (1 << 22) | (1 << 21) |\ 2288c2ecf20Sopenharmony_ci (1 << 18) | (1 << 17) | (1 << 16) | (1 << 15) |\ 2298c2ecf20Sopenharmony_ci (1 << 14) | (1 << 13) | (1 << 11) | (1 << 10) |\ 2308c2ecf20Sopenharmony_ci (1 << 9) | (1 << 8) | (1 << 7) | (1 << 6) |\ 2318c2ecf20Sopenharmony_ci (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) |\ 2328c2ecf20Sopenharmony_ci (1 << 1) | (1 << 0)) 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci#define EMASK_FBD 0xa8 2358c2ecf20Sopenharmony_ci#define EMASK_FBD_ERR_MASK ((1 << 27) | (1 << 26) | (1 << 25) | (1 << 24) |\ 2368c2ecf20Sopenharmony_ci (1 << 22) | (1 << 21) | (1 << 20) | (1 << 19) |\ 2378c2ecf20Sopenharmony_ci (1 << 18) | (1 << 17) | (1 << 16) | (1 << 14) |\ 2388c2ecf20Sopenharmony_ci (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) |\ 2398c2ecf20Sopenharmony_ci (1 << 9) | (1 << 8) | (1 << 7) | (1 << 6) |\ 2408c2ecf20Sopenharmony_ci (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) |\ 2418c2ecf20Sopenharmony_ci (1 << 1) | (1 << 0)) 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * Device 16.2: Global Error Registers 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define FERR_GLOBAL_HI 0x48 2488c2ecf20Sopenharmony_cistatic const char *ferr_global_hi_name[] = { 2498c2ecf20Sopenharmony_ci [3] = "FSB 3 Fatal Error", 2508c2ecf20Sopenharmony_ci [2] = "FSB 2 Fatal Error", 2518c2ecf20Sopenharmony_ci [1] = "FSB 1 Fatal Error", 2528c2ecf20Sopenharmony_ci [0] = "FSB 0 Fatal Error", 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci#define ferr_global_hi_is_fatal(errno) 1 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci#define FERR_GLOBAL_LO 0x40 2578c2ecf20Sopenharmony_cistatic const char *ferr_global_lo_name[] = { 2588c2ecf20Sopenharmony_ci [31] = "Internal MCH Fatal Error", 2598c2ecf20Sopenharmony_ci [30] = "Intel QuickData Technology Device Fatal Error", 2608c2ecf20Sopenharmony_ci [29] = "FSB1 Fatal Error", 2618c2ecf20Sopenharmony_ci [28] = "FSB0 Fatal Error", 2628c2ecf20Sopenharmony_ci [27] = "FBD Channel 3 Fatal Error", 2638c2ecf20Sopenharmony_ci [26] = "FBD Channel 2 Fatal Error", 2648c2ecf20Sopenharmony_ci [25] = "FBD Channel 1 Fatal Error", 2658c2ecf20Sopenharmony_ci [24] = "FBD Channel 0 Fatal Error", 2668c2ecf20Sopenharmony_ci [23] = "PCI Express Device 7Fatal Error", 2678c2ecf20Sopenharmony_ci [22] = "PCI Express Device 6 Fatal Error", 2688c2ecf20Sopenharmony_ci [21] = "PCI Express Device 5 Fatal Error", 2698c2ecf20Sopenharmony_ci [20] = "PCI Express Device 4 Fatal Error", 2708c2ecf20Sopenharmony_ci [19] = "PCI Express Device 3 Fatal Error", 2718c2ecf20Sopenharmony_ci [18] = "PCI Express Device 2 Fatal Error", 2728c2ecf20Sopenharmony_ci [17] = "PCI Express Device 1 Fatal Error", 2738c2ecf20Sopenharmony_ci [16] = "ESI Fatal Error", 2748c2ecf20Sopenharmony_ci [15] = "Internal MCH Non-Fatal Error", 2758c2ecf20Sopenharmony_ci [14] = "Intel QuickData Technology Device Non Fatal Error", 2768c2ecf20Sopenharmony_ci [13] = "FSB1 Non-Fatal Error", 2778c2ecf20Sopenharmony_ci [12] = "FSB 0 Non-Fatal Error", 2788c2ecf20Sopenharmony_ci [11] = "FBD Channel 3 Non-Fatal Error", 2798c2ecf20Sopenharmony_ci [10] = "FBD Channel 2 Non-Fatal Error", 2808c2ecf20Sopenharmony_ci [9] = "FBD Channel 1 Non-Fatal Error", 2818c2ecf20Sopenharmony_ci [8] = "FBD Channel 0 Non-Fatal Error", 2828c2ecf20Sopenharmony_ci [7] = "PCI Express Device 7 Non-Fatal Error", 2838c2ecf20Sopenharmony_ci [6] = "PCI Express Device 6 Non-Fatal Error", 2848c2ecf20Sopenharmony_ci [5] = "PCI Express Device 5 Non-Fatal Error", 2858c2ecf20Sopenharmony_ci [4] = "PCI Express Device 4 Non-Fatal Error", 2868c2ecf20Sopenharmony_ci [3] = "PCI Express Device 3 Non-Fatal Error", 2878c2ecf20Sopenharmony_ci [2] = "PCI Express Device 2 Non-Fatal Error", 2888c2ecf20Sopenharmony_ci [1] = "PCI Express Device 1 Non-Fatal Error", 2898c2ecf20Sopenharmony_ci [0] = "ESI Non-Fatal Error", 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci#define ferr_global_lo_is_fatal(errno) ((errno < 16) ? 0 : 1) 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#define NRECMEMA 0xbe 2948c2ecf20Sopenharmony_ci #define NRECMEMA_BANK(v) (((v) >> 12) & 7) 2958c2ecf20Sopenharmony_ci #define NRECMEMA_RANK(v) (((v) >> 8) & 15) 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#define NRECMEMB 0xc0 2988c2ecf20Sopenharmony_ci #define NRECMEMB_IS_WR(v) ((v) & (1 << 31)) 2998c2ecf20Sopenharmony_ci #define NRECMEMB_CAS(v) (((v) >> 16) & 0x1fff) 3008c2ecf20Sopenharmony_ci #define NRECMEMB_RAS(v) ((v) & 0xffff) 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#define REDMEMA 0xdc 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci#define REDMEMB 0x7c 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci#define RECMEMA 0xe0 3078c2ecf20Sopenharmony_ci #define RECMEMA_BANK(v) (((v) >> 12) & 7) 3088c2ecf20Sopenharmony_ci #define RECMEMA_RANK(v) (((v) >> 8) & 15) 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci#define RECMEMB 0xe4 3118c2ecf20Sopenharmony_ci #define RECMEMB_IS_WR(v) ((v) & (1 << 31)) 3128c2ecf20Sopenharmony_ci #define RECMEMB_CAS(v) (((v) >> 16) & 0x1fff) 3138c2ecf20Sopenharmony_ci #define RECMEMB_RAS(v) ((v) & 0xffff) 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/******************************************** 3168c2ecf20Sopenharmony_ci * i7300 Functions related to error detection 3178c2ecf20Sopenharmony_ci ********************************************/ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/** 3208c2ecf20Sopenharmony_ci * get_err_from_table() - Gets the error message from a table 3218c2ecf20Sopenharmony_ci * @table: table name (array of char *) 3228c2ecf20Sopenharmony_ci * @size: number of elements at the table 3238c2ecf20Sopenharmony_ci * @pos: position of the element to be returned 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * This is a small routine that gets the pos-th element of a table. If the 3268c2ecf20Sopenharmony_ci * element doesn't exist (or it is empty), it returns "reserved". 3278c2ecf20Sopenharmony_ci * Instead of calling it directly, the better is to call via the macro 3288c2ecf20Sopenharmony_ci * GET_ERR_FROM_TABLE(), that automatically checks the table size via 3298c2ecf20Sopenharmony_ci * ARRAY_SIZE() macro 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic const char *get_err_from_table(const char *table[], int size, int pos) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci if (unlikely(pos >= size)) 3348c2ecf20Sopenharmony_ci return "Reserved"; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (unlikely(!table[pos])) 3378c2ecf20Sopenharmony_ci return "Reserved"; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return table[pos]; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci#define GET_ERR_FROM_TABLE(table, pos) \ 3438c2ecf20Sopenharmony_ci get_err_from_table(table, ARRAY_SIZE(table), pos) 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/** 3468c2ecf20Sopenharmony_ci * i7300_process_error_global() - Retrieve the hardware error information from 3478c2ecf20Sopenharmony_ci * the hardware global error registers and 3488c2ecf20Sopenharmony_ci * sends it to dmesg 3498c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic void i7300_process_error_global(struct mem_ctl_info *mci) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 3548c2ecf20Sopenharmony_ci u32 errnum, error_reg; 3558c2ecf20Sopenharmony_ci unsigned long errors; 3568c2ecf20Sopenharmony_ci const char *specific; 3578c2ecf20Sopenharmony_ci bool is_fatal; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* read in the 1st FATAL error register */ 3628c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 3638c2ecf20Sopenharmony_ci FERR_GLOBAL_HI, &error_reg); 3648c2ecf20Sopenharmony_ci if (unlikely(error_reg)) { 3658c2ecf20Sopenharmony_ci errors = error_reg; 3668c2ecf20Sopenharmony_ci errnum = find_first_bit(&errors, 3678c2ecf20Sopenharmony_ci ARRAY_SIZE(ferr_global_hi_name)); 3688c2ecf20Sopenharmony_ci specific = GET_ERR_FROM_TABLE(ferr_global_hi_name, errnum); 3698c2ecf20Sopenharmony_ci is_fatal = ferr_global_hi_is_fatal(errnum); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Clear the error bit */ 3728c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 3738c2ecf20Sopenharmony_ci FERR_GLOBAL_HI, error_reg); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci goto error_global; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 3798c2ecf20Sopenharmony_ci FERR_GLOBAL_LO, &error_reg); 3808c2ecf20Sopenharmony_ci if (unlikely(error_reg)) { 3818c2ecf20Sopenharmony_ci errors = error_reg; 3828c2ecf20Sopenharmony_ci errnum = find_first_bit(&errors, 3838c2ecf20Sopenharmony_ci ARRAY_SIZE(ferr_global_lo_name)); 3848c2ecf20Sopenharmony_ci specific = GET_ERR_FROM_TABLE(ferr_global_lo_name, errnum); 3858c2ecf20Sopenharmony_ci is_fatal = ferr_global_lo_is_fatal(errnum); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Clear the error bit */ 3888c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 3898c2ecf20Sopenharmony_ci FERR_GLOBAL_LO, error_reg); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci goto error_global; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci return; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cierror_global: 3968c2ecf20Sopenharmony_ci i7300_mc_printk(mci, KERN_EMERG, "%s misc error: %s\n", 3978c2ecf20Sopenharmony_ci is_fatal ? "Fatal" : "NOT fatal", specific); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci/** 4018c2ecf20Sopenharmony_ci * i7300_process_fbd_error() - Retrieve the hardware error information from 4028c2ecf20Sopenharmony_ci * the FBD error registers and sends it via 4038c2ecf20Sopenharmony_ci * EDAC error API calls 4048c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_cistatic void i7300_process_fbd_error(struct mem_ctl_info *mci) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 4098c2ecf20Sopenharmony_ci u32 errnum, value, error_reg; 4108c2ecf20Sopenharmony_ci u16 val16; 4118c2ecf20Sopenharmony_ci unsigned branch, channel, bank, rank, cas, ras; 4128c2ecf20Sopenharmony_ci u32 syndrome; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci unsigned long errors; 4158c2ecf20Sopenharmony_ci const char *specific; 4168c2ecf20Sopenharmony_ci bool is_wr; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* read in the 1st FATAL error register */ 4218c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4228c2ecf20Sopenharmony_ci FERR_FAT_FBD, &error_reg); 4238c2ecf20Sopenharmony_ci if (unlikely(error_reg & FERR_FAT_FBD_ERR_MASK)) { 4248c2ecf20Sopenharmony_ci errors = error_reg & FERR_FAT_FBD_ERR_MASK ; 4258c2ecf20Sopenharmony_ci errnum = find_first_bit(&errors, 4268c2ecf20Sopenharmony_ci ARRAY_SIZE(ferr_fat_fbd_name)); 4278c2ecf20Sopenharmony_ci specific = GET_ERR_FROM_TABLE(ferr_fat_fbd_name, errnum); 4288c2ecf20Sopenharmony_ci branch = (GET_FBD_FAT_IDX(error_reg) == 2) ? 1 : 0; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, 4318c2ecf20Sopenharmony_ci NRECMEMA, &val16); 4328c2ecf20Sopenharmony_ci bank = NRECMEMA_BANK(val16); 4338c2ecf20Sopenharmony_ci rank = NRECMEMA_RANK(val16); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4368c2ecf20Sopenharmony_ci NRECMEMB, &value); 4378c2ecf20Sopenharmony_ci is_wr = NRECMEMB_IS_WR(value); 4388c2ecf20Sopenharmony_ci cas = NRECMEMB_CAS(value); 4398c2ecf20Sopenharmony_ci ras = NRECMEMB_RAS(value); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Clean the error register */ 4428c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4438c2ecf20Sopenharmony_ci FERR_FAT_FBD, error_reg); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci snprintf(pvt->tmp_prt_buffer, PAGE_SIZE, 4468c2ecf20Sopenharmony_ci "Bank=%d RAS=%d CAS=%d Err=0x%lx (%s))", 4478c2ecf20Sopenharmony_ci bank, ras, cas, errors, specific); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0, 4508c2ecf20Sopenharmony_ci branch, -1, rank, 4518c2ecf20Sopenharmony_ci is_wr ? "Write error" : "Read error", 4528c2ecf20Sopenharmony_ci pvt->tmp_prt_buffer); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* read in the 1st NON-FATAL error register */ 4578c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4588c2ecf20Sopenharmony_ci FERR_NF_FBD, &error_reg); 4598c2ecf20Sopenharmony_ci if (unlikely(error_reg & FERR_NF_FBD_ERR_MASK)) { 4608c2ecf20Sopenharmony_ci errors = error_reg & FERR_NF_FBD_ERR_MASK; 4618c2ecf20Sopenharmony_ci errnum = find_first_bit(&errors, 4628c2ecf20Sopenharmony_ci ARRAY_SIZE(ferr_nf_fbd_name)); 4638c2ecf20Sopenharmony_ci specific = GET_ERR_FROM_TABLE(ferr_nf_fbd_name, errnum); 4648c2ecf20Sopenharmony_ci branch = (GET_FBD_NF_IDX(error_reg) == 2) ? 1 : 0; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4678c2ecf20Sopenharmony_ci REDMEMA, &syndrome); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, 4708c2ecf20Sopenharmony_ci RECMEMA, &val16); 4718c2ecf20Sopenharmony_ci bank = RECMEMA_BANK(val16); 4728c2ecf20Sopenharmony_ci rank = RECMEMA_RANK(val16); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4758c2ecf20Sopenharmony_ci RECMEMB, &value); 4768c2ecf20Sopenharmony_ci is_wr = RECMEMB_IS_WR(value); 4778c2ecf20Sopenharmony_ci cas = RECMEMB_CAS(value); 4788c2ecf20Sopenharmony_ci ras = RECMEMB_RAS(value); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4818c2ecf20Sopenharmony_ci REDMEMB, &value); 4828c2ecf20Sopenharmony_ci channel = (branch << 1); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Second channel ? */ 4858c2ecf20Sopenharmony_ci channel += !!(value & BIT(17)); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Clear the error bit */ 4888c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 4898c2ecf20Sopenharmony_ci FERR_NF_FBD, error_reg); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Form out message */ 4928c2ecf20Sopenharmony_ci snprintf(pvt->tmp_prt_buffer, PAGE_SIZE, 4938c2ecf20Sopenharmony_ci "DRAM-Bank=%d RAS=%d CAS=%d, Err=0x%lx (%s))", 4948c2ecf20Sopenharmony_ci bank, ras, cas, errors, specific); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 4978c2ecf20Sopenharmony_ci syndrome, 4988c2ecf20Sopenharmony_ci branch >> 1, channel % 2, rank, 4998c2ecf20Sopenharmony_ci is_wr ? "Write error" : "Read error", 5008c2ecf20Sopenharmony_ci pvt->tmp_prt_buffer); 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/** 5068c2ecf20Sopenharmony_ci * i7300_check_error() - Calls the error checking subroutines 5078c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_cistatic void i7300_check_error(struct mem_ctl_info *mci) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci i7300_process_error_global(mci); 5128c2ecf20Sopenharmony_ci i7300_process_fbd_error(mci); 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci/** 5168c2ecf20Sopenharmony_ci * i7300_clear_error() - Clears the error registers 5178c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_cistatic void i7300_clear_error(struct mem_ctl_info *mci) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct i7300_pvt *pvt = mci->pvt_info; 5228c2ecf20Sopenharmony_ci u32 value; 5238c2ecf20Sopenharmony_ci /* 5248c2ecf20Sopenharmony_ci * All error values are RWC - we need to read and write 1 to the 5258c2ecf20Sopenharmony_ci * bit that we want to cleanup 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Clear global error registers */ 5298c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 5308c2ecf20Sopenharmony_ci FERR_GLOBAL_HI, &value); 5318c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 5328c2ecf20Sopenharmony_ci FERR_GLOBAL_HI, value); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 5358c2ecf20Sopenharmony_ci FERR_GLOBAL_LO, &value); 5368c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs, 5378c2ecf20Sopenharmony_ci FERR_GLOBAL_LO, value); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Clear FBD error registers */ 5408c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 5418c2ecf20Sopenharmony_ci FERR_FAT_FBD, &value); 5428c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 5438c2ecf20Sopenharmony_ci FERR_FAT_FBD, value); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 5468c2ecf20Sopenharmony_ci FERR_NF_FBD, &value); 5478c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 5488c2ecf20Sopenharmony_ci FERR_NF_FBD, value); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci/** 5528c2ecf20Sopenharmony_ci * i7300_enable_error_reporting() - Enable the memory reporting logic at the 5538c2ecf20Sopenharmony_ci * hardware 5548c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_cistatic void i7300_enable_error_reporting(struct mem_ctl_info *mci) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct i7300_pvt *pvt = mci->pvt_info; 5598c2ecf20Sopenharmony_ci u32 fbd_error_mask; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Read the FBD Error Mask Register */ 5628c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 5638c2ecf20Sopenharmony_ci EMASK_FBD, &fbd_error_mask); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Enable with a '0' */ 5668c2ecf20Sopenharmony_ci fbd_error_mask &= ~(EMASK_FBD_ERR_MASK); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map, 5698c2ecf20Sopenharmony_ci EMASK_FBD, fbd_error_mask); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/************************************************ 5738c2ecf20Sopenharmony_ci * i7300 Functions related to memory enumberation 5748c2ecf20Sopenharmony_ci ************************************************/ 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/** 5778c2ecf20Sopenharmony_ci * decode_mtr() - Decodes the MTR descriptor, filling the edac structs 5788c2ecf20Sopenharmony_ci * @pvt: pointer to the private data struct used by i7300 driver 5798c2ecf20Sopenharmony_ci * @slot: DIMM slot (0 to 7) 5808c2ecf20Sopenharmony_ci * @ch: Channel number within the branch (0 or 1) 5818c2ecf20Sopenharmony_ci * @branch: Branch number (0 or 1) 5828c2ecf20Sopenharmony_ci * @dinfo: Pointer to DIMM info where dimm size is stored 5838c2ecf20Sopenharmony_ci * @dimm: Pointer to the struct dimm_info that corresponds to that element 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_cistatic int decode_mtr(struct i7300_pvt *pvt, 5868c2ecf20Sopenharmony_ci int slot, int ch, int branch, 5878c2ecf20Sopenharmony_ci struct i7300_dimm_info *dinfo, 5888c2ecf20Sopenharmony_ci struct dimm_info *dimm) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci int mtr, ans, addrBits, channel; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci channel = to_channel(ch, branch); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci mtr = pvt->mtr[slot][branch]; 5958c2ecf20Sopenharmony_ci ans = MTR_DIMMS_PRESENT(mtr) ? 1 : 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci edac_dbg(2, "\tMTR%d CH%d: DIMMs are %sPresent (mtr)\n", 5988c2ecf20Sopenharmony_ci slot, channel, ans ? "" : "NOT "); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Determine if there is a DIMM present in this DIMM slot */ 6018c2ecf20Sopenharmony_ci if (!ans) 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* Start with the number of bits for a Bank 6058c2ecf20Sopenharmony_ci * on the DRAM */ 6068c2ecf20Sopenharmony_ci addrBits = MTR_DRAM_BANKS_ADDR_BITS; 6078c2ecf20Sopenharmony_ci /* Add thenumber of ROW bits */ 6088c2ecf20Sopenharmony_ci addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr); 6098c2ecf20Sopenharmony_ci /* add the number of COLUMN bits */ 6108c2ecf20Sopenharmony_ci addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr); 6118c2ecf20Sopenharmony_ci /* add the number of RANK bits */ 6128c2ecf20Sopenharmony_ci addrBits += MTR_DIMM_RANKS(mtr); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci addrBits += 6; /* add 64 bits per DIMM */ 6158c2ecf20Sopenharmony_ci addrBits -= 20; /* divide by 2^^20 */ 6168c2ecf20Sopenharmony_ci addrBits -= 3; /* 8 bits per bytes */ 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci dinfo->megabytes = 1 << addrBits; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr)); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n", 6238c2ecf20Sopenharmony_ci MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled"); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr)); 6268c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tNUMRANK: %s\n", 6278c2ecf20Sopenharmony_ci MTR_DIMM_RANKS(mtr) ? "double" : "single"); 6288c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tNUMROW: %s\n", 6298c2ecf20Sopenharmony_ci MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" : 6308c2ecf20Sopenharmony_ci MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" : 6318c2ecf20Sopenharmony_ci MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" : 6328c2ecf20Sopenharmony_ci "65,536 - 16 rows"); 6338c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tNUMCOL: %s\n", 6348c2ecf20Sopenharmony_ci MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" : 6358c2ecf20Sopenharmony_ci MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" : 6368c2ecf20Sopenharmony_ci MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" : 6378c2ecf20Sopenharmony_ci "reserved"); 6388c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tSIZE: %d MB\n", dinfo->megabytes); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * The type of error detection actually depends of the 6428c2ecf20Sopenharmony_ci * mode of operation. When it is just one single memory chip, at 6438c2ecf20Sopenharmony_ci * socket 0, channel 0, it uses 8-byte-over-32-byte SECDED+ code. 6448c2ecf20Sopenharmony_ci * In normal or mirrored mode, it uses Lockstep mode, 6458c2ecf20Sopenharmony_ci * with the possibility of using an extended algorithm for x8 memories 6468c2ecf20Sopenharmony_ci * See datasheet Sections 7.3.6 to 7.3.8 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci dimm->nr_pages = MiB_TO_PAGES(dinfo->megabytes); 6508c2ecf20Sopenharmony_ci dimm->grain = 8; 6518c2ecf20Sopenharmony_ci dimm->mtype = MEM_FB_DDR2; 6528c2ecf20Sopenharmony_ci if (IS_SINGLE_MODE(pvt->mc_settings_a)) { 6538c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 6548c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tECC code is 8-byte-over-32-byte SECDED+ code\n"); 6558c2ecf20Sopenharmony_ci } else { 6568c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tECC code is on Lockstep mode\n"); 6578c2ecf20Sopenharmony_ci if (MTR_DRAM_WIDTH(mtr) == 8) 6588c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_S8ECD8ED; 6598c2ecf20Sopenharmony_ci else 6608c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_S4ECD4ED; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* ask what device type on this row */ 6648c2ecf20Sopenharmony_ci if (MTR_DRAM_WIDTH(mtr) == 8) { 6658c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tScrub algorithm for x8 is on %s mode\n", 6668c2ecf20Sopenharmony_ci IS_SCRBALGO_ENHANCED(pvt->mc_settings) ? 6678c2ecf20Sopenharmony_ci "enhanced" : "normal"); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci dimm->dtype = DEV_X8; 6708c2ecf20Sopenharmony_ci } else 6718c2ecf20Sopenharmony_ci dimm->dtype = DEV_X4; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return mtr; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/** 6778c2ecf20Sopenharmony_ci * print_dimm_size() - Prints dump of the memory organization 6788c2ecf20Sopenharmony_ci * @pvt: pointer to the private data struct used by i7300 driver 6798c2ecf20Sopenharmony_ci * 6808c2ecf20Sopenharmony_ci * Useful for debug. If debug is disabled, this routine do nothing 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_cistatic void print_dimm_size(struct i7300_pvt *pvt) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 6858c2ecf20Sopenharmony_ci struct i7300_dimm_info *dinfo; 6868c2ecf20Sopenharmony_ci char *p; 6878c2ecf20Sopenharmony_ci int space, n; 6888c2ecf20Sopenharmony_ci int channel, slot; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci space = PAGE_SIZE; 6918c2ecf20Sopenharmony_ci p = pvt->tmp_prt_buffer; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci n = snprintf(p, space, " "); 6948c2ecf20Sopenharmony_ci p += n; 6958c2ecf20Sopenharmony_ci space -= n; 6968c2ecf20Sopenharmony_ci for (channel = 0; channel < MAX_CHANNELS; channel++) { 6978c2ecf20Sopenharmony_ci n = snprintf(p, space, "channel %d | ", channel); 6988c2ecf20Sopenharmony_ci p += n; 6998c2ecf20Sopenharmony_ci space -= n; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); 7028c2ecf20Sopenharmony_ci p = pvt->tmp_prt_buffer; 7038c2ecf20Sopenharmony_ci space = PAGE_SIZE; 7048c2ecf20Sopenharmony_ci n = snprintf(p, space, "-------------------------------" 7058c2ecf20Sopenharmony_ci "------------------------------"); 7068c2ecf20Sopenharmony_ci p += n; 7078c2ecf20Sopenharmony_ci space -= n; 7088c2ecf20Sopenharmony_ci edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); 7098c2ecf20Sopenharmony_ci p = pvt->tmp_prt_buffer; 7108c2ecf20Sopenharmony_ci space = PAGE_SIZE; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci for (slot = 0; slot < MAX_SLOTS; slot++) { 7138c2ecf20Sopenharmony_ci n = snprintf(p, space, "csrow/SLOT %d ", slot); 7148c2ecf20Sopenharmony_ci p += n; 7158c2ecf20Sopenharmony_ci space -= n; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci for (channel = 0; channel < MAX_CHANNELS; channel++) { 7188c2ecf20Sopenharmony_ci dinfo = &pvt->dimm_info[slot][channel]; 7198c2ecf20Sopenharmony_ci n = snprintf(p, space, "%4d MB | ", dinfo->megabytes); 7208c2ecf20Sopenharmony_ci p += n; 7218c2ecf20Sopenharmony_ci space -= n; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); 7258c2ecf20Sopenharmony_ci p = pvt->tmp_prt_buffer; 7268c2ecf20Sopenharmony_ci space = PAGE_SIZE; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci n = snprintf(p, space, "-------------------------------" 7308c2ecf20Sopenharmony_ci "------------------------------"); 7318c2ecf20Sopenharmony_ci p += n; 7328c2ecf20Sopenharmony_ci space -= n; 7338c2ecf20Sopenharmony_ci edac_dbg(2, "%s\n", pvt->tmp_prt_buffer); 7348c2ecf20Sopenharmony_ci p = pvt->tmp_prt_buffer; 7358c2ecf20Sopenharmony_ci space = PAGE_SIZE; 7368c2ecf20Sopenharmony_ci#endif 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/** 7408c2ecf20Sopenharmony_ci * i7300_init_csrows() - Initialize the 'csrows' table within 7418c2ecf20Sopenharmony_ci * the mci control structure with the 7428c2ecf20Sopenharmony_ci * addressing of memory. 7438c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_cistatic int i7300_init_csrows(struct mem_ctl_info *mci) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 7488c2ecf20Sopenharmony_ci struct i7300_dimm_info *dinfo; 7498c2ecf20Sopenharmony_ci int rc = -ENODEV; 7508c2ecf20Sopenharmony_ci int mtr; 7518c2ecf20Sopenharmony_ci int ch, branch, slot, channel, max_channel, max_branch; 7528c2ecf20Sopenharmony_ci struct dimm_info *dimm; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci edac_dbg(2, "Memory Technology Registers:\n"); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (IS_SINGLE_MODE(pvt->mc_settings_a)) { 7598c2ecf20Sopenharmony_ci max_branch = 1; 7608c2ecf20Sopenharmony_ci max_channel = 1; 7618c2ecf20Sopenharmony_ci } else { 7628c2ecf20Sopenharmony_ci max_branch = MAX_BRANCHES; 7638c2ecf20Sopenharmony_ci max_channel = MAX_CH_PER_BRANCH; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* Get the AMB present registers for the four channels */ 7678c2ecf20Sopenharmony_ci for (branch = 0; branch < max_branch; branch++) { 7688c2ecf20Sopenharmony_ci /* Read and dump branch 0's MTRs */ 7698c2ecf20Sopenharmony_ci channel = to_channel(0, branch); 7708c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], 7718c2ecf20Sopenharmony_ci AMBPRESENT_0, 7728c2ecf20Sopenharmony_ci &pvt->ambpresent[channel]); 7738c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n", 7748c2ecf20Sopenharmony_ci channel, pvt->ambpresent[channel]); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (max_channel == 1) 7778c2ecf20Sopenharmony_ci continue; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci channel = to_channel(1, branch); 7808c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], 7818c2ecf20Sopenharmony_ci AMBPRESENT_1, 7828c2ecf20Sopenharmony_ci &pvt->ambpresent[channel]); 7838c2ecf20Sopenharmony_ci edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n", 7848c2ecf20Sopenharmony_ci channel, pvt->ambpresent[channel]); 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* Get the set of MTR[0-7] regs by each branch */ 7888c2ecf20Sopenharmony_ci for (slot = 0; slot < MAX_SLOTS; slot++) { 7898c2ecf20Sopenharmony_ci int where = mtr_regs[slot]; 7908c2ecf20Sopenharmony_ci for (branch = 0; branch < max_branch; branch++) { 7918c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], 7928c2ecf20Sopenharmony_ci where, 7938c2ecf20Sopenharmony_ci &pvt->mtr[slot][branch]); 7948c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channel; ch++) { 7958c2ecf20Sopenharmony_ci int channel = to_channel(ch, branch); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, branch, ch, slot); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci dinfo = &pvt->dimm_info[slot][channel]; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci mtr = decode_mtr(pvt, slot, ch, branch, 8028c2ecf20Sopenharmony_ci dinfo, dimm); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* if no DIMMS on this row, continue */ 8058c2ecf20Sopenharmony_ci if (!MTR_DIMMS_PRESENT(mtr)) 8068c2ecf20Sopenharmony_ci continue; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci rc = 0; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return rc; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/** 8188c2ecf20Sopenharmony_ci * decode_mir() - Decodes Memory Interleave Register (MIR) info 8198c2ecf20Sopenharmony_ci * @mir_no: number of the MIR register to decode 8208c2ecf20Sopenharmony_ci * @mir: array with the MIR data cached on the driver 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_cistatic void decode_mir(int mir_no, u16 mir[MAX_MIR]) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci if (mir[mir_no] & 3) 8258c2ecf20Sopenharmony_ci edac_dbg(2, "MIR%d: limit= 0x%x Branch(es) that participate: %s %s\n", 8268c2ecf20Sopenharmony_ci mir_no, 8278c2ecf20Sopenharmony_ci (mir[mir_no] >> 4) & 0xfff, 8288c2ecf20Sopenharmony_ci (mir[mir_no] & 1) ? "B0" : "", 8298c2ecf20Sopenharmony_ci (mir[mir_no] & 2) ? "B1" : ""); 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/** 8338c2ecf20Sopenharmony_ci * i7300_get_mc_regs() - Get the contents of the MC enumeration registers 8348c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * Data read is cached internally for its usage when needed 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_cistatic int i7300_get_mc_regs(struct mem_ctl_info *mci) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 8418c2ecf20Sopenharmony_ci u32 actual_tolm; 8428c2ecf20Sopenharmony_ci int i, rc; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_0_fsb_ctlr, AMBASE, 8478c2ecf20Sopenharmony_ci (u32 *) &pvt->ambase); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci edac_dbg(2, "AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* Get the Branch Map regs */ 8528c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, TOLM, &pvt->tolm); 8538c2ecf20Sopenharmony_ci pvt->tolm >>= 12; 8548c2ecf20Sopenharmony_ci edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n", 8558c2ecf20Sopenharmony_ci pvt->tolm, pvt->tolm); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28)); 8588c2ecf20Sopenharmony_ci edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n", 8598c2ecf20Sopenharmony_ci actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* Get memory controller settings */ 8628c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS, 8638c2ecf20Sopenharmony_ci &pvt->mc_settings); 8648c2ecf20Sopenharmony_ci pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS_A, 8658c2ecf20Sopenharmony_ci &pvt->mc_settings_a); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (IS_SINGLE_MODE(pvt->mc_settings_a)) 8688c2ecf20Sopenharmony_ci edac_dbg(0, "Memory controller operating on single mode\n"); 8698c2ecf20Sopenharmony_ci else 8708c2ecf20Sopenharmony_ci edac_dbg(0, "Memory controller operating on %smirrored mode\n", 8718c2ecf20Sopenharmony_ci IS_MIRRORED(pvt->mc_settings) ? "" : "non-"); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci edac_dbg(0, "Error detection is %s\n", 8748c2ecf20Sopenharmony_ci IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled"); 8758c2ecf20Sopenharmony_ci edac_dbg(0, "Retry is %s\n", 8768c2ecf20Sopenharmony_ci IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled"); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* Get Memory Interleave Range registers */ 8798c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0, 8808c2ecf20Sopenharmony_ci &pvt->mir[0]); 8818c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR1, 8828c2ecf20Sopenharmony_ci &pvt->mir[1]); 8838c2ecf20Sopenharmony_ci pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR2, 8848c2ecf20Sopenharmony_ci &pvt->mir[2]); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* Decode the MIR regs */ 8878c2ecf20Sopenharmony_ci for (i = 0; i < MAX_MIR; i++) 8888c2ecf20Sopenharmony_ci decode_mir(i, pvt->mir); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci rc = i7300_init_csrows(mci); 8918c2ecf20Sopenharmony_ci if (rc < 0) 8928c2ecf20Sopenharmony_ci return rc; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Go and determine the size of each DIMM and place in an 8958c2ecf20Sopenharmony_ci * orderly matrix */ 8968c2ecf20Sopenharmony_ci print_dimm_size(pvt); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci return 0; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/************************************************* 9028c2ecf20Sopenharmony_ci * i7300 Functions related to device probe/release 9038c2ecf20Sopenharmony_ci *************************************************/ 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/** 9068c2ecf20Sopenharmony_ci * i7300_put_devices() - Release the PCI devices 9078c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_cistatic void i7300_put_devices(struct mem_ctl_info *mci) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 9128c2ecf20Sopenharmony_ci int branch; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* Decrement usage count for devices */ 9178c2ecf20Sopenharmony_ci for (branch = 0; branch < MAX_CH_PER_BRANCH; branch++) 9188c2ecf20Sopenharmony_ci pci_dev_put(pvt->pci_dev_2x_0_fbd_branch[branch]); 9198c2ecf20Sopenharmony_ci pci_dev_put(pvt->pci_dev_16_2_fsb_err_regs); 9208c2ecf20Sopenharmony_ci pci_dev_put(pvt->pci_dev_16_1_fsb_addr_map); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci/** 9248c2ecf20Sopenharmony_ci * i7300_get_devices() - Find and perform 'get' operation on the MCH's 9258c2ecf20Sopenharmony_ci * device/functions we want to reference for this driver 9268c2ecf20Sopenharmony_ci * @mci: struct mem_ctl_info pointer 9278c2ecf20Sopenharmony_ci * 9288c2ecf20Sopenharmony_ci * Access and prepare the several devices for usage: 9298c2ecf20Sopenharmony_ci * I7300 devices used by this driver: 9308c2ecf20Sopenharmony_ci * Device 16, functions 0,1 and 2: PCI_DEVICE_ID_INTEL_I7300_MCH_ERR 9318c2ecf20Sopenharmony_ci * Device 21 function 0: PCI_DEVICE_ID_INTEL_I7300_MCH_FB0 9328c2ecf20Sopenharmony_ci * Device 22 function 0: PCI_DEVICE_ID_INTEL_I7300_MCH_FB1 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_cistatic int i7300_get_devices(struct mem_ctl_info *mci) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 9378c2ecf20Sopenharmony_ci struct pci_dev *pdev; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* Attempt to 'get' the MCH register we want */ 9428c2ecf20Sopenharmony_ci pdev = NULL; 9438c2ecf20Sopenharmony_ci while ((pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 9448c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_I7300_MCH_ERR, 9458c2ecf20Sopenharmony_ci pdev))) { 9468c2ecf20Sopenharmony_ci /* Store device 16 funcs 1 and 2 */ 9478c2ecf20Sopenharmony_ci switch (PCI_FUNC(pdev->devfn)) { 9488c2ecf20Sopenharmony_ci case 1: 9498c2ecf20Sopenharmony_ci if (!pvt->pci_dev_16_1_fsb_addr_map) 9508c2ecf20Sopenharmony_ci pvt->pci_dev_16_1_fsb_addr_map = 9518c2ecf20Sopenharmony_ci pci_dev_get(pdev); 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci case 2: 9548c2ecf20Sopenharmony_ci if (!pvt->pci_dev_16_2_fsb_err_regs) 9558c2ecf20Sopenharmony_ci pvt->pci_dev_16_2_fsb_err_regs = 9568c2ecf20Sopenharmony_ci pci_dev_get(pdev); 9578c2ecf20Sopenharmony_ci break; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (!pvt->pci_dev_16_1_fsb_addr_map || 9628c2ecf20Sopenharmony_ci !pvt->pci_dev_16_2_fsb_err_regs) { 9638c2ecf20Sopenharmony_ci /* At least one device was not found */ 9648c2ecf20Sopenharmony_ci i7300_printk(KERN_ERR, 9658c2ecf20Sopenharmony_ci "'system address,Process Bus' device not found:" 9668c2ecf20Sopenharmony_ci "vendor 0x%x device 0x%x ERR funcs (broken BIOS?)\n", 9678c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, 9688c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_I7300_MCH_ERR); 9698c2ecf20Sopenharmony_ci goto error; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n", 9738c2ecf20Sopenharmony_ci pci_name(pvt->pci_dev_16_0_fsb_ctlr), 9748c2ecf20Sopenharmony_ci pvt->pci_dev_16_0_fsb_ctlr->vendor, 9758c2ecf20Sopenharmony_ci pvt->pci_dev_16_0_fsb_ctlr->device); 9768c2ecf20Sopenharmony_ci edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n", 9778c2ecf20Sopenharmony_ci pci_name(pvt->pci_dev_16_1_fsb_addr_map), 9788c2ecf20Sopenharmony_ci pvt->pci_dev_16_1_fsb_addr_map->vendor, 9798c2ecf20Sopenharmony_ci pvt->pci_dev_16_1_fsb_addr_map->device); 9808c2ecf20Sopenharmony_ci edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n", 9818c2ecf20Sopenharmony_ci pci_name(pvt->pci_dev_16_2_fsb_err_regs), 9828c2ecf20Sopenharmony_ci pvt->pci_dev_16_2_fsb_err_regs->vendor, 9838c2ecf20Sopenharmony_ci pvt->pci_dev_16_2_fsb_err_regs->device); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci pvt->pci_dev_2x_0_fbd_branch[0] = pci_get_device(PCI_VENDOR_ID_INTEL, 9868c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_I7300_MCH_FB0, 9878c2ecf20Sopenharmony_ci NULL); 9888c2ecf20Sopenharmony_ci if (!pvt->pci_dev_2x_0_fbd_branch[0]) { 9898c2ecf20Sopenharmony_ci i7300_printk(KERN_ERR, 9908c2ecf20Sopenharmony_ci "MC: 'BRANCH 0' device not found:" 9918c2ecf20Sopenharmony_ci "vendor 0x%x device 0x%x Func 0 (broken BIOS?)\n", 9928c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_FB0); 9938c2ecf20Sopenharmony_ci goto error; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci pvt->pci_dev_2x_0_fbd_branch[1] = pci_get_device(PCI_VENDOR_ID_INTEL, 9978c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_I7300_MCH_FB1, 9988c2ecf20Sopenharmony_ci NULL); 9998c2ecf20Sopenharmony_ci if (!pvt->pci_dev_2x_0_fbd_branch[1]) { 10008c2ecf20Sopenharmony_ci i7300_printk(KERN_ERR, 10018c2ecf20Sopenharmony_ci "MC: 'BRANCH 1' device not found:" 10028c2ecf20Sopenharmony_ci "vendor 0x%x device 0x%x Func 0 " 10038c2ecf20Sopenharmony_ci "(broken BIOS?)\n", 10048c2ecf20Sopenharmony_ci PCI_VENDOR_ID_INTEL, 10058c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_I7300_MCH_FB1); 10068c2ecf20Sopenharmony_ci goto error; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cierror: 10128c2ecf20Sopenharmony_ci i7300_put_devices(mci); 10138c2ecf20Sopenharmony_ci return -ENODEV; 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/** 10178c2ecf20Sopenharmony_ci * i7300_init_one() - Probe for one instance of the device 10188c2ecf20Sopenharmony_ci * @pdev: struct pci_dev pointer 10198c2ecf20Sopenharmony_ci * @id: struct pci_device_id pointer - currently unused 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_cistatic int i7300_init_one(struct pci_dev *pdev, const struct pci_device_id *id) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 10248c2ecf20Sopenharmony_ci struct edac_mc_layer layers[3]; 10258c2ecf20Sopenharmony_ci struct i7300_pvt *pvt; 10268c2ecf20Sopenharmony_ci int rc; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci /* wake up device */ 10298c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 10308c2ecf20Sopenharmony_ci if (rc == -EIO) 10318c2ecf20Sopenharmony_ci return rc; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n", 10348c2ecf20Sopenharmony_ci pdev->bus->number, 10358c2ecf20Sopenharmony_ci PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* We only are looking for func 0 of the set */ 10388c2ecf20Sopenharmony_ci if (PCI_FUNC(pdev->devfn) != 0) 10398c2ecf20Sopenharmony_ci return -ENODEV; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* allocate a new MC control structure */ 10428c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_BRANCH; 10438c2ecf20Sopenharmony_ci layers[0].size = MAX_BRANCHES; 10448c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = false; 10458c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 10468c2ecf20Sopenharmony_ci layers[1].size = MAX_CH_PER_BRANCH; 10478c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = true; 10488c2ecf20Sopenharmony_ci layers[2].type = EDAC_MC_LAYER_SLOT; 10498c2ecf20Sopenharmony_ci layers[2].size = MAX_SLOTS; 10508c2ecf20Sopenharmony_ci layers[2].is_virt_csrow = true; 10518c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt)); 10528c2ecf20Sopenharmony_ci if (mci == NULL) 10538c2ecf20Sopenharmony_ci return -ENOMEM; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci edac_dbg(0, "MC: mci = %p\n", mci); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; /* record ptr to the generic device */ 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 10608c2ecf20Sopenharmony_ci pvt->pci_dev_16_0_fsb_ctlr = pdev; /* Record this device in our private */ 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci pvt->tmp_prt_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); 10638c2ecf20Sopenharmony_ci if (!pvt->tmp_prt_buffer) { 10648c2ecf20Sopenharmony_ci edac_mc_free(mci); 10658c2ecf20Sopenharmony_ci return -ENOMEM; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* 'get' the pci devices we want to reserve for our use */ 10698c2ecf20Sopenharmony_ci if (i7300_get_devices(mci)) 10708c2ecf20Sopenharmony_ci goto fail0; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci mci->mc_idx = 0; 10738c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_FB_DDR2; 10748c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE; 10758c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_NONE; 10768c2ecf20Sopenharmony_ci mci->mod_name = "i7300_edac.c"; 10778c2ecf20Sopenharmony_ci mci->ctl_name = i7300_devs[0].ctl_name; 10788c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pdev); 10798c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* Set the function pointer to an actual operation function */ 10828c2ecf20Sopenharmony_ci mci->edac_check = i7300_check_error; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* initialize the MC control structure 'csrows' table 10858c2ecf20Sopenharmony_ci * with the mapping and control information */ 10868c2ecf20Sopenharmony_ci if (i7300_get_mc_regs(mci)) { 10878c2ecf20Sopenharmony_ci edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i7300_init_csrows() returned nonzero value\n"); 10888c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */ 10898c2ecf20Sopenharmony_ci } else { 10908c2ecf20Sopenharmony_ci edac_dbg(1, "MC: Enable error reporting now\n"); 10918c2ecf20Sopenharmony_ci i7300_enable_error_reporting(mci); 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* add this new MC control structure to EDAC's list of MCs */ 10958c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 10968c2ecf20Sopenharmony_ci edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); 10978c2ecf20Sopenharmony_ci /* FIXME: perhaps some code should go here that disables error 10988c2ecf20Sopenharmony_ci * reporting if we just enabled it 10998c2ecf20Sopenharmony_ci */ 11008c2ecf20Sopenharmony_ci goto fail1; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci i7300_clear_error(mci); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* allocating generic PCI control info */ 11068c2ecf20Sopenharmony_ci i7300_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); 11078c2ecf20Sopenharmony_ci if (!i7300_pci) { 11088c2ecf20Sopenharmony_ci printk(KERN_WARNING 11098c2ecf20Sopenharmony_ci "%s(): Unable to create PCI control\n", 11108c2ecf20Sopenharmony_ci __func__); 11118c2ecf20Sopenharmony_ci printk(KERN_WARNING 11128c2ecf20Sopenharmony_ci "%s(): PCI error report via EDAC not setup\n", 11138c2ecf20Sopenharmony_ci __func__); 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci return 0; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* Error exit unwinding stack */ 11198c2ecf20Sopenharmony_cifail1: 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci i7300_put_devices(mci); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cifail0: 11248c2ecf20Sopenharmony_ci kfree(pvt->tmp_prt_buffer); 11258c2ecf20Sopenharmony_ci edac_mc_free(mci); 11268c2ecf20Sopenharmony_ci return -ENODEV; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci/** 11308c2ecf20Sopenharmony_ci * i7300_remove_one() - Remove the driver 11318c2ecf20Sopenharmony_ci * @pdev: struct pci_dev pointer 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_cistatic void i7300_remove_one(struct pci_dev *pdev) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 11368c2ecf20Sopenharmony_ci char *tmp; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (i7300_pci) 11418c2ecf20Sopenharmony_ci edac_pci_release_generic_ctl(i7300_pci); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci mci = edac_mc_del_mc(&pdev->dev); 11448c2ecf20Sopenharmony_ci if (!mci) 11458c2ecf20Sopenharmony_ci return; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci tmp = ((struct i7300_pvt *)mci->pvt_info)->tmp_prt_buffer; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* retrieve references to resources, and free those resources */ 11508c2ecf20Sopenharmony_ci i7300_put_devices(mci); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci kfree(tmp); 11538c2ecf20Sopenharmony_ci edac_mc_free(mci); 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci/* 11578c2ecf20Sopenharmony_ci * pci_device_id: table for which devices we are looking for 11588c2ecf20Sopenharmony_ci * 11598c2ecf20Sopenharmony_ci * Has only 8086:360c PCI ID 11608c2ecf20Sopenharmony_ci */ 11618c2ecf20Sopenharmony_cistatic const struct pci_device_id i7300_pci_tbl[] = { 11628c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_ERR)}, 11638c2ecf20Sopenharmony_ci {0,} /* 0 terminated list. */ 11648c2ecf20Sopenharmony_ci}; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i7300_pci_tbl); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci/* 11698c2ecf20Sopenharmony_ci * i7300_driver: pci_driver structure for this module 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_cistatic struct pci_driver i7300_driver = { 11728c2ecf20Sopenharmony_ci .name = "i7300_edac", 11738c2ecf20Sopenharmony_ci .probe = i7300_init_one, 11748c2ecf20Sopenharmony_ci .remove = i7300_remove_one, 11758c2ecf20Sopenharmony_ci .id_table = i7300_pci_tbl, 11768c2ecf20Sopenharmony_ci}; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci/** 11798c2ecf20Sopenharmony_ci * i7300_init() - Registers the driver 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_cistatic int __init i7300_init(void) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci int pci_rc; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 11888c2ecf20Sopenharmony_ci opstate_init(); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci pci_rc = pci_register_driver(&i7300_driver); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return (pci_rc < 0) ? pci_rc : 0; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci/** 11968c2ecf20Sopenharmony_ci * i7300_init() - Unregisters the driver 11978c2ecf20Sopenharmony_ci */ 11988c2ecf20Sopenharmony_cistatic void __exit i7300_exit(void) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 12018c2ecf20Sopenharmony_ci pci_unregister_driver(&i7300_driver); 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_cimodule_init(i7300_init); 12058c2ecf20Sopenharmony_cimodule_exit(i7300_exit); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab"); 12098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Red Hat Inc. (https://www.redhat.com)"); 12108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC Driver for Intel I7300 memory controllers - " 12118c2ecf20Sopenharmony_ci I7300_REVISION); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444); 12148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 1215