i5100_edac.c 30.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * Intel 5100 Memory Controllers kernel module
 *
 * This file may be distributed under the terms of the
 * GNU General Public License.
 *
 * This module is based on the following document:
 *
 * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
 *      http://download.intel.com/design/chipsets/datashts/318378.pdf
 *
12 13
 * The intel 5100 has two independent channels. EDAC core currently
 * can not reflect this configuration so instead the chip-select
Lucas De Marchi's avatar
Lucas De Marchi committed
14
 * rows for each respective channel are laid out one after another,
15 16
 * the first half belonging to channel 0, the second half belonging
 * to channel 1.
17 18 19 20 21
 *
 * This driver is for DDR2 DIMMs, and it uses chip select to select among the
 * several ranks. However, instead of showing memories as ranks, it outputs
 * them as DIMM's. An internal table creates the association between ranks
 * and DIMM's.
22 23 24 25 26 27 28 29
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/edac.h>
#include <linux/delay.h>
#include <linux/mmzone.h>
30
#include <linux/debugfs.h>
31 32

#include "edac_core.h"
33
#include "edac_module.h"
34

Arthur Jones's avatar
Arthur Jones committed
35
/* register addresses */
36 37

/* device 16, func 1 */
38
#define I5100_MC		0x40	/* Memory Control Register */
Nils Carlson's avatar
Nils Carlson committed
39 40
#define 	I5100_MC_SCRBEN_MASK	(1 << 7)
#define 	I5100_MC_SCRBDONE_MASK	(1 << 4)
41 42 43 44 45 46 47 48 49 50 51 52
#define I5100_MS		0x44	/* Memory Status Register */
#define I5100_SPDDATA		0x48	/* Serial Presence Detect Status Reg */
#define I5100_SPDCMD		0x4c	/* Serial Presence Detect Command Reg */
#define I5100_TOLM		0x6c	/* Top of Low Memory */
#define I5100_MIR0		0x80	/* Memory Interleave Range 0 */
#define I5100_MIR1		0x84	/* Memory Interleave Range 1 */
#define I5100_AMIR_0		0x8c	/* Adjusted Memory Interleave Range 0 */
#define I5100_AMIR_1		0x90	/* Adjusted Memory Interleave Range 1 */
#define I5100_FERR_NF_MEM	0xa0	/* MC First Non Fatal Errors */
#define		I5100_FERR_NF_MEM_M16ERR_MASK	(1 << 16)
#define		I5100_FERR_NF_MEM_M15ERR_MASK	(1 << 15)
#define		I5100_FERR_NF_MEM_M14ERR_MASK	(1 << 14)
Arthur Jones's avatar
Arthur Jones committed
53 54 55 56 57 58
#define		I5100_FERR_NF_MEM_M12ERR_MASK	(1 << 12)
#define		I5100_FERR_NF_MEM_M11ERR_MASK	(1 << 11)
#define		I5100_FERR_NF_MEM_M10ERR_MASK	(1 << 10)
#define		I5100_FERR_NF_MEM_M6ERR_MASK	(1 << 6)
#define		I5100_FERR_NF_MEM_M5ERR_MASK	(1 << 5)
#define		I5100_FERR_NF_MEM_M4ERR_MASK	(1 << 4)
59
#define		I5100_FERR_NF_MEM_M1ERR_MASK	(1 << 1)
60 61 62
#define		I5100_FERR_NF_MEM_ANY_MASK	\
			(I5100_FERR_NF_MEM_M16ERR_MASK | \
			I5100_FERR_NF_MEM_M15ERR_MASK | \
Arthur Jones's avatar
Arthur Jones committed
63 64 65 66 67 68 69 70
			I5100_FERR_NF_MEM_M14ERR_MASK | \
			I5100_FERR_NF_MEM_M12ERR_MASK | \
			I5100_FERR_NF_MEM_M11ERR_MASK | \
			I5100_FERR_NF_MEM_M10ERR_MASK | \
			I5100_FERR_NF_MEM_M6ERR_MASK | \
			I5100_FERR_NF_MEM_M5ERR_MASK | \
			I5100_FERR_NF_MEM_M4ERR_MASK | \
			I5100_FERR_NF_MEM_M1ERR_MASK)
71
#define	I5100_NERR_NF_MEM	0xa4	/* MC Next Non-Fatal Errors */
72
#define I5100_EMASK_MEM		0xa8	/* MC Error Mask Register */
73 74 75 76 77 78 79 80
#define I5100_MEM0EINJMSK0	0x200	/* Injection Mask0 Register Channel 0 */
#define I5100_MEM1EINJMSK0	0x208	/* Injection Mask0 Register Channel 1 */
#define		I5100_MEMXEINJMSK0_EINJEN	(1 << 27)
#define I5100_MEM0EINJMSK1	0x204	/* Injection Mask1 Register Channel 0 */
#define I5100_MEM1EINJMSK1	0x206	/* Injection Mask1 Register Channel 1 */

/* Device 19, Function 0 */
#define I5100_DINJ0 0x9a
81 82 83 84 85 86 87 88 89 90 91

/* device 21 and 22, func 0 */
#define I5100_MTR_0	0x154	/* Memory Technology Registers 0-3 */
#define I5100_DMIR	0x15c	/* DIMM Interleave Range */
#define	I5100_VALIDLOG	0x18c	/* Valid Log Markers */
#define	I5100_NRECMEMA	0x190	/* Non-Recoverable Memory Error Log Reg A */
#define	I5100_NRECMEMB	0x194	/* Non-Recoverable Memory Error Log Reg B */
#define	I5100_REDMEMA	0x198	/* Recoverable Memory Data Error Log Reg A */
#define	I5100_REDMEMB	0x19c	/* Recoverable Memory Data Error Log Reg B */
#define	I5100_RECMEMA	0x1a0	/* Recoverable Memory Error Log Reg A */
#define	I5100_RECMEMB	0x1a4	/* Recoverable Memory Error Log Reg B */
Arthur Jones's avatar
Arthur Jones committed
92 93 94 95
#define I5100_MTR_4	0x1b0	/* Memory Technology Registers 4,5 */

