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