blob: fd342163cf97fa827099bbdd5a694f20134bad6f [file] [log] [blame]
Alan Cox0d88a102006-01-18 17:44:10 -08001/*
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
Alan Cox0d88a102006-01-18 17:44:10 -080012#include <linux/config.h>
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/pci.h>
16#include <linux/pci_ids.h>
17#include <linux/slab.h>
18#include "edac_mc.h"
19
Dave Peterson537fba22006-03-26 01:38:40 -080020#define i82860_printk(level, fmt, arg...) \
Dave Petersone7ecd892006-03-26 01:38:52 -080021 edac_printk(level, "i82860", fmt, ##arg)
Dave Peterson537fba22006-03-26 01:38:40 -080022
23#define i82860_mc_printk(mci, level, fmt, arg...) \
Dave Petersone7ecd892006-03-26 01:38:52 -080024 edac_mc_chipset_printk(mci, level, "i82860", fmt, ##arg)
Dave Peterson537fba22006-03-26 01:38:40 -080025
Alan Cox0d88a102006-01-18 17:44:10 -080026#ifndef PCI_DEVICE_ID_INTEL_82860_0
27#define PCI_DEVICE_ID_INTEL_82860_0 0x2531
28#endif /* PCI_DEVICE_ID_INTEL_82860_0 */
29
30#define I82860_MCHCFG 0x50
31#define I82860_GBA 0x60
32#define I82860_GBA_MASK 0x7FF
33#define I82860_GBA_SHIFT 24
34#define I82860_ERRSTS 0xC8
35#define I82860_EAP 0xE4
36#define I82860_DERRCTL_STS 0xE2
37
38enum i82860_chips {
39 I82860 = 0,
40};
41
42struct i82860_dev_info {
43 const char *ctl_name;
44};
45
46struct i82860_error_info {
47 u16 errsts;
48 u32 eap;
49 u16 derrsyn;
50 u16 errsts2;
51};
52
53static const struct i82860_dev_info i82860_devs[] = {
54 [I82860] = {
Dave Petersone7ecd892006-03-26 01:38:52 -080055 .ctl_name = "i82860"
56 },
Alan Cox0d88a102006-01-18 17:44:10 -080057};
58
59static struct pci_dev *mci_pdev = NULL; /* init dev: in case that AGP code
Dave Petersone7ecd892006-03-26 01:38:52 -080060 * has already registered driver
61 */
Alan Cox0d88a102006-01-18 17:44:10 -080062
Dave Petersone7ecd892006-03-26 01:38:52 -080063static void i82860_get_error_info(struct mem_ctl_info *mci,
Alan Cox0d88a102006-01-18 17:44:10 -080064 struct i82860_error_info *info)
65{
66 /*
67 * This is a mess because there is no atomic way to read all the
68 * registers at once and the registers can transition from CE being
69 * overwritten by UE.
70 */
71 pci_read_config_word(mci->pdev, I82860_ERRSTS, &info->errsts);
72 pci_read_config_dword(mci->pdev, I82860_EAP, &info->eap);
73 pci_read_config_word(mci->pdev, I82860_DERRCTL_STS, &info->derrsyn);
74 pci_read_config_word(mci->pdev, I82860_ERRSTS, &info->errsts2);
75
76 pci_write_bits16(mci->pdev, I82860_ERRSTS, 0x0003, 0x0003);
77
78 /*
79 * If the error is the same for both reads then the first set of reads
80 * is valid. If there is a change then there is a CE no info and the
81 * second set of reads is valid and should be UE info.
82 */
83 if (!(info->errsts2 & 0x0003))
84 return;
Dave Petersone7ecd892006-03-26 01:38:52 -080085
Alan Cox0d88a102006-01-18 17:44:10 -080086 if ((info->errsts ^ info->errsts2) & 0x0003) {
87 pci_read_config_dword(mci->pdev, I82860_EAP, &info->eap);
88 pci_read_config_word(mci->pdev, I82860_DERRCTL_STS,
Dave Petersone7ecd892006-03-26 01:38:52 -080089 &info->derrsyn);
Alan Cox0d88a102006-01-18 17:44:10 -080090 }
91}
92
Dave Petersone7ecd892006-03-26 01:38:52 -080093static int i82860_process_error_info(struct mem_ctl_info *mci,
Alan Cox0d88a102006-01-18 17:44:10 -080094 struct i82860_error_info *info, int handle_errors)
95{
96 int row;
97
98 if (!(info->errsts2 & 0x0003))
99 return 0;
100
101 if (!handle_errors)
102 return 1;
103
104 if ((info->errsts ^ info->errsts2) & 0x0003) {
105 edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
106 info->errsts = info->errsts2;
107 }
108
109 info->eap >>= PAGE_SHIFT;
110 row = edac_mc_find_csrow_by_page(mci, info->eap);
111
112 if (info->errsts & 0x0002)
113 edac_mc_handle_ue(mci, info->eap, 0, row, "i82860 UE");
114 else
Dave Petersone7ecd892006-03-26 01:38:52 -0800115 edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row, 0,
116 "i82860 UE");
Alan Cox0d88a102006-01-18 17:44:10 -0800117
118 return 1;
119}
120
121static void i82860_check(struct mem_ctl_info *mci)
122{
123 struct i82860_error_info info;
124
Dave Peterson537fba22006-03-26 01:38:40 -0800125 debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
Alan Cox0d88a102006-01-18 17:44:10 -0800126 i82860_get_error_info(mci, &info);
127 i82860_process_error_info(mci, &info, 1);
128}
129
130static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
131{
132 int rc = -ENODEV;
133 int index;
134 struct mem_ctl_info *mci = NULL;
135 unsigned long last_cumul_size;
Dave Peterson749ede52006-03-26 01:38:45 -0800136 struct i82860_error_info discard;
Alan Cox0d88a102006-01-18 17:44:10 -0800137
138 u16 mchcfg_ddim; /* DRAM Data Integrity Mode 0=none,2=edac */
139
140 /* RDRAM has channels but these don't map onto the abstractions that
141 edac uses.
142 The device groups from the GRA registers seem to map reasonably
143 well onto the notion of a chip select row.
144 There are 16 GRA registers and since the name is associated with
145 the channel and the GRA registers map to physical devices so we are
146 going to make 1 channel for group.
147 */
148 mci = edac_mc_alloc(0, 16, 1);
Dave Petersone7ecd892006-03-26 01:38:52 -0800149
Alan Cox0d88a102006-01-18 17:44:10 -0800150 if (!mci)
151 return -ENOMEM;
152
Dave Peterson537fba22006-03-26 01:38:40 -0800153 debugf3("%s(): init mci\n", __func__);
Alan Cox0d88a102006-01-18 17:44:10 -0800154 mci->pdev = pdev;
155 mci->mtype_cap = MEM_FLAG_DDR;
156
Alan Cox0d88a102006-01-18 17:44:10 -0800157 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
158 /* I"m not sure about this but I think that all RDRAM is SECDED */
159 mci->edac_cap = EDAC_FLAG_SECDED;
160 /* adjust FLAGS */
161
Dave Peterson680cbbb2006-03-26 01:38:41 -0800162 mci->mod_name = EDAC_MOD_STR;
Alan Cox0d88a102006-01-18 17:44:10 -0800163 mci->mod_ver = "$Revision: 1.1.2.6 $";
164 mci->ctl_name = i82860_devs[dev_idx].ctl_name;
165 mci->edac_check = i82860_check;
166 mci->ctl_page_to_phys = NULL;
167
168 pci_read_config_word(mci->pdev, I82860_MCHCFG, &mchcfg_ddim);
169 mchcfg_ddim = mchcfg_ddim & 0x180;
170
171 /*
172 * The group row boundary (GRA) reg values are boundary address
173 * for each DRAM row with a granularity of 16MB. GRA regs are
174 * cumulative; therefore GRA15 will contain the total memory contained
175 * in all eight rows.
176 */
177 for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
178 u16 value;
179 u32 cumul_size;
180 struct csrow_info *csrow = &mci->csrows[index];
181
182 pci_read_config_word(mci->pdev, I82860_GBA + index * 2,
Dave Petersone7ecd892006-03-26 01:38:52 -0800183 &value);
Alan Cox0d88a102006-01-18 17:44:10 -0800184
185 cumul_size = (value & I82860_GBA_MASK) <<
186 (I82860_GBA_SHIFT - PAGE_SHIFT);
Dave Peterson537fba22006-03-26 01:38:40 -0800187 debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
188 cumul_size);
Dave Petersone7ecd892006-03-26 01:38:52 -0800189
Alan Cox0d88a102006-01-18 17:44:10 -0800190 if (cumul_size == last_cumul_size)
191 continue; /* not populated */
192
193 csrow->first_page = last_cumul_size;
194 csrow->last_page = cumul_size - 1;
195 csrow->nr_pages = cumul_size - last_cumul_size;
196 last_cumul_size = cumul_size;
Dave Petersone7ecd892006-03-26 01:38:52 -0800197 csrow->grain = 1 << 12; /* I82860_EAP has 4KiB reolution */
Alan Cox0d88a102006-01-18 17:44:10 -0800198 csrow->mtype = MEM_RMBS;
199 csrow->dtype = DEV_UNKNOWN;
200 csrow->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
201 }
202
Dave Peterson749ede52006-03-26 01:38:45 -0800203 i82860_get_error_info(mci, &discard); /* clear counters */
Alan Cox0d88a102006-01-18 17:44:10 -0800204
205 if (edac_mc_add_mc(mci)) {
Dave Peterson537fba22006-03-26 01:38:40 -0800206 debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
Alan Cox0d88a102006-01-18 17:44:10 -0800207 edac_mc_free(mci);
208 } else {
209 /* get this far and it's successful */
Dave Peterson537fba22006-03-26 01:38:40 -0800210 debugf3("%s(): success\n", __func__);
Alan Cox0d88a102006-01-18 17:44:10 -0800211 rc = 0;
212 }
Dave Petersone7ecd892006-03-26 01:38:52 -0800213
Alan Cox0d88a102006-01-18 17:44:10 -0800214 return rc;
215}
216
217/* returns count (>= 0), or negative on error */
218static int __devinit i82860_init_one(struct pci_dev *pdev,
Dave Petersone7ecd892006-03-26 01:38:52 -0800219 const struct pci_device_id *ent)
Alan Cox0d88a102006-01-18 17:44:10 -0800220{
221 int rc;
222
Dave Peterson537fba22006-03-26 01:38:40 -0800223 debugf0("%s()\n", __func__);
Dave Peterson537fba22006-03-26 01:38:40 -0800224 i82860_printk(KERN_INFO, "i82860 init one\n");
Dave Petersone7ecd892006-03-26 01:38:52 -0800225
226 if (pci_enable_device(pdev) < 0)
Alan Cox0d88a102006-01-18 17:44:10 -0800227 return -EIO;
Dave Petersone7ecd892006-03-26 01:38:52 -0800228
Alan Cox0d88a102006-01-18 17:44:10 -0800229 rc = i82860_probe1(pdev, ent->driver_data);
Dave Petersone7ecd892006-03-26 01:38:52 -0800230
231 if (rc == 0)
Alan Cox0d88a102006-01-18 17:44:10 -0800232 mci_pdev = pci_dev_get(pdev);
Dave Petersone7ecd892006-03-26 01:38:52 -0800233
Alan Cox0d88a102006-01-18 17:44:10 -0800234 return rc;
235}
236
237static void __devexit i82860_remove_one(struct pci_dev *pdev)
238{
239 struct mem_ctl_info *mci;
240
Dave Peterson537fba22006-03-26 01:38:40 -0800241 debugf0("%s()\n", __func__);
Alan Cox0d88a102006-01-18 17:44:10 -0800242
Dave Peterson18dbc332006-03-26 01:38:50 -0800243 if ((mci = edac_mc_del_mc(pdev)) == NULL)
244 return;
245
246 edac_mc_free(mci);
Alan Cox0d88a102006-01-18 17:44:10 -0800247}
248
249static const struct pci_device_id i82860_pci_tbl[] __devinitdata = {
Dave Petersone7ecd892006-03-26 01:38:52 -0800250 {
251 PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
252 I82860
253 },
254 {
255 0,
256 } /* 0 terminated list. */
Alan Cox0d88a102006-01-18 17:44:10 -0800257};
258
259MODULE_DEVICE_TABLE(pci, i82860_pci_tbl);
260
261static struct pci_driver i82860_driver = {
Dave Peterson680cbbb2006-03-26 01:38:41 -0800262 .name = EDAC_MOD_STR,
Alan Cox0d88a102006-01-18 17:44:10 -0800263 .probe = i82860_init_one,
264 .remove = __devexit_p(i82860_remove_one),
265 .id_table = i82860_pci_tbl,
266};
267
Alan Coxda9bb1d2006-01-18 17:44:13 -0800268static int __init i82860_init(void)
Alan Cox0d88a102006-01-18 17:44:10 -0800269{
270 int pci_rc;
271
Dave Peterson537fba22006-03-26 01:38:40 -0800272 debugf3("%s()\n", __func__);
Dave Petersone7ecd892006-03-26 01:38:52 -0800273
Alan Cox0d88a102006-01-18 17:44:10 -0800274 if ((pci_rc = pci_register_driver(&i82860_driver)) < 0)
Dave Petersone8a491b2006-03-26 01:38:43 -0800275 goto fail0;
Alan Cox0d88a102006-01-18 17:44:10 -0800276
277 if (!mci_pdev) {
Alan Cox0d88a102006-01-18 17:44:10 -0800278 mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Dave Petersone7ecd892006-03-26 01:38:52 -0800279 PCI_DEVICE_ID_INTEL_82860_0, NULL);
280
Alan Cox0d88a102006-01-18 17:44:10 -0800281 if (mci_pdev == NULL) {
282 debugf0("860 pci_get_device fail\n");
Dave Petersone8a491b2006-03-26 01:38:43 -0800283 pci_rc = -ENODEV;
284 goto fail1;
Alan Cox0d88a102006-01-18 17:44:10 -0800285 }
Dave Petersone7ecd892006-03-26 01:38:52 -0800286
Alan Cox0d88a102006-01-18 17:44:10 -0800287 pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
Dave Petersone7ecd892006-03-26 01:38:52 -0800288
Alan Cox0d88a102006-01-18 17:44:10 -0800289 if (pci_rc < 0) {
290 debugf0("860 init fail\n");
Dave Petersone8a491b2006-03-26 01:38:43 -0800291 pci_rc = -ENODEV;
292 goto fail1;
Alan Cox0d88a102006-01-18 17:44:10 -0800293 }
294 }
Dave Petersone7ecd892006-03-26 01:38:52 -0800295
Alan Cox0d88a102006-01-18 17:44:10 -0800296 return 0;
Dave Petersone8a491b2006-03-26 01:38:43 -0800297
298fail1:
299 pci_unregister_driver(&i82860_driver);
300
301fail0:
302 if (mci_pdev != NULL)
303 pci_dev_put(mci_pdev);
304
305 return pci_rc;
Alan Cox0d88a102006-01-18 17:44:10 -0800306}
307
308static void __exit i82860_exit(void)
309{
Dave Peterson537fba22006-03-26 01:38:40 -0800310 debugf3("%s()\n", __func__);
Alan Cox0d88a102006-01-18 17:44:10 -0800311
312 pci_unregister_driver(&i82860_driver);
Dave Petersone8a491b2006-03-26 01:38:43 -0800313
314 if (mci_pdev != NULL)
Alan Cox0d88a102006-01-18 17:44:10 -0800315 pci_dev_put(mci_pdev);
Alan Cox0d88a102006-01-18 17:44:10 -0800316}
317
318module_init(i82860_init);
319module_exit(i82860_exit);
320
321MODULE_LICENSE("GPL");
Dave Petersone7ecd892006-03-26 01:38:52 -0800322MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com) "
323 "Ben Woodard <woodard@redhat.com>");
Alan Cox0d88a102006-01-18 17:44:10 -0800324MODULE_DESCRIPTION("ECC support for Intel 82860 memory hub controllers");