/* bit field accessors */

Nils Carlson's avatar
Nils Carlson committed
96 97 98 99 100
static inline u32 i5100_mc_scrben(u32 mc)
{
	return mc >> 7 & 1;
}

Arthur Jones's avatar
Arthur Jones committed
101 102 103 104 105
static inline u32 i5100_mc_errdeten(u32 mc)
{
	return mc >> 5 & 1;
}

Nils Carlson's avatar
Nils Carlson committed
106 107 108 109 110
static inline u32 i5100_mc_scrbdone(u32 mc)
{
	return mc >> 4 & 1;
}

Arthur Jones's avatar
Arthur Jones committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
static inline u16 i5100_spddata_rdo(u16 a)
{
	return a >> 15 & 1;
}

static inline u16 i5100_spddata_sbe(u16 a)
{
	return a >> 13 & 1;
}

static inline u16 i5100_spddata_busy(u16 a)
{
	return a >> 12 & 1;
}

static inline u16 i5100_spddata_data(u16 a)
{
	return a & ((1 << 8) - 1);
}

static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba,
				      u32 data, u32 cmd)
{
	return	((dti & ((1 << 4) - 1))  << 28) |
		((ckovrd & 1)            << 27) |
		((sa & ((1 << 3) - 1))   << 24) |
		((ba & ((1 << 8) - 1))   << 16) |
		((data & ((1 << 8) - 1)) <<  8) |
		(cmd & 1);
}

static inline u16 i5100_tolm_tolm(u16 a)
{
	return a >> 12 & ((1 << 4) - 1);
}

static inline u16 i5100_mir_limit(u16 a)
{
	return a >> 4 & ((1 << 12) - 1);
}

static inline u16 i5100_mir_way1(u16 a)
{
	return a >> 1 & 1;
}

static inline u16 i5100_mir_way0(u16 a)
{
	return a & 1;
}

static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a)
{
	return a >> 28 & 1;
}

static inline u32 i5100_ferr_nf_mem_any(u32 a)
{
	return a & I5100_FERR_NF_MEM_ANY_MASK;
}

static inline u32 i5100_nerr_nf_mem_any(u32 a)
{
	return i5100_ferr_nf_mem_any(a);
}

static inline u32 i5100_dmir_limit(u32 a)
{
	return a >> 16 & ((1 << 11) - 1);
}

static inline u32 i5100_dmir_rank(u32 a, u32 i)
{
	return a >> (4 * i) & ((1 << 2) - 1);
}

static inline u16 i5100_mtr_present(u16 a)
{
	return a >> 10 & 1;
}

static inline u16 i5100_mtr_ethrottle(u16 a)
{
	return a >> 9 & 1;
}

static inline u16 i5100_mtr_width(u16 a)
{
	return a >> 8 & 1;
}

static inline u16 i5100_mtr_numbank(u16 a)
{
	return a >> 6 & 1;
}

static inline u16 i5100_mtr_numrow(u16 a)
{
	return a >> 2 & ((1 << 2) - 1);
}

static inline u16 i5100_mtr_numcol(u16 a)
{
	return a & ((1 << 2) - 1);
}


static inline u32 i5100_validlog_redmemvalid(u32 a)
{
	return a >> 2 & 1;
}

static inline u32 i5100_validlog_recmemvalid(u32 a)
{
	return a >> 1 & 1;
}

static inline u32 i5100_validlog_nrecmemvalid(u32 a)
{
	return a & 1;
}

static inline u32 i5100_nrecmema_merr(u32 a)
{
	return a >> 15 & ((1 << 5) - 1);
}

static inline u32 i5100_nrecmema_bank(u32 a)
{
	return a >> 12 & ((1 << 3) - 1);
}

static inline u32 i5100_nrecmema_rank(u32 a)
{
	return a >>  8 & ((1 << 3) - 1);
}

static inline u32 i5100_nrecmema_dm_buf_id(u32 a)
{
	return a & ((1 << 8) - 1);
}

static inline u32 i5100_nrecmemb_cas(u32 a)
{
	return a >> 16 & ((1 << 13) - 1);
}

static inline u32 i5100_nrecmemb_ras(u32 a)
{
	return a & ((1 << 16) - 1);
}

static inline u32 i5100_redmemb_ecc_locator(u32 a)
{
	return a & ((1 << 18) - 1);
}

static inline u32 i5100_recmema_merr(u32 a)
{
	return i5100_nrecmema_merr(a);
}

static inline u32 i5100_recmema_bank(u32 a)
{
	return i5100_nrecmema_bank(a);
}

static inline u32 i5100_recmema_rank(u32 a)
{
	return i5100_nrecmema_rank(a);
}

static inline u32 i5100_recmemb_cas(u32 a)
{
	return i5100_nrecmemb_cas(a);
}

static inline u32 i5100_recmemb_ras(u32 a)
{
	return i5100_nrecmemb_ras(a);
}
292 293

