xref: /openbmc/linux/drivers/edac/i3200_edac.c (revision f30795fb)
1dd8ef1dbSJason Uhlenkott /*
2dd8ef1dbSJason Uhlenkott  * Intel 3200/3210 Memory Controller kernel module
3dd8ef1dbSJason Uhlenkott  * Copyright (C) 2008-2009 Akamai Technologies, Inc.
4dd8ef1dbSJason Uhlenkott  * Portions by Hitoshi Mitake <h.mitake@gmail.com>.
5dd8ef1dbSJason Uhlenkott  *
6dd8ef1dbSJason Uhlenkott  * This file may be distributed under the terms of the
7dd8ef1dbSJason Uhlenkott  * GNU General Public License.
8dd8ef1dbSJason Uhlenkott  */
9dd8ef1dbSJason Uhlenkott 
10dd8ef1dbSJason Uhlenkott #include <linux/module.h>
11dd8ef1dbSJason Uhlenkott #include <linux/init.h>
12dd8ef1dbSJason Uhlenkott #include <linux/pci.h>
13dd8ef1dbSJason Uhlenkott #include <linux/pci_ids.h>
14dd8ef1dbSJason Uhlenkott #include <linux/edac.h>
15dd8ef1dbSJason Uhlenkott #include <linux/io.h>
1678d88e8aSMauro Carvalho Chehab #include "edac_module.h"
17dd8ef1dbSJason Uhlenkott 
182f8e2c87SChristoph Hellwig #include <linux/io-64-nonatomic-lo-hi.h>
19797a796aSHitoshi Mitake 
20dd8ef1dbSJason Uhlenkott #define EDAC_MOD_STR        "i3200_edac"
21dd8ef1dbSJason Uhlenkott 
22dd8ef1dbSJason Uhlenkott #define PCI_DEVICE_ID_INTEL_3200_HB    0x29f0
23dd8ef1dbSJason Uhlenkott 
2495b93287SMauro Carvalho Chehab #define I3200_DIMMS		4
25dd8ef1dbSJason Uhlenkott #define I3200_RANKS		8
26dd8ef1dbSJason Uhlenkott #define I3200_RANKS_PER_CHANNEL	4
27dd8ef1dbSJason Uhlenkott #define I3200_CHANNELS		2
28dd8ef1dbSJason Uhlenkott 
29dd8ef1dbSJason Uhlenkott /* Intel 3200 register addresses - device 0 function 0 - DRAM Controller */
30dd8ef1dbSJason Uhlenkott 
31dd8ef1dbSJason Uhlenkott #define I3200_MCHBAR_LOW	0x48	/* MCH Memory Mapped Register BAR */
32dd8ef1dbSJason Uhlenkott #define I3200_MCHBAR_HIGH	0x4c
33dd8ef1dbSJason Uhlenkott #define I3200_MCHBAR_MASK	0xfffffc000ULL	/* bits 35:14 */
34dd8ef1dbSJason Uhlenkott #define I3200_MMR_WINDOW_SIZE	16384
35dd8ef1dbSJason Uhlenkott 
36dd8ef1dbSJason Uhlenkott #define I3200_TOM		0xa0	/* Top of Memory (16b)
37dd8ef1dbSJason Uhlenkott 		 *
38dd8ef1dbSJason Uhlenkott 		 * 15:10 reserved
39dd8ef1dbSJason Uhlenkott 		 *  9:0  total populated physical memory
40dd8ef1dbSJason Uhlenkott 		 */
41dd8ef1dbSJason Uhlenkott #define I3200_TOM_MASK		0x3ff	/* bits 9:0 */
42dd8ef1dbSJason Uhlenkott #define I3200_TOM_SHIFT		26	/* 64MiB grain */
43dd8ef1dbSJason Uhlenkott 
44dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS		0xc8	/* Error Status Register (16b)
45dd8ef1dbSJason Uhlenkott 		 *
46dd8ef1dbSJason Uhlenkott 		 * 15    reserved
47dd8ef1dbSJason Uhlenkott 		 * 14    Isochronous TBWRR Run Behind FIFO Full
48dd8ef1dbSJason Uhlenkott 		 *       (ITCV)
49dd8ef1dbSJason Uhlenkott 		 * 13    Isochronous TBWRR Run Behind FIFO Put
50dd8ef1dbSJason Uhlenkott 		 *       (ITSTV)
51dd8ef1dbSJason Uhlenkott 		 * 12    reserved
52dd8ef1dbSJason Uhlenkott 		 * 11    MCH Thermal Sensor Event
53dd8ef1dbSJason Uhlenkott 		 *       for SMI/SCI/SERR (GTSE)
54dd8ef1dbSJason Uhlenkott 		 * 10    reserved
55dd8ef1dbSJason Uhlenkott 		 *  9    LOCK to non-DRAM Memory Flag (LCKF)
56dd8ef1dbSJason Uhlenkott 		 *  8    reserved
57dd8ef1dbSJason Uhlenkott 		 *  7    DRAM Throttle Flag (DTF)
58dd8ef1dbSJason Uhlenkott 		 *  6:2  reserved
59dd8ef1dbSJason Uhlenkott 		 *  1    Multi-bit DRAM ECC Error Flag (DMERR)
60dd8ef1dbSJason Uhlenkott 		 *  0    Single-bit DRAM ECC Error Flag (DSERR)
61dd8ef1dbSJason Uhlenkott 		 */
62dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS_UE		0x0002
63dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS_CE		0x0001
64dd8ef1dbSJason Uhlenkott #define I3200_ERRSTS_BITS	(I3200_ERRSTS_UE | I3200_ERRSTS_CE)
65dd8ef1dbSJason Uhlenkott 
66dd8ef1dbSJason Uhlenkott 
67dd8ef1dbSJason Uhlenkott /* Intel  MMIO register space - device 0 function 0 - MMR space */
68dd8ef1dbSJason Uhlenkott 
69dd8ef1dbSJason Uhlenkott #define I3200_C0DRB	0x200	/* Channel 0 DRAM Rank Boundary (16b x 4)
70dd8ef1dbSJason Uhlenkott 		 *
71dd8ef1dbSJason Uhlenkott 		 * 15:10 reserved
72dd8ef1dbSJason Uhlenkott 		 *  9:0  Channel 0 DRAM Rank Boundary Address
73dd8ef1dbSJason Uhlenkott 		 */
74dd8ef1dbSJason Uhlenkott #define I3200_C1DRB	0x600	/* Channel 1 DRAM Rank Boundary (16b x 4) */
75dd8ef1dbSJason Uhlenkott #define I3200_DRB_MASK	0x3ff	/* bits 9:0 */
76dd8ef1dbSJason Uhlenkott #define I3200_DRB_SHIFT	26	/* 64MiB grain */
77dd8ef1dbSJason Uhlenkott 
78dd8ef1dbSJason Uhlenkott #define I3200_C0ECCERRLOG	0x280	/* Channel 0 ECC Error Log (64b)
79dd8ef1dbSJason Uhlenkott 		 *
80dd8ef1dbSJason Uhlenkott 		 * 63:48 Error Column Address (ERRCOL)
81dd8ef1dbSJason Uhlenkott 		 * 47:32 Error Row Address (ERRROW)
82dd8ef1dbSJason Uhlenkott 		 * 31:29 Error Bank Address (ERRBANK)
83dd8ef1dbSJason Uhlenkott 		 * 28:27 Error Rank Address (ERRRANK)
84dd8ef1dbSJason Uhlenkott 		 * 26:24 reserved
85dd8ef1dbSJason Uhlenkott 		 * 23:16 Error Syndrome (ERRSYND)
86dd8ef1dbSJason Uhlenkott 		 * 15: 2 reserved
87dd8ef1dbSJason Uhlenkott 		 *    1  Multiple Bit Error Status (MERRSTS)
88dd8ef1dbSJason Uhlenkott 		 *    0  Correctable Error Status (CERRSTS)
89dd8ef1dbSJason Uhlenkott 		 */
90dd8ef1dbSJason Uhlenkott #define I3200_C1ECCERRLOG		0x680	/* Chan 1 ECC Error Log (64b) */
91dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_CE		0x1
92dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_UE		0x2
93dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_RANK_BITS	0x18000000
94dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_RANK_SHIFT	27
95dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_SYNDROME_BITS	0xff0000
96dd8ef1dbSJason Uhlenkott #define I3200_ECCERRLOG_SYNDROME_SHIFT	16
97dd8ef1dbSJason Uhlenkott #define I3200_CAPID0			0xe0	/* P.95 of spec for details */
98dd8ef1dbSJason Uhlenkott 
99dd8ef1dbSJason Uhlenkott struct i3200_priv {
100dd8ef1dbSJason Uhlenkott 	void __iomem *window;
101dd8ef1dbSJason Uhlenkott };
102dd8ef1dbSJason Uhlenkott 
103dd8ef1dbSJason Uhlenkott static int nr_channels;
104dd8ef1dbSJason Uhlenkott 
how_many_channels(struct pci_dev * pdev)105dd8ef1dbSJason Uhlenkott static int how_many_channels(struct pci_dev *pdev)
106dd8ef1dbSJason Uhlenkott {
1075f466cb0SMauro Carvalho Chehab 	int n_channels;
1085f466cb0SMauro Carvalho Chehab 
109dd8ef1dbSJason Uhlenkott 	unsigned char capid0_8b; /* 8th byte of CAPID0 */
110dd8ef1dbSJason Uhlenkott 
111dd8ef1dbSJason Uhlenkott 	pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b);
1125f466cb0SMauro Carvalho Chehab 
113dd8ef1dbSJason Uhlenkott 	if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */
114956b9ba1SJoe Perches 		edac_dbg(0, "In single channel mode\n");
1155f466cb0SMauro Carvalho Chehab 		n_channels = 1;
116dd8ef1dbSJason Uhlenkott 	} else {
117956b9ba1SJoe Perches 		edac_dbg(0, "In dual channel mode\n");
1185f466cb0SMauro Carvalho Chehab 		n_channels = 2;
119dd8ef1dbSJason Uhlenkott 	}
1205f466cb0SMauro Carvalho Chehab 
1215f466cb0SMauro Carvalho Chehab 	if (capid0_8b & 0x10) /* check if both channels are filled */
1225f466cb0SMauro Carvalho Chehab 		edac_dbg(0, "2 DIMMS per channel disabled\n");
1235f466cb0SMauro Carvalho Chehab 	else
1245f466cb0SMauro Carvalho Chehab 		edac_dbg(0, "2 DIMMS per channel enabled\n");
1255f466cb0SMauro Carvalho Chehab 
1265f466cb0SMauro Carvalho Chehab 	return n_channels;
127dd8ef1dbSJason Uhlenkott }
128dd8ef1dbSJason Uhlenkott 
eccerrlog_syndrome(u64 log)129dd8ef1dbSJason Uhlenkott static unsigned long eccerrlog_syndrome(u64 log)
130dd8ef1dbSJason Uhlenkott {
131dd8ef1dbSJason Uhlenkott 	return (log & I3200_ECCERRLOG_SYNDROME_BITS) >>
132dd8ef1dbSJason Uhlenkott 		I3200_ECCERRLOG_SYNDROME_SHIFT;
133dd8ef1dbSJason Uhlenkott }
134dd8ef1dbSJason Uhlenkott 
eccerrlog_row(int channel,u64 log)135dd8ef1dbSJason Uhlenkott static int eccerrlog_row(int channel, u64 log)
136dd8ef1dbSJason Uhlenkott {
137dd8ef1dbSJason Uhlenkott 	u64 rank = ((log & I3200_ECCERRLOG_RANK_BITS) >>
138dd8ef1dbSJason Uhlenkott 		I3200_ECCERRLOG_RANK_SHIFT);
139dd8ef1dbSJason Uhlenkott 	return rank | (channel * I3200_RANKS_PER_CHANNEL);
140dd8ef1dbSJason Uhlenkott }
141dd8ef1dbSJason Uhlenkott 
142dd8ef1dbSJason Uhlenkott enum i3200_chips {
143dd8ef1dbSJason Uhlenkott 	I3200 = 0,
144dd8ef1dbSJason Uhlenkott };
145dd8ef1dbSJason Uhlenkott 
146dd8ef1dbSJason Uhlenkott struct i3200_dev_info {
147dd8ef1dbSJason Uhlenkott 	const char *ctl_name;
148dd8ef1dbSJason Uhlenkott };
149dd8ef1dbSJason Uhlenkott 
150dd8ef1dbSJason Uhlenkott struct i3200_error_info {
151dd8ef1dbSJason Uhlenkott 	u16 errsts;
152dd8ef1dbSJason Uhlenkott 	u16 errsts2;
153dd8ef1dbSJason Uhlenkott 	u64 eccerrlog[I3200_CHANNELS];
154dd8ef1dbSJason Uhlenkott };
155dd8ef1dbSJason Uhlenkott 
156dd8ef1dbSJason Uhlenkott static const struct i3200_dev_info i3200_devs[] = {
157dd8ef1dbSJason Uhlenkott 	[I3200] = {
158dd8ef1dbSJason Uhlenkott 		.ctl_name = "i3200"
159dd8ef1dbSJason Uhlenkott 	},
160dd8ef1dbSJason Uhlenkott };
161dd8ef1dbSJason Uhlenkott 
162dd8ef1dbSJason Uhlenkott static struct pci_dev *mci_pdev;
163dd8ef1dbSJason Uhlenkott static int i3200_registered = 1;
164dd8ef1dbSJason Uhlenkott 
165dd8ef1dbSJason Uhlenkott 
i3200_clear_error_info(struct mem_ctl_info * mci)166dd8ef1dbSJason Uhlenkott static void i3200_clear_error_info(struct mem_ctl_info *mci)
167dd8ef1dbSJason Uhlenkott {
168dd8ef1dbSJason Uhlenkott 	struct pci_dev *pdev;
169dd8ef1dbSJason Uhlenkott 
170fd687502SMauro Carvalho Chehab 	pdev = to_pci_dev(mci->pdev);
171dd8ef1dbSJason Uhlenkott 
172dd8ef1dbSJason Uhlenkott 	/*
173dd8ef1dbSJason Uhlenkott 	 * Clear any error bits.
174dd8ef1dbSJason Uhlenkott 	 * (Yes, we really clear bits by writing 1 to them.)
175dd8ef1dbSJason Uhlenkott 	 */
176dd8ef1dbSJason Uhlenkott 	pci_write_bits16(pdev, I3200_ERRSTS, I3200_ERRSTS_BITS,
177dd8ef1dbSJason Uhlenkott 		I3200_ERRSTS_BITS);
178dd8ef1dbSJason Uhlenkott }
179dd8ef1dbSJason Uhlenkott 
i3200_get_and_clear_error_info(struct mem_ctl_info * mci,struct i3200_error_info * info)180dd8ef1dbSJason Uhlenkott static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci,
181dd8ef1dbSJason Uhlenkott 		struct i3200_error_info *info)
182dd8ef1dbSJason Uhlenkott {
183dd8ef1dbSJason Uhlenkott 	struct pci_dev *pdev;
184dd8ef1dbSJason Uhlenkott 	struct i3200_priv *priv = mci->pvt_info;
185dd8ef1dbSJason Uhlenkott 	void __iomem *window = priv->window;
186dd8ef1dbSJason Uhlenkott 
187fd687502SMauro Carvalho Chehab 	pdev = to_pci_dev(mci->pdev);
188dd8ef1dbSJason Uhlenkott 
189dd8ef1dbSJason Uhlenkott 	/*
190dd8ef1dbSJason Uhlenkott 	 * This is a mess because there is no atomic way to read all the
191dd8ef1dbSJason Uhlenkott 	 * registers at once and the registers can transition from CE being
192dd8ef1dbSJason Uhlenkott 	 * overwritten by UE.
193dd8ef1dbSJason Uhlenkott 	 */
194dd8ef1dbSJason Uhlenkott 	pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts);
195dd8ef1dbSJason Uhlenkott 	if (!(info->errsts & I3200_ERRSTS_BITS))
196dd8ef1dbSJason Uhlenkott 		return;
197dd8ef1dbSJason Uhlenkott 
198dd8ef1dbSJason Uhlenkott 	info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG);
199dd8ef1dbSJason Uhlenkott 	if (nr_channels == 2)
200dd8ef1dbSJason Uhlenkott 		info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG);
201dd8ef1dbSJason Uhlenkott 
202dd8ef1dbSJason Uhlenkott 	pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts2);
203dd8ef1dbSJason Uhlenkott 
204dd8ef1dbSJason Uhlenkott 	/*
205dd8ef1dbSJason Uhlenkott 	 * If the error is the same for both reads then the first set
206dd8ef1dbSJason Uhlenkott 	 * of reads is valid.  If there is a change then there is a CE
207dd8ef1dbSJason Uhlenkott 	 * with no info and the second set of reads is valid and
208dd8ef1dbSJason Uhlenkott 	 * should be UE info.
209dd8ef1dbSJason Uhlenkott 	 */
210dd8ef1dbSJason Uhlenkott 	if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) {
211dd8ef1dbSJason Uhlenkott 		info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG);
212dd8ef1dbSJason Uhlenkott 		if (nr_channels == 2)
213dd8ef1dbSJason Uhlenkott 			info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG);
214dd8ef1dbSJason Uhlenkott 	}
215dd8ef1dbSJason Uhlenkott 
216dd8ef1dbSJason Uhlenkott 	i3200_clear_error_info(mci);
217dd8ef1dbSJason Uhlenkott }
218dd8ef1dbSJason Uhlenkott 
i3200_process_error_info(struct mem_ctl_info * mci,struct i3200_error_info * info)219dd8ef1dbSJason Uhlenkott static void i3200_process_error_info(struct mem_ctl_info *mci,
220dd8ef1dbSJason Uhlenkott 		struct i3200_error_info *info)
221dd8ef1dbSJason Uhlenkott {
222dd8ef1dbSJason Uhlenkott 	int channel;
223dd8ef1dbSJason Uhlenkott 	u64 log;
224dd8ef1dbSJason Uhlenkott 
225dd8ef1dbSJason Uhlenkott 	if (!(info->errsts & I3200_ERRSTS_BITS))
226dd8ef1dbSJason Uhlenkott 		return;
227dd8ef1dbSJason Uhlenkott 
228dd8ef1dbSJason Uhlenkott 	if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) {
2299eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
23003f7eae8SMauro Carvalho Chehab 				     -1, -1, -1, "UE overwrote CE", "");
231dd8ef1dbSJason Uhlenkott 		info->errsts = info->errsts2;
232dd8ef1dbSJason Uhlenkott 	}
233dd8ef1dbSJason Uhlenkott 
234dd8ef1dbSJason Uhlenkott 	for (channel = 0; channel < nr_channels; channel++) {
235dd8ef1dbSJason Uhlenkott 		log = info->eccerrlog[channel];
236dd8ef1dbSJason Uhlenkott 		if (log & I3200_ECCERRLOG_UE) {
2379eb07a7fSMauro Carvalho Chehab 			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
23895b93287SMauro Carvalho Chehab 					     0, 0, 0,
239dd8ef1dbSJason Uhlenkott 					     eccerrlog_row(channel, log),
24095b93287SMauro Carvalho Chehab 					     -1, -1,
24103f7eae8SMauro Carvalho Chehab 					     "i3000 UE", "");
242dd8ef1dbSJason Uhlenkott 		} else if (log & I3200_ECCERRLOG_CE) {
2438a3f075dSJason Baron 			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
24495b93287SMauro Carvalho Chehab 					     0, 0, eccerrlog_syndrome(log),
24595b93287SMauro Carvalho Chehab 					     eccerrlog_row(channel, log),
24695b93287SMauro Carvalho Chehab 					     -1, -1,
2478a3f075dSJason Baron 					     "i3000 CE", "");
248dd8ef1dbSJason Uhlenkott 		}
249dd8ef1dbSJason Uhlenkott 	}
250dd8ef1dbSJason Uhlenkott }
251dd8ef1dbSJason Uhlenkott 
i3200_check(struct mem_ctl_info * mci)252dd8ef1dbSJason Uhlenkott static void i3200_check(struct mem_ctl_info *mci)
253dd8ef1dbSJason Uhlenkott {
254dd8ef1dbSJason Uhlenkott 	struct i3200_error_info info;
255dd8ef1dbSJason Uhlenkott 
256dd8ef1dbSJason Uhlenkott 	i3200_get_and_clear_error_info(mci, &info);
257dd8ef1dbSJason Uhlenkott 	i3200_process_error_info(mci, &info);
258dd8ef1dbSJason Uhlenkott }
259dd8ef1dbSJason Uhlenkott 
i3200_map_mchbar(struct pci_dev * pdev)260166e9334SJingoo Han static void __iomem *i3200_map_mchbar(struct pci_dev *pdev)
261dd8ef1dbSJason Uhlenkott {
262dd8ef1dbSJason Uhlenkott 	union {
263dd8ef1dbSJason Uhlenkott 		u64 mchbar;
264dd8ef1dbSJason Uhlenkott 		struct {
265dd8ef1dbSJason Uhlenkott 			u32 mchbar_low;
266dd8ef1dbSJason Uhlenkott 			u32 mchbar_high;
267dd8ef1dbSJason Uhlenkott 		};
268dd8ef1dbSJason Uhlenkott 	} u;
269dd8ef1dbSJason Uhlenkott 	void __iomem *window;
270dd8ef1dbSJason Uhlenkott 
271dd8ef1dbSJason Uhlenkott 	pci_read_config_dword(pdev, I3200_MCHBAR_LOW, &u.mchbar_low);
272dd8ef1dbSJason Uhlenkott 	pci_read_config_dword(pdev, I3200_MCHBAR_HIGH, &u.mchbar_high);
273dd8ef1dbSJason Uhlenkott 	u.mchbar &= I3200_MCHBAR_MASK;
274dd8ef1dbSJason Uhlenkott 
275dd8ef1dbSJason Uhlenkott 	if (u.mchbar != (resource_size_t)u.mchbar) {
276dd8ef1dbSJason Uhlenkott 		printk(KERN_ERR
277dd8ef1dbSJason Uhlenkott 			"i3200: mmio space beyond accessible range (0x%llx)\n",
278dd8ef1dbSJason Uhlenkott 			(unsigned long long)u.mchbar);
279dd8ef1dbSJason Uhlenkott 		return NULL;
280dd8ef1dbSJason Uhlenkott 	}
281dd8ef1dbSJason Uhlenkott 
2824bdc0d67SChristoph Hellwig 	window = ioremap(u.mchbar, I3200_MMR_WINDOW_SIZE);
283dd8ef1dbSJason Uhlenkott 	if (!window)
284dd8ef1dbSJason Uhlenkott 		printk(KERN_ERR "i3200: cannot map mmio space at 0x%llx\n",
285dd8ef1dbSJason Uhlenkott 			(unsigned long long)u.mchbar);
286dd8ef1dbSJason Uhlenkott 
287dd8ef1dbSJason Uhlenkott 	return window;
288dd8ef1dbSJason Uhlenkott }
289dd8ef1dbSJason Uhlenkott 
290dd8ef1dbSJason Uhlenkott 
i3200_get_drbs(void __iomem * window,u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL])291dd8ef1dbSJason Uhlenkott static void i3200_get_drbs(void __iomem *window,
292dd8ef1dbSJason Uhlenkott 	u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL])
293dd8ef1dbSJason Uhlenkott {
294dd8ef1dbSJason Uhlenkott 	int i;
295dd8ef1dbSJason Uhlenkott 
296dd8ef1dbSJason Uhlenkott 	for (i = 0; i < I3200_RANKS_PER_CHANNEL; i++) {
297dd8ef1dbSJason Uhlenkott 		drbs[0][i] = readw(window + I3200_C0DRB + 2*i) & I3200_DRB_MASK;
298dd8ef1dbSJason Uhlenkott 		drbs[1][i] = readw(window + I3200_C1DRB + 2*i) & I3200_DRB_MASK;
2995f466cb0SMauro Carvalho Chehab 
3005f466cb0SMauro Carvalho Chehab 		edac_dbg(0, "drb[0][%d] = %d, drb[1][%d] = %d\n", i, drbs[0][i], i, drbs[1][i]);
301dd8ef1dbSJason Uhlenkott 	}
302dd8ef1dbSJason Uhlenkott }
303dd8ef1dbSJason Uhlenkott 
i3200_is_stacked(struct pci_dev * pdev,u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL])304dd8ef1dbSJason Uhlenkott static bool i3200_is_stacked(struct pci_dev *pdev,
305dd8ef1dbSJason Uhlenkott 	u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL])
306dd8ef1dbSJason Uhlenkott {
307dd8ef1dbSJason Uhlenkott 	u16 tom;
308dd8ef1dbSJason Uhlenkott 
309dd8ef1dbSJason Uhlenkott 	pci_read_config_word(pdev, I3200_TOM, &tom);
310dd8ef1dbSJason Uhlenkott 	tom &= I3200_TOM_MASK;
311dd8ef1dbSJason Uhlenkott 
312dd8ef1dbSJason Uhlenkott 	return drbs[I3200_CHANNELS - 1][I3200_RANKS_PER_CHANNEL - 1] == tom;
313dd8ef1dbSJason Uhlenkott }
314dd8ef1dbSJason Uhlenkott 
drb_to_nr_pages(u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL],bool stacked,int channel,int rank)315dd8ef1dbSJason Uhlenkott static unsigned long drb_to_nr_pages(
316dd8ef1dbSJason Uhlenkott 	u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL], bool stacked,
317dd8ef1dbSJason Uhlenkott 	int channel, int rank)
318dd8ef1dbSJason Uhlenkott {
319dd8ef1dbSJason Uhlenkott 	int n;
320dd8ef1dbSJason Uhlenkott 
321dd8ef1dbSJason Uhlenkott 	n = drbs[channel][rank];
32261734e18SMauro Carvalho Chehab 	if (!n)
32361734e18SMauro Carvalho Chehab 		return 0;
32461734e18SMauro Carvalho Chehab 
325dd8ef1dbSJason Uhlenkott 	if (rank > 0)
326dd8ef1dbSJason Uhlenkott 		n -= drbs[channel][rank - 1];
327dd8ef1dbSJason Uhlenkott 	if (stacked && (channel == 1) &&
328dd8ef1dbSJason Uhlenkott 	drbs[channel][rank] == drbs[channel][I3200_RANKS_PER_CHANNEL - 1])
329dd8ef1dbSJason Uhlenkott 		n -= drbs[0][I3200_RANKS_PER_CHANNEL - 1];
330dd8ef1dbSJason Uhlenkott 
331dd8ef1dbSJason Uhlenkott 	n <<= (I3200_DRB_SHIFT - PAGE_SHIFT);
332dd8ef1dbSJason Uhlenkott 	return n;
333dd8ef1dbSJason Uhlenkott }
334dd8ef1dbSJason Uhlenkott 
i3200_probe1(struct pci_dev * pdev,int dev_idx)335dd8ef1dbSJason Uhlenkott static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
336dd8ef1dbSJason Uhlenkott {
337dd8ef1dbSJason Uhlenkott 	int rc;
338084a4fccSMauro Carvalho Chehab 	int i, j;
339dd8ef1dbSJason Uhlenkott 	struct mem_ctl_info *mci = NULL;
34095b93287SMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
341dd8ef1dbSJason Uhlenkott 	u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL];
342dd8ef1dbSJason Uhlenkott 	bool stacked;
343dd8ef1dbSJason Uhlenkott 	void __iomem *window;
344dd8ef1dbSJason Uhlenkott 	struct i3200_priv *priv;
345dd8ef1dbSJason Uhlenkott 
346956b9ba1SJoe Perches 	edac_dbg(0, "MC:\n");
347dd8ef1dbSJason Uhlenkott 
348dd8ef1dbSJason Uhlenkott 	window = i3200_map_mchbar(pdev);
349dd8ef1dbSJason Uhlenkott 	if (!window)
350dd8ef1dbSJason Uhlenkott 		return -ENODEV;
351dd8ef1dbSJason Uhlenkott 
352dd8ef1dbSJason Uhlenkott 	i3200_get_drbs(window, drbs);
353dd8ef1dbSJason Uhlenkott 	nr_channels = how_many_channels(pdev);
354dd8ef1dbSJason Uhlenkott 
35595b93287SMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
35695b93287SMauro Carvalho Chehab 	layers[0].size = I3200_DIMMS;
35795b93287SMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
35895b93287SMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
35995b93287SMauro Carvalho Chehab 	layers[1].size = nr_channels;
36095b93287SMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
361ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
36295b93287SMauro Carvalho Chehab 			    sizeof(struct i3200_priv));
363dd8ef1dbSJason Uhlenkott 	if (!mci)
364dd8ef1dbSJason Uhlenkott 		return -ENOMEM;
365dd8ef1dbSJason Uhlenkott 
366956b9ba1SJoe Perches 	edac_dbg(3, "MC: init mci\n");
367dd8ef1dbSJason Uhlenkott 
368fd687502SMauro Carvalho Chehab 	mci->pdev = &pdev->dev;
369dd8ef1dbSJason Uhlenkott 	mci->mtype_cap = MEM_FLAG_DDR2;
370dd8ef1dbSJason Uhlenkott 
371dd8ef1dbSJason Uhlenkott 	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
372dd8ef1dbSJason Uhlenkott 	mci->edac_cap = EDAC_FLAG_SECDED;
373dd8ef1dbSJason Uhlenkott 
374dd8ef1dbSJason Uhlenkott 	mci->mod_name = EDAC_MOD_STR;
375dd8ef1dbSJason Uhlenkott 	mci->ctl_name = i3200_devs[dev_idx].ctl_name;
376dd8ef1dbSJason Uhlenkott 	mci->dev_name = pci_name(pdev);
377dd8ef1dbSJason Uhlenkott 	mci->edac_check = i3200_check;
378dd8ef1dbSJason Uhlenkott 	mci->ctl_page_to_phys = NULL;
379dd8ef1dbSJason Uhlenkott 	priv = mci->pvt_info;
380dd8ef1dbSJason Uhlenkott 	priv->window = window;
381dd8ef1dbSJason Uhlenkott 
382dd8ef1dbSJason Uhlenkott 	stacked = i3200_is_stacked(pdev, drbs);
383dd8ef1dbSJason Uhlenkott 
384dd8ef1dbSJason Uhlenkott 	/*
385dd8ef1dbSJason Uhlenkott 	 * The dram rank boundary (DRB) reg values are boundary addresses
386dd8ef1dbSJason Uhlenkott 	 * for each DRAM rank with a granularity of 64MB.  DRB regs are
387dd8ef1dbSJason Uhlenkott 	 * cumulative; the last one will contain the total memory
388dd8ef1dbSJason Uhlenkott 	 * contained in all ranks.
389dd8ef1dbSJason Uhlenkott 	 */
39061734e18SMauro Carvalho Chehab 	for (i = 0; i < I3200_DIMMS; i++) {
391dd8ef1dbSJason Uhlenkott 		unsigned long nr_pages;
392dd8ef1dbSJason Uhlenkott 
39361734e18SMauro Carvalho Chehab 		for (j = 0; j < nr_channels; j++) {
394bc9ad9e4SRobert Richter 			struct dimm_info *dimm = edac_get_dimm(mci, i, j, 0);
395dd8ef1dbSJason Uhlenkott 
39661734e18SMauro Carvalho Chehab 			nr_pages = drb_to_nr_pages(drbs, stacked, j, i);
397084a4fccSMauro Carvalho Chehab 			if (nr_pages == 0)
398dd8ef1dbSJason Uhlenkott 				continue;
399dd8ef1dbSJason Uhlenkott 
4006f6da136SQiuxu Zhuo 			edac_dbg(0, "csrow %d, channel %d%s, size = %ld MiB\n", i, j,
40161734e18SMauro Carvalho Chehab 				 stacked ? " (stacked)" : "", PAGES_TO_MiB(nr_pages));
402084a4fccSMauro Carvalho Chehab 
403582a8996SMauro Carvalho Chehab 			dimm->nr_pages = nr_pages;
404084a4fccSMauro Carvalho Chehab 			dimm->grain = nr_pages << PAGE_SHIFT;
405084a4fccSMauro Carvalho Chehab 			dimm->mtype = MEM_DDR2;
406084a4fccSMauro Carvalho Chehab 			dimm->dtype = DEV_UNKNOWN;
407084a4fccSMauro Carvalho Chehab 			dimm->edac_mode = EDAC_UNKNOWN;
408084a4fccSMauro Carvalho Chehab 		}
409dd8ef1dbSJason Uhlenkott 	}
410dd8ef1dbSJason Uhlenkott 
411dd8ef1dbSJason Uhlenkott 	i3200_clear_error_info(mci);
412dd8ef1dbSJason Uhlenkott 
413dd8ef1dbSJason Uhlenkott 	rc = -ENODEV;
414dd8ef1dbSJason Uhlenkott 	if (edac_mc_add_mc(mci)) {
415956b9ba1SJoe Perches 		edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
416dd8ef1dbSJason Uhlenkott 		goto fail;
417dd8ef1dbSJason Uhlenkott 	}
418dd8ef1dbSJason Uhlenkott 
419dd8ef1dbSJason Uhlenkott 	/* get this far and it's successful */
420956b9ba1SJoe Perches 	edac_dbg(3, "MC: success\n");
421dd8ef1dbSJason Uhlenkott 	return 0;
422dd8ef1dbSJason Uhlenkott 
423dd8ef1dbSJason Uhlenkott fail:
424dd8ef1dbSJason Uhlenkott 	iounmap(window);
425dd8ef1dbSJason Uhlenkott 	if (mci)
426dd8ef1dbSJason Uhlenkott 		edac_mc_free(mci);
427dd8ef1dbSJason Uhlenkott 
428dd8ef1dbSJason Uhlenkott 	return rc;
429dd8ef1dbSJason Uhlenkott }
430dd8ef1dbSJason Uhlenkott 
i3200_init_one(struct pci_dev * pdev,const struct pci_device_id * ent)4319b3c6e85SGreg Kroah-Hartman static int i3200_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
432dd8ef1dbSJason Uhlenkott {
433dd8ef1dbSJason Uhlenkott 	int rc;
434dd8ef1dbSJason Uhlenkott 
435956b9ba1SJoe Perches 	edac_dbg(0, "MC:\n");
436dd8ef1dbSJason Uhlenkott 
437dd8ef1dbSJason Uhlenkott 	if (pci_enable_device(pdev) < 0)
438dd8ef1dbSJason Uhlenkott 		return -EIO;
439dd8ef1dbSJason Uhlenkott 
440dd8ef1dbSJason Uhlenkott 	rc = i3200_probe1(pdev, ent->driver_data);
441dd8ef1dbSJason Uhlenkott 	if (!mci_pdev)
442dd8ef1dbSJason Uhlenkott 		mci_pdev = pci_dev_get(pdev);
443dd8ef1dbSJason Uhlenkott 
444dd8ef1dbSJason Uhlenkott 	return rc;
445dd8ef1dbSJason Uhlenkott }
446dd8ef1dbSJason Uhlenkott 
i3200_remove_one(struct pci_dev * pdev)4479b3c6e85SGreg Kroah-Hartman static void i3200_remove_one(struct pci_dev *pdev)
448dd8ef1dbSJason Uhlenkott {
449dd8ef1dbSJason Uhlenkott 	struct mem_ctl_info *mci;
450dd8ef1dbSJason Uhlenkott 	struct i3200_priv *priv;
451dd8ef1dbSJason Uhlenkott 
452956b9ba1SJoe Perches 	edac_dbg(0, "\n");
453dd8ef1dbSJason Uhlenkott 
454dd8ef1dbSJason Uhlenkott 	mci = edac_mc_del_mc(&pdev->dev);
455dd8ef1dbSJason Uhlenkott 	if (!mci)
456dd8ef1dbSJason Uhlenkott 		return;
457dd8ef1dbSJason Uhlenkott 
458dd8ef1dbSJason Uhlenkott 	priv = mci->pvt_info;
459dd8ef1dbSJason Uhlenkott 	iounmap(priv->window);
460dd8ef1dbSJason Uhlenkott 
461dd8ef1dbSJason Uhlenkott 	edac_mc_free(mci);
462b90fe156SHitoshi Mitake 
463b90fe156SHitoshi Mitake 	pci_disable_device(pdev);
464dd8ef1dbSJason Uhlenkott }
465dd8ef1dbSJason Uhlenkott 
466ba935f40SJingoo Han static const struct pci_device_id i3200_pci_tbl[] = {
467dd8ef1dbSJason Uhlenkott 	{
468dd8ef1dbSJason Uhlenkott 		PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
469dd8ef1dbSJason Uhlenkott 		I3200},
470dd8ef1dbSJason Uhlenkott 	{
471dd8ef1dbSJason Uhlenkott 		0,
472dd8ef1dbSJason Uhlenkott 	}            /* 0 terminated list. */
473dd8ef1dbSJason Uhlenkott };
474dd8ef1dbSJason Uhlenkott 
475dd8ef1dbSJason Uhlenkott MODULE_DEVICE_TABLE(pci, i3200_pci_tbl);
476dd8ef1dbSJason Uhlenkott 
477dd8ef1dbSJason Uhlenkott static struct pci_driver i3200_driver = {
478dd8ef1dbSJason Uhlenkott 	.name = EDAC_MOD_STR,
479dd8ef1dbSJason Uhlenkott 	.probe = i3200_init_one,
4809b3c6e85SGreg Kroah-Hartman 	.remove = i3200_remove_one,
481dd8ef1dbSJason Uhlenkott 	.id_table = i3200_pci_tbl,
482dd8ef1dbSJason Uhlenkott };
483dd8ef1dbSJason Uhlenkott 
i3200_init(void)484dd8ef1dbSJason Uhlenkott static int __init i3200_init(void)
485dd8ef1dbSJason Uhlenkott {
486dd8ef1dbSJason Uhlenkott 	int pci_rc;
487dd8ef1dbSJason Uhlenkott 
488956b9ba1SJoe Perches 	edac_dbg(3, "MC:\n");
489dd8ef1dbSJason Uhlenkott 
490dd8ef1dbSJason Uhlenkott 	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
491dd8ef1dbSJason Uhlenkott 	opstate_init();
492dd8ef1dbSJason Uhlenkott 
493dd8ef1dbSJason Uhlenkott 	pci_rc = pci_register_driver(&i3200_driver);
494dd8ef1dbSJason Uhlenkott 	if (pci_rc < 0)
495dd8ef1dbSJason Uhlenkott 		goto fail0;
496dd8ef1dbSJason Uhlenkott 
497dd8ef1dbSJason Uhlenkott 	if (!mci_pdev) {
498dd8ef1dbSJason Uhlenkott 		i3200_registered = 0;
499dd8ef1dbSJason Uhlenkott 		mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
500dd8ef1dbSJason Uhlenkott 				PCI_DEVICE_ID_INTEL_3200_HB, NULL);
501dd8ef1dbSJason Uhlenkott 		if (!mci_pdev) {
502956b9ba1SJoe Perches 			edac_dbg(0, "i3200 pci_get_device fail\n");
503dd8ef1dbSJason Uhlenkott 			pci_rc = -ENODEV;
504dd8ef1dbSJason Uhlenkott 			goto fail1;
505dd8ef1dbSJason Uhlenkott 		}
506dd8ef1dbSJason Uhlenkott 
507dd8ef1dbSJason Uhlenkott 		pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl);
508dd8ef1dbSJason Uhlenkott 		if (pci_rc < 0) {
509956b9ba1SJoe Perches 			edac_dbg(0, "i3200 init fail\n");
510dd8ef1dbSJason Uhlenkott 			pci_rc = -ENODEV;
511dd8ef1dbSJason Uhlenkott 			goto fail1;
512dd8ef1dbSJason Uhlenkott 		}
513dd8ef1dbSJason Uhlenkott 	}
514dd8ef1dbSJason Uhlenkott 
515dd8ef1dbSJason Uhlenkott 	return 0;
516dd8ef1dbSJason Uhlenkott 
517dd8ef1dbSJason Uhlenkott fail1:
518dd8ef1dbSJason Uhlenkott 	pci_unregister_driver(&i3200_driver);
519dd8ef1dbSJason Uhlenkott 
520dd8ef1dbSJason Uhlenkott fail0:
521dd8ef1dbSJason Uhlenkott 	pci_dev_put(mci_pdev);
522dd8ef1dbSJason Uhlenkott 
523dd8ef1dbSJason Uhlenkott 	return pci_rc;
524dd8ef1dbSJason Uhlenkott }
525dd8ef1dbSJason Uhlenkott 
i3200_exit(void)526dd8ef1dbSJason Uhlenkott static void __exit i3200_exit(void)
527dd8ef1dbSJason Uhlenkott {
528956b9ba1SJoe Perches 	edac_dbg(3, "MC:\n");
529dd8ef1dbSJason Uhlenkott 
530dd8ef1dbSJason Uhlenkott 	pci_unregister_driver(&i3200_driver);
531dd8ef1dbSJason Uhlenkott 	if (!i3200_registered) {
532dd8ef1dbSJason Uhlenkott 		i3200_remove_one(mci_pdev);
533dd8ef1dbSJason Uhlenkott 		pci_dev_put(mci_pdev);
534dd8ef1dbSJason Uhlenkott 	}
535dd8ef1dbSJason Uhlenkott }
536dd8ef1dbSJason Uhlenkott 
537dd8ef1dbSJason Uhlenkott module_init(i3200_init);
538dd8ef1dbSJason Uhlenkott module_exit(i3200_exit);
539dd8ef1dbSJason Uhlenkott 
540dd8ef1dbSJason Uhlenkott MODULE_LICENSE("GPL");
541dd8ef1dbSJason Uhlenkott MODULE_AUTHOR("Akamai Technologies, Inc.");
542dd8ef1dbSJason Uhlenkott MODULE_DESCRIPTION("MC support for Intel 3200 memory hub controllers");
543dd8ef1dbSJason Uhlenkott 
544dd8ef1dbSJason Uhlenkott module_param(edac_op_state, int, 0444);
545dd8ef1dbSJason Uhlenkott MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
546