162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intel 7300 class Memory Controllers kernel module (Clarksboro)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2010 by:
662306a36Sopenharmony_ci *	 Mauro Carvalho Chehab
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Red Hat Inc. https://www.redhat.com
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Intel 7300 Chipset Memory Controller Hub (MCH) - Datasheet
1162306a36Sopenharmony_ci *	http://www.intel.com/Assets/PDF/datasheet/318082.pdf
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * TODO: The chipset allow checking for PCI Express errors also. Currently,
1462306a36Sopenharmony_ci *	 the driver covers only memory error errors
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * This driver uses "csrows" EDAC attribute to represent DIMM slot#
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/pci.h>
2262306a36Sopenharmony_ci#include <linux/pci_ids.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/edac.h>
2562306a36Sopenharmony_ci#include <linux/mmzone.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "edac_module.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Alter this version for the I7300 module when modifications are made
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define I7300_REVISION    " Ver: 1.0.0"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define EDAC_MOD_STR      "i7300_edac"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define i7300_printk(level, fmt, arg...) \
3762306a36Sopenharmony_ci	edac_printk(level, "i7300", fmt, ##arg)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define i7300_mc_printk(mci, level, fmt, arg...) \
4062306a36Sopenharmony_ci	edac_mc_chipset_printk(mci, level, "i7300", fmt, ##arg)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/***********************************************
4362306a36Sopenharmony_ci * i7300 Limit constants Structs and static vars
4462306a36Sopenharmony_ci ***********************************************/
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Memory topology is organized as:
4862306a36Sopenharmony_ci *	Branch 0 - 2 channels: channels 0 and 1 (FDB0 PCI dev 21.0)
4962306a36Sopenharmony_ci *	Branch 1 - 2 channels: channels 2 and 3 (FDB1 PCI dev 22.0)
5062306a36Sopenharmony_ci * Each channel can have to 8 DIMM sets (called as SLOTS)
5162306a36Sopenharmony_ci * Slots should generally be filled in pairs
5262306a36Sopenharmony_ci *	Except on Single Channel mode of operation
5362306a36Sopenharmony_ci *		just slot 0/channel0 filled on this mode
5462306a36Sopenharmony_ci *	On normal operation mode, the two channels on a branch should be
5562306a36Sopenharmony_ci *		filled together for the same SLOT#
5662306a36Sopenharmony_ci * When in mirrored mode, Branch 1 replicate memory at Branch 0, so, the four
5762306a36Sopenharmony_ci *		channels on both branches should be filled
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Limits for i7300 */
6162306a36Sopenharmony_ci#define MAX_SLOTS		8
6262306a36Sopenharmony_ci#define MAX_BRANCHES		2
6362306a36Sopenharmony_ci#define MAX_CH_PER_BRANCH	2
6462306a36Sopenharmony_ci#define MAX_CHANNELS		(MAX_CH_PER_BRANCH * MAX_BRANCHES)
6562306a36Sopenharmony_ci#define MAX_MIR			3
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define to_channel(ch, branch)	((((branch)) << 1) | (ch))
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define to_csrow(slot, ch, branch)					\
7062306a36Sopenharmony_ci		(to_channel(ch, branch) | ((slot) << 2))
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Device name and register DID (Device ID) */
7362306a36Sopenharmony_cistruct i7300_dev_info {
7462306a36Sopenharmony_ci	const char *ctl_name;	/* name for this device */
7562306a36Sopenharmony_ci	u16 fsb_mapping_errors;	/* DID for the branchmap,control */
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* Table of devices attributes supported by this driver */
7962306a36Sopenharmony_cistatic const struct i7300_dev_info i7300_devs[] = {
8062306a36Sopenharmony_ci	{
8162306a36Sopenharmony_ci		.ctl_name = "I7300",
8262306a36Sopenharmony_ci		.fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7300_MCH_ERR,
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistruct i7300_dimm_info {
8762306a36Sopenharmony_ci	int megabytes;		/* size, 0 means not present  */
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* driver private data structure */
9162306a36Sopenharmony_cistruct i7300_pvt {
9262306a36Sopenharmony_ci	struct pci_dev *pci_dev_16_0_fsb_ctlr;		/* 16.0 */
9362306a36Sopenharmony_ci	struct pci_dev *pci_dev_16_1_fsb_addr_map;	/* 16.1 */
9462306a36Sopenharmony_ci	struct pci_dev *pci_dev_16_2_fsb_err_regs;	/* 16.2 */
9562306a36Sopenharmony_ci	struct pci_dev *pci_dev_2x_0_fbd_branch[MAX_BRANCHES];	/* 21.0  and 22.0 */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	u16 tolm;				/* top of low memory */
9862306a36Sopenharmony_ci	u64 ambase;				/* AMB BAR */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	u32 mc_settings;			/* Report several settings */
10162306a36Sopenharmony_ci	u32 mc_settings_a;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	u16 mir[MAX_MIR];			/* Memory Interleave Reg*/
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	u16 mtr[MAX_SLOTS][MAX_BRANCHES];	/* Memory Technlogy Reg */
10662306a36Sopenharmony_ci	u16 ambpresent[MAX_CHANNELS];		/* AMB present regs */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* DIMM information matrix, allocating architecture maximums */
10962306a36Sopenharmony_ci	struct i7300_dimm_info dimm_info[MAX_SLOTS][MAX_CHANNELS];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Temporary buffer for use when preparing error messages */
11262306a36Sopenharmony_ci	char *tmp_prt_buffer;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* FIXME: Why do we need to have this static? */
11662306a36Sopenharmony_cistatic struct edac_pci_ctl_info *i7300_pci;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/***************************************************
11962306a36Sopenharmony_ci * i7300 Register definitions for memory enumeration
12062306a36Sopenharmony_ci ***************************************************/
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/*
12362306a36Sopenharmony_ci * Device 16,
12462306a36Sopenharmony_ci * Function 0: System Address (not documented)
12562306a36Sopenharmony_ci * Function 1: Memory Branch Map, Control, Errors Register
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* OFFSETS for Function 0 */
12962306a36Sopenharmony_ci#define AMBASE			0x48 /* AMB Mem Mapped Reg Region Base */
13062306a36Sopenharmony_ci#define MAXCH			0x56 /* Max Channel Number */
13162306a36Sopenharmony_ci#define MAXDIMMPERCH		0x57 /* Max DIMM PER Channel Number */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* OFFSETS for Function 1 */
13462306a36Sopenharmony_ci#define MC_SETTINGS		0x40
13562306a36Sopenharmony_ci  #define IS_MIRRORED(mc)		((mc) & (1 << 16))
13662306a36Sopenharmony_ci  #define IS_ECC_ENABLED(mc)		((mc) & (1 << 5))
13762306a36Sopenharmony_ci  #define IS_RETRY_ENABLED(mc)		((mc) & (1 << 31))
13862306a36Sopenharmony_ci  #define IS_SCRBALGO_ENHANCED(mc)	((mc) & (1 << 8))
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci#define MC_SETTINGS_A		0x58
14162306a36Sopenharmony_ci  #define IS_SINGLE_MODE(mca)		((mca) & (1 << 14))
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define TOLM			0x6C
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#define MIR0			0x80
14662306a36Sopenharmony_ci#define MIR1			0x84
14762306a36Sopenharmony_ci#define MIR2			0x88
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Note: Other Intel EDAC drivers use AMBPRESENT to identify if the available
15162306a36Sopenharmony_ci * memory. From datasheet item 7.3.1 (FB-DIMM technology & organization), it
15262306a36Sopenharmony_ci * seems that we cannot use this information directly for the same usage.
15362306a36Sopenharmony_ci * Each memory slot may have up to 2 AMB interfaces, one for income and another
15462306a36Sopenharmony_ci * for outcome interface to the next slot.
15562306a36Sopenharmony_ci * For now, the driver just stores the AMB present registers, but rely only at
15662306a36Sopenharmony_ci * the MTR info to detect memory.
15762306a36Sopenharmony_ci * Datasheet is also not clear about how to map each AMBPRESENT registers to
15862306a36Sopenharmony_ci * one of the 4 available channels.
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_ci#define AMBPRESENT_0	0x64
16162306a36Sopenharmony_ci#define AMBPRESENT_1	0x66
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const u16 mtr_regs[MAX_SLOTS] = {
16462306a36Sopenharmony_ci	0x80, 0x84, 0x88, 0x8c,
16562306a36Sopenharmony_ci	0x82, 0x86, 0x8a, 0x8e
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * Defines to extract the vaious fields from the
17062306a36Sopenharmony_ci *	MTRx - Memory Technology Registers
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_ci#define MTR_DIMMS_PRESENT(mtr)		((mtr) & (1 << 8))
17362306a36Sopenharmony_ci#define MTR_DIMMS_ETHROTTLE(mtr)	((mtr) & (1 << 7))
17462306a36Sopenharmony_ci#define MTR_DRAM_WIDTH(mtr)		(((mtr) & (1 << 6)) ? 8 : 4)
17562306a36Sopenharmony_ci#define MTR_DRAM_BANKS(mtr)		(((mtr) & (1 << 5)) ? 8 : 4)
17662306a36Sopenharmony_ci#define MTR_DIMM_RANKS(mtr)		(((mtr) & (1 << 4)) ? 1 : 0)
17762306a36Sopenharmony_ci#define MTR_DIMM_ROWS(mtr)		(((mtr) >> 2) & 0x3)
17862306a36Sopenharmony_ci#define MTR_DRAM_BANKS_ADDR_BITS	2
17962306a36Sopenharmony_ci#define MTR_DIMM_ROWS_ADDR_BITS(mtr)	(MTR_DIMM_ROWS(mtr) + 13)
18062306a36Sopenharmony_ci#define MTR_DIMM_COLS(mtr)		((mtr) & 0x3)
18162306a36Sopenharmony_ci#define MTR_DIMM_COLS_ADDR_BITS(mtr)	(MTR_DIMM_COLS(mtr) + 10)
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/************************************************
18462306a36Sopenharmony_ci * i7300 Register definitions for error detection
18562306a36Sopenharmony_ci ************************************************/
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/*
18862306a36Sopenharmony_ci * Device 16.1: FBD Error Registers
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_ci#define FERR_FAT_FBD	0x98
19162306a36Sopenharmony_cistatic const char *ferr_fat_fbd_name[] = {
19262306a36Sopenharmony_ci	[22] = "Non-Redundant Fast Reset Timeout",
19362306a36Sopenharmony_ci	[2]  = ">Tmid Thermal event with intelligent throttling disabled",
19462306a36Sopenharmony_ci	[1]  = "Memory or FBD configuration CRC read error",
19562306a36Sopenharmony_ci	[0]  = "Memory Write error on non-redundant retry or "
19662306a36Sopenharmony_ci	       "FBD configuration Write error on retry",
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci#define GET_FBD_FAT_IDX(fbderr)	(((fbderr) >> 28) & 3)
19962306a36Sopenharmony_ci#define FERR_FAT_FBD_ERR_MASK ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 22))
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci#define FERR_NF_FBD	0xa0
20262306a36Sopenharmony_cistatic const char *ferr_nf_fbd_name[] = {
20362306a36Sopenharmony_ci	[24] = "DIMM-Spare Copy Completed",
20462306a36Sopenharmony_ci	[23] = "DIMM-Spare Copy Initiated",
20562306a36Sopenharmony_ci	[22] = "Redundant Fast Reset Timeout",
20662306a36Sopenharmony_ci	[21] = "Memory Write error on redundant retry",
20762306a36Sopenharmony_ci	[18] = "SPD protocol Error",
20862306a36Sopenharmony_ci	[17] = "FBD Northbound parity error on FBD Sync Status",
20962306a36Sopenharmony_ci	[16] = "Correctable Patrol Data ECC",
21062306a36Sopenharmony_ci	[15] = "Correctable Resilver- or Spare-Copy Data ECC",
21162306a36Sopenharmony_ci	[14] = "Correctable Mirrored Demand Data ECC",
21262306a36Sopenharmony_ci	[13] = "Correctable Non-Mirrored Demand Data ECC",
21362306a36Sopenharmony_ci	[11] = "Memory or FBD configuration CRC read error",
21462306a36Sopenharmony_ci	[10] = "FBD Configuration Write error on first attempt",
21562306a36Sopenharmony_ci	[9]  = "Memory Write error on first attempt",
21662306a36Sopenharmony_ci	[8]  = "Non-Aliased Uncorrectable Patrol Data ECC",
21762306a36Sopenharmony_ci	[7]  = "Non-Aliased Uncorrectable Resilver- or Spare-Copy Data ECC",
21862306a36Sopenharmony_ci	[6]  = "Non-Aliased Uncorrectable Mirrored Demand Data ECC",
21962306a36Sopenharmony_ci	[5]  = "Non-Aliased Uncorrectable Non-Mirrored Demand Data ECC",
22062306a36Sopenharmony_ci	[4]  = "Aliased Uncorrectable Patrol Data ECC",
22162306a36Sopenharmony_ci	[3]  = "Aliased Uncorrectable Resilver- or Spare-Copy Data ECC",
22262306a36Sopenharmony_ci	[2]  = "Aliased Uncorrectable Mirrored Demand Data ECC",
22362306a36Sopenharmony_ci	[1]  = "Aliased Uncorrectable Non-Mirrored Demand Data ECC",
22462306a36Sopenharmony_ci	[0]  = "Uncorrectable Data ECC on Replay",
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci#define GET_FBD_NF_IDX(fbderr)	(((fbderr) >> 28) & 3)
22762306a36Sopenharmony_ci#define FERR_NF_FBD_ERR_MASK ((1 << 24) | (1 << 23) | (1 << 22) | (1 << 21) |\
22862306a36Sopenharmony_ci			      (1 << 18) | (1 << 17) | (1 << 16) | (1 << 15) |\
22962306a36Sopenharmony_ci			      (1 << 14) | (1 << 13) | (1 << 11) | (1 << 10) |\
23062306a36Sopenharmony_ci			      (1 << 9)  | (1 << 8)  | (1 << 7)  | (1 << 6)  |\
23162306a36Sopenharmony_ci			      (1 << 5)  | (1 << 4)  | (1 << 3)  | (1 << 2)  |\
23262306a36Sopenharmony_ci			      (1 << 1)  | (1 << 0))
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci#define EMASK_FBD	0xa8
23562306a36Sopenharmony_ci#define EMASK_FBD_ERR_MASK ((1 << 27) | (1 << 26) | (1 << 25) | (1 << 24) |\
23662306a36Sopenharmony_ci			    (1 << 22) | (1 << 21) | (1 << 20) | (1 << 19) |\
23762306a36Sopenharmony_ci			    (1 << 18) | (1 << 17) | (1 << 16) | (1 << 14) |\
23862306a36Sopenharmony_ci			    (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) |\
23962306a36Sopenharmony_ci			    (1 << 9)  | (1 << 8)  | (1 << 7)  | (1 << 6)  |\
24062306a36Sopenharmony_ci			    (1 << 5)  | (1 << 4)  | (1 << 3)  | (1 << 2)  |\
24162306a36Sopenharmony_ci			    (1 << 1)  | (1 << 0))
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/*
24462306a36Sopenharmony_ci * Device 16.2: Global Error Registers
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci#define FERR_GLOBAL_HI	0x48
24862306a36Sopenharmony_cistatic const char *ferr_global_hi_name[] = {
24962306a36Sopenharmony_ci	[3] = "FSB 3 Fatal Error",
25062306a36Sopenharmony_ci	[2] = "FSB 2 Fatal Error",
25162306a36Sopenharmony_ci	[1] = "FSB 1 Fatal Error",
25262306a36Sopenharmony_ci	[0] = "FSB 0 Fatal Error",
25362306a36Sopenharmony_ci};
25462306a36Sopenharmony_ci#define ferr_global_hi_is_fatal(errno)	1
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci#define FERR_GLOBAL_LO	0x40
25762306a36Sopenharmony_cistatic const char *ferr_global_lo_name[] = {
25862306a36Sopenharmony_ci	[31] = "Internal MCH Fatal Error",
25962306a36Sopenharmony_ci	[30] = "Intel QuickData Technology Device Fatal Error",
26062306a36Sopenharmony_ci	[29] = "FSB1 Fatal Error",
26162306a36Sopenharmony_ci	[28] = "FSB0 Fatal Error",
26262306a36Sopenharmony_ci	[27] = "FBD Channel 3 Fatal Error",
26362306a36Sopenharmony_ci	[26] = "FBD Channel 2 Fatal Error",
26462306a36Sopenharmony_ci	[25] = "FBD Channel 1 Fatal Error",
26562306a36Sopenharmony_ci	[24] = "FBD Channel 0 Fatal Error",
26662306a36Sopenharmony_ci	[23] = "PCI Express Device 7Fatal Error",
26762306a36Sopenharmony_ci	[22] = "PCI Express Device 6 Fatal Error",
26862306a36Sopenharmony_ci	[21] = "PCI Express Device 5 Fatal Error",
26962306a36Sopenharmony_ci	[20] = "PCI Express Device 4 Fatal Error",
27062306a36Sopenharmony_ci	[19] = "PCI Express Device 3 Fatal Error",
27162306a36Sopenharmony_ci	[18] = "PCI Express Device 2 Fatal Error",
27262306a36Sopenharmony_ci	[17] = "PCI Express Device 1 Fatal Error",
27362306a36Sopenharmony_ci	[16] = "ESI Fatal Error",
27462306a36Sopenharmony_ci	[15] = "Internal MCH Non-Fatal Error",
27562306a36Sopenharmony_ci	[14] = "Intel QuickData Technology Device Non Fatal Error",
27662306a36Sopenharmony_ci	[13] = "FSB1 Non-Fatal Error",
27762306a36Sopenharmony_ci	[12] = "FSB 0 Non-Fatal Error",
27862306a36Sopenharmony_ci	[11] = "FBD Channel 3 Non-Fatal Error",
27962306a36Sopenharmony_ci	[10] = "FBD Channel 2 Non-Fatal Error",
28062306a36Sopenharmony_ci	[9]  = "FBD Channel 1 Non-Fatal Error",
28162306a36Sopenharmony_ci	[8]  = "FBD Channel 0 Non-Fatal Error",
28262306a36Sopenharmony_ci	[7]  = "PCI Express Device 7 Non-Fatal Error",
28362306a36Sopenharmony_ci	[6]  = "PCI Express Device 6 Non-Fatal Error",
28462306a36Sopenharmony_ci	[5]  = "PCI Express Device 5 Non-Fatal Error",
28562306a36Sopenharmony_ci	[4]  = "PCI Express Device 4 Non-Fatal Error",
28662306a36Sopenharmony_ci	[3]  = "PCI Express Device 3 Non-Fatal Error",
28762306a36Sopenharmony_ci	[2]  = "PCI Express Device 2 Non-Fatal Error",
28862306a36Sopenharmony_ci	[1]  = "PCI Express Device 1 Non-Fatal Error",
28962306a36Sopenharmony_ci	[0]  = "ESI Non-Fatal Error",
29062306a36Sopenharmony_ci};
29162306a36Sopenharmony_ci#define ferr_global_lo_is_fatal(errno)	((errno < 16) ? 0 : 1)
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci#define NRECMEMA	0xbe
29462306a36Sopenharmony_ci  #define NRECMEMA_BANK(v)	(((v) >> 12) & 7)
29562306a36Sopenharmony_ci  #define NRECMEMA_RANK(v)	(((v) >> 8) & 15)
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci#define NRECMEMB	0xc0
29862306a36Sopenharmony_ci  #define NRECMEMB_IS_WR(v)	((v) & (1 << 31))
29962306a36Sopenharmony_ci  #define NRECMEMB_CAS(v)	(((v) >> 16) & 0x1fff)
30062306a36Sopenharmony_ci  #define NRECMEMB_RAS(v)	((v) & 0xffff)
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci#define REDMEMA		0xdc
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci#define REDMEMB		0x7c
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci#define RECMEMA		0xe0
30762306a36Sopenharmony_ci  #define RECMEMA_BANK(v)	(((v) >> 12) & 7)
30862306a36Sopenharmony_ci  #define RECMEMA_RANK(v)	(((v) >> 8) & 15)
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci#define RECMEMB		0xe4
31162306a36Sopenharmony_ci  #define RECMEMB_IS_WR(v)	((v) & (1 << 31))
31262306a36Sopenharmony_ci  #define RECMEMB_CAS(v)	(((v) >> 16) & 0x1fff)
31362306a36Sopenharmony_ci  #define RECMEMB_RAS(v)	((v) & 0xffff)
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/********************************************
31662306a36Sopenharmony_ci * i7300 Functions related to error detection
31762306a36Sopenharmony_ci ********************************************/
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/**
32062306a36Sopenharmony_ci * get_err_from_table() - Gets the error message from a table
32162306a36Sopenharmony_ci * @table:	table name (array of char *)
32262306a36Sopenharmony_ci * @size:	number of elements at the table
32362306a36Sopenharmony_ci * @pos:	position of the element to be returned
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci * This is a small routine that gets the pos-th element of a table. If the
32662306a36Sopenharmony_ci * element doesn't exist (or it is empty), it returns "reserved".
32762306a36Sopenharmony_ci * Instead of calling it directly, the better is to call via the macro
32862306a36Sopenharmony_ci * GET_ERR_FROM_TABLE(), that automatically checks the table size via
32962306a36Sopenharmony_ci * ARRAY_SIZE() macro
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic const char *get_err_from_table(const char *table[], int size, int pos)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	if (unlikely(pos >= size))
33462306a36Sopenharmony_ci		return "Reserved";
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (unlikely(!table[pos]))
33762306a36Sopenharmony_ci		return "Reserved";
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return table[pos];
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci#define GET_ERR_FROM_TABLE(table, pos)				\
34362306a36Sopenharmony_ci	get_err_from_table(table, ARRAY_SIZE(table), pos)
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/**
34662306a36Sopenharmony_ci * i7300_process_error_global() - Retrieve the hardware error information from
34762306a36Sopenharmony_ci *				  the hardware global error registers and
34862306a36Sopenharmony_ci *				  sends it to dmesg
34962306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
35062306a36Sopenharmony_ci */
35162306a36Sopenharmony_cistatic void i7300_process_error_global(struct mem_ctl_info *mci)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct i7300_pvt *pvt;
35462306a36Sopenharmony_ci	u32 errnum, error_reg;
35562306a36Sopenharmony_ci	unsigned long errors;
35662306a36Sopenharmony_ci	const char *specific;
35762306a36Sopenharmony_ci	bool is_fatal;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	pvt = mci->pvt_info;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* read in the 1st FATAL error register */
36262306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
36362306a36Sopenharmony_ci			      FERR_GLOBAL_HI, &error_reg);
36462306a36Sopenharmony_ci	if (unlikely(error_reg)) {
36562306a36Sopenharmony_ci		errors = error_reg;
36662306a36Sopenharmony_ci		errnum = find_first_bit(&errors,
36762306a36Sopenharmony_ci					ARRAY_SIZE(ferr_global_hi_name));
36862306a36Sopenharmony_ci		specific = GET_ERR_FROM_TABLE(ferr_global_hi_name, errnum);
36962306a36Sopenharmony_ci		is_fatal = ferr_global_hi_is_fatal(errnum);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		/* Clear the error bit */
37262306a36Sopenharmony_ci		pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
37362306a36Sopenharmony_ci				       FERR_GLOBAL_HI, error_reg);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		goto error_global;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
37962306a36Sopenharmony_ci			      FERR_GLOBAL_LO, &error_reg);
38062306a36Sopenharmony_ci	if (unlikely(error_reg)) {
38162306a36Sopenharmony_ci		errors = error_reg;
38262306a36Sopenharmony_ci		errnum = find_first_bit(&errors,
38362306a36Sopenharmony_ci					ARRAY_SIZE(ferr_global_lo_name));
38462306a36Sopenharmony_ci		specific = GET_ERR_FROM_TABLE(ferr_global_lo_name, errnum);
38562306a36Sopenharmony_ci		is_fatal = ferr_global_lo_is_fatal(errnum);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		/* Clear the error bit */
38862306a36Sopenharmony_ci		pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
38962306a36Sopenharmony_ci				       FERR_GLOBAL_LO, error_reg);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		goto error_global;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci	return;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cierror_global:
39662306a36Sopenharmony_ci	i7300_mc_printk(mci, KERN_EMERG, "%s misc error: %s\n",
39762306a36Sopenharmony_ci			is_fatal ? "Fatal" : "NOT fatal", specific);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/**
40162306a36Sopenharmony_ci * i7300_process_fbd_error() - Retrieve the hardware error information from
40262306a36Sopenharmony_ci *			       the FBD error registers and sends it via
40362306a36Sopenharmony_ci *			       EDAC error API calls
40462306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cistatic void i7300_process_fbd_error(struct mem_ctl_info *mci)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct i7300_pvt *pvt;
40962306a36Sopenharmony_ci	u32 errnum, value, error_reg;
41062306a36Sopenharmony_ci	u16 val16;
41162306a36Sopenharmony_ci	unsigned branch, channel, bank, rank, cas, ras;
41262306a36Sopenharmony_ci	u32 syndrome;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	unsigned long errors;
41562306a36Sopenharmony_ci	const char *specific;
41662306a36Sopenharmony_ci	bool is_wr;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	pvt = mci->pvt_info;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* read in the 1st FATAL error register */
42162306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
42262306a36Sopenharmony_ci			      FERR_FAT_FBD, &error_reg);
42362306a36Sopenharmony_ci	if (unlikely(error_reg & FERR_FAT_FBD_ERR_MASK)) {
42462306a36Sopenharmony_ci		errors = error_reg & FERR_FAT_FBD_ERR_MASK ;
42562306a36Sopenharmony_ci		errnum = find_first_bit(&errors,
42662306a36Sopenharmony_ci					ARRAY_SIZE(ferr_fat_fbd_name));
42762306a36Sopenharmony_ci		specific = GET_ERR_FROM_TABLE(ferr_fat_fbd_name, errnum);
42862306a36Sopenharmony_ci		branch = (GET_FBD_FAT_IDX(error_reg) == 2) ? 1 : 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
43162306a36Sopenharmony_ci				     NRECMEMA, &val16);
43262306a36Sopenharmony_ci		bank = NRECMEMA_BANK(val16);
43362306a36Sopenharmony_ci		rank = NRECMEMA_RANK(val16);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
43662306a36Sopenharmony_ci				NRECMEMB, &value);
43762306a36Sopenharmony_ci		is_wr = NRECMEMB_IS_WR(value);
43862306a36Sopenharmony_ci		cas = NRECMEMB_CAS(value);
43962306a36Sopenharmony_ci		ras = NRECMEMB_RAS(value);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		/* Clean the error register */
44262306a36Sopenharmony_ci		pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
44362306a36Sopenharmony_ci				FERR_FAT_FBD, error_reg);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
44662306a36Sopenharmony_ci			 "Bank=%d RAS=%d CAS=%d Err=0x%lx (%s))",
44762306a36Sopenharmony_ci			 bank, ras, cas, errors, specific);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0,
45062306a36Sopenharmony_ci				     branch, -1, rank,
45162306a36Sopenharmony_ci				     is_wr ? "Write error" : "Read error",
45262306a36Sopenharmony_ci				     pvt->tmp_prt_buffer);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* read in the 1st NON-FATAL error register */
45762306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
45862306a36Sopenharmony_ci			      FERR_NF_FBD, &error_reg);
45962306a36Sopenharmony_ci	if (unlikely(error_reg & FERR_NF_FBD_ERR_MASK)) {
46062306a36Sopenharmony_ci		errors = error_reg & FERR_NF_FBD_ERR_MASK;
46162306a36Sopenharmony_ci		errnum = find_first_bit(&errors,
46262306a36Sopenharmony_ci					ARRAY_SIZE(ferr_nf_fbd_name));
46362306a36Sopenharmony_ci		specific = GET_ERR_FROM_TABLE(ferr_nf_fbd_name, errnum);
46462306a36Sopenharmony_ci		branch = (GET_FBD_NF_IDX(error_reg) == 2) ? 1 : 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
46762306a36Sopenharmony_ci			REDMEMA, &syndrome);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
47062306a36Sopenharmony_ci				     RECMEMA, &val16);
47162306a36Sopenharmony_ci		bank = RECMEMA_BANK(val16);
47262306a36Sopenharmony_ci		rank = RECMEMA_RANK(val16);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
47562306a36Sopenharmony_ci				RECMEMB, &value);
47662306a36Sopenharmony_ci		is_wr = RECMEMB_IS_WR(value);
47762306a36Sopenharmony_ci		cas = RECMEMB_CAS(value);
47862306a36Sopenharmony_ci		ras = RECMEMB_RAS(value);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
48162306a36Sopenharmony_ci				     REDMEMB, &value);
48262306a36Sopenharmony_ci		channel = (branch << 1);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		/* Second channel ? */
48562306a36Sopenharmony_ci		channel += !!(value & BIT(17));
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		/* Clear the error bit */
48862306a36Sopenharmony_ci		pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
48962306a36Sopenharmony_ci				FERR_NF_FBD, error_reg);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		/* Form out message */
49262306a36Sopenharmony_ci		snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
49362306a36Sopenharmony_ci			 "DRAM-Bank=%d RAS=%d CAS=%d, Err=0x%lx (%s))",
49462306a36Sopenharmony_ci			 bank, ras, cas, errors, specific);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0,
49762306a36Sopenharmony_ci				     syndrome,
49862306a36Sopenharmony_ci				     branch >> 1, channel % 2, rank,
49962306a36Sopenharmony_ci				     is_wr ? "Write error" : "Read error",
50062306a36Sopenharmony_ci				     pvt->tmp_prt_buffer);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	return;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/**
50662306a36Sopenharmony_ci * i7300_check_error() - Calls the error checking subroutines
50762306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_cistatic void i7300_check_error(struct mem_ctl_info *mci)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	i7300_process_error_global(mci);
51262306a36Sopenharmony_ci	i7300_process_fbd_error(mci);
51362306a36Sopenharmony_ci};
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci/**
51662306a36Sopenharmony_ci * i7300_clear_error() - Clears the error registers
51762306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
51862306a36Sopenharmony_ci */
51962306a36Sopenharmony_cistatic void i7300_clear_error(struct mem_ctl_info *mci)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct i7300_pvt *pvt = mci->pvt_info;
52262306a36Sopenharmony_ci	u32 value;
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * All error values are RWC - we need to read and write 1 to the
52562306a36Sopenharmony_ci	 * bit that we want to cleanup
52662306a36Sopenharmony_ci	 */
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* Clear global error registers */
52962306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
53062306a36Sopenharmony_ci			      FERR_GLOBAL_HI, &value);
53162306a36Sopenharmony_ci	pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
53262306a36Sopenharmony_ci			      FERR_GLOBAL_HI, value);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
53562306a36Sopenharmony_ci			      FERR_GLOBAL_LO, &value);
53662306a36Sopenharmony_ci	pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
53762306a36Sopenharmony_ci			      FERR_GLOBAL_LO, value);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* Clear FBD error registers */
54062306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
54162306a36Sopenharmony_ci			      FERR_FAT_FBD, &value);
54262306a36Sopenharmony_ci	pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
54362306a36Sopenharmony_ci			      FERR_FAT_FBD, value);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
54662306a36Sopenharmony_ci			      FERR_NF_FBD, &value);
54762306a36Sopenharmony_ci	pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
54862306a36Sopenharmony_ci			      FERR_NF_FBD, value);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci/**
55262306a36Sopenharmony_ci * i7300_enable_error_reporting() - Enable the memory reporting logic at the
55362306a36Sopenharmony_ci *				    hardware
55462306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
55562306a36Sopenharmony_ci */
55662306a36Sopenharmony_cistatic void i7300_enable_error_reporting(struct mem_ctl_info *mci)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct i7300_pvt *pvt = mci->pvt_info;
55962306a36Sopenharmony_ci	u32 fbd_error_mask;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* Read the FBD Error Mask Register */
56262306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
56362306a36Sopenharmony_ci			      EMASK_FBD, &fbd_error_mask);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* Enable with a '0' */
56662306a36Sopenharmony_ci	fbd_error_mask &= ~(EMASK_FBD_ERR_MASK);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
56962306a36Sopenharmony_ci			       EMASK_FBD, fbd_error_mask);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/************************************************
57362306a36Sopenharmony_ci * i7300 Functions related to memory enumberation
57462306a36Sopenharmony_ci ************************************************/
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/**
57762306a36Sopenharmony_ci * decode_mtr() - Decodes the MTR descriptor, filling the edac structs
57862306a36Sopenharmony_ci * @pvt: pointer to the private data struct used by i7300 driver
57962306a36Sopenharmony_ci * @slot: DIMM slot (0 to 7)
58062306a36Sopenharmony_ci * @ch: Channel number within the branch (0 or 1)
58162306a36Sopenharmony_ci * @branch: Branch number (0 or 1)
58262306a36Sopenharmony_ci * @dinfo: Pointer to DIMM info where dimm size is stored
58362306a36Sopenharmony_ci * @dimm: Pointer to the struct dimm_info that corresponds to that element
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_cistatic int decode_mtr(struct i7300_pvt *pvt,
58662306a36Sopenharmony_ci		      int slot, int ch, int branch,
58762306a36Sopenharmony_ci		      struct i7300_dimm_info *dinfo,
58862306a36Sopenharmony_ci		      struct dimm_info *dimm)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	int mtr, ans, addrBits, channel;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	channel = to_channel(ch, branch);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	mtr = pvt->mtr[slot][branch];
59562306a36Sopenharmony_ci	ans = MTR_DIMMS_PRESENT(mtr) ? 1 : 0;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	edac_dbg(2, "\tMTR%d CH%d: DIMMs are %sPresent (mtr)\n",
59862306a36Sopenharmony_ci		 slot, channel, ans ? "" : "NOT ");
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Determine if there is a DIMM present in this DIMM slot */
60162306a36Sopenharmony_ci	if (!ans)
60262306a36Sopenharmony_ci		return 0;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/* Start with the number of bits for a Bank
60562306a36Sopenharmony_ci	* on the DRAM */
60662306a36Sopenharmony_ci	addrBits = MTR_DRAM_BANKS_ADDR_BITS;
60762306a36Sopenharmony_ci	/* Add thenumber of ROW bits */
60862306a36Sopenharmony_ci	addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr);
60962306a36Sopenharmony_ci	/* add the number of COLUMN bits */
61062306a36Sopenharmony_ci	addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
61162306a36Sopenharmony_ci	/* add the number of RANK bits */
61262306a36Sopenharmony_ci	addrBits += MTR_DIMM_RANKS(mtr);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	addrBits += 6;	/* add 64 bits per DIMM */
61562306a36Sopenharmony_ci	addrBits -= 20;	/* divide by 2^^20 */
61662306a36Sopenharmony_ci	addrBits -= 3;	/* 8 bits per bytes */
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	dinfo->megabytes = 1 << addrBits;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n",
62362306a36Sopenharmony_ci		 MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
62662306a36Sopenharmony_ci	edac_dbg(2, "\t\tNUMRANK: %s\n",
62762306a36Sopenharmony_ci		 MTR_DIMM_RANKS(mtr) ? "double" : "single");
62862306a36Sopenharmony_ci	edac_dbg(2, "\t\tNUMROW: %s\n",
62962306a36Sopenharmony_ci		 MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
63062306a36Sopenharmony_ci		 MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
63162306a36Sopenharmony_ci		 MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
63262306a36Sopenharmony_ci		 "65,536 - 16 rows");
63362306a36Sopenharmony_ci	edac_dbg(2, "\t\tNUMCOL: %s\n",
63462306a36Sopenharmony_ci		 MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
63562306a36Sopenharmony_ci		 MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
63662306a36Sopenharmony_ci		 MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
63762306a36Sopenharmony_ci		 "reserved");
63862306a36Sopenharmony_ci	edac_dbg(2, "\t\tSIZE: %d MB\n", dinfo->megabytes);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/*
64162306a36Sopenharmony_ci	 * The type of error detection actually depends of the
64262306a36Sopenharmony_ci	 * mode of operation. When it is just one single memory chip, at
64362306a36Sopenharmony_ci	 * socket 0, channel 0, it uses 8-byte-over-32-byte SECDED+ code.
64462306a36Sopenharmony_ci	 * In normal or mirrored mode, it uses Lockstep mode,
64562306a36Sopenharmony_ci	 * with the possibility of using an extended algorithm for x8 memories
64662306a36Sopenharmony_ci	 * See datasheet Sections 7.3.6 to 7.3.8
64762306a36Sopenharmony_ci	 */
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	dimm->nr_pages = MiB_TO_PAGES(dinfo->megabytes);
65062306a36Sopenharmony_ci	dimm->grain = 8;
65162306a36Sopenharmony_ci	dimm->mtype = MEM_FB_DDR2;
65262306a36Sopenharmony_ci	if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
65362306a36Sopenharmony_ci		dimm->edac_mode = EDAC_SECDED;
65462306a36Sopenharmony_ci		edac_dbg(2, "\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
65562306a36Sopenharmony_ci	} else {
65662306a36Sopenharmony_ci		edac_dbg(2, "\t\tECC code is on Lockstep mode\n");
65762306a36Sopenharmony_ci		if (MTR_DRAM_WIDTH(mtr) == 8)
65862306a36Sopenharmony_ci			dimm->edac_mode = EDAC_S8ECD8ED;
65962306a36Sopenharmony_ci		else
66062306a36Sopenharmony_ci			dimm->edac_mode = EDAC_S4ECD4ED;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* ask what device type on this row */
66462306a36Sopenharmony_ci	if (MTR_DRAM_WIDTH(mtr) == 8) {
66562306a36Sopenharmony_ci		edac_dbg(2, "\t\tScrub algorithm for x8 is on %s mode\n",
66662306a36Sopenharmony_ci			 IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
66762306a36Sopenharmony_ci			 "enhanced" : "normal");
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		dimm->dtype = DEV_X8;
67062306a36Sopenharmony_ci	} else
67162306a36Sopenharmony_ci		dimm->dtype = DEV_X4;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return mtr;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci/**
67762306a36Sopenharmony_ci * print_dimm_size() - Prints dump of the memory organization
67862306a36Sopenharmony_ci * @pvt: pointer to the private data struct used by i7300 driver
67962306a36Sopenharmony_ci *
68062306a36Sopenharmony_ci * Useful for debug. If debug is disabled, this routine do nothing
68162306a36Sopenharmony_ci */
68262306a36Sopenharmony_cistatic void print_dimm_size(struct i7300_pvt *pvt)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
68562306a36Sopenharmony_ci	struct i7300_dimm_info *dinfo;
68662306a36Sopenharmony_ci	char *p;
68762306a36Sopenharmony_ci	int space, n;
68862306a36Sopenharmony_ci	int channel, slot;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	space = PAGE_SIZE;
69162306a36Sopenharmony_ci	p = pvt->tmp_prt_buffer;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	n = snprintf(p, space, "              ");
69462306a36Sopenharmony_ci	p += n;
69562306a36Sopenharmony_ci	space -= n;
69662306a36Sopenharmony_ci	for (channel = 0; channel < MAX_CHANNELS; channel++) {
69762306a36Sopenharmony_ci		n = snprintf(p, space, "channel %d | ", channel);
69862306a36Sopenharmony_ci		p += n;
69962306a36Sopenharmony_ci		space -= n;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
70262306a36Sopenharmony_ci	p = pvt->tmp_prt_buffer;
70362306a36Sopenharmony_ci	space = PAGE_SIZE;
70462306a36Sopenharmony_ci	n = snprintf(p, space, "-------------------------------"
70562306a36Sopenharmony_ci			       "------------------------------");
70662306a36Sopenharmony_ci	p += n;
70762306a36Sopenharmony_ci	space -= n;
70862306a36Sopenharmony_ci	edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
70962306a36Sopenharmony_ci	p = pvt->tmp_prt_buffer;
71062306a36Sopenharmony_ci	space = PAGE_SIZE;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	for (slot = 0; slot < MAX_SLOTS; slot++) {
71362306a36Sopenharmony_ci		n = snprintf(p, space, "csrow/SLOT %d  ", slot);
71462306a36Sopenharmony_ci		p += n;
71562306a36Sopenharmony_ci		space -= n;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		for (channel = 0; channel < MAX_CHANNELS; channel++) {
71862306a36Sopenharmony_ci			dinfo = &pvt->dimm_info[slot][channel];
71962306a36Sopenharmony_ci			n = snprintf(p, space, "%4d MB   | ", dinfo->megabytes);
72062306a36Sopenharmony_ci			p += n;
72162306a36Sopenharmony_ci			space -= n;
72262306a36Sopenharmony_ci		}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
72562306a36Sopenharmony_ci		p = pvt->tmp_prt_buffer;
72662306a36Sopenharmony_ci		space = PAGE_SIZE;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	n = snprintf(p, space, "-------------------------------"
73062306a36Sopenharmony_ci			       "------------------------------");
73162306a36Sopenharmony_ci	p += n;
73262306a36Sopenharmony_ci	space -= n;
73362306a36Sopenharmony_ci	edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
73462306a36Sopenharmony_ci	p = pvt->tmp_prt_buffer;
73562306a36Sopenharmony_ci	space = PAGE_SIZE;
73662306a36Sopenharmony_ci#endif
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci/**
74062306a36Sopenharmony_ci * i7300_init_csrows() - Initialize the 'csrows' table within
74162306a36Sopenharmony_ci *			 the mci control structure with the
74262306a36Sopenharmony_ci *			 addressing of memory.
74362306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
74462306a36Sopenharmony_ci */
74562306a36Sopenharmony_cistatic int i7300_init_csrows(struct mem_ctl_info *mci)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct i7300_pvt *pvt;
74862306a36Sopenharmony_ci	struct i7300_dimm_info *dinfo;
74962306a36Sopenharmony_ci	int rc = -ENODEV;
75062306a36Sopenharmony_ci	int mtr;
75162306a36Sopenharmony_ci	int ch, branch, slot, channel, max_channel, max_branch;
75262306a36Sopenharmony_ci	struct dimm_info *dimm;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	pvt = mci->pvt_info;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	edac_dbg(2, "Memory Technology Registers:\n");
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
75962306a36Sopenharmony_ci		max_branch = 1;
76062306a36Sopenharmony_ci		max_channel = 1;
76162306a36Sopenharmony_ci	} else {
76262306a36Sopenharmony_ci		max_branch = MAX_BRANCHES;
76362306a36Sopenharmony_ci		max_channel = MAX_CH_PER_BRANCH;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* Get the AMB present registers for the four channels */
76762306a36Sopenharmony_ci	for (branch = 0; branch < max_branch; branch++) {
76862306a36Sopenharmony_ci		/* Read and dump branch 0's MTRs */
76962306a36Sopenharmony_ci		channel = to_channel(0, branch);
77062306a36Sopenharmony_ci		pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
77162306a36Sopenharmony_ci				     AMBPRESENT_0,
77262306a36Sopenharmony_ci				&pvt->ambpresent[channel]);
77362306a36Sopenharmony_ci		edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n",
77462306a36Sopenharmony_ci			 channel, pvt->ambpresent[channel]);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		if (max_channel == 1)
77762306a36Sopenharmony_ci			continue;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		channel = to_channel(1, branch);
78062306a36Sopenharmony_ci		pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
78162306a36Sopenharmony_ci				     AMBPRESENT_1,
78262306a36Sopenharmony_ci				&pvt->ambpresent[channel]);
78362306a36Sopenharmony_ci		edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n",
78462306a36Sopenharmony_ci			 channel, pvt->ambpresent[channel]);
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Get the set of MTR[0-7] regs by each branch */
78862306a36Sopenharmony_ci	for (slot = 0; slot < MAX_SLOTS; slot++) {
78962306a36Sopenharmony_ci		int where = mtr_regs[slot];
79062306a36Sopenharmony_ci		for (branch = 0; branch < max_branch; branch++) {
79162306a36Sopenharmony_ci			pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
79262306a36Sopenharmony_ci					where,
79362306a36Sopenharmony_ci					&pvt->mtr[slot][branch]);
79462306a36Sopenharmony_ci			for (ch = 0; ch < max_channel; ch++) {
79562306a36Sopenharmony_ci				int channel = to_channel(ch, branch);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci				dimm = edac_get_dimm(mci, branch, ch, slot);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci				dinfo = &pvt->dimm_info[slot][channel];
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci				mtr = decode_mtr(pvt, slot, ch, branch,
80262306a36Sopenharmony_ci						 dinfo, dimm);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci				/* if no DIMMS on this row, continue */
80562306a36Sopenharmony_ci				if (!MTR_DIMMS_PRESENT(mtr))
80662306a36Sopenharmony_ci					continue;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci				rc = 0;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci			}
81162306a36Sopenharmony_ci		}
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return rc;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/**
81862306a36Sopenharmony_ci * decode_mir() - Decodes Memory Interleave Register (MIR) info
81962306a36Sopenharmony_ci * @mir_no: number of the MIR register to decode
82062306a36Sopenharmony_ci * @mir: array with the MIR data cached on the driver
82162306a36Sopenharmony_ci */
82262306a36Sopenharmony_cistatic void decode_mir(int mir_no, u16 mir[MAX_MIR])
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	if (mir[mir_no] & 3)
82562306a36Sopenharmony_ci		edac_dbg(2, "MIR%d: limit= 0x%x Branch(es) that participate: %s %s\n",
82662306a36Sopenharmony_ci			 mir_no,
82762306a36Sopenharmony_ci			 (mir[mir_no] >> 4) & 0xfff,
82862306a36Sopenharmony_ci			 (mir[mir_no] & 1) ? "B0" : "",
82962306a36Sopenharmony_ci			 (mir[mir_no] & 2) ? "B1" : "");
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/**
83362306a36Sopenharmony_ci * i7300_get_mc_regs() - Get the contents of the MC enumeration registers
83462306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
83562306a36Sopenharmony_ci *
83662306a36Sopenharmony_ci * Data read is cached internally for its usage when needed
83762306a36Sopenharmony_ci */
83862306a36Sopenharmony_cistatic int i7300_get_mc_regs(struct mem_ctl_info *mci)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	struct i7300_pvt *pvt;
84162306a36Sopenharmony_ci	u32 actual_tolm;
84262306a36Sopenharmony_ci	int i, rc;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	pvt = mci->pvt_info;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_0_fsb_ctlr, AMBASE,
84762306a36Sopenharmony_ci			(u32 *) &pvt->ambase);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	edac_dbg(2, "AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* Get the Branch Map regs */
85262306a36Sopenharmony_ci	pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, TOLM, &pvt->tolm);
85362306a36Sopenharmony_ci	pvt->tolm >>= 12;
85462306a36Sopenharmony_ci	edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n",
85562306a36Sopenharmony_ci		 pvt->tolm, pvt->tolm);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28));
85862306a36Sopenharmony_ci	edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
85962306a36Sopenharmony_ci		 actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* Get memory controller settings */
86262306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS,
86362306a36Sopenharmony_ci			     &pvt->mc_settings);
86462306a36Sopenharmony_ci	pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS_A,
86562306a36Sopenharmony_ci			     &pvt->mc_settings_a);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (IS_SINGLE_MODE(pvt->mc_settings_a))
86862306a36Sopenharmony_ci		edac_dbg(0, "Memory controller operating on single mode\n");
86962306a36Sopenharmony_ci	else
87062306a36Sopenharmony_ci		edac_dbg(0, "Memory controller operating on %smirrored mode\n",
87162306a36Sopenharmony_ci			 IS_MIRRORED(pvt->mc_settings) ? "" : "non-");
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	edac_dbg(0, "Error detection is %s\n",
87462306a36Sopenharmony_ci		 IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
87562306a36Sopenharmony_ci	edac_dbg(0, "Retry is %s\n",
87662306a36Sopenharmony_ci		 IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Get Memory Interleave Range registers */
87962306a36Sopenharmony_ci	pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0,
88062306a36Sopenharmony_ci			     &pvt->mir[0]);
88162306a36Sopenharmony_ci	pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR1,
88262306a36Sopenharmony_ci			     &pvt->mir[1]);
88362306a36Sopenharmony_ci	pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR2,
88462306a36Sopenharmony_ci			     &pvt->mir[2]);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/* Decode the MIR regs */
88762306a36Sopenharmony_ci	for (i = 0; i < MAX_MIR; i++)
88862306a36Sopenharmony_ci		decode_mir(i, pvt->mir);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	rc = i7300_init_csrows(mci);
89162306a36Sopenharmony_ci	if (rc < 0)
89262306a36Sopenharmony_ci		return rc;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* Go and determine the size of each DIMM and place in an
89562306a36Sopenharmony_ci	 * orderly matrix */
89662306a36Sopenharmony_ci	print_dimm_size(pvt);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return 0;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci/*************************************************
90262306a36Sopenharmony_ci * i7300 Functions related to device probe/release
90362306a36Sopenharmony_ci *************************************************/
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci/**
90662306a36Sopenharmony_ci * i7300_put_devices() - Release the PCI devices
90762306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
90862306a36Sopenharmony_ci */
90962306a36Sopenharmony_cistatic void i7300_put_devices(struct mem_ctl_info *mci)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct i7300_pvt *pvt;
91262306a36Sopenharmony_ci	int branch;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	pvt = mci->pvt_info;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Decrement usage count for devices */
91762306a36Sopenharmony_ci	for (branch = 0; branch < MAX_CH_PER_BRANCH; branch++)
91862306a36Sopenharmony_ci		pci_dev_put(pvt->pci_dev_2x_0_fbd_branch[branch]);
91962306a36Sopenharmony_ci	pci_dev_put(pvt->pci_dev_16_2_fsb_err_regs);
92062306a36Sopenharmony_ci	pci_dev_put(pvt->pci_dev_16_1_fsb_addr_map);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci/**
92462306a36Sopenharmony_ci * i7300_get_devices() - Find and perform 'get' operation on the MCH's
92562306a36Sopenharmony_ci *			 device/functions we want to reference for this driver
92662306a36Sopenharmony_ci * @mci: struct mem_ctl_info pointer
92762306a36Sopenharmony_ci *
92862306a36Sopenharmony_ci * Access and prepare the several devices for usage:
92962306a36Sopenharmony_ci * I7300 devices used by this driver:
93062306a36Sopenharmony_ci *    Device 16, functions 0,1 and 2:	PCI_DEVICE_ID_INTEL_I7300_MCH_ERR
93162306a36Sopenharmony_ci *    Device 21 function 0:		PCI_DEVICE_ID_INTEL_I7300_MCH_FB0
93262306a36Sopenharmony_ci *    Device 22 function 0:		PCI_DEVICE_ID_INTEL_I7300_MCH_FB1
93362306a36Sopenharmony_ci */
93462306a36Sopenharmony_cistatic int i7300_get_devices(struct mem_ctl_info *mci)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct i7300_pvt *pvt;
93762306a36Sopenharmony_ci	struct pci_dev *pdev;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	pvt = mci->pvt_info;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* Attempt to 'get' the MCH register we want */
94262306a36Sopenharmony_ci	pdev = NULL;
94362306a36Sopenharmony_ci	while ((pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
94462306a36Sopenharmony_ci				      PCI_DEVICE_ID_INTEL_I7300_MCH_ERR,
94562306a36Sopenharmony_ci				      pdev))) {
94662306a36Sopenharmony_ci		/* Store device 16 funcs 1 and 2 */
94762306a36Sopenharmony_ci		switch (PCI_FUNC(pdev->devfn)) {
94862306a36Sopenharmony_ci		case 1:
94962306a36Sopenharmony_ci			if (!pvt->pci_dev_16_1_fsb_addr_map)
95062306a36Sopenharmony_ci				pvt->pci_dev_16_1_fsb_addr_map =
95162306a36Sopenharmony_ci							pci_dev_get(pdev);
95262306a36Sopenharmony_ci			break;
95362306a36Sopenharmony_ci		case 2:
95462306a36Sopenharmony_ci			if (!pvt->pci_dev_16_2_fsb_err_regs)
95562306a36Sopenharmony_ci				pvt->pci_dev_16_2_fsb_err_regs =
95662306a36Sopenharmony_ci							pci_dev_get(pdev);
95762306a36Sopenharmony_ci			break;
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (!pvt->pci_dev_16_1_fsb_addr_map ||
96262306a36Sopenharmony_ci	    !pvt->pci_dev_16_2_fsb_err_regs) {
96362306a36Sopenharmony_ci		/* At least one device was not found */
96462306a36Sopenharmony_ci		i7300_printk(KERN_ERR,
96562306a36Sopenharmony_ci			"'system address,Process Bus' device not found:"
96662306a36Sopenharmony_ci			"vendor 0x%x device 0x%x ERR funcs (broken BIOS?)\n",
96762306a36Sopenharmony_ci			PCI_VENDOR_ID_INTEL,
96862306a36Sopenharmony_ci			PCI_DEVICE_ID_INTEL_I7300_MCH_ERR);
96962306a36Sopenharmony_ci		goto error;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
97362306a36Sopenharmony_ci		 pci_name(pvt->pci_dev_16_0_fsb_ctlr),
97462306a36Sopenharmony_ci		 pvt->pci_dev_16_0_fsb_ctlr->vendor,
97562306a36Sopenharmony_ci		 pvt->pci_dev_16_0_fsb_ctlr->device);
97662306a36Sopenharmony_ci	edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
97762306a36Sopenharmony_ci		 pci_name(pvt->pci_dev_16_1_fsb_addr_map),
97862306a36Sopenharmony_ci		 pvt->pci_dev_16_1_fsb_addr_map->vendor,
97962306a36Sopenharmony_ci		 pvt->pci_dev_16_1_fsb_addr_map->device);
98062306a36Sopenharmony_ci	edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
98162306a36Sopenharmony_ci		 pci_name(pvt->pci_dev_16_2_fsb_err_regs),
98262306a36Sopenharmony_ci		 pvt->pci_dev_16_2_fsb_err_regs->vendor,
98362306a36Sopenharmony_ci		 pvt->pci_dev_16_2_fsb_err_regs->device);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	pvt->pci_dev_2x_0_fbd_branch[0] = pci_get_device(PCI_VENDOR_ID_INTEL,
98662306a36Sopenharmony_ci					    PCI_DEVICE_ID_INTEL_I7300_MCH_FB0,
98762306a36Sopenharmony_ci					    NULL);
98862306a36Sopenharmony_ci	if (!pvt->pci_dev_2x_0_fbd_branch[0]) {
98962306a36Sopenharmony_ci		i7300_printk(KERN_ERR,
99062306a36Sopenharmony_ci			"MC: 'BRANCH 0' device not found:"
99162306a36Sopenharmony_ci			"vendor 0x%x device 0x%x Func 0 (broken BIOS?)\n",
99262306a36Sopenharmony_ci			PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_FB0);
99362306a36Sopenharmony_ci		goto error;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	pvt->pci_dev_2x_0_fbd_branch[1] = pci_get_device(PCI_VENDOR_ID_INTEL,
99762306a36Sopenharmony_ci					    PCI_DEVICE_ID_INTEL_I7300_MCH_FB1,
99862306a36Sopenharmony_ci					    NULL);
99962306a36Sopenharmony_ci	if (!pvt->pci_dev_2x_0_fbd_branch[1]) {
100062306a36Sopenharmony_ci		i7300_printk(KERN_ERR,
100162306a36Sopenharmony_ci			"MC: 'BRANCH 1' device not found:"
100262306a36Sopenharmony_ci			"vendor 0x%x device 0x%x Func 0 "
100362306a36Sopenharmony_ci			"(broken BIOS?)\n",
100462306a36Sopenharmony_ci			PCI_VENDOR_ID_INTEL,
100562306a36Sopenharmony_ci			PCI_DEVICE_ID_INTEL_I7300_MCH_FB1);
100662306a36Sopenharmony_ci		goto error;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return 0;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cierror:
101262306a36Sopenharmony_ci	i7300_put_devices(mci);
101362306a36Sopenharmony_ci	return -ENODEV;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci/**
101762306a36Sopenharmony_ci * i7300_init_one() - Probe for one instance of the device
101862306a36Sopenharmony_ci * @pdev: struct pci_dev pointer
101962306a36Sopenharmony_ci * @id: struct pci_device_id pointer - currently unused
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_cistatic int i7300_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct mem_ctl_info *mci;
102462306a36Sopenharmony_ci	struct edac_mc_layer layers[3];
102562306a36Sopenharmony_ci	struct i7300_pvt *pvt;
102662306a36Sopenharmony_ci	int rc;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* wake up device */
102962306a36Sopenharmony_ci	rc = pci_enable_device(pdev);
103062306a36Sopenharmony_ci	if (rc == -EIO)
103162306a36Sopenharmony_ci		return rc;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
103462306a36Sopenharmony_ci		 pdev->bus->number,
103562306a36Sopenharmony_ci		 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	/* We only are looking for func 0 of the set */
103862306a36Sopenharmony_ci	if (PCI_FUNC(pdev->devfn) != 0)
103962306a36Sopenharmony_ci		return -ENODEV;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* allocate a new MC control structure */
104262306a36Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_BRANCH;
104362306a36Sopenharmony_ci	layers[0].size = MAX_BRANCHES;
104462306a36Sopenharmony_ci	layers[0].is_virt_csrow = false;
104562306a36Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_CHANNEL;
104662306a36Sopenharmony_ci	layers[1].size = MAX_CH_PER_BRANCH;
104762306a36Sopenharmony_ci	layers[1].is_virt_csrow = true;
104862306a36Sopenharmony_ci	layers[2].type = EDAC_MC_LAYER_SLOT;
104962306a36Sopenharmony_ci	layers[2].size = MAX_SLOTS;
105062306a36Sopenharmony_ci	layers[2].is_virt_csrow = true;
105162306a36Sopenharmony_ci	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
105262306a36Sopenharmony_ci	if (mci == NULL)
105362306a36Sopenharmony_ci		return -ENOMEM;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	edac_dbg(0, "MC: mci = %p\n", mci);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	mci->pdev = &pdev->dev;	/* record ptr  to the generic device */
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	pvt = mci->pvt_info;
106062306a36Sopenharmony_ci	pvt->pci_dev_16_0_fsb_ctlr = pdev;	/* Record this device in our private */
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	pvt->tmp_prt_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
106362306a36Sopenharmony_ci	if (!pvt->tmp_prt_buffer) {
106462306a36Sopenharmony_ci		edac_mc_free(mci);
106562306a36Sopenharmony_ci		return -ENOMEM;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* 'get' the pci devices we want to reserve for our use */
106962306a36Sopenharmony_ci	if (i7300_get_devices(mci))
107062306a36Sopenharmony_ci		goto fail0;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	mci->mc_idx = 0;
107362306a36Sopenharmony_ci	mci->mtype_cap = MEM_FLAG_FB_DDR2;
107462306a36Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_NONE;
107562306a36Sopenharmony_ci	mci->edac_cap = EDAC_FLAG_NONE;
107662306a36Sopenharmony_ci	mci->mod_name = "i7300_edac.c";
107762306a36Sopenharmony_ci	mci->ctl_name = i7300_devs[0].ctl_name;
107862306a36Sopenharmony_ci	mci->dev_name = pci_name(pdev);
107962306a36Sopenharmony_ci	mci->ctl_page_to_phys = NULL;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* Set the function pointer to an actual operation function */
108262306a36Sopenharmony_ci	mci->edac_check = i7300_check_error;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* initialize the MC control structure 'csrows' table
108562306a36Sopenharmony_ci	 * with the mapping and control information */
108662306a36Sopenharmony_ci	if (i7300_get_mc_regs(mci)) {
108762306a36Sopenharmony_ci		edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i7300_init_csrows() returned nonzero value\n");
108862306a36Sopenharmony_ci		mci->edac_cap = EDAC_FLAG_NONE;	/* no csrows found */
108962306a36Sopenharmony_ci	} else {
109062306a36Sopenharmony_ci		edac_dbg(1, "MC: Enable error reporting now\n");
109162306a36Sopenharmony_ci		i7300_enable_error_reporting(mci);
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	/* add this new MC control structure to EDAC's list of MCs */
109562306a36Sopenharmony_ci	if (edac_mc_add_mc(mci)) {
109662306a36Sopenharmony_ci		edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
109762306a36Sopenharmony_ci		/* FIXME: perhaps some code should go here that disables error
109862306a36Sopenharmony_ci		 * reporting if we just enabled it
109962306a36Sopenharmony_ci		 */
110062306a36Sopenharmony_ci		goto fail1;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	i7300_clear_error(mci);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	/* allocating generic PCI control info */
110662306a36Sopenharmony_ci	i7300_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
110762306a36Sopenharmony_ci	if (!i7300_pci) {
110862306a36Sopenharmony_ci		printk(KERN_WARNING
110962306a36Sopenharmony_ci			"%s(): Unable to create PCI control\n",
111062306a36Sopenharmony_ci			__func__);
111162306a36Sopenharmony_ci		printk(KERN_WARNING
111262306a36Sopenharmony_ci			"%s(): PCI error report via EDAC not setup\n",
111362306a36Sopenharmony_ci			__func__);
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return 0;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	/* Error exit unwinding stack */
111962306a36Sopenharmony_cifail1:
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	i7300_put_devices(mci);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cifail0:
112462306a36Sopenharmony_ci	kfree(pvt->tmp_prt_buffer);
112562306a36Sopenharmony_ci	edac_mc_free(mci);
112662306a36Sopenharmony_ci	return -ENODEV;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci/**
113062306a36Sopenharmony_ci * i7300_remove_one() - Remove the driver
113162306a36Sopenharmony_ci * @pdev: struct pci_dev pointer
113262306a36Sopenharmony_ci */
113362306a36Sopenharmony_cistatic void i7300_remove_one(struct pci_dev *pdev)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct mem_ctl_info *mci;
113662306a36Sopenharmony_ci	char *tmp;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	edac_dbg(0, "\n");
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	if (i7300_pci)
114162306a36Sopenharmony_ci		edac_pci_release_generic_ctl(i7300_pci);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	mci = edac_mc_del_mc(&pdev->dev);
114462306a36Sopenharmony_ci	if (!mci)
114562306a36Sopenharmony_ci		return;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	tmp = ((struct i7300_pvt *)mci->pvt_info)->tmp_prt_buffer;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	/* retrieve references to resources, and free those resources */
115062306a36Sopenharmony_ci	i7300_put_devices(mci);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	kfree(tmp);
115362306a36Sopenharmony_ci	edac_mc_free(mci);
115462306a36Sopenharmony_ci}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci/*
115762306a36Sopenharmony_ci * pci_device_id: table for which devices we are looking for
115862306a36Sopenharmony_ci *
115962306a36Sopenharmony_ci * Has only 8086:360c PCI ID
116062306a36Sopenharmony_ci */
116162306a36Sopenharmony_cistatic const struct pci_device_id i7300_pci_tbl[] = {
116262306a36Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_ERR)},
116362306a36Sopenharmony_ci	{0,}			/* 0 terminated list. */
116462306a36Sopenharmony_ci};
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i7300_pci_tbl);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/*
116962306a36Sopenharmony_ci * i7300_driver: pci_driver structure for this module
117062306a36Sopenharmony_ci */
117162306a36Sopenharmony_cistatic struct pci_driver i7300_driver = {
117262306a36Sopenharmony_ci	.name = "i7300_edac",
117362306a36Sopenharmony_ci	.probe = i7300_init_one,
117462306a36Sopenharmony_ci	.remove = i7300_remove_one,
117562306a36Sopenharmony_ci	.id_table = i7300_pci_tbl,
117662306a36Sopenharmony_ci};
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci/**
117962306a36Sopenharmony_ci * i7300_init() - Registers the driver
118062306a36Sopenharmony_ci */
118162306a36Sopenharmony_cistatic int __init i7300_init(void)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	int pci_rc;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	edac_dbg(2, "\n");
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
118862306a36Sopenharmony_ci	opstate_init();
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	pci_rc = pci_register_driver(&i7300_driver);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	return (pci_rc < 0) ? pci_rc : 0;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci/**
119662306a36Sopenharmony_ci * i7300_exit() - Unregisters the driver
119762306a36Sopenharmony_ci */
119862306a36Sopenharmony_cistatic void __exit i7300_exit(void)
119962306a36Sopenharmony_ci{
120062306a36Sopenharmony_ci	edac_dbg(2, "\n");
120162306a36Sopenharmony_ci	pci_unregister_driver(&i7300_driver);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cimodule_init(i7300_init);
120562306a36Sopenharmony_cimodule_exit(i7300_exit);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
120862306a36Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab");
120962306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat Inc. (https://www.redhat.com)");
121062306a36Sopenharmony_ciMODULE_DESCRIPTION("MC Driver for Intel I7300 memory controllers - "
121162306a36Sopenharmony_ci		   I7300_REVISION);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cimodule_param(edac_op_state, int, 0444);
121462306a36Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
1215