/* some generic limits */
294 295
#define I5100_MAX_RANKS_PER_CHAN	6
#define I5100_CHANNELS			    2
296 297
#define I5100_MAX_RANKS_PER_DIMM	4
#define I5100_DIMM_ADDR_LINES		(6 - 3)	/* 64 bits / 8 bits per byte */
298
#define I5100_MAX_DIMM_SLOTS_PER_CHAN	4
299 300
#define I5100_MAX_RANK_INTERLEAVE	4
#define I5100_MAX_DMIRS			5
Nils Carlson's avatar
Nils Carlson committed
301
#define I5100_SCRUB_REFRESH_RATE	(5 * 60 * HZ)
302 303 304

struct i5100_priv {
	/* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
305
	int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
306 307 308 309

	/*
	 * mainboard chip select map -- maps i5100 chip selects to
	 * DIMM slot chip selects.  In the case of only 4 ranks per
310 311
	 * channel, the mapping is fairly obvious but not unique.
	 * we map -1 -> NC and assume both channels use the same
312 313 314
	 * map...
	 *
	 */
315
	int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
316 317 318 319 320

	/* memory interleave range */
	struct {
		u64	 limit;
		unsigned way[2];
321
	} mir[I5100_CHANNELS];
322 323

	/* adjusted memory interleave range register */
324
	unsigned amir[I5100_CHANNELS];
325 326 327 328 329

	/* dimm interleave range */
	struct {
		unsigned rank[I5100_MAX_RANK_INTERLEAVE];
		u64	 limit;
330
	} dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
331 332 333 334 335 336 337 338 339

	/* memory technology registers... */
	struct {
		unsigned present;	/* 0 or 1 */
		unsigned ethrottle;	/* 0 or 1 */
		unsigned width;		/* 4 or 8 bits  */
		unsigned numbank;	/* 2 or 3 lines */
		unsigned numrow;	/* 13 .. 16 lines */
		unsigned numcol;	/* 11 .. 12 lines */
340
	} mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
341 342

	u64 tolm;		/* top of low memory in bytes */
343
	unsigned ranksperchan;	/* number of ranks per channel */
344 345

	struct pci_dev *mc;	/* device 16 func 1 */
346
	struct pci_dev *einj;	/* device 19 func 0 */
347 348
	struct pci_dev *ch0mm;	/* device 21 func 0 */
	struct pci_dev *ch1mm;	/* device 22 func 0 */
Nils Carlson's avatar
Nils Carlson committed
349 350 351

	struct delayed_work i5100_scrubbing;
	int scrub_enable;
352 353 354 355 356 357 358 359

	/* Error injection */
	u8 inject_channel;
	u8 inject_hlinesel;
	u8 inject_deviceptr1;
	u8 inject_deviceptr2;
	u16 inject_eccmask1;
	u16 inject_eccmask2;
360 361

	struct dentry *debugfs;
362 363
};

364 365
static struct dentry *i5100_debugfs;

366
/* map a rank/chan to a slot number on the mainboard */
367
static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
368
			      int chan, int rank)
369 370 371 372
{
	const struct i5100_priv *priv = mci->pvt_info;
	int i;

373
	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
374
		int j;
375
		const int numrank = priv->dimm_numrank[chan][i];
376 377 378

		for (j = 0; j < numrank; j++)
			if (priv->dimm_csmap[i][j] == rank)
379
				return i * 2 + chan;
380 381 382 383 384 385 386
	}

	return -1;
}

static const char *i5100_err_msg(unsigned err)
{
Arthur Jones's avatar
Arthur Jones committed
387
	static const char *merrs[] = {
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
		"unknown", /* 0 */
		"uncorrectable data ECC on replay", /* 1 */
		"unknown", /* 2 */
		"unknown", /* 3 */
		"aliased uncorrectable demand data ECC", /* 4 */
		"aliased uncorrectable spare-copy data ECC", /* 5 */
		"aliased uncorrectable patrol data ECC", /* 6 */
		"unknown", /* 7 */
		"unknown", /* 8 */
		"unknown", /* 9 */
		"non-aliased uncorrectable demand data ECC", /* 10 */
		"non-aliased uncorrectable spare-copy data ECC", /* 11 */
		"non-aliased uncorrectable patrol data ECC", /* 12 */
		"unknown", /* 13 */
		"correctable demand data ECC", /* 14 */
		"correctable spare-copy data ECC", /* 15 */
		"correctable patrol data ECC", /* 16 */
		"unknown", /* 17 */
		"SPD protocol error", /* 18 */
		"unknown", /* 19 */
		"spare copy initiated", /* 20 */
		"spare copy completed", /* 21 */
	};
	unsigned i;

	for (i = 0; i < ARRAY_SIZE(merrs); i++)
		if (1 << i & err)
			return merrs[i];

	return "none";
}

420
/* convert csrow index into a rank (per channel -- 0..5) */
421 422 423 424
static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
{
	const struct i5100_priv *priv = mci->pvt_info;

425
	return csrow % priv->ranksperchan;
426 427
}

428 429
/* convert csrow index into a channel (0..1) */
static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow)
430 431 432
{
	const struct i5100_priv *priv = mci->pvt_info;

433
	return csrow / priv->ranksperchan;
434 435 436
}

static void i5100_handle_ce(struct mem_ctl_info *mci,
437
			    int chan,
438 439 440 441 442 443 444
			    unsigned bank,
			    unsigned rank,
			    unsigned long syndrome,
			    unsigned cas,
			    unsigned ras,
			    const char *msg)
{
445
	char detail[80];
446

447 448 449 450
	/* Form out message */
	snprintf(detail, sizeof(detail),
		 "bank %u, cas %u, ras %u\n",
		 bank, cas, ras);
451

452
	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
453 454
			     0, 0, syndrome,
			     chan, rank, -1,
455
			     msg, detail);
456 457 458
}

