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