aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTim Small <tim@buttersideup.com>2007-07-19 04:49:42 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 13:04:53 -0400
commit5a2c675c891960f86c025d4ab3d3904364bf4f96 (patch)
treee0c4198ae33408cd7b2e06e2d9e0b9ae885d8793 /drivers
parent522a94bd1e18a2acf9428f48db585a2fc816559e (diff)
drivers/edac: new i82443bxgz MC driver
This is a NEW EDAC Memory Controller driver for the 440BX chipset (I82443BXGX) created and submitted by Timm Small Signed-off-by: Tim Small <tim@buttersideup.com> Signed-off-by: Douglas Thompson <dougthompson@xmission.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/i82443bxgx_edac.c400
3 files changed, 408 insertions, 0 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 772a29079916..b56e4782f317 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -73,6 +73,13 @@ config EDAC_E752X
73 Support for error detection and correction on the Intel 73 Support for error detection and correction on the Intel
74 E7520, E7525, E7320 server chipsets. 74 E7520, E7525, E7320 server chipsets.
75 75
76config EDAC_I82443BXGX
77 tristate "Intel 82443BX/GX (440BX/GX)"
78 depends on EDAC_MM_EDAC && PCI && X86_32
79 help
80 Support for error detection and correction on the Intel
81 82443BX/GX memory controllers (440BX/GX chipsets).
82
76config EDAC_I82875P 83config EDAC_I82875P
77 tristate "Intel 82875p (D82875P, E7210)" 84 tristate "Intel 82875p (D82875P, E7210)"
78 depends on EDAC_MM_EDAC && PCI && X86_32 85 depends on EDAC_MM_EDAC && PCI && X86_32
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index d2674fbde86f..773472cef76e 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o
18obj-$(CONFIG_EDAC_I5000) += i5000_edac.o 18obj-$(CONFIG_EDAC_I5000) += i5000_edac.o
19obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o 19obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
20obj-$(CONFIG_EDAC_E752X) += e752x_edac.o 20obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
21obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o
21obj-$(CONFIG_EDAC_I82875P) += i82875p_edac.o 22obj-$(CONFIG_EDAC_I82875P) += i82875p_edac.o
22obj-$(CONFIG_EDAC_I82860) += i82860_edac.o 23obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
23obj-$(CONFIG_EDAC_R82600) += r82600_edac.o 24obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c
new file mode 100644
index 000000000000..ecf2ba82989c
--- /dev/null
+++ b/drivers/edac/i82443bxgx_edac.c
@@ -0,0 +1,400 @@
1/*
2 * Intel 82443BX/GX (440BX/GX chipset) Memory Controller EDAC kernel
3 * module (C) 2006 Tim Small
4 *
5 * This file may be distributed under the terms of the GNU General
6 * Public License.
7 *
8 * Written by Tim Small <tim@buttersideup.com>, based on work by Linux
9 * Networx, Thayne Harbaugh, Dan Hollis <goemon at anime dot net> and
10 * others.
11 *
12 * 440GX fix by Jason Uhlenkott <juhlenko@akamai.com>.
13 *
14 * Written with reference to 82443BX Host Bridge Datasheet:
15 * http://www.intel.com/design/chipsets/440/documentation.htm
16 * references to this document given in [].
17 *
18 * This module doesn't support the 440LX, but it may be possible to
19 * make it do so (the 440LX's register definitions are different, but
20 * not completely so - I haven't studied them in enough detail to know
21 * how easy this would be).
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26
27#include <linux/pci.h>
28#include <linux/pci_ids.h>
29
30#include <linux/slab.h>
31
32#include "edac_mc.h"
33
34#define I82443_REVISION "0.1"
35
36#define EDAC_MOD_STR "i82443bxgx_edac"
37
38
39/* The 82443BX supports SDRAM, or EDO (EDO for mobile only), "Memory
40 * Size: 8 MB to 512 MB (1GB with Registered DIMMs) with eight memory
41 * rows" "The 82443BX supports multiple-bit error detection and
42 * single-bit error correction when ECC mode is enabled and
43 * single/multi-bit error detection when correction is disabled.
44 * During writes to the DRAM, the 82443BX generates ECC for the data
45 * on a QWord basis. Partial QWord writes require a read-modify-write
46 * cycle when ECC is enabled."
47*/
48
49/* "Additionally, the 82443BX ensures that the data is corrected in
50 * main memory so that accumulation of errors is prevented. Another
51 * error within the same QWord would result in a double-bit error
52 * which is unrecoverable. This is known as hardware scrubbing since
53 * it requires no software intervention to correct the data in memory."
54 */
55
56/* [Also see page 100 (section 4.3), "DRAM Interface"]
57 * [Also see page 112 (section 4.6.1.4), ECC]
58 */
59
60#define I82443BXGX_NR_CSROWS 8
61#define I82443BXGX_NR_CHANS 1
62#define I82443BXGX_NR_DIMMS 4
63
64
65
66/* 82443 PCI Device 0 */
67#define I82443BXGX_NBXCFG 0x50 /* 32bit register starting at this PCI
68 * config space offset */
69#define I82443BXGX_NBXCFG_OFFSET_NON_ECCROW 24 /* Array of bits, zero if
70 * row is non-ECC */
71#define I82443BXGX_NBXCFG_OFFSET_DRAM_FREQ 12 /* 2 bits,00=100MHz,10=66 MHz */
72
73#define I82443BXGX_NBXCFG_OFFSET_DRAM_INTEGRITY 7 /* 2 bits: */
74#define I82443BXGX_NBXCFG_INTEGRITY_NONE 0x0 /* 00 = Non-ECC */
75#define I82443BXGX_NBXCFG_INTEGRITY_EC 0x1 /* 01 = EC (only) */
76#define I82443BXGX_NBXCFG_INTEGRITY_ECC 0x2 /* 10 = ECC */
77#define I82443BXGX_NBXCFG_INTEGRITY_SCRUB 0x3 /* 11 = ECC + HW Scrub */
78
79#define I82443BXGX_NBXCFG_OFFSET_ECC_DIAG_ENABLE 6
80
81
82/* 82443 PCI Device 0 */
83#define I82443BXGX_EAP 0x80 /* 32bit register starting at this PCI
84 * config space offset, Error Address
85 * Pointer Register */
86#define I82443BXGX_EAP_OFFSET_EAP 12 /* High 20 bits of error address */
87#define I82443BXGX_EAP_OFFSET_MBE BIT(1) /* Err at EAP was multi-bit (W1TC) */
88#define I82443BXGX_EAP_OFFSET_SBE BIT(0) /* Err at EAP was single-bit (W1TC)*/
89
90#define I82443BXGX_ERRCMD 0x90 /* 8bit register starting at this PCI
91 * config space offset. */
92#define I82443BXGX_ERRCMD_OFFSET_SERR_ON_MBE BIT(1) /* 1 = enable */
93#define I82443BXGX_ERRCMD_OFFSET_SERR_ON_SBE BIT(0) /* 1 = enable */
94
95#define I82443BXGX_ERRSTS 0x91 /* 16bit register starting at this PCI
96 * config space offset. */
97#define I82443BXGX_ERRSTS_OFFSET_MBFRE 5 /* 3 bits - first err row multibit */
98#define I82443BXGX_ERRSTS_OFFSET_MEF BIT(4) /* 1 = MBE occurred */
99#define I82443BXGX_ERRSTS_OFFSET_SBFRE 1 /* 3 bits - first err row singlebit */
100#define I82443BXGX_ERRSTS_OFFSET_SEF BIT(0) /* 1 = SBE occurred */
101
102
103#define I82443BXGX_DRAMC 0x57 /* 8bit register starting at this PCI
104 * config space offset. */
105#define I82443BXGX_DRAMC_OFFSET_DT 3 /* 2 bits, DRAM Type */
106#define I82443BXGX_DRAMC_DRAM_IS_EDO 0 /* 00 = EDO */
107#define I82443BXGX_DRAMC_DRAM_IS_SDRAM 1 /* 01 = SDRAM */
108#define I82443BXGX_DRAMC_DRAM_IS_RSDRAM 2 /* 10 = Registered SDRAM */
109
110
111#define I82443BXGX_DRB 0x60 /* 8x 8bit registers starting at this PCI
112 * config space offset. */
113
114
115/* FIXME - don't poll when ECC disabled? */
116
117
118struct i82443bxgx_edacmc_error_info {
119 u32 eap;
120};
121
122
123static void i82443bxgx_edacmc_get_error_info (struct mem_ctl_info *mci,
124 struct i82443bxgx_edacmc_error_info *info)
125{
126 struct pci_dev *pdev;
127 pdev = to_pci_dev(mci->dev);
128 pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap);
129 if (info->eap & I82443BXGX_EAP_OFFSET_SBE)
130 /* Clear error to allow next error to be reported [p.61] */
131 pci_write_bits32(pdev, I82443BXGX_EAP,
132 I82443BXGX_EAP_OFFSET_SBE,
133 I82443BXGX_EAP_OFFSET_SBE);
134
135 if (info->eap & I82443BXGX_EAP_OFFSET_MBE)
136 /* Clear error to allow next error to be reported [p.61] */
137 pci_write_bits32(pdev, I82443BXGX_EAP,
138 I82443BXGX_EAP_OFFSET_MBE,
139 I82443BXGX_EAP_OFFSET_MBE);
140}
141
142
143static int i82443bxgx_edacmc_process_error_info (struct mem_ctl_info *mci,
144 struct i82443bxgx_edacmc_error_info *info, int handle_errors)
145{
146 int error_found = 0;
147 u32 eapaddr, page, pageoffset;
148
149 /* bits 30:12 hold the 4kb block in which the error occurred
150 * [p.61] */
151 eapaddr = (info->eap & 0xfffff000);
152 page = eapaddr >> PAGE_SHIFT;
153 pageoffset = eapaddr - (page << PAGE_SHIFT);
154
155 if (info->eap & I82443BXGX_EAP_OFFSET_SBE) {
156 error_found = 1;
157 if (handle_errors)
158 edac_mc_handle_ce(
159 mci, page, pageoffset,
160 /* 440BX/GX don't make syndrome information available */
161 0,
162 edac_mc_find_csrow_by_page(mci, page),
163 0, /* channel */
164 mci->ctl_name);
165 }
166
167 if (info->eap & I82443BXGX_EAP_OFFSET_MBE) {
168 error_found = 1;
169 if (handle_errors)
170 edac_mc_handle_ue(
171 mci, page, pageoffset,
172 edac_mc_find_csrow_by_page(mci, page),
173 mci->ctl_name);
174 }
175
176 return error_found;
177}
178
179
180static void i82443bxgx_edacmc_check(struct mem_ctl_info *mci)
181{
182 struct i82443bxgx_edacmc_error_info info;
183
184 debugf1("MC%d: " __FILE__ ": %s()\n", mci->mc_idx, __func__);
185 i82443bxgx_edacmc_get_error_info(mci, &info);
186 i82443bxgx_edacmc_process_error_info(mci, &info, 1);
187}
188
189
190static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
191 struct pci_dev *pdev,
192 enum edac_type edac_mode,
193 enum mem_type mtype)
194{
195 struct csrow_info *csrow;
196 int index;
197 u8 drbar, dramc;
198 u32 row_base, row_high_limit, row_high_limit_last;
199
200 pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc);
201 row_high_limit_last = 0;
202 for (index = 0; index < mci->nr_csrows; index++) {
203 csrow = &mci->csrows[index];
204 pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar);
205 debugf1("MC%d: " __FILE__ ": %s() Row=%d DRB = %#0x\n",
206 mci->mc_idx, __func__, index, drbar);
207 row_high_limit = ((u32) drbar << 23);
208 /* find the DRAM Chip Select Base address and mask */
209 debugf1("MC%d: " __FILE__ ": %s() Row=%d, "
210 "Boundry Address=%#0x, Last = %#0x \n",
211 mci->mc_idx, __func__, index, row_high_limit,
212 row_high_limit_last);
213
214 /* 440GX goes to 2GB, represented with a DRB of 0. */
215 if (row_high_limit_last && !row_high_limit)
216 row_high_limit = 1UL << 31;
217
218 /* This row is empty [p.49] */
219 if (row_high_limit == row_high_limit_last)
220 continue;
221 row_base = row_high_limit_last;
222 csrow->first_page = row_base >> PAGE_SHIFT;
223 csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
224 csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
225 /* EAP reports in 4kilobyte granularity [61] */
226 csrow->grain = 1 << 12;
227 csrow->mtype = mtype;
228 /* I don't think 440BX can tell you device type? FIXME? */
229 csrow->dtype = DEV_UNKNOWN;
230 /* Mode is global to all rows on 440BX */
231 csrow->edac_mode = edac_mode;
232 row_high_limit_last = row_high_limit;
233 }
234}
235
236
237static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
238{
239 struct mem_ctl_info *mci;
240 u8 dramc;
241 u32 nbxcfg, ecc_mode;
242 enum mem_type mtype;
243 enum edac_type edac_mode;
244
245 debugf0("MC: " __FILE__ ": %s()\n", __func__);
246
247 /* Something is really hosed if PCI config space reads from
248 the MC aren't working. */
249 if (pci_read_config_dword(pdev, I82443BXGX_NBXCFG, &nbxcfg))
250 return -EIO;
251
252 mci = edac_mc_alloc(0, I82443BXGX_NR_CSROWS, I82443BXGX_NR_CHANS);
253
254 if (mci == NULL)
255 return -ENOMEM;
256
257 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
258 mci->dev = &pdev->dev;
259 mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR;
260 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
261 pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc);
262 switch ((dramc >> I82443BXGX_DRAMC_OFFSET_DT) & (BIT(0) | BIT(1))) {
263 case I82443BXGX_DRAMC_DRAM_IS_EDO:
264 mtype = MEM_EDO;
265 break;
266 case I82443BXGX_DRAMC_DRAM_IS_SDRAM:
267 mtype = MEM_SDR;
268 break;
269 case I82443BXGX_DRAMC_DRAM_IS_RSDRAM:
270 mtype = MEM_RDR;
271 break;
272 default:
273 debugf0("Unknown/reserved DRAM type value in DRAMC register!\n");
274 mtype = -MEM_UNKNOWN;
275 }
276
277 if ((mtype == MEM_SDR) || (mtype == MEM_RDR))
278 mci->edac_cap = mci->edac_ctl_cap;
279 else
280 mci->edac_cap = EDAC_FLAG_NONE;
281
282 mci->scrub_cap = SCRUB_FLAG_HW_SRC;
283 pci_read_config_dword(pdev, I82443BXGX_NBXCFG, &nbxcfg);
284 ecc_mode = ((nbxcfg >> I82443BXGX_NBXCFG_OFFSET_DRAM_INTEGRITY) &
285 (BIT(0) | BIT(1)));
286
287 mci->scrub_mode = (ecc_mode == I82443BXGX_NBXCFG_INTEGRITY_SCRUB)
288 ? SCRUB_HW_SRC
289 : SCRUB_NONE;
290
291 switch(ecc_mode) {
292 case I82443BXGX_NBXCFG_INTEGRITY_NONE:
293 edac_mode = EDAC_NONE;
294 break;
295 case I82443BXGX_NBXCFG_INTEGRITY_EC:
296 edac_mode = EDAC_EC;
297 break;
298 case I82443BXGX_NBXCFG_INTEGRITY_ECC:
299 case I82443BXGX_NBXCFG_INTEGRITY_SCRUB:
300 edac_mode = EDAC_SECDED;
301 break;
302 default:
303 debugf0("%s(): Unknown/reserved ECC state in NBXCFG register!\n",
304 __func__);
305 edac_mode = EDAC_UNKNOWN;
306 break;
307 }
308
309 i82443bxgx_init_csrows(mci, pdev, edac_mode, mtype);
310
311 /* Many BIOSes don't clear error flags on boot, so do this
312 * here, or we get "phantom" errors occuring at module-load
313 * time. */
314 pci_write_bits32(pdev, I82443BXGX_EAP,
315 (I82443BXGX_EAP_OFFSET_SBE | I82443BXGX_EAP_OFFSET_MBE),
316 (I82443BXGX_EAP_OFFSET_SBE | I82443BXGX_EAP_OFFSET_MBE));
317
318 mci->mod_name = EDAC_MOD_STR;
319 mci->mod_ver = I82443_REVISION;
320 mci->ctl_name = "I82443BXGX";
321 mci->edac_check = i82443bxgx_edacmc_check;
322 mci->ctl_page_to_phys = NULL;
323
324 if (edac_mc_add_mc(mci, 0)) {
325 debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
326 goto fail;
327 }
328
329 debugf3("MC: " __FILE__ ": %s(): success\n", __func__);
330 return 0;
331
332fail:
333 edac_mc_free(mci);
334 return -ENODEV;
335}
336EXPORT_SYMBOL_GPL(i82443bxgx_edacmc_probe1);
337
338/* returns count (>= 0), or negative on error */
339static int __devinit i82443bxgx_edacmc_init_one(struct pci_dev *pdev,
340 const struct pci_device_id *ent)
341{
342 debugf0("MC: " __FILE__ ": %s()\n", __func__);
343
344 /* don't need to call pci_device_enable() */
345 return i82443bxgx_edacmc_probe1(pdev, ent->driver_data);
346}
347
348
349static void __devexit i82443bxgx_edacmc_remove_one(struct pci_dev *pdev)
350{
351 struct mem_ctl_info *mci;
352
353 debugf0(__FILE__ ": %s()\n", __func__);
354
355 if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL )
356 return;
357
358 edac_mc_free(mci);
359}
360EXPORT_SYMBOL_GPL(i82443bxgx_edacmc_remove_one);
361
362
363static const struct pci_device_id i82443bxgx_pci_tbl[] __devinitdata = {
364 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0)},
365 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2)},
366 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0)},
367 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_2)},
368 {0,} /* 0 terminated list. */
369};
370
371MODULE_DEVICE_TABLE(pci, i82443bxgx_pci_tbl);
372
373
374static struct pci_driver i82443bxgx_edacmc_driver = {
375 .name = EDAC_MOD_STR,
376 .probe = i82443bxgx_edacmc_init_one,
377 .remove = __devexit_p(i82443bxgx_edacmc_remove_one),
378 .id_table = i82443bxgx_pci_tbl,
379};
380
381
382static int __init i82443bxgx_edacmc_init(void)
383{
384 return pci_register_driver(&i82443bxgx_edacmc_driver);
385}
386
387
388static void __exit i82443bxgx_edacmc_exit(void)
389{
390 pci_unregister_driver(&i82443bxgx_edacmc_driver);
391}
392
393
394module_init(i82443bxgx_edacmc_init);
395module_exit(i82443bxgx_edacmc_exit);
396
397
398MODULE_LICENSE("GPL");
399MODULE_AUTHOR("Tim Small <tim@buttersideup.com> - WPAD");
400MODULE_DESCRIPTION("EDAC MC support for Intel 82443BX/GX memory controllers");