static void i5100_handle_ue(struct mem_ctl_info *mci,
459
			    int chan,
460 461 462 463 464 465 466
			    unsigned bank,
			    unsigned rank,
			    unsigned long syndrome,
			    unsigned cas,
			    unsigned ras,
			    const char *msg)
{
467
	char detail[80];
468

469 470 471 472
	/* Form out message */
	snprintf(detail, sizeof(detail),
		 "bank %u, cas %u, ras %u\n",
		 bank, cas, ras);
473

474
	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
475 476
			     0, 0, syndrome,
			     chan, rank, -1,
477
			     msg, detail);
478 479
}

480
static void i5100_read_log(struct mem_ctl_info *mci, int chan,
481 482 483
			   u32 ferr, u32 nerr)
{
	struct i5100_priv *priv = mci->pvt_info;
484
	struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
485 486 487 488 489 490 491 492 493 494 495 496
	u32 dw;
	u32 dw2;
	unsigned syndrome = 0;
	unsigned ecc_loc = 0;
	unsigned merr;
	unsigned bank;
	unsigned rank;
	unsigned cas;
	unsigned ras;

	pci_read_config_dword(pdev, I5100_VALIDLOG, &dw);

Arthur Jones's avatar
Arthur Jones committed
497
	if (i5100_validlog_redmemvalid(dw)) {
498
		pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
Arthur Jones's avatar
Arthur Jones committed
499
		syndrome = dw2;
500
		pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
Arthur Jones's avatar
Arthur Jones committed
501
		ecc_loc = i5100_redmemb_ecc_locator(dw2);
502 503
	}

Arthur Jones's avatar
Arthur Jones committed
504
	if (i5100_validlog_recmemvalid(dw)) {
505 506 507
		const char *msg;

		pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
Arthur Jones's avatar
Arthur Jones committed
508 509 510
		merr = i5100_recmema_merr(dw2);
		bank = i5100_recmema_bank(dw2);
		rank = i5100_recmema_rank(dw2);
511 512

		pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
Arthur Jones's avatar
Arthur Jones committed
513 514
		cas = i5100_recmemb_cas(dw2);
		ras = i5100_recmemb_ras(dw2);
515 516 517 518 519 520 521 522

		/* FIXME:  not really sure if this is what merr is...
		 */
		if (!merr)
			msg = i5100_err_msg(ferr);
		else
			msg = i5100_err_msg(nerr);

523
		i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
524 525
	}

Arthur Jones's avatar
Arthur Jones committed
526
	if (i5100_validlog_nrecmemvalid(dw)) {
527 528 529
		const char *msg;

		pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
Arthur Jones's avatar
Arthur Jones committed
530 531 532
		merr = i5100_nrecmema_merr(dw2);
		bank = i5100_nrecmema_bank(dw2);
		rank = i5100_nrecmema_rank(dw2);
533 534

		pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
Arthur Jones's avatar
Arthur Jones committed
535 536
		cas = i5100_nrecmemb_cas(dw2);
		ras = i5100_nrecmemb_ras(dw2);
537 538 539 540 541 542 543 544

		/* FIXME:  not really sure if this is what merr is...
		 */
		if (!merr)
			msg = i5100_err_msg(ferr);
		else
			msg = i5100_err_msg(nerr);

545
		i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
546 547 548 549 550 551 552 553
	}

	pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
}

static void i5100_check_error(struct mem_ctl_info *mci)
{
	struct i5100_priv *priv = mci->pvt_info;
554
	u32 dw, dw2;
555 556

	pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
Arthur Jones's avatar
Arthur Jones committed
557
	if (i5100_ferr_nf_mem_any(dw)) {
558 559 560

		pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);

Arthur Jones's avatar
Arthur Jones committed
561 562 563
		i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
			       i5100_ferr_nf_mem_any(dw),
			       i5100_nerr_nf_mem_any(dw2));
564 565

		pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2);
566
	}
567
	pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
568 569
}

Nils Carlson's avatar
Nils Carlson committed
570 571 572 573 574 575 576 577
/* The i5100 chipset will scrub the entire memory once, then
 * set a done bit. Continuous scrubbing is achieved by enqueing
 * delayed work to a workqueue, checking every few minutes if
 * the scrubbing has completed and if so reinitiating it.
 */

static void i5100_refresh_scrubbing(struct work_struct *work)
{
578
	struct delayed_work *i5100_scrubbing = to_delayed_work(work);
Nils Carlson's avatar
Nils Carlson committed
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	struct i5100_priv *priv = container_of(i5100_scrubbing,
					       struct i5100_priv,
					       i5100_scrubbing);
	u32 dw;

	pci_read_config_dword(priv->mc, I5100_MC, &dw);

	if (priv->scrub_enable) {

		pci_read_config_dword(priv->mc, I5100_MC, &dw);

		if (i5100_mc_scrbdone(dw)) {
			dw |= I5100_MC_SCRBEN_MASK;
			pci_write_config_dword(priv->mc, I5100_MC, dw);
			pci_read_config_dword(priv->mc, I5100_MC, &dw);
		}

		schedule_delayed_work(&(priv->i5100_scrubbing),
				      I5100_SCRUB_REFRESH_RATE);
	}
}
/*
 * The bandwidth is based on experimentation, feel free to refine it.
 */
