18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Intel e7xxx Memory Controller kernel module
38c2ecf20Sopenharmony_ci * (C) 2003 Linux Networx (http://lnxi.com)
48c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the
58c2ecf20Sopenharmony_ci * GNU General Public License.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * See "enum e7xxx_chips" below for supported chipsets
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Written by Thayne Harbaugh
108c2ecf20Sopenharmony_ci * Based on work by Dan Hollis <goemon at anime dot net> and others.
118c2ecf20Sopenharmony_ci *	http://www.anime.net/~goemon/linux-ecc/
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Datasheet:
148c2ecf20Sopenharmony_ci *	http://www.intel.com/content/www/us/en/chipsets/e7501-chipset-memory-controller-hub-datasheet.html
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Contributors:
178c2ecf20Sopenharmony_ci *	Eric Biederman (Linux Networx)
188c2ecf20Sopenharmony_ci *	Tom Zimmerman (Linux Networx)
198c2ecf20Sopenharmony_ci *	Jim Garlick (Lawrence Livermore National Labs)
208c2ecf20Sopenharmony_ci *	Dave Peterson (Lawrence Livermore National Labs)
218c2ecf20Sopenharmony_ci *	That One Guy (Some other place)
228c2ecf20Sopenharmony_ci *	Wang Zhenyu (intel.com)
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * $Id: edac_e7xxx.c,v 1.5.2.9 2005/10/05 00:43:44 dsp_llnl Exp $
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/init.h>
308c2ecf20Sopenharmony_ci#include <linux/pci.h>
318c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
328c2ecf20Sopenharmony_ci#include <linux/edac.h>
338c2ecf20Sopenharmony_ci#include "edac_module.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define	EDAC_MOD_STR	"e7xxx_edac"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define e7xxx_printk(level, fmt, arg...) \
388c2ecf20Sopenharmony_ci	edac_printk(level, "e7xxx", fmt, ##arg)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define e7xxx_mc_printk(mci, level, fmt, arg...) \
418c2ecf20Sopenharmony_ci	edac_mc_chipset_printk(mci, level, "e7xxx", fmt, ##arg)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7205_0
448c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7205_0	0x255d
458c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7205_0 */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7205_1_ERR
488c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7205_1_ERR	0x2551
498c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7205_1_ERR */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7500_0
528c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7500_0	0x2540
538c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7500_0 */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7500_1_ERR
568c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7500_1_ERR	0x2541
578c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7500_1_ERR */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7501_0
608c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7501_0	0x254c
618c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7501_0 */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7501_1_ERR
648c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7501_1_ERR	0x2541
658c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7501_1_ERR */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7505_0
688c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7505_0	0x2550
698c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7505_0 */
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_7505_1_ERR
728c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_7505_1_ERR	0x2551
738c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_7505_1_ERR */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define E7XXX_NR_CSROWS		8	/* number of csrows */
768c2ecf20Sopenharmony_ci#define E7XXX_NR_DIMMS		8	/* 2 channels, 4 dimms/channel */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* E7XXX register addresses - device 0 function 0 */
798c2ecf20Sopenharmony_ci#define E7XXX_DRB		0x60	/* DRAM row boundary register (8b) */
808c2ecf20Sopenharmony_ci#define E7XXX_DRA		0x70	/* DRAM row attribute register (8b) */
818c2ecf20Sopenharmony_ci					/*
828c2ecf20Sopenharmony_ci					 * 31   Device width row 7 0=x8 1=x4
838c2ecf20Sopenharmony_ci					 * 27   Device width row 6
848c2ecf20Sopenharmony_ci					 * 23   Device width row 5
858c2ecf20Sopenharmony_ci					 * 19   Device width row 4
868c2ecf20Sopenharmony_ci					 * 15   Device width row 3
878c2ecf20Sopenharmony_ci					 * 11   Device width row 2
888c2ecf20Sopenharmony_ci					 *  7   Device width row 1
898c2ecf20Sopenharmony_ci					 *  3   Device width row 0
908c2ecf20Sopenharmony_ci					 */
918c2ecf20Sopenharmony_ci#define E7XXX_DRC		0x7C	/* DRAM controller mode reg (32b) */
928c2ecf20Sopenharmony_ci					/*
938c2ecf20Sopenharmony_ci					 * 22    Number channels 0=1,1=2
948c2ecf20Sopenharmony_ci					 * 19:18 DRB Granularity 32/64MB
958c2ecf20Sopenharmony_ci					 */
968c2ecf20Sopenharmony_ci#define E7XXX_TOLM		0xC4	/* DRAM top of low memory reg (16b) */
978c2ecf20Sopenharmony_ci#define E7XXX_REMAPBASE		0xC6	/* DRAM remap base address reg (16b) */
988c2ecf20Sopenharmony_ci#define E7XXX_REMAPLIMIT	0xC8	/* DRAM remap limit address reg (16b) */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* E7XXX register addresses - device 0 function 1 */
1018c2ecf20Sopenharmony_ci#define E7XXX_DRAM_FERR		0x80	/* DRAM first error register (8b) */
1028c2ecf20Sopenharmony_ci#define E7XXX_DRAM_NERR		0x82	/* DRAM next error register (8b) */
1038c2ecf20Sopenharmony_ci#define E7XXX_DRAM_CELOG_ADD	0xA0	/* DRAM first correctable memory */
1048c2ecf20Sopenharmony_ci					/*     error address register (32b) */
1058c2ecf20Sopenharmony_ci					/*
1068c2ecf20Sopenharmony_ci					 * 31:28 Reserved
1078c2ecf20Sopenharmony_ci					 * 27:6  CE address (4k block 33:12)
1088c2ecf20Sopenharmony_ci					 *  5:0  Reserved
1098c2ecf20Sopenharmony_ci					 */
1108c2ecf20Sopenharmony_ci#define E7XXX_DRAM_UELOG_ADD	0xB0	/* DRAM first uncorrectable memory */
1118c2ecf20Sopenharmony_ci					/*     error address register (32b) */
1128c2ecf20Sopenharmony_ci					/*
1138c2ecf20Sopenharmony_ci					 * 31:28 Reserved
1148c2ecf20Sopenharmony_ci					 * 27:6  CE address (4k block 33:12)
1158c2ecf20Sopenharmony_ci					 *  5:0  Reserved
1168c2ecf20Sopenharmony_ci					 */
1178c2ecf20Sopenharmony_ci#define E7XXX_DRAM_CELOG_SYNDROME 0xD0	/* DRAM first correctable memory */
1188c2ecf20Sopenharmony_ci					/*     error syndrome register (16b) */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cienum e7xxx_chips {
1218c2ecf20Sopenharmony_ci	E7500 = 0,
1228c2ecf20Sopenharmony_ci	E7501,
1238c2ecf20Sopenharmony_ci	E7505,
1248c2ecf20Sopenharmony_ci	E7205,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistruct e7xxx_pvt {
1288c2ecf20Sopenharmony_ci	struct pci_dev *bridge_ck;
1298c2ecf20Sopenharmony_ci	u32 tolm;
1308c2ecf20Sopenharmony_ci	u32 remapbase;
1318c2ecf20Sopenharmony_ci	u32 remaplimit;
1328c2ecf20Sopenharmony_ci	const struct e7xxx_dev_info *dev_info;
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistruct e7xxx_dev_info {
1368c2ecf20Sopenharmony_ci	u16 err_dev;
1378c2ecf20Sopenharmony_ci	const char *ctl_name;
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct e7xxx_error_info {
1418c2ecf20Sopenharmony_ci	u8 dram_ferr;
1428c2ecf20Sopenharmony_ci	u8 dram_nerr;
1438c2ecf20Sopenharmony_ci	u32 dram_celog_add;
1448c2ecf20Sopenharmony_ci	u16 dram_celog_syndrome;
1458c2ecf20Sopenharmony_ci	u32 dram_uelog_add;
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic struct edac_pci_ctl_info *e7xxx_pci;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic const struct e7xxx_dev_info e7xxx_devs[] = {
1518c2ecf20Sopenharmony_ci	[E7500] = {
1528c2ecf20Sopenharmony_ci		.err_dev = PCI_DEVICE_ID_INTEL_7500_1_ERR,
1538c2ecf20Sopenharmony_ci		.ctl_name = "E7500"},
1548c2ecf20Sopenharmony_ci	[E7501] = {
1558c2ecf20Sopenharmony_ci		.err_dev = PCI_DEVICE_ID_INTEL_7501_1_ERR,
1568c2ecf20Sopenharmony_ci		.ctl_name = "E7501"},
1578c2ecf20Sopenharmony_ci	[E7505] = {
1588c2ecf20Sopenharmony_ci		.err_dev = PCI_DEVICE_ID_INTEL_7505_1_ERR,
1598c2ecf20Sopenharmony_ci		.ctl_name = "E7505"},
1608c2ecf20Sopenharmony_ci	[E7205] = {
1618c2ecf20Sopenharmony_ci		.err_dev = PCI_DEVICE_ID_INTEL_7205_1_ERR,
1628c2ecf20Sopenharmony_ci		.ctl_name = "E7205"},
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* FIXME - is this valid for both SECDED and S4ECD4ED? */
1668c2ecf20Sopenharmony_cistatic inline int e7xxx_find_channel(u16 syndrome)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if ((syndrome & 0xff00) == 0)
1718c2ecf20Sopenharmony_ci		return 0;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if ((syndrome & 0x00ff) == 0)
1748c2ecf20Sopenharmony_ci		return 1;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if ((syndrome & 0xf000) == 0 || (syndrome & 0x0f00) == 0)
1778c2ecf20Sopenharmony_ci		return 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return 1;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic unsigned long ctl_page_to_phys(struct mem_ctl_info *mci,
1838c2ecf20Sopenharmony_ci				unsigned long page)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	u32 remap;
1868c2ecf20Sopenharmony_ci	struct e7xxx_pvt *pvt = (struct e7xxx_pvt *)mci->pvt_info;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if ((page < pvt->tolm) ||
1918c2ecf20Sopenharmony_ci		((page >= 0x100000) && (page < pvt->remapbase)))
1928c2ecf20Sopenharmony_ci		return page;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	remap = (page - pvt->tolm) + pvt->remapbase;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (remap < pvt->remaplimit)
1978c2ecf20Sopenharmony_ci		return remap;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	e7xxx_printk(KERN_ERR, "Invalid page %lx - out of range\n", page);
2008c2ecf20Sopenharmony_ci	return pvt->tolm - 1;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	u32 error_1b, page;
2068c2ecf20Sopenharmony_ci	u16 syndrome;
2078c2ecf20Sopenharmony_ci	int row;
2088c2ecf20Sopenharmony_ci	int channel;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
2118c2ecf20Sopenharmony_ci	/* read the error address */
2128c2ecf20Sopenharmony_ci	error_1b = info->dram_celog_add;
2138c2ecf20Sopenharmony_ci	/* FIXME - should use PAGE_SHIFT */
2148c2ecf20Sopenharmony_ci	page = error_1b >> 6;	/* convert the address to 4k page */
2158c2ecf20Sopenharmony_ci	/* read the syndrome */
2168c2ecf20Sopenharmony_ci	syndrome = info->dram_celog_syndrome;
2178c2ecf20Sopenharmony_ci	/* FIXME - check for -1 */
2188c2ecf20Sopenharmony_ci	row = edac_mc_find_csrow_by_page(mci, page);
2198c2ecf20Sopenharmony_ci	/* convert syndrome to channel */
2208c2ecf20Sopenharmony_ci	channel = e7xxx_find_channel(syndrome);
2218c2ecf20Sopenharmony_ci	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, 0, syndrome,
2228c2ecf20Sopenharmony_ci			     row, channel, -1, "e7xxx CE", "");
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void process_ce_no_info(struct mem_ctl_info *mci)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
2288c2ecf20Sopenharmony_ci	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
2298c2ecf20Sopenharmony_ci			     "e7xxx CE log register overflow", "");
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	u32 error_2b, block_page;
2358c2ecf20Sopenharmony_ci	int row;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
2388c2ecf20Sopenharmony_ci	/* read the error address */
2398c2ecf20Sopenharmony_ci	error_2b = info->dram_uelog_add;
2408c2ecf20Sopenharmony_ci	/* FIXME - should use PAGE_SHIFT */
2418c2ecf20Sopenharmony_ci	block_page = error_2b >> 6;	/* convert to 4k address */
2428c2ecf20Sopenharmony_ci	row = edac_mc_find_csrow_by_page(mci, block_page);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, block_page, 0, 0,
2458c2ecf20Sopenharmony_ci			     row, -1, -1, "e7xxx UE", "");
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void process_ue_no_info(struct mem_ctl_info *mci)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
2538c2ecf20Sopenharmony_ci			     "e7xxx UE log register overflow", "");
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic void e7xxx_get_error_info(struct mem_ctl_info *mci,
2578c2ecf20Sopenharmony_ci				 struct e7xxx_error_info *info)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct e7xxx_pvt *pvt;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	pvt = (struct e7xxx_pvt *)mci->pvt_info;
2628c2ecf20Sopenharmony_ci	pci_read_config_byte(pvt->bridge_ck, E7XXX_DRAM_FERR, &info->dram_ferr);
2638c2ecf20Sopenharmony_ci	pci_read_config_byte(pvt->bridge_ck, E7XXX_DRAM_NERR, &info->dram_nerr);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if ((info->dram_ferr & 1) || (info->dram_nerr & 1)) {
2668c2ecf20Sopenharmony_ci		pci_read_config_dword(pvt->bridge_ck, E7XXX_DRAM_CELOG_ADD,
2678c2ecf20Sopenharmony_ci				&info->dram_celog_add);
2688c2ecf20Sopenharmony_ci		pci_read_config_word(pvt->bridge_ck,
2698c2ecf20Sopenharmony_ci				E7XXX_DRAM_CELOG_SYNDROME,
2708c2ecf20Sopenharmony_ci				&info->dram_celog_syndrome);
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if ((info->dram_ferr & 2) || (info->dram_nerr & 2))
2748c2ecf20Sopenharmony_ci		pci_read_config_dword(pvt->bridge_ck, E7XXX_DRAM_UELOG_ADD,
2758c2ecf20Sopenharmony_ci				&info->dram_uelog_add);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (info->dram_ferr & 3)
2788c2ecf20Sopenharmony_ci		pci_write_bits8(pvt->bridge_ck, E7XXX_DRAM_FERR, 0x03, 0x03);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (info->dram_nerr & 3)
2818c2ecf20Sopenharmony_ci		pci_write_bits8(pvt->bridge_ck, E7XXX_DRAM_NERR, 0x03, 0x03);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int e7xxx_process_error_info(struct mem_ctl_info *mci,
2858c2ecf20Sopenharmony_ci				struct e7xxx_error_info *info,
2868c2ecf20Sopenharmony_ci				int handle_errors)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	int error_found;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	error_found = 0;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* decode and report errors */
2938c2ecf20Sopenharmony_ci	if (info->dram_ferr & 1) {	/* check first error correctable */
2948c2ecf20Sopenharmony_ci		error_found = 1;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		if (handle_errors)
2978c2ecf20Sopenharmony_ci			process_ce(mci, info);
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (info->dram_ferr & 2) {	/* check first error uncorrectable */
3018c2ecf20Sopenharmony_ci		error_found = 1;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		if (handle_errors)
3048c2ecf20Sopenharmony_ci			process_ue(mci, info);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (info->dram_nerr & 1) {	/* check next error correctable */
3088c2ecf20Sopenharmony_ci		error_found = 1;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		if (handle_errors) {
3118c2ecf20Sopenharmony_ci			if (info->dram_ferr & 1)
3128c2ecf20Sopenharmony_ci				process_ce_no_info(mci);
3138c2ecf20Sopenharmony_ci			else
3148c2ecf20Sopenharmony_ci				process_ce(mci, info);
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (info->dram_nerr & 2) {	/* check next error uncorrectable */
3198c2ecf20Sopenharmony_ci		error_found = 1;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		if (handle_errors) {
3228c2ecf20Sopenharmony_ci			if (info->dram_ferr & 2)
3238c2ecf20Sopenharmony_ci				process_ue_no_info(mci);
3248c2ecf20Sopenharmony_ci			else
3258c2ecf20Sopenharmony_ci				process_ue(mci, info);
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return error_found;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void e7xxx_check(struct mem_ctl_info *mci)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct e7xxx_error_info info;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
3378c2ecf20Sopenharmony_ci	e7xxx_get_error_info(mci, &info);
3388c2ecf20Sopenharmony_ci	e7xxx_process_error_info(mci, &info, 1);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/* Return 1 if dual channel mode is active.  Else return 0. */
3428c2ecf20Sopenharmony_cistatic inline int dual_channel_active(u32 drc, int dev_idx)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	return (dev_idx == E7501) ? ((drc >> 22) & 0x1) : 1;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/* Return DRB granularity (0=32mb, 1=64mb). */
3488c2ecf20Sopenharmony_cistatic inline int drb_granularity(u32 drc, int dev_idx)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	/* only e7501 can be single channel */
3518c2ecf20Sopenharmony_ci	return (dev_idx == E7501) ? ((drc >> 18) & 0x3) : 1;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
3558c2ecf20Sopenharmony_ci			int dev_idx, u32 drc)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	unsigned long last_cumul_size;
3588c2ecf20Sopenharmony_ci	int index, j;
3598c2ecf20Sopenharmony_ci	u8 value;
3608c2ecf20Sopenharmony_ci	u32 dra, cumul_size, nr_pages;
3618c2ecf20Sopenharmony_ci	int drc_chan, drc_drbg, drc_ddim, mem_dev;
3628c2ecf20Sopenharmony_ci	struct csrow_info *csrow;
3638c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
3648c2ecf20Sopenharmony_ci	enum edac_type edac_mode;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev, E7XXX_DRA, &dra);
3678c2ecf20Sopenharmony_ci	drc_chan = dual_channel_active(drc, dev_idx);
3688c2ecf20Sopenharmony_ci	drc_drbg = drb_granularity(drc, dev_idx);
3698c2ecf20Sopenharmony_ci	drc_ddim = (drc >> 20) & 0x3;
3708c2ecf20Sopenharmony_ci	last_cumul_size = 0;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* The dram row boundary (DRB) reg values are boundary address
3738c2ecf20Sopenharmony_ci	 * for each DRAM row with a granularity of 32 or 64MB (single/dual
3748c2ecf20Sopenharmony_ci	 * channel operation).  DRB regs are cumulative; therefore DRB7 will
3758c2ecf20Sopenharmony_ci	 * contain the total memory contained in all eight rows.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	for (index = 0; index < mci->nr_csrows; index++) {
3788c2ecf20Sopenharmony_ci		/* mem_dev 0=x8, 1=x4 */
3798c2ecf20Sopenharmony_ci		mem_dev = (dra >> (index * 4 + 3)) & 0x1;
3808c2ecf20Sopenharmony_ci		csrow = mci->csrows[index];
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		pci_read_config_byte(pdev, E7XXX_DRB + index, &value);
3838c2ecf20Sopenharmony_ci		/* convert a 64 or 32 MiB DRB to a page size. */
3848c2ecf20Sopenharmony_ci		cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
3858c2ecf20Sopenharmony_ci		edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
3868c2ecf20Sopenharmony_ci		if (cumul_size == last_cumul_size)
3878c2ecf20Sopenharmony_ci			continue;	/* not populated */
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		csrow->first_page = last_cumul_size;
3908c2ecf20Sopenharmony_ci		csrow->last_page = cumul_size - 1;
3918c2ecf20Sopenharmony_ci		nr_pages = cumul_size - last_cumul_size;
3928c2ecf20Sopenharmony_ci		last_cumul_size = cumul_size;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		/*
3958c2ecf20Sopenharmony_ci		* if single channel or x8 devices then SECDED
3968c2ecf20Sopenharmony_ci		* if dual channel and x4 then S4ECD4ED
3978c2ecf20Sopenharmony_ci		*/
3988c2ecf20Sopenharmony_ci		if (drc_ddim) {
3998c2ecf20Sopenharmony_ci			if (drc_chan && mem_dev) {
4008c2ecf20Sopenharmony_ci				edac_mode = EDAC_S4ECD4ED;
4018c2ecf20Sopenharmony_ci				mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
4028c2ecf20Sopenharmony_ci			} else {
4038c2ecf20Sopenharmony_ci				edac_mode = EDAC_SECDED;
4048c2ecf20Sopenharmony_ci				mci->edac_cap |= EDAC_FLAG_SECDED;
4058c2ecf20Sopenharmony_ci			}
4068c2ecf20Sopenharmony_ci		} else
4078c2ecf20Sopenharmony_ci			edac_mode = EDAC_NONE;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		for (j = 0; j < drc_chan + 1; j++) {
4108c2ecf20Sopenharmony_ci			dimm = csrow->channels[j]->dimm;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci			dimm->nr_pages = nr_pages / (drc_chan + 1);
4138c2ecf20Sopenharmony_ci			dimm->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
4148c2ecf20Sopenharmony_ci			dimm->mtype = MEM_RDDR;	/* only one type supported */
4158c2ecf20Sopenharmony_ci			dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
4168c2ecf20Sopenharmony_ci			dimm->edac_mode = edac_mode;
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	u16 pci_data;
4248c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = NULL;
4258c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[2];
4268c2ecf20Sopenharmony_ci	struct e7xxx_pvt *pvt = NULL;
4278c2ecf20Sopenharmony_ci	u32 drc;
4288c2ecf20Sopenharmony_ci	int drc_chan;
4298c2ecf20Sopenharmony_ci	struct e7xxx_error_info discard;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	edac_dbg(0, "mci\n");
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev, E7XXX_DRC, &drc);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	drc_chan = dual_channel_active(drc, dev_idx);
4368c2ecf20Sopenharmony_ci	/*
4378c2ecf20Sopenharmony_ci	 * According with the datasheet, this device has a maximum of
4388c2ecf20Sopenharmony_ci	 * 4 DIMMS per channel, either single-rank or dual-rank. So, the
4398c2ecf20Sopenharmony_ci	 * total amount of dimms is 8 (E7XXX_NR_DIMMS).
4408c2ecf20Sopenharmony_ci	 * That means that the DIMM is mapped as CSROWs, and the channel
4418c2ecf20Sopenharmony_ci	 * will map the rank. So, an error to either channel should be
4428c2ecf20Sopenharmony_ci	 * attributed to the same dimm.
4438c2ecf20Sopenharmony_ci	 */
4448c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
4458c2ecf20Sopenharmony_ci	layers[0].size = E7XXX_NR_CSROWS;
4468c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = true;
4478c2ecf20Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_CHANNEL;
4488c2ecf20Sopenharmony_ci	layers[1].size = drc_chan + 1;
4498c2ecf20Sopenharmony_ci	layers[1].is_virt_csrow = false;
4508c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
4518c2ecf20Sopenharmony_ci	if (mci == NULL)
4528c2ecf20Sopenharmony_ci		return -ENOMEM;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	edac_dbg(3, "init mci\n");
4558c2ecf20Sopenharmony_ci	mci->mtype_cap = MEM_FLAG_RDDR;
4568c2ecf20Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED |
4578c2ecf20Sopenharmony_ci		EDAC_FLAG_S4ECD4ED;
4588c2ecf20Sopenharmony_ci	/* FIXME - what if different memory types are in different csrows? */
4598c2ecf20Sopenharmony_ci	mci->mod_name = EDAC_MOD_STR;
4608c2ecf20Sopenharmony_ci	mci->pdev = &pdev->dev;
4618c2ecf20Sopenharmony_ci	edac_dbg(3, "init pvt\n");
4628c2ecf20Sopenharmony_ci	pvt = (struct e7xxx_pvt *)mci->pvt_info;
4638c2ecf20Sopenharmony_ci	pvt->dev_info = &e7xxx_devs[dev_idx];
4648c2ecf20Sopenharmony_ci	pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
4658c2ecf20Sopenharmony_ci					pvt->dev_info->err_dev, pvt->bridge_ck);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (!pvt->bridge_ck) {
4688c2ecf20Sopenharmony_ci		e7xxx_printk(KERN_ERR, "error reporting device not found:"
4698c2ecf20Sopenharmony_ci			"vendor %x device 0x%x (broken BIOS?)\n",
4708c2ecf20Sopenharmony_ci			PCI_VENDOR_ID_INTEL, e7xxx_devs[dev_idx].err_dev);
4718c2ecf20Sopenharmony_ci		goto fail0;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	edac_dbg(3, "more mci init\n");
4758c2ecf20Sopenharmony_ci	mci->ctl_name = pvt->dev_info->ctl_name;
4768c2ecf20Sopenharmony_ci	mci->dev_name = pci_name(pdev);
4778c2ecf20Sopenharmony_ci	mci->edac_check = e7xxx_check;
4788c2ecf20Sopenharmony_ci	mci->ctl_page_to_phys = ctl_page_to_phys;
4798c2ecf20Sopenharmony_ci	e7xxx_init_csrows(mci, pdev, dev_idx, drc);
4808c2ecf20Sopenharmony_ci	mci->edac_cap |= EDAC_FLAG_NONE;
4818c2ecf20Sopenharmony_ci	edac_dbg(3, "tolm, remapbase, remaplimit\n");
4828c2ecf20Sopenharmony_ci	/* load the top of low memory, remap base, and remap limit vars */
4838c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, E7XXX_TOLM, &pci_data);
4848c2ecf20Sopenharmony_ci	pvt->tolm = ((u32) pci_data) << 4;
4858c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, E7XXX_REMAPBASE, &pci_data);
4868c2ecf20Sopenharmony_ci	pvt->remapbase = ((u32) pci_data) << 14;
4878c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, E7XXX_REMAPLIMIT, &pci_data);
4888c2ecf20Sopenharmony_ci	pvt->remaplimit = ((u32) pci_data) << 14;
4898c2ecf20Sopenharmony_ci	e7xxx_printk(KERN_INFO,
4908c2ecf20Sopenharmony_ci		"tolm = %x, remapbase = %x, remaplimit = %x\n", pvt->tolm,
4918c2ecf20Sopenharmony_ci		pvt->remapbase, pvt->remaplimit);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* clear any pending errors, or initial state bits */
4948c2ecf20Sopenharmony_ci	e7xxx_get_error_info(mci, &discard);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Here we assume that we will never see multiple instances of this
4978c2ecf20Sopenharmony_ci	 * type of memory controller.  The ID is therefore hardcoded to 0.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci	if (edac_mc_add_mc(mci)) {
5008c2ecf20Sopenharmony_ci		edac_dbg(3, "failed edac_mc_add_mc()\n");
5018c2ecf20Sopenharmony_ci		goto fail1;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* allocating generic PCI control info */
5058c2ecf20Sopenharmony_ci	e7xxx_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
5068c2ecf20Sopenharmony_ci	if (!e7xxx_pci) {
5078c2ecf20Sopenharmony_ci		printk(KERN_WARNING
5088c2ecf20Sopenharmony_ci			"%s(): Unable to create PCI control\n",
5098c2ecf20Sopenharmony_ci			__func__);
5108c2ecf20Sopenharmony_ci		printk(KERN_WARNING
5118c2ecf20Sopenharmony_ci			"%s(): PCI error report via EDAC not setup\n",
5128c2ecf20Sopenharmony_ci			__func__);
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* get this far and it's successful */
5168c2ecf20Sopenharmony_ci	edac_dbg(3, "success\n");
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cifail1:
5208c2ecf20Sopenharmony_ci	pci_dev_put(pvt->bridge_ck);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cifail0:
5238c2ecf20Sopenharmony_ci	edac_mc_free(mci);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return -ENODEV;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/* returns count (>= 0), or negative on error */
5298c2ecf20Sopenharmony_cistatic int e7xxx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* wake up and enable device */
5348c2ecf20Sopenharmony_ci	return pci_enable_device(pdev) ?
5358c2ecf20Sopenharmony_ci		-EIO : e7xxx_probe1(pdev, ent->driver_data);
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic void e7xxx_remove_one(struct pci_dev *pdev)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
5418c2ecf20Sopenharmony_ci	struct e7xxx_pvt *pvt;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (e7xxx_pci)
5468c2ecf20Sopenharmony_ci		edac_pci_release_generic_ctl(e7xxx_pci);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
5498c2ecf20Sopenharmony_ci		return;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	pvt = (struct e7xxx_pvt *)mci->pvt_info;
5528c2ecf20Sopenharmony_ci	pci_dev_put(pvt->bridge_ck);
5538c2ecf20Sopenharmony_ci	edac_mc_free(mci);
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic const struct pci_device_id e7xxx_pci_tbl[] = {
5578c2ecf20Sopenharmony_ci	{
5588c2ecf20Sopenharmony_ci	 PCI_VEND_DEV(INTEL, 7205_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
5598c2ecf20Sopenharmony_ci	 E7205},
5608c2ecf20Sopenharmony_ci	{
5618c2ecf20Sopenharmony_ci	 PCI_VEND_DEV(INTEL, 7500_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
5628c2ecf20Sopenharmony_ci	 E7500},
5638c2ecf20Sopenharmony_ci	{
5648c2ecf20Sopenharmony_ci	 PCI_VEND_DEV(INTEL, 7501_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
5658c2ecf20Sopenharmony_ci	 E7501},
5668c2ecf20Sopenharmony_ci	{
5678c2ecf20Sopenharmony_ci	 PCI_VEND_DEV(INTEL, 7505_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
5688c2ecf20Sopenharmony_ci	 E7505},
5698c2ecf20Sopenharmony_ci	{
5708c2ecf20Sopenharmony_ci	 0,
5718c2ecf20Sopenharmony_ci	 }			/* 0 terminated list. */
5728c2ecf20Sopenharmony_ci};
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, e7xxx_pci_tbl);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic struct pci_driver e7xxx_driver = {
5778c2ecf20Sopenharmony_ci	.name = EDAC_MOD_STR,
5788c2ecf20Sopenharmony_ci	.probe = e7xxx_init_one,
5798c2ecf20Sopenharmony_ci	.remove = e7xxx_remove_one,
5808c2ecf20Sopenharmony_ci	.id_table = e7xxx_pci_tbl,
5818c2ecf20Sopenharmony_ci};
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic int __init e7xxx_init(void)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci       /* Ensure that the OPSTATE is set correctly for POLL or NMI */
5868c2ecf20Sopenharmony_ci       opstate_init();
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return pci_register_driver(&e7xxx_driver);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic void __exit e7xxx_exit(void)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	pci_unregister_driver(&e7xxx_driver);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cimodule_init(e7xxx_init);
5978c2ecf20Sopenharmony_cimodule_exit(e7xxx_exit);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh et al\n"
6018c2ecf20Sopenharmony_ci		"Based on.work by Dan Hollis et al");
6028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC support for Intel e7xxx memory controllers");
6038c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444);
6048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
605