1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * amd8111_edac.c, AMD8111 Hyper Transport chip EDAC kernel module
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2008 Wind River Systems, Inc.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Authors: Cao Qingtao <qingtao.cao@windriver.com>
8*4882a593Smuzhiyun * Benjamin Walsh <benjamin.walsh@windriver.com>
9*4882a593Smuzhiyun * Hu Yongqi <yongqi.hu@windriver.com>
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/interrupt.h>
15*4882a593Smuzhiyun #include <linux/bitops.h>
16*4882a593Smuzhiyun #include <linux/edac.h>
17*4882a593Smuzhiyun #include <linux/pci_ids.h>
18*4882a593Smuzhiyun #include <asm/io.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include "edac_module.h"
21*4882a593Smuzhiyun #include "amd8111_edac.h"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define AMD8111_EDAC_REVISION " Ver: 1.0.0"
24*4882a593Smuzhiyun #define AMD8111_EDAC_MOD_STR "amd8111_edac"
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define PCI_DEVICE_ID_AMD_8111_PCI 0x7460
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun enum amd8111_edac_devs {
29*4882a593Smuzhiyun LPC_BRIDGE = 0,
30*4882a593Smuzhiyun };
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun enum amd8111_edac_pcis {
33*4882a593Smuzhiyun PCI_BRIDGE = 0,
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /* Wrapper functions for accessing PCI configuration space */
edac_pci_read_dword(struct pci_dev * dev,int reg,u32 * val32)37*4882a593Smuzhiyun static int edac_pci_read_dword(struct pci_dev *dev, int reg, u32 *val32)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun int ret;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun ret = pci_read_config_dword(dev, reg, val32);
42*4882a593Smuzhiyun if (ret != 0)
43*4882a593Smuzhiyun printk(KERN_ERR AMD8111_EDAC_MOD_STR
44*4882a593Smuzhiyun " PCI Access Read Error at 0x%x\n", reg);
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun return ret;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
edac_pci_read_byte(struct pci_dev * dev,int reg,u8 * val8)49*4882a593Smuzhiyun static void edac_pci_read_byte(struct pci_dev *dev, int reg, u8 *val8)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun int ret;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun ret = pci_read_config_byte(dev, reg, val8);
54*4882a593Smuzhiyun if (ret != 0)
55*4882a593Smuzhiyun printk(KERN_ERR AMD8111_EDAC_MOD_STR
56*4882a593Smuzhiyun " PCI Access Read Error at 0x%x\n", reg);
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun
edac_pci_write_dword(struct pci_dev * dev,int reg,u32 val32)59*4882a593Smuzhiyun static void edac_pci_write_dword(struct pci_dev *dev, int reg, u32 val32)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun int ret;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun ret = pci_write_config_dword(dev, reg, val32);
64*4882a593Smuzhiyun if (ret != 0)
65*4882a593Smuzhiyun printk(KERN_ERR AMD8111_EDAC_MOD_STR
66*4882a593Smuzhiyun " PCI Access Write Error at 0x%x\n", reg);
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
edac_pci_write_byte(struct pci_dev * dev,int reg,u8 val8)69*4882a593Smuzhiyun static void edac_pci_write_byte(struct pci_dev *dev, int reg, u8 val8)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun int ret;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun ret = pci_write_config_byte(dev, reg, val8);
74*4882a593Smuzhiyun if (ret != 0)
75*4882a593Smuzhiyun printk(KERN_ERR AMD8111_EDAC_MOD_STR
76*4882a593Smuzhiyun " PCI Access Write Error at 0x%x\n", reg);
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun /*
80*4882a593Smuzhiyun * device-specific methods for amd8111 PCI Bridge Controller
81*4882a593Smuzhiyun *
82*4882a593Smuzhiyun * Error Reporting and Handling for amd8111 chipset could be found
83*4882a593Smuzhiyun * in its datasheet 3.1.2 section, P37
84*4882a593Smuzhiyun */
amd8111_pci_bridge_init(struct amd8111_pci_info * pci_info)85*4882a593Smuzhiyun static void amd8111_pci_bridge_init(struct amd8111_pci_info *pci_info)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun u32 val32;
88*4882a593Smuzhiyun struct pci_dev *dev = pci_info->dev;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun /* First clear error detection flags on the host interface */
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun /* Clear SSE/SMA/STA flags in the global status register*/
93*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
94*4882a593Smuzhiyun if (val32 & PCI_STSCMD_CLEAR_MASK)
95*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun /* Clear CRC and Link Fail flags in HT Link Control reg */
98*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_HT_LINK, &val32);
99*4882a593Smuzhiyun if (val32 & HT_LINK_CLEAR_MASK)
100*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_HT_LINK, val32);
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun /* Second clear all fault on the secondary interface */
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /* Clear error flags in the memory-base limit reg. */
105*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
106*4882a593Smuzhiyun if (val32 & MEM_LIMIT_CLEAR_MASK)
107*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_MEM_LIM, val32);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /* Clear Discard Timer Expired flag in Interrupt/Bridge Control reg */
110*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
111*4882a593Smuzhiyun if (val32 & PCI_INTBRG_CTRL_CLEAR_MASK)
112*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* Last enable error detections */
115*4882a593Smuzhiyun if (edac_op_state == EDAC_OPSTATE_POLL) {
116*4882a593Smuzhiyun /* Enable System Error reporting in global status register */
117*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
118*4882a593Smuzhiyun val32 |= PCI_STSCMD_SERREN;
119*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun /* Enable CRC Sync flood packets to HyperTransport Link */
122*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_HT_LINK, &val32);
123*4882a593Smuzhiyun val32 |= HT_LINK_CRCFEN;
124*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_HT_LINK, val32);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /* Enable SSE reporting etc in Interrupt control reg */
127*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
128*4882a593Smuzhiyun val32 |= PCI_INTBRG_CTRL_POLL_MASK;
129*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
amd8111_pci_bridge_exit(struct amd8111_pci_info * pci_info)133*4882a593Smuzhiyun static void amd8111_pci_bridge_exit(struct amd8111_pci_info *pci_info)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun u32 val32;
136*4882a593Smuzhiyun struct pci_dev *dev = pci_info->dev;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (edac_op_state == EDAC_OPSTATE_POLL) {
139*4882a593Smuzhiyun /* Disable System Error reporting */
140*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
141*4882a593Smuzhiyun val32 &= ~PCI_STSCMD_SERREN;
142*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* Disable CRC flood packets */
145*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_HT_LINK, &val32);
146*4882a593Smuzhiyun val32 &= ~HT_LINK_CRCFEN;
147*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_HT_LINK, val32);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun /* Disable DTSERREN/MARSP/SERREN in Interrupt Control reg */
150*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
151*4882a593Smuzhiyun val32 &= ~PCI_INTBRG_CTRL_POLL_MASK;
152*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
amd8111_pci_bridge_check(struct edac_pci_ctl_info * edac_dev)156*4882a593Smuzhiyun static void amd8111_pci_bridge_check(struct edac_pci_ctl_info *edac_dev)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct amd8111_pci_info *pci_info = edac_dev->pvt_info;
159*4882a593Smuzhiyun struct pci_dev *dev = pci_info->dev;
160*4882a593Smuzhiyun u32 val32;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun /* Check out PCI Bridge Status and Command Register */
163*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
164*4882a593Smuzhiyun if (val32 & PCI_STSCMD_CLEAR_MASK) {
165*4882a593Smuzhiyun printk(KERN_INFO "Error(s) in PCI bridge status and command"
166*4882a593Smuzhiyun "register on device %s\n", pci_info->ctl_name);
167*4882a593Smuzhiyun printk(KERN_INFO "SSE: %d, RMA: %d, RTA: %d\n",
168*4882a593Smuzhiyun (val32 & PCI_STSCMD_SSE) != 0,
169*4882a593Smuzhiyun (val32 & PCI_STSCMD_RMA) != 0,
170*4882a593Smuzhiyun (val32 & PCI_STSCMD_RTA) != 0);
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun val32 |= PCI_STSCMD_CLEAR_MASK;
173*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /* Check out HyperTransport Link Control Register */
179*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_HT_LINK, &val32);
180*4882a593Smuzhiyun if (val32 & HT_LINK_LKFAIL) {
181*4882a593Smuzhiyun printk(KERN_INFO "Error(s) in hypertransport link control"
182*4882a593Smuzhiyun "register on device %s\n", pci_info->ctl_name);
183*4882a593Smuzhiyun printk(KERN_INFO "LKFAIL: %d\n",
184*4882a593Smuzhiyun (val32 & HT_LINK_LKFAIL) != 0);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun val32 |= HT_LINK_LKFAIL;
187*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_HT_LINK, val32);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /* Check out PCI Interrupt and Bridge Control Register */
193*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
194*4882a593Smuzhiyun if (val32 & PCI_INTBRG_CTRL_DTSTAT) {
195*4882a593Smuzhiyun printk(KERN_INFO "Error(s) in PCI interrupt and bridge control"
196*4882a593Smuzhiyun "register on device %s\n", pci_info->ctl_name);
197*4882a593Smuzhiyun printk(KERN_INFO "DTSTAT: %d\n",
198*4882a593Smuzhiyun (val32 & PCI_INTBRG_CTRL_DTSTAT) != 0);
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun val32 |= PCI_INTBRG_CTRL_DTSTAT;
201*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* Check out PCI Bridge Memory Base-Limit Register */
207*4882a593Smuzhiyun edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
208*4882a593Smuzhiyun if (val32 & MEM_LIMIT_CLEAR_MASK) {
209*4882a593Smuzhiyun printk(KERN_INFO
210*4882a593Smuzhiyun "Error(s) in mem limit register on %s device\n",
211*4882a593Smuzhiyun pci_info->ctl_name);
212*4882a593Smuzhiyun printk(KERN_INFO "DPE: %d, RSE: %d, RMA: %d\n"
213*4882a593Smuzhiyun "RTA: %d, STA: %d, MDPE: %d\n",
214*4882a593Smuzhiyun (val32 & MEM_LIMIT_DPE) != 0,
215*4882a593Smuzhiyun (val32 & MEM_LIMIT_RSE) != 0,
216*4882a593Smuzhiyun (val32 & MEM_LIMIT_RMA) != 0,
217*4882a593Smuzhiyun (val32 & MEM_LIMIT_RTA) != 0,
218*4882a593Smuzhiyun (val32 & MEM_LIMIT_STA) != 0,
219*4882a593Smuzhiyun (val32 & MEM_LIMIT_MDPE) != 0);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun val32 |= MEM_LIMIT_CLEAR_MASK;
222*4882a593Smuzhiyun edac_pci_write_dword(dev, REG_MEM_LIM, val32);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun static struct resource *legacy_io_res;
229*4882a593Smuzhiyun static int at_compat_reg_broken;
230*4882a593Smuzhiyun #define LEGACY_NR_PORTS 1
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun /* device-specific methods for amd8111 LPC Bridge device */
amd8111_lpc_bridge_init(struct amd8111_dev_info * dev_info)233*4882a593Smuzhiyun static void amd8111_lpc_bridge_init(struct amd8111_dev_info *dev_info)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun u8 val8;
236*4882a593Smuzhiyun struct pci_dev *dev = dev_info->dev;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* First clear REG_AT_COMPAT[SERR, IOCHK] if necessary */
239*4882a593Smuzhiyun legacy_io_res = request_region(REG_AT_COMPAT, LEGACY_NR_PORTS,
240*4882a593Smuzhiyun AMD8111_EDAC_MOD_STR);
241*4882a593Smuzhiyun if (!legacy_io_res)
242*4882a593Smuzhiyun printk(KERN_INFO "%s: failed to request legacy I/O region "
243*4882a593Smuzhiyun "start %d, len %d\n", __func__,
244*4882a593Smuzhiyun REG_AT_COMPAT, LEGACY_NR_PORTS);
245*4882a593Smuzhiyun else {
246*4882a593Smuzhiyun val8 = __do_inb(REG_AT_COMPAT);
247*4882a593Smuzhiyun if (val8 == 0xff) { /* buggy port */
248*4882a593Smuzhiyun printk(KERN_INFO "%s: port %d is buggy, not supported"
249*4882a593Smuzhiyun " by hardware?\n", __func__, REG_AT_COMPAT);
250*4882a593Smuzhiyun at_compat_reg_broken = 1;
251*4882a593Smuzhiyun release_region(REG_AT_COMPAT, LEGACY_NR_PORTS);
252*4882a593Smuzhiyun legacy_io_res = NULL;
253*4882a593Smuzhiyun } else {
254*4882a593Smuzhiyun u8 out8 = 0;
255*4882a593Smuzhiyun if (val8 & AT_COMPAT_SERR)
256*4882a593Smuzhiyun out8 = AT_COMPAT_CLRSERR;
257*4882a593Smuzhiyun if (val8 & AT_COMPAT_IOCHK)
258*4882a593Smuzhiyun out8 |= AT_COMPAT_CLRIOCHK;
259*4882a593Smuzhiyun if (out8 > 0)
260*4882a593Smuzhiyun __do_outb(out8, REG_AT_COMPAT);
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun /* Second clear error flags on LPC bridge */
265*4882a593Smuzhiyun edac_pci_read_byte(dev, REG_IO_CTRL_1, &val8);
266*4882a593Smuzhiyun if (val8 & IO_CTRL_1_CLEAR_MASK)
267*4882a593Smuzhiyun edac_pci_write_byte(dev, REG_IO_CTRL_1, val8);
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
amd8111_lpc_bridge_exit(struct amd8111_dev_info * dev_info)270*4882a593Smuzhiyun static void amd8111_lpc_bridge_exit(struct amd8111_dev_info *dev_info)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun if (legacy_io_res)
273*4882a593Smuzhiyun release_region(REG_AT_COMPAT, LEGACY_NR_PORTS);
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun
amd8111_lpc_bridge_check(struct edac_device_ctl_info * edac_dev)276*4882a593Smuzhiyun static void amd8111_lpc_bridge_check(struct edac_device_ctl_info *edac_dev)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun struct amd8111_dev_info *dev_info = edac_dev->pvt_info;
279*4882a593Smuzhiyun struct pci_dev *dev = dev_info->dev;
280*4882a593Smuzhiyun u8 val8;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun edac_pci_read_byte(dev, REG_IO_CTRL_1, &val8);
283*4882a593Smuzhiyun if (val8 & IO_CTRL_1_CLEAR_MASK) {
284*4882a593Smuzhiyun printk(KERN_INFO
285*4882a593Smuzhiyun "Error(s) in IO control register on %s device\n",
286*4882a593Smuzhiyun dev_info->ctl_name);
287*4882a593Smuzhiyun printk(KERN_INFO "LPC ERR: %d, PW2LPC: %d\n",
288*4882a593Smuzhiyun (val8 & IO_CTRL_1_LPC_ERR) != 0,
289*4882a593Smuzhiyun (val8 & IO_CTRL_1_PW2LPC) != 0);
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun val8 |= IO_CTRL_1_CLEAR_MASK;
292*4882a593Smuzhiyun edac_pci_write_byte(dev, REG_IO_CTRL_1, val8);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun if (at_compat_reg_broken == 0) {
298*4882a593Smuzhiyun u8 out8 = 0;
299*4882a593Smuzhiyun val8 = __do_inb(REG_AT_COMPAT);
300*4882a593Smuzhiyun if (val8 & AT_COMPAT_SERR)
301*4882a593Smuzhiyun out8 = AT_COMPAT_CLRSERR;
302*4882a593Smuzhiyun if (val8 & AT_COMPAT_IOCHK)
303*4882a593Smuzhiyun out8 |= AT_COMPAT_CLRIOCHK;
304*4882a593Smuzhiyun if (out8 > 0) {
305*4882a593Smuzhiyun __do_outb(out8, REG_AT_COMPAT);
306*4882a593Smuzhiyun edac_device_handle_ue(edac_dev, 0, 0,
307*4882a593Smuzhiyun edac_dev->ctl_name);
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /* General devices represented by edac_device_ctl_info */
313*4882a593Smuzhiyun static struct amd8111_dev_info amd8111_devices[] = {
314*4882a593Smuzhiyun [LPC_BRIDGE] = {
315*4882a593Smuzhiyun .err_dev = PCI_DEVICE_ID_AMD_8111_LPC,
316*4882a593Smuzhiyun .ctl_name = "lpc",
317*4882a593Smuzhiyun .init = amd8111_lpc_bridge_init,
318*4882a593Smuzhiyun .exit = amd8111_lpc_bridge_exit,
319*4882a593Smuzhiyun .check = amd8111_lpc_bridge_check,
320*4882a593Smuzhiyun },
321*4882a593Smuzhiyun {0},
322*4882a593Smuzhiyun };
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun /* PCI controllers represented by edac_pci_ctl_info */
325*4882a593Smuzhiyun static struct amd8111_pci_info amd8111_pcis[] = {
326*4882a593Smuzhiyun [PCI_BRIDGE] = {
327*4882a593Smuzhiyun .err_dev = PCI_DEVICE_ID_AMD_8111_PCI,
328*4882a593Smuzhiyun .ctl_name = "AMD8111_PCI_Controller",
329*4882a593Smuzhiyun .init = amd8111_pci_bridge_init,
330*4882a593Smuzhiyun .exit = amd8111_pci_bridge_exit,
331*4882a593Smuzhiyun .check = amd8111_pci_bridge_check,
332*4882a593Smuzhiyun },
333*4882a593Smuzhiyun {0},
334*4882a593Smuzhiyun };
335*4882a593Smuzhiyun
amd8111_dev_probe(struct pci_dev * dev,const struct pci_device_id * id)336*4882a593Smuzhiyun static int amd8111_dev_probe(struct pci_dev *dev,
337*4882a593Smuzhiyun const struct pci_device_id *id)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun struct amd8111_dev_info *dev_info = &amd8111_devices[id->driver_data];
340*4882a593Smuzhiyun int ret = -ENODEV;
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun dev_info->dev = pci_get_device(PCI_VENDOR_ID_AMD,
343*4882a593Smuzhiyun dev_info->err_dev, NULL);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun if (!dev_info->dev) {
346*4882a593Smuzhiyun printk(KERN_ERR "EDAC device not found:"
347*4882a593Smuzhiyun "vendor %x, device %x, name %s\n",
348*4882a593Smuzhiyun PCI_VENDOR_ID_AMD, dev_info->err_dev,
349*4882a593Smuzhiyun dev_info->ctl_name);
350*4882a593Smuzhiyun goto err;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (pci_enable_device(dev_info->dev)) {
354*4882a593Smuzhiyun printk(KERN_ERR "failed to enable:"
355*4882a593Smuzhiyun "vendor %x, device %x, name %s\n",
356*4882a593Smuzhiyun PCI_VENDOR_ID_AMD, dev_info->err_dev,
357*4882a593Smuzhiyun dev_info->ctl_name);
358*4882a593Smuzhiyun goto err_dev_put;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /*
362*4882a593Smuzhiyun * we do not allocate extra private structure for
363*4882a593Smuzhiyun * edac_device_ctl_info, but make use of existing
364*4882a593Smuzhiyun * one instead.
365*4882a593Smuzhiyun */
366*4882a593Smuzhiyun dev_info->edac_idx = edac_device_alloc_index();
367*4882a593Smuzhiyun dev_info->edac_dev =
368*4882a593Smuzhiyun edac_device_alloc_ctl_info(0, dev_info->ctl_name, 1,
369*4882a593Smuzhiyun NULL, 0, 0,
370*4882a593Smuzhiyun NULL, 0, dev_info->edac_idx);
371*4882a593Smuzhiyun if (!dev_info->edac_dev) {
372*4882a593Smuzhiyun ret = -ENOMEM;
373*4882a593Smuzhiyun goto err_dev_put;
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun dev_info->edac_dev->pvt_info = dev_info;
377*4882a593Smuzhiyun dev_info->edac_dev->dev = &dev_info->dev->dev;
378*4882a593Smuzhiyun dev_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR;
379*4882a593Smuzhiyun dev_info->edac_dev->ctl_name = dev_info->ctl_name;
380*4882a593Smuzhiyun dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev);
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun if (edac_op_state == EDAC_OPSTATE_POLL)
383*4882a593Smuzhiyun dev_info->edac_dev->edac_check = dev_info->check;
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun if (dev_info->init)
386*4882a593Smuzhiyun dev_info->init(dev_info);
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun if (edac_device_add_device(dev_info->edac_dev) > 0) {
389*4882a593Smuzhiyun printk(KERN_ERR "failed to add edac_dev for %s\n",
390*4882a593Smuzhiyun dev_info->ctl_name);
391*4882a593Smuzhiyun goto err_edac_free_ctl;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun printk(KERN_INFO "added one edac_dev on AMD8111 "
395*4882a593Smuzhiyun "vendor %x, device %x, name %s\n",
396*4882a593Smuzhiyun PCI_VENDOR_ID_AMD, dev_info->err_dev,
397*4882a593Smuzhiyun dev_info->ctl_name);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun return 0;
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun err_edac_free_ctl:
402*4882a593Smuzhiyun edac_device_free_ctl_info(dev_info->edac_dev);
403*4882a593Smuzhiyun err_dev_put:
404*4882a593Smuzhiyun pci_dev_put(dev_info->dev);
405*4882a593Smuzhiyun err:
406*4882a593Smuzhiyun return ret;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
amd8111_dev_remove(struct pci_dev * dev)409*4882a593Smuzhiyun static void amd8111_dev_remove(struct pci_dev *dev)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun struct amd8111_dev_info *dev_info;
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun for (dev_info = amd8111_devices; dev_info->err_dev; dev_info++)
414*4882a593Smuzhiyun if (dev_info->dev->device == dev->device)
415*4882a593Smuzhiyun break;
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun if (!dev_info->err_dev) /* should never happen */
418*4882a593Smuzhiyun return;
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun if (dev_info->edac_dev) {
421*4882a593Smuzhiyun edac_device_del_device(dev_info->edac_dev->dev);
422*4882a593Smuzhiyun edac_device_free_ctl_info(dev_info->edac_dev);
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun if (dev_info->exit)
426*4882a593Smuzhiyun dev_info->exit(dev_info);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun pci_dev_put(dev_info->dev);
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun
amd8111_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)431*4882a593Smuzhiyun static int amd8111_pci_probe(struct pci_dev *dev,
432*4882a593Smuzhiyun const struct pci_device_id *id)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun struct amd8111_pci_info *pci_info = &amd8111_pcis[id->driver_data];
435*4882a593Smuzhiyun int ret = -ENODEV;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun pci_info->dev = pci_get_device(PCI_VENDOR_ID_AMD,
438*4882a593Smuzhiyun pci_info->err_dev, NULL);
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun if (!pci_info->dev) {
441*4882a593Smuzhiyun printk(KERN_ERR "EDAC device not found:"
442*4882a593Smuzhiyun "vendor %x, device %x, name %s\n",
443*4882a593Smuzhiyun PCI_VENDOR_ID_AMD, pci_info->err_dev,
444*4882a593Smuzhiyun pci_info->ctl_name);
445*4882a593Smuzhiyun goto err;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun if (pci_enable_device(pci_info->dev)) {
449*4882a593Smuzhiyun printk(KERN_ERR "failed to enable:"
450*4882a593Smuzhiyun "vendor %x, device %x, name %s\n",
451*4882a593Smuzhiyun PCI_VENDOR_ID_AMD, pci_info->err_dev,
452*4882a593Smuzhiyun pci_info->ctl_name);
453*4882a593Smuzhiyun goto err_dev_put;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun /*
457*4882a593Smuzhiyun * we do not allocate extra private structure for
458*4882a593Smuzhiyun * edac_pci_ctl_info, but make use of existing
459*4882a593Smuzhiyun * one instead.
460*4882a593Smuzhiyun */
461*4882a593Smuzhiyun pci_info->edac_idx = edac_pci_alloc_index();
462*4882a593Smuzhiyun pci_info->edac_dev = edac_pci_alloc_ctl_info(0, pci_info->ctl_name);
463*4882a593Smuzhiyun if (!pci_info->edac_dev) {
464*4882a593Smuzhiyun ret = -ENOMEM;
465*4882a593Smuzhiyun goto err_dev_put;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun pci_info->edac_dev->pvt_info = pci_info;
469*4882a593Smuzhiyun pci_info->edac_dev->dev = &pci_info->dev->dev;
470*4882a593Smuzhiyun pci_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR;
471*4882a593Smuzhiyun pci_info->edac_dev->ctl_name = pci_info->ctl_name;
472*4882a593Smuzhiyun pci_info->edac_dev->dev_name = dev_name(&pci_info->dev->dev);
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun if (edac_op_state == EDAC_OPSTATE_POLL)
475*4882a593Smuzhiyun pci_info->edac_dev->edac_check = pci_info->check;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun if (pci_info->init)
478*4882a593Smuzhiyun pci_info->init(pci_info);
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun if (edac_pci_add_device(pci_info->edac_dev, pci_info->edac_idx) > 0) {
481*4882a593Smuzhiyun printk(KERN_ERR "failed to add edac_pci for %s\n",
482*4882a593Smuzhiyun pci_info->ctl_name);
483*4882a593Smuzhiyun goto err_edac_free_ctl;
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun printk(KERN_INFO "added one edac_pci on AMD8111 "
487*4882a593Smuzhiyun "vendor %x, device %x, name %s\n",
488*4882a593Smuzhiyun PCI_VENDOR_ID_AMD, pci_info->err_dev,
489*4882a593Smuzhiyun pci_info->ctl_name);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun return 0;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun err_edac_free_ctl:
494*4882a593Smuzhiyun edac_pci_free_ctl_info(pci_info->edac_dev);
495*4882a593Smuzhiyun err_dev_put:
496*4882a593Smuzhiyun pci_dev_put(pci_info->dev);
497*4882a593Smuzhiyun err:
498*4882a593Smuzhiyun return ret;
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun
amd8111_pci_remove(struct pci_dev * dev)501*4882a593Smuzhiyun static void amd8111_pci_remove(struct pci_dev *dev)
502*4882a593Smuzhiyun {
503*4882a593Smuzhiyun struct amd8111_pci_info *pci_info;
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun for (pci_info = amd8111_pcis; pci_info->err_dev; pci_info++)
506*4882a593Smuzhiyun if (pci_info->dev->device == dev->device)
507*4882a593Smuzhiyun break;
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun if (!pci_info->err_dev) /* should never happen */
510*4882a593Smuzhiyun return;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun if (pci_info->edac_dev) {
513*4882a593Smuzhiyun edac_pci_del_device(pci_info->edac_dev->dev);
514*4882a593Smuzhiyun edac_pci_free_ctl_info(pci_info->edac_dev);
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun if (pci_info->exit)
518*4882a593Smuzhiyun pci_info->exit(pci_info);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun pci_dev_put(pci_info->dev);
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun /* PCI Device ID talbe for general EDAC device */
524*4882a593Smuzhiyun static const struct pci_device_id amd8111_edac_dev_tbl[] = {
525*4882a593Smuzhiyun {
526*4882a593Smuzhiyun PCI_VEND_DEV(AMD, 8111_LPC),
527*4882a593Smuzhiyun .subvendor = PCI_ANY_ID,
528*4882a593Smuzhiyun .subdevice = PCI_ANY_ID,
529*4882a593Smuzhiyun .class = 0,
530*4882a593Smuzhiyun .class_mask = 0,
531*4882a593Smuzhiyun .driver_data = LPC_BRIDGE,
532*4882a593Smuzhiyun },
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 0,
535*4882a593Smuzhiyun } /* table is NULL-terminated */
536*4882a593Smuzhiyun };
537*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, amd8111_edac_dev_tbl);
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun static struct pci_driver amd8111_edac_dev_driver = {
540*4882a593Smuzhiyun .name = "AMD8111_EDAC_DEV",
541*4882a593Smuzhiyun .probe = amd8111_dev_probe,
542*4882a593Smuzhiyun .remove = amd8111_dev_remove,
543*4882a593Smuzhiyun .id_table = amd8111_edac_dev_tbl,
544*4882a593Smuzhiyun };
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun /* PCI Device ID table for EDAC PCI controller */
547*4882a593Smuzhiyun static const struct pci_device_id amd8111_edac_pci_tbl[] = {
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun PCI_VEND_DEV(AMD, 8111_PCI),
550*4882a593Smuzhiyun .subvendor = PCI_ANY_ID,
551*4882a593Smuzhiyun .subdevice = PCI_ANY_ID,
552*4882a593Smuzhiyun .class = 0,
553*4882a593Smuzhiyun .class_mask = 0,
554*4882a593Smuzhiyun .driver_data = PCI_BRIDGE,
555*4882a593Smuzhiyun },
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun 0,
558*4882a593Smuzhiyun } /* table is NULL-terminated */
559*4882a593Smuzhiyun };
560*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, amd8111_edac_pci_tbl);
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun static struct pci_driver amd8111_edac_pci_driver = {
563*4882a593Smuzhiyun .name = "AMD8111_EDAC_PCI",
564*4882a593Smuzhiyun .probe = amd8111_pci_probe,
565*4882a593Smuzhiyun .remove = amd8111_pci_remove,
566*4882a593Smuzhiyun .id_table = amd8111_edac_pci_tbl,
567*4882a593Smuzhiyun };
568*4882a593Smuzhiyun
amd8111_edac_init(void)569*4882a593Smuzhiyun static int __init amd8111_edac_init(void)
570*4882a593Smuzhiyun {
571*4882a593Smuzhiyun int val;
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun printk(KERN_INFO "AMD8111 EDAC driver " AMD8111_EDAC_REVISION "\n");
574*4882a593Smuzhiyun printk(KERN_INFO "\t(c) 2008 Wind River Systems, Inc.\n");
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun /* Only POLL mode supported so far */
577*4882a593Smuzhiyun edac_op_state = EDAC_OPSTATE_POLL;
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun val = pci_register_driver(&amd8111_edac_dev_driver);
580*4882a593Smuzhiyun val |= pci_register_driver(&amd8111_edac_pci_driver);
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun return val;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun
amd8111_edac_exit(void)585*4882a593Smuzhiyun static void __exit amd8111_edac_exit(void)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun pci_unregister_driver(&amd8111_edac_pci_driver);
588*4882a593Smuzhiyun pci_unregister_driver(&amd8111_edac_dev_driver);
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun module_init(amd8111_edac_init);
593*4882a593Smuzhiyun module_exit(amd8111_edac_exit);
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun MODULE_LICENSE("GPL");
596*4882a593Smuzhiyun MODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>\n");
597*4882a593Smuzhiyun MODULE_DESCRIPTION("AMD8111 HyperTransport I/O Hub EDAC kernel module");
598