603
static int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth)
Nils Carlson's avatar
Nils Carlson committed
604 605 606 607 608
{
	struct i5100_priv *priv = mci->pvt_info;
	u32 dw;

	pci_read_config_dword(priv->mc, I5100_MC, &dw);
609
	if (bandwidth) {
Nils Carlson's avatar
Nils Carlson committed
610 611 612 613 614 615 616 617 618 619 620 621 622
		priv->scrub_enable = 1;
		dw |= I5100_MC_SCRBEN_MASK;
		schedule_delayed_work(&(priv->i5100_scrubbing),
				      I5100_SCRUB_REFRESH_RATE);
	} else {
		priv->scrub_enable = 0;
		dw &= ~I5100_MC_SCRBEN_MASK;
		cancel_delayed_work(&(priv->i5100_scrubbing));
	}
	pci_write_config_dword(priv->mc, I5100_MC, dw);

	pci_read_config_dword(priv->mc, I5100_MC, &dw);

623
	bandwidth = 5900000 * i5100_mc_scrben(dw);
Nils Carlson's avatar
Nils Carlson committed
624

625
	return bandwidth;
Nils Carlson's avatar
Nils Carlson committed
626 627
}

628
static int i5100_get_scrub_rate(struct mem_ctl_info *mci)
Nils Carlson's avatar
Nils Carlson committed
629 630 631 632 633 634
{
	struct i5100_priv *priv = mci->pvt_info;
	u32 dw;

	pci_read_config_dword(priv->mc, I5100_MC, &dw);

635
	return 5900000 * i5100_mc_scrben(dw);
Nils Carlson's avatar
Nils Carlson committed
636 637
}

638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
static struct pci_dev *pci_get_device_func(unsigned vendor,
					   unsigned device,
					   unsigned func)
{
	struct pci_dev *ret = NULL;

	while (1) {
		ret = pci_get_device(vendor, device, ret);

		if (!ret)
			break;

		if (PCI_FUNC(ret->devfn) == func)
			break;
	}

	return ret;
}

657
static unsigned long i5100_npages(struct mem_ctl_info *mci, int csrow)
658 659
{
	struct i5100_priv *priv = mci->pvt_info;
660 661
	const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow);
	const unsigned chan = i5100_csrow_to_chan(mci, csrow);
662 663 664
	unsigned addr_lines;

	/* dimm present? */
665
	if (!priv->mtr[chan][chan_rank].present)
666 667 668 669
		return 0ULL;

	addr_lines =
		I5100_DIMM_ADDR_LINES +
670 671 672
		priv->mtr[chan][chan_rank].numcol +
		priv->mtr[chan][chan_rank].numrow +
		priv->mtr[chan][chan_rank].numbank;
673 674 675 676 677

	return (unsigned long)
		((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
}

678
static void i5100_init_mtr(struct mem_ctl_info *mci)
679 680 681 682 683
{
	struct i5100_priv *priv = mci->pvt_info;
	struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
	int i;

684
	for (i = 0; i < I5100_CHANNELS; i++) {
685 686 687
		int j;
		struct pci_dev *pdev = mms[i];

688
		for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
689 690 691 692 693 694 695
			const unsigned addr =
				(j < 4) ? I5100_MTR_0 + j * 2 :
					  I5100_MTR_4 + (j - 4) * 2;
			u16 w;

			pci_read_config_word(pdev, addr, &w);

Arthur Jones's avatar
Arthur Jones committed
696 697 698 699 700 701
			priv->mtr[i][j].present = i5100_mtr_present(w);
			priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w);
			priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w);
			priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w);
			priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w);
			priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w);
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
		}
	}
}

/*
 * FIXME: make this into a real i2c adapter (so that dimm-decode
 * will work)?
 */
static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
			       u8 ch, u8 slot, u8 addr, u8 *byte)
{
	struct i5100_priv *priv = mci->pvt_info;
	u16 w;
	unsigned long et;

	pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
Arthur Jones's avatar
Arthur Jones committed
718
	if (i5100_spddata_busy(w))
719 720
		return -1;

Arthur Jones's avatar
Arthur Jones committed
721 722 723
	pci_write_config_dword(priv->mc, I5100_SPDCMD,
			       i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr,
						   0, 0));
724 725 726 727 728 729

	/* wait up to 100ms */
	et = jiffies + HZ / 10;
	udelay(100);
	while (1) {
		pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
Arthur Jones's avatar
Arthur Jones committed
730
		if (!i5100_spddata_busy(w))
731 732 733 734
			break;
		udelay(100);
	}

Arthur Jones's avatar
Arthur Jones committed
735
	if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
736 737
		return -1;

Arthur Jones's avatar
Arthur Jones committed
738
	*byte = i5100_spddata_data(w);
739 740 741 742 743 744 745 746 747 748 749

	return 0;
}

/*
 * fill dimm chip select map
 *
 * FIXME:
 *   o not the only way to may chip selects to dimm slots
 *   o investigate if there is some way to obtain this map from the bios
 */
750
static void i5100_init_dimm_csmap(struct mem_ctl_info *mci)
751 752 753 754
{
	struct i5100_priv *priv = mci->pvt_info;
	int i;

755
	for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
756 757 758 759 760 761 762
		int j;

		for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
			priv->dimm_csmap[i][j] = -1; /* default NC */
	}

	/* only 2 chip selects per slot... */
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
	if (priv->ranksperchan == 4) {
		priv->dimm_csmap[0][0] = 0;
		priv->dimm_csmap[0][1] = 3;
		priv->dimm_csmap[1][0] = 1;
		priv->dimm_csmap[1][1] = 2;
		priv->dimm_csmap[2][0] = 2;
		priv->dimm_csmap[3][0] = 3;
	} else {
		priv->dimm_csmap[0][0] = 0;
		priv->dimm_csmap[0][1] = 1;
		priv->dimm_csmap[1][0] = 2;
		priv->dimm_csmap[1][1] = 3;
		priv->dimm_csmap[2][0] = 4;
		priv->dimm_csmap[2][1] = 5;
	}
