18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Intel 82860 Memory Controller kernel module
38c2ecf20Sopenharmony_ci * (C) 2005 Red Hat (http://www.redhat.com)
48c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the
58c2ecf20Sopenharmony_ci * GNU General Public License.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Written by Ben Woodard <woodard@redhat.com>
88c2ecf20Sopenharmony_ci * shamelessly copied from and based upon the edac_i82875 driver
98c2ecf20Sopenharmony_ci * by Thayne Harbaugh of Linux Networx. (http://lnxi.com)
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
168c2ecf20Sopenharmony_ci#include <linux/edac.h>
178c2ecf20Sopenharmony_ci#include "edac_module.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define EDAC_MOD_STR	"i82860_edac"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define i82860_printk(level, fmt, arg...) \
228c2ecf20Sopenharmony_ci	edac_printk(level, "i82860", fmt, ##arg)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define i82860_mc_printk(mci, level, fmt, arg...) \
258c2ecf20Sopenharmony_ci	edac_mc_chipset_printk(mci, level, "i82860", fmt, ##arg)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_INTEL_82860_0
288c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_82860_0	0x2531
298c2ecf20Sopenharmony_ci#endif				/* PCI_DEVICE_ID_INTEL_82860_0 */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define I82860_MCHCFG 0x50
328c2ecf20Sopenharmony_ci#define I82860_GBA 0x60
338c2ecf20Sopenharmony_ci#define I82860_GBA_MASK 0x7FF
348c2ecf20Sopenharmony_ci#define I82860_GBA_SHIFT 24
358c2ecf20Sopenharmony_ci#define I82860_ERRSTS 0xC8
368c2ecf20Sopenharmony_ci#define I82860_EAP 0xE4
378c2ecf20Sopenharmony_ci#define I82860_DERRCTL_STS 0xE2
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cienum i82860_chips {
408c2ecf20Sopenharmony_ci	I82860 = 0,
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct i82860_dev_info {
448c2ecf20Sopenharmony_ci	const char *ctl_name;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct i82860_error_info {
488c2ecf20Sopenharmony_ci	u16 errsts;
498c2ecf20Sopenharmony_ci	u32 eap;
508c2ecf20Sopenharmony_ci	u16 derrsyn;
518c2ecf20Sopenharmony_ci	u16 errsts2;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct i82860_dev_info i82860_devs[] = {
558c2ecf20Sopenharmony_ci	[I82860] = {
568c2ecf20Sopenharmony_ci		.ctl_name = "i82860"},
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct pci_dev *mci_pdev;	/* init dev: in case that AGP code
608c2ecf20Sopenharmony_ci					 * has already registered driver
618c2ecf20Sopenharmony_ci					 */
628c2ecf20Sopenharmony_cistatic struct edac_pci_ctl_info *i82860_pci;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void i82860_get_error_info(struct mem_ctl_info *mci,
658c2ecf20Sopenharmony_ci				struct i82860_error_info *info)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	pdev = to_pci_dev(mci->pdev);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * This is a mess because there is no atomic way to read all the
738c2ecf20Sopenharmony_ci	 * registers at once and the registers can transition from CE being
748c2ecf20Sopenharmony_ci	 * overwritten by UE.
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, I82860_ERRSTS, &info->errsts);
778c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev, I82860_EAP, &info->eap);
788c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, I82860_DERRCTL_STS, &info->derrsyn);
798c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, I82860_ERRSTS, &info->errsts2);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	pci_write_bits16(pdev, I82860_ERRSTS, 0x0003, 0x0003);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/*
848c2ecf20Sopenharmony_ci	 * If the error is the same for both reads then the first set of reads
858c2ecf20Sopenharmony_ci	 * is valid.  If there is a change then there is a CE no info and the
868c2ecf20Sopenharmony_ci	 * second set of reads is valid and should be UE info.
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	if (!(info->errsts2 & 0x0003))
898c2ecf20Sopenharmony_ci		return;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if ((info->errsts ^ info->errsts2) & 0x0003) {
928c2ecf20Sopenharmony_ci		pci_read_config_dword(pdev, I82860_EAP, &info->eap);
938c2ecf20Sopenharmony_ci		pci_read_config_word(pdev, I82860_DERRCTL_STS, &info->derrsyn);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int i82860_process_error_info(struct mem_ctl_info *mci,
988c2ecf20Sopenharmony_ci				struct i82860_error_info *info,
998c2ecf20Sopenharmony_ci				int handle_errors)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
1028c2ecf20Sopenharmony_ci	int row;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!(info->errsts2 & 0x0003))
1058c2ecf20Sopenharmony_ci		return 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!handle_errors)
1088c2ecf20Sopenharmony_ci		return 1;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if ((info->errsts ^ info->errsts2) & 0x0003) {
1118c2ecf20Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
1128c2ecf20Sopenharmony_ci				     -1, -1, -1, "UE overwrote CE", "");
1138c2ecf20Sopenharmony_ci		info->errsts = info->errsts2;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	info->eap >>= PAGE_SHIFT;
1178c2ecf20Sopenharmony_ci	row = edac_mc_find_csrow_by_page(mci, info->eap);
1188c2ecf20Sopenharmony_ci	dimm = mci->csrows[row]->channels[0]->dimm;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (info->errsts & 0x0002)
1218c2ecf20Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
1228c2ecf20Sopenharmony_ci				     info->eap, 0, 0,
1238c2ecf20Sopenharmony_ci				     dimm->location[0], dimm->location[1], -1,
1248c2ecf20Sopenharmony_ci				     "i82860 UE", "");
1258c2ecf20Sopenharmony_ci	else
1268c2ecf20Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1278c2ecf20Sopenharmony_ci				     info->eap, 0, info->derrsyn,
1288c2ecf20Sopenharmony_ci				     dimm->location[0], dimm->location[1], -1,
1298c2ecf20Sopenharmony_ci				     "i82860 CE", "");
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return 1;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void i82860_check(struct mem_ctl_info *mci)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct i82860_error_info info;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	edac_dbg(1, "MC%d\n", mci->mc_idx);
1398c2ecf20Sopenharmony_ci	i82860_get_error_info(mci, &info);
1408c2ecf20Sopenharmony_ci	i82860_process_error_info(mci, &info, 1);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	unsigned long last_cumul_size;
1468c2ecf20Sopenharmony_ci	u16 mchcfg_ddim;	/* DRAM Data Integrity Mode 0=none, 2=edac */
1478c2ecf20Sopenharmony_ci	u16 value;
1488c2ecf20Sopenharmony_ci	u32 cumul_size;
1498c2ecf20Sopenharmony_ci	struct csrow_info *csrow;
1508c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
1518c2ecf20Sopenharmony_ci	int index;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, I82860_MCHCFG, &mchcfg_ddim);
1548c2ecf20Sopenharmony_ci	mchcfg_ddim = mchcfg_ddim & 0x180;
1558c2ecf20Sopenharmony_ci	last_cumul_size = 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* The group row boundary (GRA) reg values are boundary address
1588c2ecf20Sopenharmony_ci	 * for each DRAM row with a granularity of 16MB.  GRA regs are
1598c2ecf20Sopenharmony_ci	 * cumulative; therefore GRA15 will contain the total memory contained
1608c2ecf20Sopenharmony_ci	 * in all eight rows.
1618c2ecf20Sopenharmony_ci	 */
1628c2ecf20Sopenharmony_ci	for (index = 0; index < mci->nr_csrows; index++) {
1638c2ecf20Sopenharmony_ci		csrow = mci->csrows[index];
1648c2ecf20Sopenharmony_ci		dimm = csrow->channels[0]->dimm;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		pci_read_config_word(pdev, I82860_GBA + index * 2, &value);
1678c2ecf20Sopenharmony_ci		cumul_size = (value & I82860_GBA_MASK) <<
1688c2ecf20Sopenharmony_ci			(I82860_GBA_SHIFT - PAGE_SHIFT);
1698c2ecf20Sopenharmony_ci		edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (cumul_size == last_cumul_size)
1728c2ecf20Sopenharmony_ci			continue;	/* not populated */
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		csrow->first_page = last_cumul_size;
1758c2ecf20Sopenharmony_ci		csrow->last_page = cumul_size - 1;
1768c2ecf20Sopenharmony_ci		dimm->nr_pages = cumul_size - last_cumul_size;
1778c2ecf20Sopenharmony_ci		last_cumul_size = cumul_size;
1788c2ecf20Sopenharmony_ci		dimm->grain = 1 << 12;	/* I82860_EAP has 4KiB reolution */
1798c2ecf20Sopenharmony_ci		dimm->mtype = MEM_RMBS;
1808c2ecf20Sopenharmony_ci		dimm->dtype = DEV_UNKNOWN;
1818c2ecf20Sopenharmony_ci		dimm->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int i82860_probe1(struct pci_dev *pdev, int dev_idx)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
1888c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[2];
1898c2ecf20Sopenharmony_ci	struct i82860_error_info discard;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/*
1928c2ecf20Sopenharmony_ci	 * RDRAM has channels but these don't map onto the csrow abstraction.
1938c2ecf20Sopenharmony_ci	 * According with the datasheet, there are 2 Rambus channels, supporting
1948c2ecf20Sopenharmony_ci	 * up to 16 direct RDRAM devices.
1958c2ecf20Sopenharmony_ci	 * The device groups from the GRA registers seem to map reasonably
1968c2ecf20Sopenharmony_ci	 * well onto the notion of a chip select row.
1978c2ecf20Sopenharmony_ci	 * There are 16 GRA registers and since the name is associated with
1988c2ecf20Sopenharmony_ci	 * the channel and the GRA registers map to physical devices so we are
1998c2ecf20Sopenharmony_ci	 * going to make 1 channel for group.
2008c2ecf20Sopenharmony_ci	 */
2018c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHANNEL;
2028c2ecf20Sopenharmony_ci	layers[0].size = 2;
2038c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = true;
2048c2ecf20Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_SLOT;
2058c2ecf20Sopenharmony_ci	layers[1].size = 8;
2068c2ecf20Sopenharmony_ci	layers[1].is_virt_csrow = true;
2078c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
2088c2ecf20Sopenharmony_ci	if (!mci)
2098c2ecf20Sopenharmony_ci		return -ENOMEM;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	edac_dbg(3, "init mci\n");
2128c2ecf20Sopenharmony_ci	mci->pdev = &pdev->dev;
2138c2ecf20Sopenharmony_ci	mci->mtype_cap = MEM_FLAG_DDR;
2148c2ecf20Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
2158c2ecf20Sopenharmony_ci	/* I"m not sure about this but I think that all RDRAM is SECDED */
2168c2ecf20Sopenharmony_ci	mci->edac_cap = EDAC_FLAG_SECDED;
2178c2ecf20Sopenharmony_ci	mci->mod_name = EDAC_MOD_STR;
2188c2ecf20Sopenharmony_ci	mci->ctl_name = i82860_devs[dev_idx].ctl_name;
2198c2ecf20Sopenharmony_ci	mci->dev_name = pci_name(pdev);
2208c2ecf20Sopenharmony_ci	mci->edac_check = i82860_check;
2218c2ecf20Sopenharmony_ci	mci->ctl_page_to_phys = NULL;
2228c2ecf20Sopenharmony_ci	i82860_init_csrows(mci, pdev);
2238c2ecf20Sopenharmony_ci	i82860_get_error_info(mci, &discard);	/* clear counters */
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Here we assume that we will never see multiple instances of this
2268c2ecf20Sopenharmony_ci	 * type of memory controller.  The ID is therefore hardcoded to 0.
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	if (edac_mc_add_mc(mci)) {
2298c2ecf20Sopenharmony_ci		edac_dbg(3, "failed edac_mc_add_mc()\n");
2308c2ecf20Sopenharmony_ci		goto fail;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* allocating generic PCI control info */
2348c2ecf20Sopenharmony_ci	i82860_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
2358c2ecf20Sopenharmony_ci	if (!i82860_pci) {
2368c2ecf20Sopenharmony_ci		printk(KERN_WARNING
2378c2ecf20Sopenharmony_ci			"%s(): Unable to create PCI control\n",
2388c2ecf20Sopenharmony_ci			__func__);
2398c2ecf20Sopenharmony_ci		printk(KERN_WARNING
2408c2ecf20Sopenharmony_ci			"%s(): PCI error report via EDAC not setup\n",
2418c2ecf20Sopenharmony_ci			__func__);
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* get this far and it's successful */
2458c2ecf20Sopenharmony_ci	edac_dbg(3, "success\n");
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cifail:
2508c2ecf20Sopenharmony_ci	edac_mc_free(mci);
2518c2ecf20Sopenharmony_ci	return -ENODEV;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/* returns count (>= 0), or negative on error */
2558c2ecf20Sopenharmony_cistatic int i82860_init_one(struct pci_dev *pdev,
2568c2ecf20Sopenharmony_ci			   const struct pci_device_id *ent)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int rc;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
2618c2ecf20Sopenharmony_ci	i82860_printk(KERN_INFO, "i82860 init one\n");
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (pci_enable_device(pdev) < 0)
2648c2ecf20Sopenharmony_ci		return -EIO;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	rc = i82860_probe1(pdev, ent->driver_data);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (rc == 0)
2698c2ecf20Sopenharmony_ci		mci_pdev = pci_dev_get(pdev);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return rc;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void i82860_remove_one(struct pci_dev *pdev)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (i82860_pci)
2818c2ecf20Sopenharmony_ci		edac_pci_release_generic_ctl(i82860_pci);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
2848c2ecf20Sopenharmony_ci		return;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	edac_mc_free(mci);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic const struct pci_device_id i82860_pci_tbl[] = {
2908c2ecf20Sopenharmony_ci	{
2918c2ecf20Sopenharmony_ci	 PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
2928c2ecf20Sopenharmony_ci	 I82860},
2938c2ecf20Sopenharmony_ci	{
2948c2ecf20Sopenharmony_ci	 0,
2958c2ecf20Sopenharmony_ci	 }			/* 0 terminated list. */
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i82860_pci_tbl);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic struct pci_driver i82860_driver = {
3018c2ecf20Sopenharmony_ci	.name = EDAC_MOD_STR,
3028c2ecf20Sopenharmony_ci	.probe = i82860_init_one,
3038c2ecf20Sopenharmony_ci	.remove = i82860_remove_one,
3048c2ecf20Sopenharmony_ci	.id_table = i82860_pci_tbl,
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int __init i82860_init(void)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	int pci_rc;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci       /* Ensure that the OPSTATE is set correctly for POLL or NMI */
3148c2ecf20Sopenharmony_ci       opstate_init();
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if ((pci_rc = pci_register_driver(&i82860_driver)) < 0)
3178c2ecf20Sopenharmony_ci		goto fail0;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (!mci_pdev) {
3208c2ecf20Sopenharmony_ci		mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
3218c2ecf20Sopenharmony_ci					PCI_DEVICE_ID_INTEL_82860_0, NULL);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		if (mci_pdev == NULL) {
3248c2ecf20Sopenharmony_ci			edac_dbg(0, "860 pci_get_device fail\n");
3258c2ecf20Sopenharmony_ci			pci_rc = -ENODEV;
3268c2ecf20Sopenharmony_ci			goto fail1;
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		if (pci_rc < 0) {
3328c2ecf20Sopenharmony_ci			edac_dbg(0, "860 init fail\n");
3338c2ecf20Sopenharmony_ci			pci_rc = -ENODEV;
3348c2ecf20Sopenharmony_ci			goto fail1;
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cifail1:
3418c2ecf20Sopenharmony_ci	pci_unregister_driver(&i82860_driver);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cifail0:
3448c2ecf20Sopenharmony_ci	pci_dev_put(mci_pdev);
3458c2ecf20Sopenharmony_ci	return pci_rc;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void __exit i82860_exit(void)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	edac_dbg(3, "\n");
3518c2ecf20Sopenharmony_ci	pci_unregister_driver(&i82860_driver);
3528c2ecf20Sopenharmony_ci	pci_dev_put(mci_pdev);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cimodule_init(i82860_init);
3568c2ecf20Sopenharmony_cimodule_exit(i82860_exit);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com) "
3608c2ecf20Sopenharmony_ci		"Ben Woodard <woodard@redhat.com>");
3618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ECC support for Intel 82860 memory hub controllers");
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444);
3648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
365