i82860_edac.c 7.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Intel 82860 Memory Controller kernel module
 * (C) 2005 Red Hat (http://www.redhat.com)
 * This file may be distributed under the terms of the
 * GNU General Public License.
 *
 * Written by Ben Woodard <woodard@redhat.com>
 * shamelessly copied from and based upon the edac_i82875 driver
 * by Thayne Harbaugh of Linux Networx. (http://lnxi.com)
 */


#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include "edac_mc.h"


D
Dave Peterson 已提交
22 23 24 25 26 27 28 29
#define i82860_printk(level, fmt, arg...) \
    edac_printk(level, "i82860", fmt, ##arg)


#define i82860_mc_printk(mci, level, fmt, arg...) \
    edac_mc_chipset_printk(mci, level, "i82860", fmt, ##arg)


30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
#ifndef PCI_DEVICE_ID_INTEL_82860_0
#define PCI_DEVICE_ID_INTEL_82860_0	0x2531
#endif				/* PCI_DEVICE_ID_INTEL_82860_0 */

#define I82860_MCHCFG 0x50
#define I82860_GBA 0x60
#define I82860_GBA_MASK 0x7FF
#define I82860_GBA_SHIFT 24
#define I82860_ERRSTS 0xC8
#define I82860_EAP 0xE4
#define I82860_DERRCTL_STS 0xE2

enum i82860_chips {
	I82860 = 0,
};

struct i82860_dev_info {
	const char *ctl_name;
};

struct i82860_error_info {
	u16 errsts;
	u32 eap;
	u16 derrsyn;
	u16 errsts2;
};

static const struct i82860_dev_info i82860_devs[] = {
	[I82860] = {
		    .ctl_name = "i82860"},
};

static struct pci_dev *mci_pdev = NULL;	/* init dev: in case that AGP code
					   has already registered driver */

static void i82860_get_error_info (struct mem_ctl_info *mci,
		struct i82860_error_info *info)
{
	/*
	 * This is a mess because there is no atomic way to read all the
	 * registers at once and the registers can transition from CE being
	 * overwritten by UE.
	 */
	pci_read_config_word(mci->pdev, I82860_ERRSTS, &info->errsts);
	pci_read_config_dword(mci->pdev, I82860_EAP, &info->eap);
	pci_read_config_word(mci->pdev, I82860_DERRCTL_STS, &info->derrsyn);
	pci_read_config_word(mci->pdev, I82860_ERRSTS, &info->errsts2);

	pci_write_bits16(mci->pdev, I82860_ERRSTS, 0x0003, 0x0003);

	/*
	 * If the error is the same for both reads then the first set of reads
	 * is valid.  If there is a change then there is a CE no info and the
	 * second set of reads is valid and should be UE info.
	 */
	if (!(info->errsts2 & 0x0003))
		return;
	if ((info->errsts ^ info->errsts2) & 0x0003) {
		pci_read_config_dword(mci->pdev, I82860_EAP, &info->eap);
		pci_read_config_word(mci->pdev, I82860_DERRCTL_STS,
		    &info->derrsyn);
	}
}

static int i82860_process_error_info (struct mem_ctl_info *mci,
		struct i82860_error_info *info, int handle_errors)
{
	int row;

	if (!(info->errsts2 & 0x0003))
		return 0;

	if (!handle_errors)
		return 1;

	if ((info->errsts ^ info->errsts2) & 0x0003) {
		edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
		info->errsts = info->errsts2;
	}

	info->eap >>= PAGE_SHIFT;
	row = edac_mc_find_csrow_by_page(mci, info->eap);

	if (info->errsts & 0x0002)
		edac_mc_handle_ue(mci, info->eap, 0, row, "i82860 UE");
	else
		edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row,
				       0, "i82860 UE");

	return 1;
}

static void i82860_check(struct mem_ctl_info *mci)
{
	struct i82860_error_info info;

D
Dave Peterson 已提交
126
	debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
127 128 129 130 131 132 133 134 135 136
	i82860_get_error_info(mci, &info);
	i82860_process_error_info(mci, &info, 1);
}

static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
{
	int rc = -ENODEV;
	int index;
	struct mem_ctl_info *mci = NULL;
	unsigned long last_cumul_size;
137
	struct i82860_error_info discard;
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

	u16 mchcfg_ddim;	/* DRAM Data Integrity Mode 0=none,2=edac */

	/* RDRAM has channels but these don't map onto the abstractions that
	   edac uses.
	   The device groups from the GRA registers seem to map reasonably
	   well onto the notion of a chip select row.
	   There are 16 GRA registers and since the name is associated with
	   the channel and the GRA registers map to physical devices so we are
	   going to make 1 channel for group.
	 */
	mci = edac_mc_alloc(0, 16, 1);
	if (!mci)
		return -ENOMEM;

D
Dave Peterson 已提交
153
	debugf3("%s(): init mci\n", __func__);
154 155 156 157 158 159 160 161 162 163

	mci->pdev = pdev;
	mci->mtype_cap = MEM_FLAG_DDR;


	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
	/* I"m not sure about this but I think that all RDRAM is SECDED */
	mci->edac_cap = EDAC_FLAG_SECDED;
	/* adjust FLAGS */

D
Dave Peterson 已提交
164
	mci->mod_name = EDAC_MOD_STR;
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
	mci->mod_ver = "$Revision: 1.1.2.6 $";
	mci->ctl_name = i82860_devs[dev_idx].ctl_name;
	mci->edac_check = i82860_check;
	mci->ctl_page_to_phys = NULL;

	pci_read_config_word(mci->pdev, I82860_MCHCFG, &mchcfg_ddim);
	mchcfg_ddim = mchcfg_ddim & 0x180;

	/*
	 * The group row boundary (GRA) reg values are boundary address
	 * for each DRAM row with a granularity of 16MB.  GRA regs are
	 * cumulative; therefore GRA15 will contain the total memory contained
	 * in all eight rows.
	 */
	for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
		u16 value;
		u32 cumul_size;
		struct csrow_info *csrow = &mci->csrows[index];

		pci_read_config_word(mci->pdev, I82860_GBA + index * 2,
				     &value);

		cumul_size = (value & I82860_GBA_MASK) <<
		    (I82860_GBA_SHIFT - PAGE_SHIFT);
D
Dave Peterson 已提交
189 190
		debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
			cumul_size);
191 192 193 194 195 196 197 198 199 200 201 202 203
		if (cumul_size == last_cumul_size)
			continue;	/* not populated */

		csrow->first_page = last_cumul_size;
		csrow->last_page = cumul_size - 1;
		csrow->nr_pages = cumul_size - last_cumul_size;
		last_cumul_size = cumul_size;
		csrow->grain = 1 << 12;	/* I82860_EAP has 4KiB reolution */
		csrow->mtype = MEM_RMBS;
		csrow->dtype = DEV_UNKNOWN;
		csrow->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
	}

204
	i82860_get_error_info(mci, &discard);  /* clear counters */
205 206

	if (edac_mc_add_mc(mci)) {
D
Dave Peterson 已提交
207
		debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
208 209 210
		edac_mc_free(mci);
	} else {
		/* get this far and it's successful */
D
Dave Peterson 已提交
211
		debugf3("%s(): success\n", __func__);
212 213 214 215 216 217 218 219 220 221 222
		rc = 0;
	}
	return rc;
}

/* returns count (>= 0), or negative on error */
static int __devinit i82860_init_one(struct pci_dev *pdev,
				     const struct pci_device_id *ent)
{
	int rc;

D
Dave Peterson 已提交
223
	debugf0("%s()\n", __func__);
224

D
Dave Peterson 已提交
225
	i82860_printk(KERN_INFO, "i82860 init one\n");
226 227 228 229 230 231 232 233 234 235 236 237
	if(pci_enable_device(pdev) < 0)
		return -EIO;
	rc = i82860_probe1(pdev, ent->driver_data);
	if(rc == 0)
		mci_pdev = pci_dev_get(pdev);
	return rc;
}

static void __devexit i82860_remove_one(struct pci_dev *pdev)
{
	struct mem_ctl_info *mci;

D
Dave Peterson 已提交
238
	debugf0("%s()\n", __func__);
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

	mci = edac_mc_find_mci_by_pdev(pdev);
	if ((mci != NULL) && (edac_mc_del_mc(mci) == 0))
		edac_mc_free(mci);
}

static const struct pci_device_id i82860_pci_tbl[] __devinitdata = {
	{PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
	 I82860},
	{0,}			/* 0 terminated list. */
};

MODULE_DEVICE_TABLE(pci, i82860_pci_tbl);

static struct pci_driver i82860_driver = {
D
Dave Peterson 已提交
254
	.name = EDAC_MOD_STR,
255 256 257 258 259
	.probe = i82860_init_one,
	.remove = __devexit_p(i82860_remove_one),
	.id_table = i82860_pci_tbl,
};

A
Alan Cox 已提交
260
static int __init i82860_init(void)
261 262 263
{
	int pci_rc;

D
Dave Peterson 已提交
264
	debugf3("%s()\n", __func__);
265
	if ((pci_rc = pci_register_driver(&i82860_driver)) < 0)
D
Dave Peterson 已提交
266
		goto fail0;
267 268 269 270 271 272

	if (!mci_pdev) {
		mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
					  PCI_DEVICE_ID_INTEL_82860_0, NULL);
		if (mci_pdev == NULL) {
			debugf0("860 pci_get_device fail\n");
D
Dave Peterson 已提交
273 274
			pci_rc = -ENODEV;
			goto fail1;
275 276 277 278
		}
		pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
		if (pci_rc < 0) {
			debugf0("860 init fail\n");
D
Dave Peterson 已提交
279 280
			pci_rc = -ENODEV;
			goto fail1;
281 282 283
		}
	}
	return 0;
D
Dave Peterson 已提交
284 285 286 287 288 289 290 291 292

fail1:
	pci_unregister_driver(&i82860_driver);

fail0:
	if (mci_pdev != NULL)
		pci_dev_put(mci_pdev);

	return pci_rc;
293 294 295 296
}

static void __exit i82860_exit(void)
{
D
Dave Peterson 已提交
297
	debugf3("%s()\n", __func__);
298 299

	pci_unregister_driver(&i82860_driver);
D
Dave Peterson 已提交
300 301

	if (mci_pdev != NULL)
302 303 304 305 306 307 308 309
		pci_dev_put(mci_pdev);
}

module_init(i82860_init);
module_exit(i82860_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR
D
Dave Peterson 已提交
310
    ("Red Hat Inc. (http://www.redhat.com) Ben Woodard <woodard@redhat.com>");
311
MODULE_DESCRIPTION("ECC support for Intel 82860 memory hub controllers");