778 779
}

780 781
static void i5100_init_dimm_layout(struct pci_dev *pdev,
				   struct mem_ctl_info *mci)
782 783 784 785
{
	struct i5100_priv *priv = mci->pvt_info;
	int i;

786
	for (i = 0; i < I5100_CHANNELS; i++) {
787 788
		int j;

789
		for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
790 791 792 793 794 795 796 797 798 799 800 801
			u8 rank;

			if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
				priv->dimm_numrank[i][j] = 0;
			else
				priv->dimm_numrank[i][j] = (rank & 3) + 1;
		}
	}

	i5100_init_dimm_csmap(mci);
}

802 803
static void i5100_init_interleaving(struct pci_dev *pdev,
				    struct mem_ctl_info *mci)
804 805 806 807 808 809 810 811
{
	u16 w;
	u32 dw;
	struct i5100_priv *priv = mci->pvt_info;
	struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
	int i;

	pci_read_config_word(pdev, I5100_TOLM, &w);
Arthur Jones's avatar
Arthur Jones committed
812
	priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
813 814

	pci_read_config_word(pdev, I5100_MIR0, &w);
Arthur Jones's avatar
Arthur Jones committed
815 816 817
	priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28;
	priv->mir[0].way[1] = i5100_mir_way1(w);
	priv->mir[0].way[0] = i5100_mir_way0(w);
818 819

	pci_read_config_word(pdev, I5100_MIR1, &w);
Arthur Jones's avatar
Arthur Jones committed
820 821 822
	priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28;
	priv->mir[1].way[1] = i5100_mir_way1(w);
	priv->mir[1].way[0] = i5100_mir_way0(w);
823 824 825 826 827 828

	pci_read_config_word(pdev, I5100_AMIR_0, &w);
	priv->amir[0] = w;
	pci_read_config_word(pdev, I5100_AMIR_1, &w);
	priv->amir[1] = w;

829
	for (i = 0; i < I5100_CHANNELS; i++) {
830 831 832 833 834 835 836 837
		int j;

		for (j = 0; j < 5; j++) {
			int k;

			pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw);

			priv->dmir[i][j].limit =
Arthur Jones's avatar
Arthur Jones committed
838
				(u64) i5100_dmir_limit(dw) << 28;
839 840
			for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
				priv->dmir[i][j].rank[k] =
Arthur Jones's avatar
Arthur Jones committed
841
					i5100_dmir_rank(dw, k);
842 843 844 845 846 847
		}
	}

	i5100_init_mtr(mci);
}

848
static void i5100_init_csrows(struct mem_ctl_info *mci)
849 850 851 852
{
	int i;
	struct i5100_priv *priv = mci->pvt_info;

853 854
	for (i = 0; i < mci->tot_dimms; i++) {
		struct dimm_info *dimm;
855
		const unsigned long npages = i5100_npages(mci, i);
856
		const unsigned chan = i5100_csrow_to_chan(mci, i);
857 858 859 860 861
		const unsigned rank = i5100_csrow_to_rank(mci, i);

		if (!npages)
			continue;

862 863
		dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
			       chan, rank, 0);
864

865
		dimm->nr_pages = npages;
866 867 868 869 870 871 872
		dimm->grain = 32;
		dimm->dtype = (priv->mtr[chan][rank].width == 4) ?
				DEV_X4 : DEV_X8;
		dimm->mtype = MEM_RDDR2;
		dimm->edac_mode = EDAC_SECDED;
		snprintf(dimm->label, sizeof(dimm->label), "DIMM%u",
			 i5100_rank_to_slot(mci, chan, rank));
873

874 875
		edac_dbg(2, "dimm channel %d, rank %d, size %ld\n",
			 chan, rank, (long)PAGES_TO_MiB(npages));
876 877 878
	}
}

879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
/****************************************************************************
 *                       Error injection routines
 ****************************************************************************/

static void i5100_do_inject(struct mem_ctl_info *mci)
{
	struct i5100_priv *priv = mci->pvt_info;
	u32 mask0;
	u16 mask1;

	/* MEM[1:0]EINJMSK0
	 * 31    - ADDRMATCHEN
	 * 29:28 - HLINESEL
	 *         00 Reserved
	 *         01 Lower half of cache line
	 *         10 Upper half of cache line
	 *         11 Both upper and lower parts of cache line
	 * 27    - EINJEN
	 * 25:19 - XORMASK1 for deviceptr1
	 * 9:5   - SEC2RAM for deviceptr2
	 * 4:0   - FIR2RAM for deviceptr1
	 */
	mask0 = ((priv->inject_hlinesel & 0x3) << 28) |
		I5100_MEMXEINJMSK0_EINJEN |
		((priv->inject_eccmask1 & 0xffff) << 10) |
		((priv->inject_deviceptr2 & 0x1f) << 5) |
		(priv->inject_deviceptr1 & 0x1f);

	/* MEM[1:0]EINJMSK1
	 * 15:0  - XORMASK2 for deviceptr2
	 */
	mask1 = priv->inject_eccmask2;

	if (priv->inject_channel == 0) {
		pci_write_config_dword(priv->mc, I5100_MEM0EINJMSK0, mask0);
		pci_write_config_word(priv->mc, I5100_MEM0EINJMSK1, mask1);
	} else {
		pci_write_config_dword(priv->mc, I5100_MEM1EINJMSK0, mask0);
		pci_write_config_word(priv->mc, I5100_MEM1EINJMSK1, mask1);
	}

	/* Error Injection Response Function
	 * Intel 5100 Memory Controller Hub Chipset (318378) datasheet
	 * hints about this register but carry no data about them. All
	 * data regarding device 19 is based on experimentation and the
	 * Intel 7300 Chipset Memory Controller Hub (318082) datasheet
	 * which appears to be accurate for the i5100 in this area.
	 *
	 * The injection code don't work without setting this register.
	 * The register needs to be flipped off then on else the hardware
	 * will only preform the first injection.
	 *
	 * Stop condition bits 7:4
	 * 1010 - Stop after one injection
	 * 1011 - Never stop injecting faults
	 *
	 * Start condition bits 3:0
	 * 1010 - Never start
	 * 1011 - Start immediately
	 */
	pci_write_config_byte(priv->einj, I5100_DINJ0, 0xaa);
	pci_write_config_byte(priv->einj, I5100_DINJ0, 0xab);
}

