18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Intel 5100 Memory Controllers kernel module 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the 58c2ecf20Sopenharmony_ci * GNU General Public License. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This module is based on the following document: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet 108c2ecf20Sopenharmony_ci * http://download.intel.com/design/chipsets/datashts/318378.pdf 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The intel 5100 has two independent channels. EDAC core currently 138c2ecf20Sopenharmony_ci * can not reflect this configuration so instead the chip-select 148c2ecf20Sopenharmony_ci * rows for each respective channel are laid out one after another, 158c2ecf20Sopenharmony_ci * the first half belonging to channel 0, the second half belonging 168c2ecf20Sopenharmony_ci * to channel 1. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This driver is for DDR2 DIMMs, and it uses chip select to select among the 198c2ecf20Sopenharmony_ci * several ranks. However, instead of showing memories as ranks, it outputs 208c2ecf20Sopenharmony_ci * them as DIMM's. An internal table creates the association between ranks 218c2ecf20Sopenharmony_ci * and DIMM's. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/pci.h> 268c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 278c2ecf20Sopenharmony_ci#include <linux/edac.h> 288c2ecf20Sopenharmony_ci#include <linux/delay.h> 298c2ecf20Sopenharmony_ci#include <linux/mmzone.h> 308c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "edac_module.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* register addresses */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* device 16, func 1 */ 378c2ecf20Sopenharmony_ci#define I5100_MC 0x40 /* Memory Control Register */ 388c2ecf20Sopenharmony_ci#define I5100_MC_SCRBEN_MASK (1 << 7) 398c2ecf20Sopenharmony_ci#define I5100_MC_SCRBDONE_MASK (1 << 4) 408c2ecf20Sopenharmony_ci#define I5100_MS 0x44 /* Memory Status Register */ 418c2ecf20Sopenharmony_ci#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ 428c2ecf20Sopenharmony_ci#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ 438c2ecf20Sopenharmony_ci#define I5100_TOLM 0x6c /* Top of Low Memory */ 448c2ecf20Sopenharmony_ci#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */ 458c2ecf20Sopenharmony_ci#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */ 468c2ecf20Sopenharmony_ci#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */ 478c2ecf20Sopenharmony_ci#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */ 488c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */ 498c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16) 508c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15) 518c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14) 528c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12) 538c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11) 548c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10) 558c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6) 568c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5) 578c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4) 588c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_M1ERR_MASK (1 << 1) 598c2ecf20Sopenharmony_ci#define I5100_FERR_NF_MEM_ANY_MASK \ 608c2ecf20Sopenharmony_ci (I5100_FERR_NF_MEM_M16ERR_MASK | \ 618c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M15ERR_MASK | \ 628c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M14ERR_MASK | \ 638c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M12ERR_MASK | \ 648c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M11ERR_MASK | \ 658c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M10ERR_MASK | \ 668c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M6ERR_MASK | \ 678c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M5ERR_MASK | \ 688c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M4ERR_MASK | \ 698c2ecf20Sopenharmony_ci I5100_FERR_NF_MEM_M1ERR_MASK) 708c2ecf20Sopenharmony_ci#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */ 718c2ecf20Sopenharmony_ci#define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */ 728c2ecf20Sopenharmony_ci#define I5100_MEM0EINJMSK0 0x200 /* Injection Mask0 Register Channel 0 */ 738c2ecf20Sopenharmony_ci#define I5100_MEM1EINJMSK0 0x208 /* Injection Mask0 Register Channel 1 */ 748c2ecf20Sopenharmony_ci#define I5100_MEMXEINJMSK0_EINJEN (1 << 27) 758c2ecf20Sopenharmony_ci#define I5100_MEM0EINJMSK1 0x204 /* Injection Mask1 Register Channel 0 */ 768c2ecf20Sopenharmony_ci#define I5100_MEM1EINJMSK1 0x206 /* Injection Mask1 Register Channel 1 */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Device 19, Function 0 */ 798c2ecf20Sopenharmony_ci#define I5100_DINJ0 0x9a 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* device 21 and 22, func 0 */ 828c2ecf20Sopenharmony_ci#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */ 838c2ecf20Sopenharmony_ci#define I5100_DMIR 0x15c /* DIMM Interleave Range */ 848c2ecf20Sopenharmony_ci#define I5100_VALIDLOG 0x18c /* Valid Log Markers */ 858c2ecf20Sopenharmony_ci#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */ 868c2ecf20Sopenharmony_ci#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */ 878c2ecf20Sopenharmony_ci#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */ 888c2ecf20Sopenharmony_ci#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */ 898c2ecf20Sopenharmony_ci#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */ 908c2ecf20Sopenharmony_ci#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */ 918c2ecf20Sopenharmony_ci#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* bit field accessors */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline u32 i5100_mc_scrben(u32 mc) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return mc >> 7 & 1; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline u32 i5100_mc_errdeten(u32 mc) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return mc >> 5 & 1; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline u32 i5100_mc_scrbdone(u32 mc) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci return mc >> 4 & 1; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline u16 i5100_spddata_rdo(u16 a) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return a >> 15 & 1; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline u16 i5100_spddata_sbe(u16 a) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return a >> 13 & 1; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline u16 i5100_spddata_busy(u16 a) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return a >> 12 & 1; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic inline u16 i5100_spddata_data(u16 a) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return a & ((1 << 8) - 1); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba, 1318c2ecf20Sopenharmony_ci u32 data, u32 cmd) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci return ((dti & ((1 << 4) - 1)) << 28) | 1348c2ecf20Sopenharmony_ci ((ckovrd & 1) << 27) | 1358c2ecf20Sopenharmony_ci ((sa & ((1 << 3) - 1)) << 24) | 1368c2ecf20Sopenharmony_ci ((ba & ((1 << 8) - 1)) << 16) | 1378c2ecf20Sopenharmony_ci ((data & ((1 << 8) - 1)) << 8) | 1388c2ecf20Sopenharmony_ci (cmd & 1); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic inline u16 i5100_tolm_tolm(u16 a) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return a >> 12 & ((1 << 4) - 1); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline u16 i5100_mir_limit(u16 a) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return a >> 4 & ((1 << 12) - 1); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic inline u16 i5100_mir_way1(u16 a) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return a >> 1 & 1; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic inline u16 i5100_mir_way0(u16 a) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return a & 1; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic inline u32 i5100_ferr_nf_mem_chan_indx(u32 a) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci return a >> 28 & 1; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline u32 i5100_ferr_nf_mem_any(u32 a) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci return a & I5100_FERR_NF_MEM_ANY_MASK; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic inline u32 i5100_nerr_nf_mem_any(u32 a) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci return i5100_ferr_nf_mem_any(a); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic inline u32 i5100_dmir_limit(u32 a) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return a >> 16 & ((1 << 11) - 1); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic inline u32 i5100_dmir_rank(u32 a, u32 i) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci return a >> (4 * i) & ((1 << 2) - 1); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic inline u16 i5100_mtr_present(u16 a) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci return a >> 10 & 1; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic inline u16 i5100_mtr_ethrottle(u16 a) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci return a >> 9 & 1; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic inline u16 i5100_mtr_width(u16 a) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci return a >> 8 & 1; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline u16 i5100_mtr_numbank(u16 a) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci return a >> 6 & 1; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic inline u16 i5100_mtr_numrow(u16 a) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return a >> 2 & ((1 << 2) - 1); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic inline u16 i5100_mtr_numcol(u16 a) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return a & ((1 << 2) - 1); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic inline u32 i5100_validlog_redmemvalid(u32 a) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return a >> 2 & 1; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline u32 i5100_validlog_recmemvalid(u32 a) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return a >> 1 & 1; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic inline u32 i5100_validlog_nrecmemvalid(u32 a) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return a & 1; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic inline u32 i5100_nrecmema_merr(u32 a) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci return a >> 15 & ((1 << 5) - 1); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic inline u32 i5100_nrecmema_bank(u32 a) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return a >> 12 & ((1 << 3) - 1); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic inline u32 i5100_nrecmema_rank(u32 a) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci return a >> 8 & ((1 << 3) - 1); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic inline u32 i5100_nrecmema_dm_buf_id(u32 a) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci return a & ((1 << 8) - 1); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline u32 i5100_nrecmemb_cas(u32 a) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci return a >> 16 & ((1 << 13) - 1); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic inline u32 i5100_nrecmemb_ras(u32 a) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return a & ((1 << 16) - 1); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic inline u32 i5100_recmema_merr(u32 a) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci return i5100_nrecmema_merr(a); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic inline u32 i5100_recmema_bank(u32 a) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return i5100_nrecmema_bank(a); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic inline u32 i5100_recmema_rank(u32 a) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return i5100_nrecmema_rank(a); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic inline u32 i5100_recmemb_cas(u32 a) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci return i5100_nrecmemb_cas(a); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic inline u32 i5100_recmemb_ras(u32 a) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci return i5100_nrecmemb_ras(a); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* some generic limits */ 2888c2ecf20Sopenharmony_ci#define I5100_MAX_RANKS_PER_CHAN 6 2898c2ecf20Sopenharmony_ci#define I5100_CHANNELS 2 2908c2ecf20Sopenharmony_ci#define I5100_MAX_RANKS_PER_DIMM 4 2918c2ecf20Sopenharmony_ci#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ 2928c2ecf20Sopenharmony_ci#define I5100_MAX_DIMM_SLOTS_PER_CHAN 4 2938c2ecf20Sopenharmony_ci#define I5100_MAX_RANK_INTERLEAVE 4 2948c2ecf20Sopenharmony_ci#define I5100_MAX_DMIRS 5 2958c2ecf20Sopenharmony_ci#define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ) 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistruct i5100_priv { 2988c2ecf20Sopenharmony_ci /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ 2998c2ecf20Sopenharmony_ci int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN]; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * mainboard chip select map -- maps i5100 chip selects to 3038c2ecf20Sopenharmony_ci * DIMM slot chip selects. In the case of only 4 ranks per 3048c2ecf20Sopenharmony_ci * channel, the mapping is fairly obvious but not unique. 3058c2ecf20Sopenharmony_ci * we map -1 -> NC and assume both channels use the same 3068c2ecf20Sopenharmony_ci * map... 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM]; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* memory interleave range */ 3128c2ecf20Sopenharmony_ci struct { 3138c2ecf20Sopenharmony_ci u64 limit; 3148c2ecf20Sopenharmony_ci unsigned way[2]; 3158c2ecf20Sopenharmony_ci } mir[I5100_CHANNELS]; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* adjusted memory interleave range register */ 3188c2ecf20Sopenharmony_ci unsigned amir[I5100_CHANNELS]; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* dimm interleave range */ 3218c2ecf20Sopenharmony_ci struct { 3228c2ecf20Sopenharmony_ci unsigned rank[I5100_MAX_RANK_INTERLEAVE]; 3238c2ecf20Sopenharmony_ci u64 limit; 3248c2ecf20Sopenharmony_ci } dmir[I5100_CHANNELS][I5100_MAX_DMIRS]; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* memory technology registers... */ 3278c2ecf20Sopenharmony_ci struct { 3288c2ecf20Sopenharmony_ci unsigned present; /* 0 or 1 */ 3298c2ecf20Sopenharmony_ci unsigned ethrottle; /* 0 or 1 */ 3308c2ecf20Sopenharmony_ci unsigned width; /* 4 or 8 bits */ 3318c2ecf20Sopenharmony_ci unsigned numbank; /* 2 or 3 lines */ 3328c2ecf20Sopenharmony_ci unsigned numrow; /* 13 .. 16 lines */ 3338c2ecf20Sopenharmony_ci unsigned numcol; /* 11 .. 12 lines */ 3348c2ecf20Sopenharmony_ci } mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN]; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci u64 tolm; /* top of low memory in bytes */ 3378c2ecf20Sopenharmony_ci unsigned ranksperchan; /* number of ranks per channel */ 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci struct pci_dev *mc; /* device 16 func 1 */ 3408c2ecf20Sopenharmony_ci struct pci_dev *einj; /* device 19 func 0 */ 3418c2ecf20Sopenharmony_ci struct pci_dev *ch0mm; /* device 21 func 0 */ 3428c2ecf20Sopenharmony_ci struct pci_dev *ch1mm; /* device 22 func 0 */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci struct delayed_work i5100_scrubbing; 3458c2ecf20Sopenharmony_ci int scrub_enable; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Error injection */ 3488c2ecf20Sopenharmony_ci u8 inject_channel; 3498c2ecf20Sopenharmony_ci u8 inject_hlinesel; 3508c2ecf20Sopenharmony_ci u8 inject_deviceptr1; 3518c2ecf20Sopenharmony_ci u8 inject_deviceptr2; 3528c2ecf20Sopenharmony_ci u16 inject_eccmask1; 3538c2ecf20Sopenharmony_ci u16 inject_eccmask2; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci struct dentry *debugfs; 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic struct dentry *i5100_debugfs; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/* map a rank/chan to a slot number on the mainboard */ 3618c2ecf20Sopenharmony_cistatic int i5100_rank_to_slot(const struct mem_ctl_info *mci, 3628c2ecf20Sopenharmony_ci int chan, int rank) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci const struct i5100_priv *priv = mci->pvt_info; 3658c2ecf20Sopenharmony_ci int i; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) { 3688c2ecf20Sopenharmony_ci int j; 3698c2ecf20Sopenharmony_ci const int numrank = priv->dimm_numrank[chan][i]; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci for (j = 0; j < numrank; j++) 3728c2ecf20Sopenharmony_ci if (priv->dimm_csmap[i][j] == rank) 3738c2ecf20Sopenharmony_ci return i * 2 + chan; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return -1; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic const char *i5100_err_msg(unsigned err) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci static const char *merrs[] = { 3828c2ecf20Sopenharmony_ci "unknown", /* 0 */ 3838c2ecf20Sopenharmony_ci "uncorrectable data ECC on replay", /* 1 */ 3848c2ecf20Sopenharmony_ci "unknown", /* 2 */ 3858c2ecf20Sopenharmony_ci "unknown", /* 3 */ 3868c2ecf20Sopenharmony_ci "aliased uncorrectable demand data ECC", /* 4 */ 3878c2ecf20Sopenharmony_ci "aliased uncorrectable spare-copy data ECC", /* 5 */ 3888c2ecf20Sopenharmony_ci "aliased uncorrectable patrol data ECC", /* 6 */ 3898c2ecf20Sopenharmony_ci "unknown", /* 7 */ 3908c2ecf20Sopenharmony_ci "unknown", /* 8 */ 3918c2ecf20Sopenharmony_ci "unknown", /* 9 */ 3928c2ecf20Sopenharmony_ci "non-aliased uncorrectable demand data ECC", /* 10 */ 3938c2ecf20Sopenharmony_ci "non-aliased uncorrectable spare-copy data ECC", /* 11 */ 3948c2ecf20Sopenharmony_ci "non-aliased uncorrectable patrol data ECC", /* 12 */ 3958c2ecf20Sopenharmony_ci "unknown", /* 13 */ 3968c2ecf20Sopenharmony_ci "correctable demand data ECC", /* 14 */ 3978c2ecf20Sopenharmony_ci "correctable spare-copy data ECC", /* 15 */ 3988c2ecf20Sopenharmony_ci "correctable patrol data ECC", /* 16 */ 3998c2ecf20Sopenharmony_ci "unknown", /* 17 */ 4008c2ecf20Sopenharmony_ci "SPD protocol error", /* 18 */ 4018c2ecf20Sopenharmony_ci "unknown", /* 19 */ 4028c2ecf20Sopenharmony_ci "spare copy initiated", /* 20 */ 4038c2ecf20Sopenharmony_ci "spare copy completed", /* 21 */ 4048c2ecf20Sopenharmony_ci }; 4058c2ecf20Sopenharmony_ci unsigned i; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(merrs); i++) 4088c2ecf20Sopenharmony_ci if (1 << i & err) 4098c2ecf20Sopenharmony_ci return merrs[i]; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return "none"; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* convert csrow index into a rank (per channel -- 0..5) */ 4158c2ecf20Sopenharmony_cistatic unsigned int i5100_csrow_to_rank(const struct mem_ctl_info *mci, 4168c2ecf20Sopenharmony_ci unsigned int csrow) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci const struct i5100_priv *priv = mci->pvt_info; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return csrow % priv->ranksperchan; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* convert csrow index into a channel (0..1) */ 4248c2ecf20Sopenharmony_cistatic unsigned int i5100_csrow_to_chan(const struct mem_ctl_info *mci, 4258c2ecf20Sopenharmony_ci unsigned int csrow) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci const struct i5100_priv *priv = mci->pvt_info; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return csrow / priv->ranksperchan; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void i5100_handle_ce(struct mem_ctl_info *mci, 4338c2ecf20Sopenharmony_ci int chan, 4348c2ecf20Sopenharmony_ci unsigned bank, 4358c2ecf20Sopenharmony_ci unsigned rank, 4368c2ecf20Sopenharmony_ci unsigned long syndrome, 4378c2ecf20Sopenharmony_ci unsigned cas, 4388c2ecf20Sopenharmony_ci unsigned ras, 4398c2ecf20Sopenharmony_ci const char *msg) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci char detail[80]; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* Form out message */ 4448c2ecf20Sopenharmony_ci snprintf(detail, sizeof(detail), 4458c2ecf20Sopenharmony_ci "bank %u, cas %u, ras %u\n", 4468c2ecf20Sopenharmony_ci bank, cas, ras); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 4498c2ecf20Sopenharmony_ci 0, 0, syndrome, 4508c2ecf20Sopenharmony_ci chan, rank, -1, 4518c2ecf20Sopenharmony_ci msg, detail); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void i5100_handle_ue(struct mem_ctl_info *mci, 4558c2ecf20Sopenharmony_ci int chan, 4568c2ecf20Sopenharmony_ci unsigned bank, 4578c2ecf20Sopenharmony_ci unsigned rank, 4588c2ecf20Sopenharmony_ci unsigned long syndrome, 4598c2ecf20Sopenharmony_ci unsigned cas, 4608c2ecf20Sopenharmony_ci unsigned ras, 4618c2ecf20Sopenharmony_ci const char *msg) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci char detail[80]; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Form out message */ 4668c2ecf20Sopenharmony_ci snprintf(detail, sizeof(detail), 4678c2ecf20Sopenharmony_ci "bank %u, cas %u, ras %u\n", 4688c2ecf20Sopenharmony_ci bank, cas, ras); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 4718c2ecf20Sopenharmony_ci 0, 0, syndrome, 4728c2ecf20Sopenharmony_ci chan, rank, -1, 4738c2ecf20Sopenharmony_ci msg, detail); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void i5100_read_log(struct mem_ctl_info *mci, int chan, 4778c2ecf20Sopenharmony_ci u32 ferr, u32 nerr) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 4808c2ecf20Sopenharmony_ci struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm; 4818c2ecf20Sopenharmony_ci u32 dw; 4828c2ecf20Sopenharmony_ci u32 dw2; 4838c2ecf20Sopenharmony_ci unsigned syndrome = 0; 4848c2ecf20Sopenharmony_ci unsigned merr; 4858c2ecf20Sopenharmony_ci unsigned bank; 4868c2ecf20Sopenharmony_ci unsigned rank; 4878c2ecf20Sopenharmony_ci unsigned cas; 4888c2ecf20Sopenharmony_ci unsigned ras; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_VALIDLOG, &dw); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (i5100_validlog_redmemvalid(dw)) { 4938c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_REDMEMA, &dw2); 4948c2ecf20Sopenharmony_ci syndrome = dw2; 4958c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_REDMEMB, &dw2); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (i5100_validlog_recmemvalid(dw)) { 4998c2ecf20Sopenharmony_ci const char *msg; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_RECMEMA, &dw2); 5028c2ecf20Sopenharmony_ci merr = i5100_recmema_merr(dw2); 5038c2ecf20Sopenharmony_ci bank = i5100_recmema_bank(dw2); 5048c2ecf20Sopenharmony_ci rank = i5100_recmema_rank(dw2); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_RECMEMB, &dw2); 5078c2ecf20Sopenharmony_ci cas = i5100_recmemb_cas(dw2); 5088c2ecf20Sopenharmony_ci ras = i5100_recmemb_ras(dw2); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* FIXME: not really sure if this is what merr is... 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci if (!merr) 5138c2ecf20Sopenharmony_ci msg = i5100_err_msg(ferr); 5148c2ecf20Sopenharmony_ci else 5158c2ecf20Sopenharmony_ci msg = i5100_err_msg(nerr); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (i5100_validlog_nrecmemvalid(dw)) { 5218c2ecf20Sopenharmony_ci const char *msg; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2); 5248c2ecf20Sopenharmony_ci merr = i5100_nrecmema_merr(dw2); 5258c2ecf20Sopenharmony_ci bank = i5100_nrecmema_bank(dw2); 5268c2ecf20Sopenharmony_ci rank = i5100_nrecmema_rank(dw2); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2); 5298c2ecf20Sopenharmony_ci cas = i5100_nrecmemb_cas(dw2); 5308c2ecf20Sopenharmony_ci ras = i5100_nrecmemb_ras(dw2); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* FIXME: not really sure if this is what merr is... 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci if (!merr) 5358c2ecf20Sopenharmony_ci msg = i5100_err_msg(ferr); 5368c2ecf20Sopenharmony_ci else 5378c2ecf20Sopenharmony_ci msg = i5100_err_msg(nerr); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, I5100_VALIDLOG, dw); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void i5100_check_error(struct mem_ctl_info *mci) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 5488c2ecf20Sopenharmony_ci u32 dw, dw2; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw); 5518c2ecf20Sopenharmony_ci if (i5100_ferr_nf_mem_any(dw)) { 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw), 5568c2ecf20Sopenharmony_ci i5100_ferr_nf_mem_any(dw), 5578c2ecf20Sopenharmony_ci i5100_nerr_nf_mem_any(dw2)); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* The i5100 chipset will scrub the entire memory once, then 5658c2ecf20Sopenharmony_ci * set a done bit. Continuous scrubbing is achieved by enqueing 5668c2ecf20Sopenharmony_ci * delayed work to a workqueue, checking every few minutes if 5678c2ecf20Sopenharmony_ci * the scrubbing has completed and if so reinitiating it. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic void i5100_refresh_scrubbing(struct work_struct *work) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct delayed_work *i5100_scrubbing = to_delayed_work(work); 5738c2ecf20Sopenharmony_ci struct i5100_priv *priv = container_of(i5100_scrubbing, 5748c2ecf20Sopenharmony_ci struct i5100_priv, 5758c2ecf20Sopenharmony_ci i5100_scrubbing); 5768c2ecf20Sopenharmony_ci u32 dw; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_MC, &dw); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (priv->scrub_enable) { 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_MC, &dw); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (i5100_mc_scrbdone(dw)) { 5858c2ecf20Sopenharmony_ci dw |= I5100_MC_SCRBEN_MASK; 5868c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_MC, dw); 5878c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_MC, &dw); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci schedule_delayed_work(&(priv->i5100_scrubbing), 5918c2ecf20Sopenharmony_ci I5100_SCRUB_REFRESH_RATE); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci/* 5958c2ecf20Sopenharmony_ci * The bandwidth is based on experimentation, feel free to refine it. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_cistatic int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 6008c2ecf20Sopenharmony_ci u32 dw; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_MC, &dw); 6038c2ecf20Sopenharmony_ci if (bandwidth) { 6048c2ecf20Sopenharmony_ci priv->scrub_enable = 1; 6058c2ecf20Sopenharmony_ci dw |= I5100_MC_SCRBEN_MASK; 6068c2ecf20Sopenharmony_ci schedule_delayed_work(&(priv->i5100_scrubbing), 6078c2ecf20Sopenharmony_ci I5100_SCRUB_REFRESH_RATE); 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci priv->scrub_enable = 0; 6108c2ecf20Sopenharmony_ci dw &= ~I5100_MC_SCRBEN_MASK; 6118c2ecf20Sopenharmony_ci cancel_delayed_work(&(priv->i5100_scrubbing)); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_MC, dw); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_MC, &dw); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci bandwidth = 5900000 * i5100_mc_scrben(dw); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return bandwidth; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int i5100_get_scrub_rate(struct mem_ctl_info *mci) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 6258c2ecf20Sopenharmony_ci u32 dw; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci pci_read_config_dword(priv->mc, I5100_MC, &dw); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 5900000 * i5100_mc_scrben(dw); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic struct pci_dev *pci_get_device_func(unsigned vendor, 6338c2ecf20Sopenharmony_ci unsigned device, 6348c2ecf20Sopenharmony_ci unsigned func) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct pci_dev *ret = NULL; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci while (1) { 6398c2ecf20Sopenharmony_ci ret = pci_get_device(vendor, device, ret); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (!ret) 6428c2ecf20Sopenharmony_ci break; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (PCI_FUNC(ret->devfn) == func) 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return ret; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic unsigned long i5100_npages(struct mem_ctl_info *mci, unsigned int csrow) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 6548c2ecf20Sopenharmony_ci const unsigned int chan_rank = i5100_csrow_to_rank(mci, csrow); 6558c2ecf20Sopenharmony_ci const unsigned int chan = i5100_csrow_to_chan(mci, csrow); 6568c2ecf20Sopenharmony_ci unsigned addr_lines; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* dimm present? */ 6598c2ecf20Sopenharmony_ci if (!priv->mtr[chan][chan_rank].present) 6608c2ecf20Sopenharmony_ci return 0ULL; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci addr_lines = 6638c2ecf20Sopenharmony_ci I5100_DIMM_ADDR_LINES + 6648c2ecf20Sopenharmony_ci priv->mtr[chan][chan_rank].numcol + 6658c2ecf20Sopenharmony_ci priv->mtr[chan][chan_rank].numrow + 6668c2ecf20Sopenharmony_ci priv->mtr[chan][chan_rank].numbank; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return (unsigned long) 6698c2ecf20Sopenharmony_ci ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic void i5100_init_mtr(struct mem_ctl_info *mci) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 6758c2ecf20Sopenharmony_ci struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; 6768c2ecf20Sopenharmony_ci int i; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci for (i = 0; i < I5100_CHANNELS; i++) { 6798c2ecf20Sopenharmony_ci int j; 6808c2ecf20Sopenharmony_ci struct pci_dev *pdev = mms[i]; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) { 6838c2ecf20Sopenharmony_ci const unsigned addr = 6848c2ecf20Sopenharmony_ci (j < 4) ? I5100_MTR_0 + j * 2 : 6858c2ecf20Sopenharmony_ci I5100_MTR_4 + (j - 4) * 2; 6868c2ecf20Sopenharmony_ci u16 w; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci pci_read_config_word(pdev, addr, &w); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci priv->mtr[i][j].present = i5100_mtr_present(w); 6918c2ecf20Sopenharmony_ci priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w); 6928c2ecf20Sopenharmony_ci priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w); 6938c2ecf20Sopenharmony_ci priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w); 6948c2ecf20Sopenharmony_ci priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w); 6958c2ecf20Sopenharmony_ci priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/* 7018c2ecf20Sopenharmony_ci * FIXME: make this into a real i2c adapter (so that dimm-decode 7028c2ecf20Sopenharmony_ci * will work)? 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_cistatic int i5100_read_spd_byte(const struct mem_ctl_info *mci, 7058c2ecf20Sopenharmony_ci u8 ch, u8 slot, u8 addr, u8 *byte) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 7088c2ecf20Sopenharmony_ci u16 w; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci pci_read_config_word(priv->mc, I5100_SPDDATA, &w); 7118c2ecf20Sopenharmony_ci if (i5100_spddata_busy(w)) 7128c2ecf20Sopenharmony_ci return -1; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_SPDCMD, 7158c2ecf20Sopenharmony_ci i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr, 7168c2ecf20Sopenharmony_ci 0, 0)); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* wait up to 100ms */ 7198c2ecf20Sopenharmony_ci udelay(100); 7208c2ecf20Sopenharmony_ci while (1) { 7218c2ecf20Sopenharmony_ci pci_read_config_word(priv->mc, I5100_SPDDATA, &w); 7228c2ecf20Sopenharmony_ci if (!i5100_spddata_busy(w)) 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci udelay(100); 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w)) 7288c2ecf20Sopenharmony_ci return -1; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci *byte = i5100_spddata_data(w); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci/* 7368c2ecf20Sopenharmony_ci * fill dimm chip select map 7378c2ecf20Sopenharmony_ci * 7388c2ecf20Sopenharmony_ci * FIXME: 7398c2ecf20Sopenharmony_ci * o not the only way to may chip selects to dimm slots 7408c2ecf20Sopenharmony_ci * o investigate if there is some way to obtain this map from the bios 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_cistatic void i5100_init_dimm_csmap(struct mem_ctl_info *mci) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 7458c2ecf20Sopenharmony_ci int i; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) { 7488c2ecf20Sopenharmony_ci int j; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) 7518c2ecf20Sopenharmony_ci priv->dimm_csmap[i][j] = -1; /* default NC */ 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* only 2 chip selects per slot... */ 7558c2ecf20Sopenharmony_ci if (priv->ranksperchan == 4) { 7568c2ecf20Sopenharmony_ci priv->dimm_csmap[0][0] = 0; 7578c2ecf20Sopenharmony_ci priv->dimm_csmap[0][1] = 3; 7588c2ecf20Sopenharmony_ci priv->dimm_csmap[1][0] = 1; 7598c2ecf20Sopenharmony_ci priv->dimm_csmap[1][1] = 2; 7608c2ecf20Sopenharmony_ci priv->dimm_csmap[2][0] = 2; 7618c2ecf20Sopenharmony_ci priv->dimm_csmap[3][0] = 3; 7628c2ecf20Sopenharmony_ci } else { 7638c2ecf20Sopenharmony_ci priv->dimm_csmap[0][0] = 0; 7648c2ecf20Sopenharmony_ci priv->dimm_csmap[0][1] = 1; 7658c2ecf20Sopenharmony_ci priv->dimm_csmap[1][0] = 2; 7668c2ecf20Sopenharmony_ci priv->dimm_csmap[1][1] = 3; 7678c2ecf20Sopenharmony_ci priv->dimm_csmap[2][0] = 4; 7688c2ecf20Sopenharmony_ci priv->dimm_csmap[2][1] = 5; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic void i5100_init_dimm_layout(struct pci_dev *pdev, 7738c2ecf20Sopenharmony_ci struct mem_ctl_info *mci) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 7768c2ecf20Sopenharmony_ci int i; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci for (i = 0; i < I5100_CHANNELS; i++) { 7798c2ecf20Sopenharmony_ci int j; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) { 7828c2ecf20Sopenharmony_ci u8 rank; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) 7858c2ecf20Sopenharmony_ci priv->dimm_numrank[i][j] = 0; 7868c2ecf20Sopenharmony_ci else 7878c2ecf20Sopenharmony_ci priv->dimm_numrank[i][j] = (rank & 3) + 1; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci i5100_init_dimm_csmap(mci); 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void i5100_init_interleaving(struct pci_dev *pdev, 7958c2ecf20Sopenharmony_ci struct mem_ctl_info *mci) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci u16 w; 7988c2ecf20Sopenharmony_ci u32 dw; 7998c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 8008c2ecf20Sopenharmony_ci struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; 8018c2ecf20Sopenharmony_ci int i; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I5100_TOLM, &w); 8048c2ecf20Sopenharmony_ci priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I5100_MIR0, &w); 8078c2ecf20Sopenharmony_ci priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28; 8088c2ecf20Sopenharmony_ci priv->mir[0].way[1] = i5100_mir_way1(w); 8098c2ecf20Sopenharmony_ci priv->mir[0].way[0] = i5100_mir_way0(w); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I5100_MIR1, &w); 8128c2ecf20Sopenharmony_ci priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28; 8138c2ecf20Sopenharmony_ci priv->mir[1].way[1] = i5100_mir_way1(w); 8148c2ecf20Sopenharmony_ci priv->mir[1].way[0] = i5100_mir_way0(w); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I5100_AMIR_0, &w); 8178c2ecf20Sopenharmony_ci priv->amir[0] = w; 8188c2ecf20Sopenharmony_ci pci_read_config_word(pdev, I5100_AMIR_1, &w); 8198c2ecf20Sopenharmony_ci priv->amir[1] = w; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci for (i = 0; i < I5100_CHANNELS; i++) { 8228c2ecf20Sopenharmony_ci int j; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci for (j = 0; j < 5; j++) { 8258c2ecf20Sopenharmony_ci int k; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci priv->dmir[i][j].limit = 8308c2ecf20Sopenharmony_ci (u64) i5100_dmir_limit(dw) << 28; 8318c2ecf20Sopenharmony_ci for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++) 8328c2ecf20Sopenharmony_ci priv->dmir[i][j].rank[k] = 8338c2ecf20Sopenharmony_ci i5100_dmir_rank(dw, k); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci i5100_init_mtr(mci); 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic void i5100_init_csrows(struct mem_ctl_info *mci) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 8438c2ecf20Sopenharmony_ci struct dimm_info *dimm; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci mci_for_each_dimm(mci, dimm) { 8468c2ecf20Sopenharmony_ci const unsigned long npages = i5100_npages(mci, dimm->idx); 8478c2ecf20Sopenharmony_ci const unsigned int chan = i5100_csrow_to_chan(mci, dimm->idx); 8488c2ecf20Sopenharmony_ci const unsigned int rank = i5100_csrow_to_rank(mci, dimm->idx); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (!npages) 8518c2ecf20Sopenharmony_ci continue; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci dimm->nr_pages = npages; 8548c2ecf20Sopenharmony_ci dimm->grain = 32; 8558c2ecf20Sopenharmony_ci dimm->dtype = (priv->mtr[chan][rank].width == 4) ? 8568c2ecf20Sopenharmony_ci DEV_X4 : DEV_X8; 8578c2ecf20Sopenharmony_ci dimm->mtype = MEM_RDDR2; 8588c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 8598c2ecf20Sopenharmony_ci snprintf(dimm->label, sizeof(dimm->label), "DIMM%u", 8608c2ecf20Sopenharmony_ci i5100_rank_to_slot(mci, chan, rank)); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci edac_dbg(2, "dimm channel %d, rank %d, size %ld\n", 8638c2ecf20Sopenharmony_ci chan, rank, (long)PAGES_TO_MiB(npages)); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/**************************************************************************** 8688c2ecf20Sopenharmony_ci * Error injection routines 8698c2ecf20Sopenharmony_ci ****************************************************************************/ 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic void i5100_do_inject(struct mem_ctl_info *mci) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 8748c2ecf20Sopenharmony_ci u32 mask0; 8758c2ecf20Sopenharmony_ci u16 mask1; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* MEM[1:0]EINJMSK0 8788c2ecf20Sopenharmony_ci * 31 - ADDRMATCHEN 8798c2ecf20Sopenharmony_ci * 29:28 - HLINESEL 8808c2ecf20Sopenharmony_ci * 00 Reserved 8818c2ecf20Sopenharmony_ci * 01 Lower half of cache line 8828c2ecf20Sopenharmony_ci * 10 Upper half of cache line 8838c2ecf20Sopenharmony_ci * 11 Both upper and lower parts of cache line 8848c2ecf20Sopenharmony_ci * 27 - EINJEN 8858c2ecf20Sopenharmony_ci * 25:19 - XORMASK1 for deviceptr1 8868c2ecf20Sopenharmony_ci * 9:5 - SEC2RAM for deviceptr2 8878c2ecf20Sopenharmony_ci * 4:0 - FIR2RAM for deviceptr1 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci mask0 = ((priv->inject_hlinesel & 0x3) << 28) | 8908c2ecf20Sopenharmony_ci I5100_MEMXEINJMSK0_EINJEN | 8918c2ecf20Sopenharmony_ci ((priv->inject_eccmask1 & 0xffff) << 10) | 8928c2ecf20Sopenharmony_ci ((priv->inject_deviceptr2 & 0x1f) << 5) | 8938c2ecf20Sopenharmony_ci (priv->inject_deviceptr1 & 0x1f); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* MEM[1:0]EINJMSK1 8968c2ecf20Sopenharmony_ci * 15:0 - XORMASK2 for deviceptr2 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_ci mask1 = priv->inject_eccmask2; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (priv->inject_channel == 0) { 9018c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_MEM0EINJMSK0, mask0); 9028c2ecf20Sopenharmony_ci pci_write_config_word(priv->mc, I5100_MEM0EINJMSK1, mask1); 9038c2ecf20Sopenharmony_ci } else { 9048c2ecf20Sopenharmony_ci pci_write_config_dword(priv->mc, I5100_MEM1EINJMSK0, mask0); 9058c2ecf20Sopenharmony_ci pci_write_config_word(priv->mc, I5100_MEM1EINJMSK1, mask1); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* Error Injection Response Function 9098c2ecf20Sopenharmony_ci * Intel 5100 Memory Controller Hub Chipset (318378) datasheet 9108c2ecf20Sopenharmony_ci * hints about this register but carry no data about them. All 9118c2ecf20Sopenharmony_ci * data regarding device 19 is based on experimentation and the 9128c2ecf20Sopenharmony_ci * Intel 7300 Chipset Memory Controller Hub (318082) datasheet 9138c2ecf20Sopenharmony_ci * which appears to be accurate for the i5100 in this area. 9148c2ecf20Sopenharmony_ci * 9158c2ecf20Sopenharmony_ci * The injection code don't work without setting this register. 9168c2ecf20Sopenharmony_ci * The register needs to be flipped off then on else the hardware 9178c2ecf20Sopenharmony_ci * will only preform the first injection. 9188c2ecf20Sopenharmony_ci * 9198c2ecf20Sopenharmony_ci * Stop condition bits 7:4 9208c2ecf20Sopenharmony_ci * 1010 - Stop after one injection 9218c2ecf20Sopenharmony_ci * 1011 - Never stop injecting faults 9228c2ecf20Sopenharmony_ci * 9238c2ecf20Sopenharmony_ci * Start condition bits 3:0 9248c2ecf20Sopenharmony_ci * 1010 - Never start 9258c2ecf20Sopenharmony_ci * 1011 - Start immediately 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_ci pci_write_config_byte(priv->einj, I5100_DINJ0, 0xaa); 9288c2ecf20Sopenharmony_ci pci_write_config_byte(priv->einj, I5100_DINJ0, 0xab); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci#define to_mci(k) container_of(k, struct mem_ctl_info, dev) 9328c2ecf20Sopenharmony_cistatic ssize_t inject_enable_write(struct file *file, const char __user *data, 9338c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct device *dev = file->private_data; 9368c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci i5100_do_inject(mci); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci return count; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic const struct file_operations i5100_inject_enable_fops = { 9448c2ecf20Sopenharmony_ci .open = simple_open, 9458c2ecf20Sopenharmony_ci .write = inject_enable_write, 9468c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 9478c2ecf20Sopenharmony_ci}; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic int i5100_setup_debugfs(struct mem_ctl_info *mci) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct i5100_priv *priv = mci->pvt_info; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (!i5100_debugfs) 9548c2ecf20Sopenharmony_ci return -ENODEV; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (!priv->debugfs) 9598c2ecf20Sopenharmony_ci return -ENOMEM; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs, 9628c2ecf20Sopenharmony_ci &priv->inject_channel); 9638c2ecf20Sopenharmony_ci edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs, 9648c2ecf20Sopenharmony_ci &priv->inject_hlinesel); 9658c2ecf20Sopenharmony_ci edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs, 9668c2ecf20Sopenharmony_ci &priv->inject_deviceptr1); 9678c2ecf20Sopenharmony_ci edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs, 9688c2ecf20Sopenharmony_ci &priv->inject_deviceptr2); 9698c2ecf20Sopenharmony_ci edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs, 9708c2ecf20Sopenharmony_ci &priv->inject_eccmask1); 9718c2ecf20Sopenharmony_ci edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs, 9728c2ecf20Sopenharmony_ci &priv->inject_eccmask2); 9738c2ecf20Sopenharmony_ci edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs, 9748c2ecf20Sopenharmony_ci &mci->dev, &i5100_inject_enable_fops); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci int rc; 9838c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 9848c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 9858c2ecf20Sopenharmony_ci struct i5100_priv *priv; 9868c2ecf20Sopenharmony_ci struct pci_dev *ch0mm, *ch1mm, *einj; 9878c2ecf20Sopenharmony_ci int ret = 0; 9888c2ecf20Sopenharmony_ci u32 dw; 9898c2ecf20Sopenharmony_ci int ranksperch; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (PCI_FUNC(pdev->devfn) != 1) 9928c2ecf20Sopenharmony_ci return -ENODEV; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 9958c2ecf20Sopenharmony_ci if (rc < 0) { 9968c2ecf20Sopenharmony_ci ret = rc; 9978c2ecf20Sopenharmony_ci goto bail; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* ECC enabled? */ 10018c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_MC, &dw); 10028c2ecf20Sopenharmony_ci if (!i5100_mc_errdeten(dw)) { 10038c2ecf20Sopenharmony_ci printk(KERN_INFO "i5100_edac: ECC not enabled.\n"); 10048c2ecf20Sopenharmony_ci ret = -ENODEV; 10058c2ecf20Sopenharmony_ci goto bail_pdev; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* figure out how many ranks, from strapped state of 48GB_Mode input */ 10098c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_MS, &dw); 10108c2ecf20Sopenharmony_ci ranksperch = !!(dw & (1 << 8)) * 2 + 4; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* enable error reporting... */ 10138c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); 10148c2ecf20Sopenharmony_ci dw &= ~I5100_FERR_NF_MEM_ANY_MASK; 10158c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, I5100_EMASK_MEM, dw); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */ 10188c2ecf20Sopenharmony_ci ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, 10198c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_5100_21, 0); 10208c2ecf20Sopenharmony_ci if (!ch0mm) { 10218c2ecf20Sopenharmony_ci ret = -ENODEV; 10228c2ecf20Sopenharmony_ci goto bail_pdev; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci rc = pci_enable_device(ch0mm); 10268c2ecf20Sopenharmony_ci if (rc < 0) { 10278c2ecf20Sopenharmony_ci ret = rc; 10288c2ecf20Sopenharmony_ci goto bail_ch0; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */ 10328c2ecf20Sopenharmony_ci ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, 10338c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_5100_22, 0); 10348c2ecf20Sopenharmony_ci if (!ch1mm) { 10358c2ecf20Sopenharmony_ci ret = -ENODEV; 10368c2ecf20Sopenharmony_ci goto bail_disable_ch0; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci rc = pci_enable_device(ch1mm); 10408c2ecf20Sopenharmony_ci if (rc < 0) { 10418c2ecf20Sopenharmony_ci ret = rc; 10428c2ecf20Sopenharmony_ci goto bail_ch1; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHANNEL; 10468c2ecf20Sopenharmony_ci layers[0].size = 2; 10478c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = false; 10488c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_SLOT; 10498c2ecf20Sopenharmony_ci layers[1].size = ranksperch; 10508c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = true; 10518c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 10528c2ecf20Sopenharmony_ci sizeof(*priv)); 10538c2ecf20Sopenharmony_ci if (!mci) { 10548c2ecf20Sopenharmony_ci ret = -ENOMEM; 10558c2ecf20Sopenharmony_ci goto bail_disable_ch1; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci /* device 19, func 0, Error injection */ 10608c2ecf20Sopenharmony_ci einj = pci_get_device_func(PCI_VENDOR_ID_INTEL, 10618c2ecf20Sopenharmony_ci PCI_DEVICE_ID_INTEL_5100_19, 0); 10628c2ecf20Sopenharmony_ci if (!einj) { 10638c2ecf20Sopenharmony_ci ret = -ENODEV; 10648c2ecf20Sopenharmony_ci goto bail_mc_free; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci rc = pci_enable_device(einj); 10688c2ecf20Sopenharmony_ci if (rc < 0) { 10698c2ecf20Sopenharmony_ci ret = rc; 10708c2ecf20Sopenharmony_ci goto bail_einj; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci priv = mci->pvt_info; 10768c2ecf20Sopenharmony_ci priv->ranksperchan = ranksperch; 10778c2ecf20Sopenharmony_ci priv->mc = pdev; 10788c2ecf20Sopenharmony_ci priv->ch0mm = ch0mm; 10798c2ecf20Sopenharmony_ci priv->ch1mm = ch1mm; 10808c2ecf20Sopenharmony_ci priv->einj = einj; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* If scrubbing was already enabled by the bios, start maintaining it */ 10858c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, I5100_MC, &dw); 10868c2ecf20Sopenharmony_ci if (i5100_mc_scrben(dw)) { 10878c2ecf20Sopenharmony_ci priv->scrub_enable = 1; 10888c2ecf20Sopenharmony_ci schedule_delayed_work(&(priv->i5100_scrubbing), 10898c2ecf20Sopenharmony_ci I5100_SCRUB_REFRESH_RATE); 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci i5100_init_dimm_layout(pdev, mci); 10938c2ecf20Sopenharmony_ci i5100_init_interleaving(pdev, mci); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_FB_DDR2; 10968c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_SECDED; 10978c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 10988c2ecf20Sopenharmony_ci mci->mod_name = "i5100_edac.c"; 10998c2ecf20Sopenharmony_ci mci->ctl_name = "i5100"; 11008c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pdev); 11018c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci mci->edac_check = i5100_check_error; 11048c2ecf20Sopenharmony_ci mci->set_sdram_scrub_rate = i5100_set_scrub_rate; 11058c2ecf20Sopenharmony_ci mci->get_sdram_scrub_rate = i5100_get_scrub_rate; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci priv->inject_channel = 0; 11088c2ecf20Sopenharmony_ci priv->inject_hlinesel = 0; 11098c2ecf20Sopenharmony_ci priv->inject_deviceptr1 = 0; 11108c2ecf20Sopenharmony_ci priv->inject_deviceptr2 = 0; 11118c2ecf20Sopenharmony_ci priv->inject_eccmask1 = 0; 11128c2ecf20Sopenharmony_ci priv->inject_eccmask2 = 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci i5100_init_csrows(mci); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* this strange construction seems to be in every driver, dunno why */ 11178c2ecf20Sopenharmony_ci switch (edac_op_state) { 11188c2ecf20Sopenharmony_ci case EDAC_OPSTATE_POLL: 11198c2ecf20Sopenharmony_ci case EDAC_OPSTATE_NMI: 11208c2ecf20Sopenharmony_ci break; 11218c2ecf20Sopenharmony_ci default: 11228c2ecf20Sopenharmony_ci edac_op_state = EDAC_OPSTATE_POLL; 11238c2ecf20Sopenharmony_ci break; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 11278c2ecf20Sopenharmony_ci ret = -ENODEV; 11288c2ecf20Sopenharmony_ci goto bail_scrub; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci i5100_setup_debugfs(mci); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci return ret; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cibail_scrub: 11368c2ecf20Sopenharmony_ci priv->scrub_enable = 0; 11378c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&(priv->i5100_scrubbing)); 11388c2ecf20Sopenharmony_ci pci_disable_device(einj); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cibail_einj: 11418c2ecf20Sopenharmony_ci pci_dev_put(einj); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cibail_mc_free: 11448c2ecf20Sopenharmony_ci edac_mc_free(mci); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cibail_disable_ch1: 11478c2ecf20Sopenharmony_ci pci_disable_device(ch1mm); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cibail_ch1: 11508c2ecf20Sopenharmony_ci pci_dev_put(ch1mm); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cibail_disable_ch0: 11538c2ecf20Sopenharmony_ci pci_disable_device(ch0mm); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cibail_ch0: 11568c2ecf20Sopenharmony_ci pci_dev_put(ch0mm); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cibail_pdev: 11598c2ecf20Sopenharmony_ci pci_disable_device(pdev); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cibail: 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic void i5100_remove_one(struct pci_dev *pdev) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 11688c2ecf20Sopenharmony_ci struct i5100_priv *priv; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci mci = edac_mc_del_mc(&pdev->dev); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (!mci) 11738c2ecf20Sopenharmony_ci return; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci priv = mci->pvt_info; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci edac_debugfs_remove_recursive(priv->debugfs); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci priv->scrub_enable = 0; 11808c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&(priv->i5100_scrubbing)); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci pci_disable_device(pdev); 11838c2ecf20Sopenharmony_ci pci_disable_device(priv->ch0mm); 11848c2ecf20Sopenharmony_ci pci_disable_device(priv->ch1mm); 11858c2ecf20Sopenharmony_ci pci_disable_device(priv->einj); 11868c2ecf20Sopenharmony_ci pci_dev_put(priv->ch0mm); 11878c2ecf20Sopenharmony_ci pci_dev_put(priv->ch1mm); 11888c2ecf20Sopenharmony_ci pci_dev_put(priv->einj); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci edac_mc_free(mci); 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic const struct pci_device_id i5100_pci_tbl[] = { 11948c2ecf20Sopenharmony_ci /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */ 11958c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) }, 11968c2ecf20Sopenharmony_ci { 0, } 11978c2ecf20Sopenharmony_ci}; 11988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i5100_pci_tbl); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic struct pci_driver i5100_driver = { 12018c2ecf20Sopenharmony_ci .name = KBUILD_BASENAME, 12028c2ecf20Sopenharmony_ci .probe = i5100_init_one, 12038c2ecf20Sopenharmony_ci .remove = i5100_remove_one, 12048c2ecf20Sopenharmony_ci .id_table = i5100_pci_tbl, 12058c2ecf20Sopenharmony_ci}; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic int __init i5100_init(void) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci int pci_rc; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci pci_rc = pci_register_driver(&i5100_driver); 12148c2ecf20Sopenharmony_ci return (pci_rc < 0) ? pci_rc : 0; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void __exit i5100_exit(void) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci edac_debugfs_remove(i5100_debugfs); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci pci_unregister_driver(&i5100_driver); 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_cimodule_init(i5100_init); 12258c2ecf20Sopenharmony_cimodule_exit(i5100_exit); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12288c2ecf20Sopenharmony_ciMODULE_AUTHOR 12298c2ecf20Sopenharmony_ci ("Arthur Jones <ajones@riverbed.com>"); 12308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers"); 1231