aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/i82860_edac.c
diff options
context:
space:
mode:
authorAlan Cox <alan@lxorguk.ukuu.org.uk>2006-01-18 20:44:10 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-18 22:20:31 -0500
commit0d88a10e566d46bffc214c974e5cf5abe38d8da8 (patch)
tree63a6c0ad4fbd33c192c2ed62808f0220364ba07e /drivers/edac/i82860_edac.c
parent806c35f5057a64d3061ee4e2b1023bf6f6d328e2 (diff)
[PATCH] EDAC: drivers for Intel i82860, i82875
Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/edac/i82860_edac.c')
-rw-r--r--drivers/edac/i82860_edac.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c
new file mode 100644
index 000000000000..bfb7ae02e379
--- /dev/null
+++ b/drivers/edac/i82860_edac.c
@@ -0,0 +1,299 @@
1/*
2 * Intel 82860 Memory Controller kernel module
3 * (C) 2005 Red Hat (http://www.redhat.com)
4 * This file may be distributed under the terms of the
5 * GNU General Public License.
6 *
7 * Written by Ben Woodard <woodard@redhat.com>
8 * shamelessly copied from and based upon the edac_i82875 driver
9 * by Thayne Harbaugh of Linux Networx. (http://lnxi.com)
10 */
11
12
13#include <linux/config.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/pci.h>
17#include <linux/pci_ids.h>
18#include <linux/slab.h>
19#include "edac_mc.h"
20
21
22#ifndef PCI_DEVICE_ID_INTEL_82860_0
23#define PCI_DEVICE_ID_INTEL_82860_0 0x2531
24#endif /* PCI_DEVICE_ID_INTEL_82860_0 */
25
26#define I82860_MCHCFG 0x50
27#define I82860_GBA 0x60
28#define I82860_GBA_MASK 0x7FF
29#define I82860_GBA_SHIFT 24
30#define I82860_ERRSTS 0xC8
31#define I82860_EAP 0xE4
32#define I82860_DERRCTL_STS 0xE2
33
34enum i82860_chips {
35 I82860 = 0,
36};
37
38struct i82860_dev_info {
39 const char *ctl_name;
40};
41
42struct i82860_error_info {
43 u16 errsts;
44 u32 eap;
45 u16 derrsyn;
46 u16 errsts2;
47};
48
49static const struct i82860_dev_info i82860_devs[] = {
50 [I82860] = {
51 .ctl_name = "i82860"},
52};
53
54static struct pci_dev *mci_pdev = NULL; /* init dev: in case that AGP code
55 has already registered driver */
56
57static int i82860_registered = 1;
58
59static void i82860_get_error_info (struct mem_ctl_info *mci,
60 struct i82860_error_info *info)
61{
62 /*
63 * This is a mess because there is no atomic way to read all the
64 * registers at once and the registers can transition from CE being
65 * overwritten by UE.
66 */
67 pci_read_config_word(mci->pdev, I82860_ERRSTS, &info->errsts);
68 pci_read_config_dword(mci->pdev, I82860_EAP, &info->eap);
69 pci_read_config_word(mci->pdev, I82860_DERRCTL_STS, &info->derrsyn);
70 pci_read_config_word(mci->pdev, I82860_ERRSTS, &info->errsts2);
71
72 pci_write_bits16(mci->pdev, I82860_ERRSTS, 0x0003, 0x0003);
73
74 /*
75 * If the error is the same for both reads then the first set of reads
76 * is valid. If there is a change then there is a CE no info and the
77 * second set of reads is valid and should be UE info.
78 */
79 if (!(info->errsts2 & 0x0003))
80 return;
81 if ((info->errsts ^ info->errsts2) & 0x0003) {
82 pci_read_config_dword(mci->pdev, I82860_EAP, &info->eap);
83 pci_read_config_word(mci->pdev, I82860_DERRCTL_STS,
84 &info->derrsyn);
85 }
86}
87
88static int i82860_process_error_info (struct mem_ctl_info *mci,
89 struct i82860_error_info *info, int handle_errors)
90{
91 int row;
92
93 if (!(info->errsts2 & 0x0003))
94 return 0;
95
96 if (!handle_errors)
97 return 1;
98
99 if ((info->errsts ^ info->errsts2) & 0x0003) {
100 edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
101 info->errsts = info->errsts2;
102 }
103
104 info->eap >>= PAGE_SHIFT;
105 row = edac_mc_find_csrow_by_page(mci, info->eap);
106
107 if (info->errsts & 0x0002)
108 edac_mc_handle_ue(mci, info->eap, 0, row, "i82860 UE");
109 else
110 edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row,
111 0, "i82860 UE");
112
113 return 1;
114}
115
116static void i82860_check(struct mem_ctl_info *mci)
117{
118 struct i82860_error_info info;
119
120 debugf1("MC%d: " __FILE__ ": %s()\n", mci->mc_idx, __func__);
121 i82860_get_error_info(mci, &info);
122 i82860_process_error_info(mci, &info, 1);
123}
124
125static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
126{
127 int rc = -ENODEV;
128 int index;
129 struct mem_ctl_info *mci = NULL;
130 unsigned long last_cumul_size;
131
132 u16 mchcfg_ddim; /* DRAM Data Integrity Mode 0=none,2=edac */
133
134 /* RDRAM has channels but these don't map onto the abstractions that
135 edac uses.
136 The device groups from the GRA registers seem to map reasonably
137 well onto the notion of a chip select row.
138 There are 16 GRA registers and since the name is associated with
139 the channel and the GRA registers map to physical devices so we are
140 going to make 1 channel for group.
141 */
142 mci = edac_mc_alloc(0, 16, 1);
143 if (!mci)
144 return -ENOMEM;
145
146 debugf3("MC: " __FILE__ ": %s(): init mci\n", __func__);
147
148 mci->pdev = pdev;
149 mci->mtype_cap = MEM_FLAG_DDR;
150
151
152 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
153 /* I"m not sure about this but I think that all RDRAM is SECDED */
154 mci->edac_cap = EDAC_FLAG_SECDED;
155 /* adjust FLAGS */
156
157 mci->mod_name = BS_MOD_STR;
158 mci->mod_ver = "$Revision: 1.1.2.6 $";
159 mci->ctl_name = i82860_devs[dev_idx].ctl_name;
160 mci->edac_check = i82860_check;
161 mci->ctl_page_to_phys = NULL;
162
163 pci_read_config_word(mci->pdev, I82860_MCHCFG, &mchcfg_ddim);
164 mchcfg_ddim = mchcfg_ddim & 0x180;
165
166 /*
167 * The group row boundary (GRA) reg values are boundary address
168 * for each DRAM row with a granularity of 16MB. GRA regs are
169 * cumulative; therefore GRA15 will contain the total memory contained
170 * in all eight rows.
171 */
172 for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
173 u16 value;
174 u32 cumul_size;
175 struct csrow_info *csrow = &mci->csrows[index];
176
177 pci_read_config_word(mci->pdev, I82860_GBA + index * 2,
178 &value);
179
180 cumul_size = (value & I82860_GBA_MASK) <<
181 (I82860_GBA_SHIFT - PAGE_SHIFT);
182 debugf3("MC: " __FILE__ ": %s(): (%d) cumul_size 0x%x\n",
183 __func__, index, cumul_size);
184 if (cumul_size == last_cumul_size)
185 continue; /* not populated */
186
187 csrow->first_page = last_cumul_size;
188 csrow->last_page = cumul_size - 1;
189 csrow->nr_pages = cumul_size - last_cumul_size;
190 last_cumul_size = cumul_size;
191 csrow->grain = 1 << 12; /* I82860_EAP has 4KiB reolution */
192 csrow->mtype = MEM_RMBS;
193 csrow->dtype = DEV_UNKNOWN;
194 csrow->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
195 }
196
197 /* clear counters */
198 pci_write_bits16(mci->pdev, I82860_ERRSTS, 0x0003, 0x0003);
199
200 if (edac_mc_add_mc(mci)) {
201 debugf3("MC: " __FILE__
202 ": %s(): failed edac_mc_add_mc()\n",
203 __func__);
204 edac_mc_free(mci);
205 } else {
206 /* get this far and it's successful */
207 debugf3("MC: " __FILE__ ": %s(): success\n", __func__);
208 rc = 0;
209 }
210 return rc;
211}
212
213/* returns count (>= 0), or negative on error */
214static int __devinit i82860_init_one(struct pci_dev *pdev,
215 const struct pci_device_id *ent)
216{
217 int rc;
218
219 debugf0("MC: " __FILE__ ": %s()\n", __func__);
220
221 printk(KERN_INFO "i82860 init one\n");
222 if(pci_enable_device(pdev) < 0)
223 return -EIO;
224 rc = i82860_probe1(pdev, ent->driver_data);
225 if(rc == 0)
226 mci_pdev = pci_dev_get(pdev);
227 return rc;
228}
229
230static void __devexit i82860_remove_one(struct pci_dev *pdev)
231{
232 struct mem_ctl_info *mci;
233
234 debugf0(__FILE__ ": %s()\n", __func__);
235
236 mci = edac_mc_find_mci_by_pdev(pdev);
237 if ((mci != NULL) && (edac_mc_del_mc(mci) == 0))
238 edac_mc_free(mci);
239}
240
241static const struct pci_device_id i82860_pci_tbl[] __devinitdata = {
242 {PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
243 I82860},
244 {0,} /* 0 terminated list. */
245};
246
247MODULE_DEVICE_TABLE(pci, i82860_pci_tbl);
248
249static struct pci_driver i82860_driver = {
250 .name = BS_MOD_STR,
251 .probe = i82860_init_one,
252 .remove = __devexit_p(i82860_remove_one),
253 .id_table = i82860_pci_tbl,
254};
255
256int __init i82860_init(void)
257{
258 int pci_rc;
259
260 debugf3("MC: " __FILE__ ": %s()\n", __func__);
261 if ((pci_rc = pci_register_driver(&i82860_driver)) < 0)
262 return pci_rc;
263
264 if (!mci_pdev) {
265 i82860_registered = 0;
266 mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
267 PCI_DEVICE_ID_INTEL_82860_0, NULL);
268 if (mci_pdev == NULL) {
269 debugf0("860 pci_get_device fail\n");
270 return -ENODEV;
271 }
272 pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
273 if (pci_rc < 0) {
274 debugf0("860 init fail\n");
275 pci_dev_put(mci_pdev);
276 return -ENODEV;
277 }
278 }
279 return 0;
280}
281
282static void __exit i82860_exit(void)
283{
284 debugf3("MC: " __FILE__ ": %s()\n", __func__);
285
286 pci_unregister_driver(&i82860_driver);
287 if (!i82860_registered) {
288 i82860_remove_one(mci_pdev);
289 pci_dev_put(mci_pdev);
290 }
291}
292
293module_init(i82860_init);
294module_exit(i82860_exit);
295
296MODULE_LICENSE("GPL");
297MODULE_AUTHOR
298 ("Red Hat Inc. (http://www.redhat.com.com) Ben Woodard <woodard@redhat.com>");
299MODULE_DESCRIPTION("ECC support for Intel 82860 memory hub controllers");