943 944 945 946 947 948 949 950 951 952 953 954 955
#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
static ssize_t inject_enable_write(struct file *file, const char __user *data,
		size_t count, loff_t *ppos)
{
	struct device *dev = file->private_data;
	struct mem_ctl_info *mci = to_mci(dev);

	i5100_do_inject(mci);

	return count;
}

static const struct file_operations i5100_inject_enable_fops = {
956
	.open = simple_open,
957 958 959 960 961 962 963 964 965 966 967
	.write = inject_enable_write,
	.llseek = generic_file_llseek,
};

static int i5100_setup_debugfs(struct mem_ctl_info *mci)
{
	struct i5100_priv *priv = mci->pvt_info;

	if (!i5100_debugfs)
		return -ENODEV;

968
	priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs);
969 970 971 972

	if (!priv->debugfs)
		return -ENOMEM;

973 974 975 976 977 978 979 980 981 982 983 984 985 986
	edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
				&priv->inject_channel);
	edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
				&priv->inject_hlinesel);
	edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
				&priv->inject_deviceptr1);
	edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
				&priv->inject_deviceptr2);
	edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
				&priv->inject_eccmask1);
	edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
				&priv->inject_eccmask2);
	edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
				&mci->dev, &i5100_inject_enable_fops);
987 988 989 990 991

	return 0;

}

992
static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
993 994 995
{
	int rc;
	struct mem_ctl_info *mci;
996
	struct edac_mc_layer layers[2];
997
	struct i5100_priv *priv;
998
	struct pci_dev *ch0mm, *ch1mm, *einj;
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
	int ret = 0;
	u32 dw;
	int ranksperch;

	if (PCI_FUNC(pdev->devfn) != 1)
		return -ENODEV;

	rc = pci_enable_device(pdev);
	if (rc < 0) {
		ret = rc;
		goto bail;
	}

1012 1013
	/* ECC enabled? */
	pci_read_config_dword(pdev, I5100_MC, &dw);
Arthur Jones's avatar
Arthur Jones committed
1014
	if (!i5100_mc_errdeten(dw)) {
1015 1016
		printk(KERN_INFO "i5100_edac: ECC not enabled.\n");
		ret = -ENODEV;
Arthur Jones's avatar
Arthur Jones committed
1017
		goto bail_pdev;
1018 1019
	}

1020 1021 1022 1023
	/* figure out how many ranks, from strapped state of 48GB_Mode input */
	pci_read_config_dword(pdev, I5100_MS, &dw);
	ranksperch = !!(dw & (1 << 8)) * 2 + 4;

1024 1025 1026 1027 1028
	/* enable error reporting... */
	pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
	dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
	pci_write_config_dword(pdev, I5100_EMASK_MEM, dw);

1029 1030 1031
	/* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */
	ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
				    PCI_DEVICE_ID_INTEL_5100_21, 0);
Arthur Jones's avatar
Arthur Jones committed
1032 1033 1034 1035
	if (!ch0mm) {
		ret = -ENODEV;
		goto bail_pdev;
	}
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047

	rc = pci_enable_device(ch0mm);
	if (rc < 0) {
		ret = rc;
		goto bail_ch0;
	}

	/* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */
	ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
				    PCI_DEVICE_ID_INTEL_5100_22, 0);
	if (!ch1mm) {
		ret = -ENODEV;
Arthur Jones's avatar
Arthur Jones committed
1048
		goto bail_disable_ch0;
1049 1050 1051 1052 1053 1054 1055 1056
	}

	rc = pci_enable_device(ch1mm);
	if (rc < 0) {
		ret = rc;
		goto bail_ch1;
	}

1057 1058 1059 1060 1061 1062
	layers[0].type = EDAC_MC_LAYER_CHANNEL;
	layers[0].size = 2;
	layers[0].is_virt_csrow = false;
	layers[1].type = EDAC_MC_LAYER_SLOT;
	layers[1].size = ranksperch;
	layers[1].is_virt_csrow = true;
1063
	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
1064
			    sizeof(*priv));
1065 1066
	if (!mci) {
		ret = -ENOMEM;
Arthur Jones's avatar
Arthur Jones committed
1067
		goto bail_disable_ch1;
1068 1069
	}

1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085

	/* device 19, func 0, Error injection */
	einj = pci_get_device_func(PCI_VENDOR_ID_INTEL,
				    PCI_DEVICE_ID_INTEL_5100_19, 0);
	if (!einj) {
		ret = -ENODEV;
		goto bail_einj;
	}

	rc = pci_enable_device(einj);
	if (rc < 0) {
		ret = rc;
		goto bail_disable_einj;
	}


1086
	mci->pdev = &pdev->dev;
1087 1088

	priv = mci->pvt_info;
1089
	priv->ranksperchan = ranksperch;
