18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * amd8131_edac.c, AMD8131 hypertransport chip EDAC kernel module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Wind River Systems, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:	Cao Qingtao <qingtao.cao@windriver.com>
88c2ecf20Sopenharmony_ci * 		Benjamin Walsh <benjamin.walsh@windriver.com>
98c2ecf20Sopenharmony_ci * 		Hu Yongqi <yongqi.hu@windriver.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/bitops.h>
178c2ecf20Sopenharmony_ci#include <linux/edac.h>
188c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "edac_module.h"
218c2ecf20Sopenharmony_ci#include "amd8131_edac.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define AMD8131_EDAC_REVISION	" Ver: 1.0.0"
248c2ecf20Sopenharmony_ci#define AMD8131_EDAC_MOD_STR	"amd8131_edac"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* Wrapper functions for accessing PCI configuration space */
278c2ecf20Sopenharmony_cistatic void edac_pci_read_dword(struct pci_dev *dev, int reg, u32 *val32)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int ret;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	ret = pci_read_config_dword(dev, reg, val32);
328c2ecf20Sopenharmony_ci	if (ret != 0)
338c2ecf20Sopenharmony_ci		printk(KERN_ERR AMD8131_EDAC_MOD_STR
348c2ecf20Sopenharmony_ci			" PCI Access Read Error at 0x%x\n", reg);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void edac_pci_write_dword(struct pci_dev *dev, int reg, u32 val32)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	int ret;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	ret = pci_write_config_dword(dev, reg, val32);
428c2ecf20Sopenharmony_ci	if (ret != 0)
438c2ecf20Sopenharmony_ci		printk(KERN_ERR AMD8131_EDAC_MOD_STR
448c2ecf20Sopenharmony_ci			" PCI Access Write Error at 0x%x\n", reg);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Support up to two AMD8131 chipsets on a platform */
488c2ecf20Sopenharmony_cistatic struct amd8131_dev_info amd8131_devices[] = {
498c2ecf20Sopenharmony_ci	{
508c2ecf20Sopenharmony_ci	.inst = NORTH_A,
518c2ecf20Sopenharmony_ci	.devfn = DEVFN_PCIX_BRIDGE_NORTH_A,
528c2ecf20Sopenharmony_ci	.ctl_name = "AMD8131_PCIX_NORTH_A",
538c2ecf20Sopenharmony_ci	},
548c2ecf20Sopenharmony_ci	{
558c2ecf20Sopenharmony_ci	.inst = NORTH_B,
568c2ecf20Sopenharmony_ci	.devfn = DEVFN_PCIX_BRIDGE_NORTH_B,
578c2ecf20Sopenharmony_ci	.ctl_name = "AMD8131_PCIX_NORTH_B",
588c2ecf20Sopenharmony_ci	},
598c2ecf20Sopenharmony_ci	{
608c2ecf20Sopenharmony_ci	.inst = SOUTH_A,
618c2ecf20Sopenharmony_ci	.devfn = DEVFN_PCIX_BRIDGE_SOUTH_A,
628c2ecf20Sopenharmony_ci	.ctl_name = "AMD8131_PCIX_SOUTH_A",
638c2ecf20Sopenharmony_ci	},
648c2ecf20Sopenharmony_ci	{
658c2ecf20Sopenharmony_ci	.inst = SOUTH_B,
668c2ecf20Sopenharmony_ci	.devfn = DEVFN_PCIX_BRIDGE_SOUTH_B,
678c2ecf20Sopenharmony_ci	.ctl_name = "AMD8131_PCIX_SOUTH_B",
688c2ecf20Sopenharmony_ci	},
698c2ecf20Sopenharmony_ci	{.inst = NO_BRIDGE,},
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void amd8131_pcix_init(struct amd8131_dev_info *dev_info)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u32 val32;
758c2ecf20Sopenharmony_ci	struct pci_dev *dev = dev_info->dev;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* First clear error detection flags */
788c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
798c2ecf20Sopenharmony_ci	if (val32 & MEM_LIMIT_MASK)
808c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_MEM_LIM, val32);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* Clear Discard Timer Timedout flag */
838c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
848c2ecf20Sopenharmony_ci	if (val32 & INT_CTLR_DTS)
858c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_INT_CTLR, val32);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* Clear CRC Error flag on link side A */
888c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
898c2ecf20Sopenharmony_ci	if (val32 & LNK_CTRL_CRCERR_A)
908c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Clear CRC Error flag on link side B */
938c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
948c2ecf20Sopenharmony_ci	if (val32 & LNK_CTRL_CRCERR_B)
958c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/*
988c2ecf20Sopenharmony_ci	 * Then enable all error detections.
998c2ecf20Sopenharmony_ci	 *
1008c2ecf20Sopenharmony_ci	 * Setup Discard Timer Sync Flood Enable,
1018c2ecf20Sopenharmony_ci	 * System Error Enable and Parity Error Enable.
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
1048c2ecf20Sopenharmony_ci	val32 |= INT_CTLR_PERR | INT_CTLR_SERR | INT_CTLR_DTSE;
1058c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_INT_CTLR, val32);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Enable overall SERR Error detection */
1088c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_STS_CMD, &val32);
1098c2ecf20Sopenharmony_ci	val32 |= STS_CMD_SERREN;
1108c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_STS_CMD, val32);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Setup CRC Flood Enable for link side A */
1138c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
1148c2ecf20Sopenharmony_ci	val32 |= LNK_CTRL_CRCFEN;
1158c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Setup CRC Flood Enable for link side B */
1188c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
1198c2ecf20Sopenharmony_ci	val32 |= LNK_CTRL_CRCFEN;
1208c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic void amd8131_pcix_exit(struct amd8131_dev_info *dev_info)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	u32 val32;
1268c2ecf20Sopenharmony_ci	struct pci_dev *dev = dev_info->dev;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* Disable SERR, PERR and DTSE Error detection */
1298c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
1308c2ecf20Sopenharmony_ci	val32 &= ~(INT_CTLR_PERR | INT_CTLR_SERR | INT_CTLR_DTSE);
1318c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_INT_CTLR, val32);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Disable overall System Error detection */
1348c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_STS_CMD, &val32);
1358c2ecf20Sopenharmony_ci	val32 &= ~STS_CMD_SERREN;
1368c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_STS_CMD, val32);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Disable CRC Sync Flood on link side A */
1398c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
1408c2ecf20Sopenharmony_ci	val32 &= ~LNK_CTRL_CRCFEN;
1418c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Disable CRC Sync Flood on link side B */
1448c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
1458c2ecf20Sopenharmony_ci	val32 &= ~LNK_CTRL_CRCFEN;
1468c2ecf20Sopenharmony_ci	edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void amd8131_pcix_check(struct edac_pci_ctl_info *edac_dev)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct amd8131_dev_info *dev_info = edac_dev->pvt_info;
1528c2ecf20Sopenharmony_ci	struct pci_dev *dev = dev_info->dev;
1538c2ecf20Sopenharmony_ci	u32 val32;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Check PCI-X Bridge Memory Base-Limit Register for errors */
1568c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
1578c2ecf20Sopenharmony_ci	if (val32 & MEM_LIMIT_MASK) {
1588c2ecf20Sopenharmony_ci		printk(KERN_INFO "Error(s) in mem limit register "
1598c2ecf20Sopenharmony_ci			"on %s bridge\n", dev_info->ctl_name);
1608c2ecf20Sopenharmony_ci		printk(KERN_INFO "DPE: %d, RSE: %d, RMA: %d\n"
1618c2ecf20Sopenharmony_ci			"RTA: %d, STA: %d, MDPE: %d\n",
1628c2ecf20Sopenharmony_ci			val32 & MEM_LIMIT_DPE,
1638c2ecf20Sopenharmony_ci			val32 & MEM_LIMIT_RSE,
1648c2ecf20Sopenharmony_ci			val32 & MEM_LIMIT_RMA,
1658c2ecf20Sopenharmony_ci			val32 & MEM_LIMIT_RTA,
1668c2ecf20Sopenharmony_ci			val32 & MEM_LIMIT_STA,
1678c2ecf20Sopenharmony_ci			val32 & MEM_LIMIT_MDPE);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		val32 |= MEM_LIMIT_MASK;
1708c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_MEM_LIM, val32);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Check if Discard Timer timed out */
1768c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
1778c2ecf20Sopenharmony_ci	if (val32 & INT_CTLR_DTS) {
1788c2ecf20Sopenharmony_ci		printk(KERN_INFO "Error(s) in interrupt and control register "
1798c2ecf20Sopenharmony_ci			"on %s bridge\n", dev_info->ctl_name);
1808c2ecf20Sopenharmony_ci		printk(KERN_INFO "DTS: %d\n", val32 & INT_CTLR_DTS);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		val32 |= INT_CTLR_DTS;
1838c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_INT_CTLR, val32);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Check if CRC error happens on link side A */
1898c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
1908c2ecf20Sopenharmony_ci	if (val32 & LNK_CTRL_CRCERR_A) {
1918c2ecf20Sopenharmony_ci		printk(KERN_INFO "Error(s) in link conf and control register "
1928c2ecf20Sopenharmony_ci			"on %s bridge\n", dev_info->ctl_name);
1938c2ecf20Sopenharmony_ci		printk(KERN_INFO "CRCERR: %d\n", val32 & LNK_CTRL_CRCERR_A);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		val32 |= LNK_CTRL_CRCERR_A;
1968c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Check if CRC error happens on link side B */
2028c2ecf20Sopenharmony_ci	edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
2038c2ecf20Sopenharmony_ci	if (val32 & LNK_CTRL_CRCERR_B) {
2048c2ecf20Sopenharmony_ci		printk(KERN_INFO "Error(s) in link conf and control register "
2058c2ecf20Sopenharmony_ci			"on %s bridge\n", dev_info->ctl_name);
2068c2ecf20Sopenharmony_ci		printk(KERN_INFO "CRCERR: %d\n", val32 & LNK_CTRL_CRCERR_B);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		val32 |= LNK_CTRL_CRCERR_B;
2098c2ecf20Sopenharmony_ci		edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic struct amd8131_info amd8131_chipset = {
2168c2ecf20Sopenharmony_ci	.err_dev = PCI_DEVICE_ID_AMD_8131_APIC,
2178c2ecf20Sopenharmony_ci	.devices = amd8131_devices,
2188c2ecf20Sopenharmony_ci	.init = amd8131_pcix_init,
2198c2ecf20Sopenharmony_ci	.exit = amd8131_pcix_exit,
2208c2ecf20Sopenharmony_ci	.check = amd8131_pcix_check,
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*
2248c2ecf20Sopenharmony_ci * There are 4 PCIX Bridges on ATCA-6101 that share the same PCI Device ID,
2258c2ecf20Sopenharmony_ci * so amd8131_probe() would be called by kernel 4 times, with different
2268c2ecf20Sopenharmony_ci * address of pci_dev for each of them each time.
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_cistatic int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct amd8131_dev_info *dev_info;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	for (dev_info = amd8131_chipset.devices; dev_info->inst != NO_BRIDGE;
2338c2ecf20Sopenharmony_ci		dev_info++)
2348c2ecf20Sopenharmony_ci		if (dev_info->devfn == dev->devfn)
2358c2ecf20Sopenharmony_ci			break;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (dev_info->inst == NO_BRIDGE) /* should never happen */
2388c2ecf20Sopenharmony_ci		return -ENODEV;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/*
2418c2ecf20Sopenharmony_ci	 * We can't call pci_get_device() as we are used to do because
2428c2ecf20Sopenharmony_ci	 * there are 4 of them but pci_dev_get() instead.
2438c2ecf20Sopenharmony_ci	 */
2448c2ecf20Sopenharmony_ci	dev_info->dev = pci_dev_get(dev);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (pci_enable_device(dev_info->dev)) {
2478c2ecf20Sopenharmony_ci		pci_dev_put(dev_info->dev);
2488c2ecf20Sopenharmony_ci		printk(KERN_ERR "failed to enable:"
2498c2ecf20Sopenharmony_ci			"vendor %x, device %x, devfn %x, name %s\n",
2508c2ecf20Sopenharmony_ci			PCI_VENDOR_ID_AMD, amd8131_chipset.err_dev,
2518c2ecf20Sopenharmony_ci			dev_info->devfn, dev_info->ctl_name);
2528c2ecf20Sopenharmony_ci		return -ENODEV;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/*
2568c2ecf20Sopenharmony_ci	 * we do not allocate extra private structure for
2578c2ecf20Sopenharmony_ci	 * edac_pci_ctl_info, but make use of existing
2588c2ecf20Sopenharmony_ci	 * one instead.
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci	dev_info->edac_idx = edac_pci_alloc_index();
2618c2ecf20Sopenharmony_ci	dev_info->edac_dev = edac_pci_alloc_ctl_info(0, dev_info->ctl_name);
2628c2ecf20Sopenharmony_ci	if (!dev_info->edac_dev)
2638c2ecf20Sopenharmony_ci		return -ENOMEM;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	dev_info->edac_dev->pvt_info = dev_info;
2668c2ecf20Sopenharmony_ci	dev_info->edac_dev->dev = &dev_info->dev->dev;
2678c2ecf20Sopenharmony_ci	dev_info->edac_dev->mod_name = AMD8131_EDAC_MOD_STR;
2688c2ecf20Sopenharmony_ci	dev_info->edac_dev->ctl_name = dev_info->ctl_name;
2698c2ecf20Sopenharmony_ci	dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
2728c2ecf20Sopenharmony_ci		dev_info->edac_dev->edac_check = amd8131_chipset.check;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (amd8131_chipset.init)
2758c2ecf20Sopenharmony_ci		amd8131_chipset.init(dev_info);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (edac_pci_add_device(dev_info->edac_dev, dev_info->edac_idx) > 0) {
2788c2ecf20Sopenharmony_ci		printk(KERN_ERR "failed edac_pci_add_device() for %s\n",
2798c2ecf20Sopenharmony_ci			dev_info->ctl_name);
2808c2ecf20Sopenharmony_ci		edac_pci_free_ctl_info(dev_info->edac_dev);
2818c2ecf20Sopenharmony_ci		return -ENODEV;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	printk(KERN_INFO "added one device on AMD8131 "
2858c2ecf20Sopenharmony_ci		"vendor %x, device %x, devfn %x, name %s\n",
2868c2ecf20Sopenharmony_ci		PCI_VENDOR_ID_AMD, amd8131_chipset.err_dev,
2878c2ecf20Sopenharmony_ci		dev_info->devfn, dev_info->ctl_name);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return 0;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void amd8131_remove(struct pci_dev *dev)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct amd8131_dev_info *dev_info;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	for (dev_info = amd8131_chipset.devices; dev_info->inst != NO_BRIDGE;
2978c2ecf20Sopenharmony_ci		dev_info++)
2988c2ecf20Sopenharmony_ci		if (dev_info->devfn == dev->devfn)
2998c2ecf20Sopenharmony_ci			break;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (dev_info->inst == NO_BRIDGE) /* should never happen */
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (dev_info->edac_dev) {
3058c2ecf20Sopenharmony_ci		edac_pci_del_device(dev_info->edac_dev->dev);
3068c2ecf20Sopenharmony_ci		edac_pci_free_ctl_info(dev_info->edac_dev);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (amd8131_chipset.exit)
3108c2ecf20Sopenharmony_ci		amd8131_chipset.exit(dev_info);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	pci_dev_put(dev_info->dev);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic const struct pci_device_id amd8131_edac_pci_tbl[] = {
3168c2ecf20Sopenharmony_ci	{
3178c2ecf20Sopenharmony_ci	PCI_VEND_DEV(AMD, 8131_BRIDGE),
3188c2ecf20Sopenharmony_ci	.subvendor = PCI_ANY_ID,
3198c2ecf20Sopenharmony_ci	.subdevice = PCI_ANY_ID,
3208c2ecf20Sopenharmony_ci	.class = 0,
3218c2ecf20Sopenharmony_ci	.class_mask = 0,
3228c2ecf20Sopenharmony_ci	.driver_data = 0,
3238c2ecf20Sopenharmony_ci	},
3248c2ecf20Sopenharmony_ci	{
3258c2ecf20Sopenharmony_ci	0,
3268c2ecf20Sopenharmony_ci	}			/* table is NULL-terminated */
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amd8131_edac_pci_tbl);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic struct pci_driver amd8131_edac_driver = {
3318c2ecf20Sopenharmony_ci	.name = AMD8131_EDAC_MOD_STR,
3328c2ecf20Sopenharmony_ci	.probe = amd8131_probe,
3338c2ecf20Sopenharmony_ci	.remove = amd8131_remove,
3348c2ecf20Sopenharmony_ci	.id_table = amd8131_edac_pci_tbl,
3358c2ecf20Sopenharmony_ci};
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int __init amd8131_edac_init(void)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	printk(KERN_INFO "AMD8131 EDAC driver " AMD8131_EDAC_REVISION "\n");
3408c2ecf20Sopenharmony_ci	printk(KERN_INFO "\t(c) 2008 Wind River Systems, Inc.\n");
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* Only POLL mode supported so far */
3438c2ecf20Sopenharmony_ci	edac_op_state = EDAC_OPSTATE_POLL;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return pci_register_driver(&amd8131_edac_driver);
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void __exit amd8131_edac_exit(void)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	pci_unregister_driver(&amd8131_edac_driver);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cimodule_init(amd8131_edac_init);
3548c2ecf20Sopenharmony_cimodule_exit(amd8131_edac_exit);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>\n");
3588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD8131 HyperTransport PCI-X Tunnel EDAC kernel module");
359