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
282*4bdc0d67SChristoph 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