1090 1091 1092
	priv->mc = pdev;
	priv->ch0mm = ch0mm;
	priv->ch1mm = ch1mm;
1093
	priv->einj = einj;
1094

Nils Carlson's avatar
Nils Carlson committed
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
	INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing);

	/* If scrubbing was already enabled by the bios, start maintaining it */
	pci_read_config_dword(pdev, I5100_MC, &dw);
	if (i5100_mc_scrben(dw)) {
		priv->scrub_enable = 1;
		schedule_delayed_work(&(priv->i5100_scrubbing),
				      I5100_SCRUB_REFRESH_RATE);
	}

1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
	i5100_init_dimm_layout(pdev, mci);
	i5100_init_interleaving(pdev, mci);

	mci->mtype_cap = MEM_FLAG_FB_DDR2;
	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
	mci->edac_cap = EDAC_FLAG_SECDED;
	mci->mod_name = "i5100_edac.c";
	mci->mod_ver = "not versioned";
	mci->ctl_name = "i5100";
	mci->dev_name = pci_name(pdev);
Arthur Jones's avatar
Arthur Jones committed
1115
	mci->ctl_page_to_phys = NULL;
1116 1117

	mci->edac_check = i5100_check_error;
Nils Carlson's avatar
Nils Carlson committed
1118 1119
	mci->set_sdram_scrub_rate = i5100_set_scrub_rate;
	mci->get_sdram_scrub_rate = i5100_get_scrub_rate;
1120

1121 1122 1123 1124 1125 1126 1127
	priv->inject_channel = 0;
	priv->inject_hlinesel = 0;
	priv->inject_deviceptr1 = 0;
	priv->inject_deviceptr2 = 0;
	priv->inject_eccmask1 = 0;
	priv->inject_eccmask2 = 0;

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
	i5100_init_csrows(mci);

	/* this strange construction seems to be in every driver, dunno why */
	switch (edac_op_state) {
	case EDAC_OPSTATE_POLL:
	case EDAC_OPSTATE_NMI:
		break;
	default:
		edac_op_state = EDAC_OPSTATE_POLL;
		break;
	}

	if (edac_mc_add_mc(mci)) {
		ret = -ENODEV;
Nils Carlson's avatar
Nils Carlson committed
1142
		goto bail_scrub;
1143 1144
	}

1145 1146
	i5100_setup_debugfs(mci);

Arthur Jones's avatar
Arthur Jones committed
1147
	return ret;
1148

Nils Carlson's avatar
Nils Carlson committed
1149 1150 1151
bail_scrub:
	priv->scrub_enable = 0;
	cancel_delayed_work_sync(&(priv->i5100_scrubbing));
1152 1153
	edac_mc_free(mci);

1154 1155 1156 1157 1158 1159
bail_disable_einj:
	pci_disable_device(einj);

bail_einj:
	pci_dev_put(einj);

Arthur Jones's avatar
Arthur Jones committed
1160 1161 1162
bail_disable_ch1:
	pci_disable_device(ch1mm);

1163 1164 1165
bail_ch1:
	pci_dev_put(ch1mm);

Arthur Jones's avatar
Arthur Jones committed
1166 1167 1168
bail_disable_ch0:
	pci_disable_device(ch0mm);

1169 1170 1171
bail_ch0:
	pci_dev_put(ch0mm);

Arthur Jones's avatar
Arthur Jones committed
1172 1173 1174
bail_pdev:
	pci_disable_device(pdev);

1175 1176 1177 1178
bail:
	return ret;
}

1179
static void i5100_remove_one(struct pci_dev *pdev)
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
{
	struct mem_ctl_info *mci;
	struct i5100_priv *priv;

	mci = edac_mc_del_mc(&pdev->dev);

	if (!mci)
		return;

	priv = mci->pvt_info;
Nils Carlson's avatar
Nils Carlson committed
1190

1191
	edac_debugfs_remove_recursive(priv->debugfs);
1192

Nils Carlson's avatar
Nils Carlson committed
1193 1194 1195
	priv->scrub_enable = 0;
	cancel_delayed_work_sync(&(priv->i5100_scrubbing));

Arthur Jones's avatar
Arthur Jones committed
1196 1197 1198
	pci_disable_device(pdev);
	pci_disable_device(priv->ch0mm);
	pci_disable_device(priv->ch1mm);
1199
	pci_disable_device(priv->einj);
1200 1201
	pci_dev_put(priv->ch0mm);
	pci_dev_put(priv->ch1mm);
1202
	pci_dev_put(priv->einj);
1203 1204 1205 1206

	edac_mc_free(mci);
}

1207
static const struct pci_device_id i5100_pci_tbl[] = {
1208 1209 1210 1211 1212 1213 1214 1215 1216
	/* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
	{ 0, }
};
MODULE_DEVICE_TABLE(pci, i5100_pci_tbl);

static struct pci_driver i5100_driver = {
	.name = KBUILD_BASENAME,
	.probe = i5100_init_one,
1217
	.remove = i5100_remove_one,
1218 1219 1220 1221 1222 1223 1224
	.id_table = i5100_pci_tbl,
};

static int __init i5100_init(void)
{
	int pci_rc;

1225
	i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL);
1226

1227
	pci_rc = pci_register_driver(&i5100_driver);
1228 1229 1230 1231 1232
	return (pci_rc < 0) ? pci_rc : 0;
}

static void __exit i5100_exit(void)
{
1233
	edac_debugfs_remove(i5100_debugfs);
1234

1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
	pci_unregister_driver(&i5100_driver);
}

module_init(i5100_init);
module_exit(i5100_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Arthur Jones <ajones@riverbed.com>");
MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers");