1da9bb1d2SAlan Cox /*
2da9bb1d2SAlan Cox * edac_mc kernel module
349c0dab7SDoug Thompson * (C) 2005, 2006 Linux Networx (http://lnxi.com)
4da9bb1d2SAlan Cox * This file may be distributed under the terms of the
5da9bb1d2SAlan Cox * GNU General Public License.
6da9bb1d2SAlan Cox *
7da9bb1d2SAlan Cox * Written by Thayne Harbaugh
8da9bb1d2SAlan Cox * Based on work by Dan Hollis <goemon at anime dot net> and others.
9da9bb1d2SAlan Cox * http://www.anime.net/~goemon/linux-ecc/
10da9bb1d2SAlan Cox *
11da9bb1d2SAlan Cox * Modified by Dave Peterson and Doug Thompson
12da9bb1d2SAlan Cox *
13da9bb1d2SAlan Cox */
14da9bb1d2SAlan Cox
15da9bb1d2SAlan Cox #include <linux/module.h>
16da9bb1d2SAlan Cox #include <linux/proc_fs.h>
17da9bb1d2SAlan Cox #include <linux/kernel.h>
18da9bb1d2SAlan Cox #include <linux/types.h>
19da9bb1d2SAlan Cox #include <linux/smp.h>
20da9bb1d2SAlan Cox #include <linux/init.h>
21da9bb1d2SAlan Cox #include <linux/sysctl.h>
22da9bb1d2SAlan Cox #include <linux/highmem.h>
23da9bb1d2SAlan Cox #include <linux/timer.h>
24da9bb1d2SAlan Cox #include <linux/slab.h>
25da9bb1d2SAlan Cox #include <linux/jiffies.h>
26da9bb1d2SAlan Cox #include <linux/spinlock.h>
27da9bb1d2SAlan Cox #include <linux/list.h>
28da9bb1d2SAlan Cox #include <linux/ctype.h>
29c0d12172SDave Jiang #include <linux/edac.h>
3053f2d028SMauro Carvalho Chehab #include <linux/bitops.h>
317c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
32da9bb1d2SAlan Cox #include <asm/page.h>
3378d88e8aSMauro Carvalho Chehab #include "edac_mc.h"
347c9281d7SDouglas Thompson #include "edac_module.h"
3553f2d028SMauro Carvalho Chehab #include <ras/ras_event.h>
3653f2d028SMauro Carvalho Chehab
37b01aec9bSBorislav Petkov #ifdef CONFIG_EDAC_ATOMIC_SCRUB
38b01aec9bSBorislav Petkov #include <asm/edac.h>
39b01aec9bSBorislav Petkov #else
40b01aec9bSBorislav Petkov #define edac_atomic_scrub(va, size) do { } while (0)
41b01aec9bSBorislav Petkov #endif
42b01aec9bSBorislav Petkov
438c22b4feSBorislav Petkov int edac_op_state = EDAC_OPSTATE_INVAL;
448c22b4feSBorislav Petkov EXPORT_SYMBOL_GPL(edac_op_state);
458c22b4feSBorislav Petkov
46da9bb1d2SAlan Cox /* lock to memory controller's control array */
4763b7df91SMatthias Kaehlcke static DEFINE_MUTEX(mem_ctls_mutex);
48ff6ac2a6SRobert P. J. Day static LIST_HEAD(mc_devices);
49da9bb1d2SAlan Cox
5080cc7d87SMauro Carvalho Chehab /*
5180cc7d87SMauro Carvalho Chehab * Used to lock EDAC MC to just one module, avoiding two drivers e. g.
5280cc7d87SMauro Carvalho Chehab * apei/ghes and i7core_edac to be used at the same time.
5380cc7d87SMauro Carvalho Chehab */
543877c7d1SToshi Kani static const char *edac_mc_owner;
5580cc7d87SMauro Carvalho Chehab
error_desc_to_mci(struct edac_raw_error_desc * e)5691b327f6SRobert Richter static struct mem_ctl_info *error_desc_to_mci(struct edac_raw_error_desc *e)
5791b327f6SRobert Richter {
5891b327f6SRobert Richter return container_of(e, struct mem_ctl_info, error_desc);
5991b327f6SRobert Richter }
6091b327f6SRobert Richter
edac_dimm_info_location(struct dimm_info * dimm,char * buf,unsigned int len)61d55c79acSRobert Richter unsigned int edac_dimm_info_location(struct dimm_info *dimm, char *buf,
62d55c79acSRobert Richter unsigned int len)
636e84d359SMauro Carvalho Chehab {
646e84d359SMauro Carvalho Chehab struct mem_ctl_info *mci = dimm->mci;
656e84d359SMauro Carvalho Chehab int i, n, count = 0;
666e84d359SMauro Carvalho Chehab char *p = buf;
676e84d359SMauro Carvalho Chehab
686e84d359SMauro Carvalho Chehab for (i = 0; i < mci->n_layers; i++) {
69fca61165SLen Baker n = scnprintf(p, len, "%s %d ",
706e84d359SMauro Carvalho Chehab edac_layer_name[mci->layers[i].type],
716e84d359SMauro Carvalho Chehab dimm->location[i]);
726e84d359SMauro Carvalho Chehab p += n;
736e84d359SMauro Carvalho Chehab len -= n;
746e84d359SMauro Carvalho Chehab count += n;
756e84d359SMauro Carvalho Chehab }
766e84d359SMauro Carvalho Chehab
776e84d359SMauro Carvalho Chehab return count;
786e84d359SMauro Carvalho Chehab }
796e84d359SMauro Carvalho Chehab
80da9bb1d2SAlan Cox #ifdef CONFIG_EDAC_DEBUG
81da9bb1d2SAlan Cox
edac_mc_dump_channel(struct rank_info * chan)82a4b4be3fSMauro Carvalho Chehab static void edac_mc_dump_channel(struct rank_info *chan)
83da9bb1d2SAlan Cox {
846e84d359SMauro Carvalho Chehab edac_dbg(4, " channel->chan_idx = %d\n", chan->chan_idx);
856e84d359SMauro Carvalho Chehab edac_dbg(4, " channel = %p\n", chan);
866e84d359SMauro Carvalho Chehab edac_dbg(4, " channel->csrow = %p\n", chan->csrow);
876e84d359SMauro Carvalho Chehab edac_dbg(4, " channel->dimm = %p\n", chan->dimm);
884275be63SMauro Carvalho Chehab }
894275be63SMauro Carvalho Chehab
edac_mc_dump_dimm(struct dimm_info * dimm)90c498afafSRobert Richter static void edac_mc_dump_dimm(struct dimm_info *dimm)
914275be63SMauro Carvalho Chehab {
926e84d359SMauro Carvalho Chehab char location[80];
934275be63SMauro Carvalho Chehab
94c498afafSRobert Richter if (!dimm->nr_pages)
95c498afafSRobert Richter return;
96c498afafSRobert Richter
976e84d359SMauro Carvalho Chehab edac_dimm_info_location(dimm, location, sizeof(location));
986e84d359SMauro Carvalho Chehab
996e84d359SMauro Carvalho Chehab edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n",
1009713faecSMauro Carvalho Chehab dimm->mci->csbased ? "rank" : "dimm",
101c498afafSRobert Richter dimm->idx, location, dimm->csrow, dimm->cschannel);
1026e84d359SMauro Carvalho Chehab edac_dbg(4, " dimm = %p\n", dimm);
1036e84d359SMauro Carvalho Chehab edac_dbg(4, " dimm->label = '%s'\n", dimm->label);
1046e84d359SMauro Carvalho Chehab edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
1056e84d359SMauro Carvalho Chehab edac_dbg(4, " dimm->grain = %d\n", dimm->grain);
106da9bb1d2SAlan Cox }
107da9bb1d2SAlan Cox
edac_mc_dump_csrow(struct csrow_info * csrow)1082da1c119SAdrian Bunk static void edac_mc_dump_csrow(struct csrow_info *csrow)
109da9bb1d2SAlan Cox {
1106e84d359SMauro Carvalho Chehab edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx);
1116e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow = %p\n", csrow);
1126e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow->first_page = 0x%lx\n", csrow->first_page);
1136e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow->last_page = 0x%lx\n", csrow->last_page);
1146e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow->page_mask = 0x%lx\n", csrow->page_mask);
1156e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow->nr_channels = %d\n", csrow->nr_channels);
1166e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow->channels = %p\n", csrow->channels);
1176e84d359SMauro Carvalho Chehab edac_dbg(4, " csrow->mci = %p\n", csrow->mci);
118da9bb1d2SAlan Cox }
119da9bb1d2SAlan Cox
edac_mc_dump_mci(struct mem_ctl_info * mci)1202da1c119SAdrian Bunk static void edac_mc_dump_mci(struct mem_ctl_info *mci)
121da9bb1d2SAlan Cox {
122956b9ba1SJoe Perches edac_dbg(3, "\tmci = %p\n", mci);
123956b9ba1SJoe Perches edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap);
124956b9ba1SJoe Perches edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
125956b9ba1SJoe Perches edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap);
126956b9ba1SJoe Perches edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check);
127956b9ba1SJoe Perches edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n",
128da9bb1d2SAlan Cox mci->nr_csrows, mci->csrows);
129956b9ba1SJoe Perches edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n",
1304275be63SMauro Carvalho Chehab mci->tot_dimms, mci->dimms);
131956b9ba1SJoe Perches edac_dbg(3, "\tdev = %p\n", mci->pdev);
132956b9ba1SJoe Perches edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
133956b9ba1SJoe Perches mci->mod_name, mci->ctl_name);
134956b9ba1SJoe Perches edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info);
135da9bb1d2SAlan Cox }
136da9bb1d2SAlan Cox
13724f9a7feSBorislav Petkov #endif /* CONFIG_EDAC_DEBUG */
13824f9a7feSBorislav Petkov
139f4ce6ecaSBorislav Petkov const char * const edac_mem_types[] = {
140d6dd77ebSTony Luck [MEM_EMPTY] = "Empty",
141d6dd77ebSTony Luck [MEM_RESERVED] = "Reserved",
142d6dd77ebSTony Luck [MEM_UNKNOWN] = "Unknown",
143d6dd77ebSTony Luck [MEM_FPM] = "FPM",
144d6dd77ebSTony Luck [MEM_EDO] = "EDO",
145d6dd77ebSTony Luck [MEM_BEDO] = "BEDO",
146d6dd77ebSTony Luck [MEM_SDR] = "Unbuffered-SDR",
147d6dd77ebSTony Luck [MEM_RDR] = "Registered-SDR",
148d6dd77ebSTony Luck [MEM_DDR] = "Unbuffered-DDR",
149d6dd77ebSTony Luck [MEM_RDDR] = "Registered-DDR",
150d6dd77ebSTony Luck [MEM_RMBS] = "RMBS",
151d6dd77ebSTony Luck [MEM_DDR2] = "Unbuffered-DDR2",
152d6dd77ebSTony Luck [MEM_FB_DDR2] = "FullyBuffered-DDR2",
153d6dd77ebSTony Luck [MEM_RDDR2] = "Registered-DDR2",
154d6dd77ebSTony Luck [MEM_XDR] = "XDR",
155d6dd77ebSTony Luck [MEM_DDR3] = "Unbuffered-DDR3",
156d6dd77ebSTony Luck [MEM_RDDR3] = "Registered-DDR3",
157d6dd77ebSTony Luck [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM",
1583b203693SQiuxu Zhuo [MEM_LPDDR3] = "Low-Power-DDR3-RAM",
159d6dd77ebSTony Luck [MEM_DDR4] = "Unbuffered-DDR4",
160001f8613STony Luck [MEM_RDDR4] = "Registered-DDR4",
1613b203693SQiuxu Zhuo [MEM_LPDDR4] = "Low-Power-DDR4-RAM",
162b748f2deSTakashi Iwai [MEM_LRDDR4] = "Load-Reduced-DDR4-RAM",
163bc1c99a5SQiuxu Zhuo [MEM_DDR5] = "Unbuffered-DDR5",
164f9571124SYazen Ghannam [MEM_RDDR5] = "Registered-DDR5",
165f9571124SYazen Ghannam [MEM_LRDDR5] = "Load-Reduced-DDR5-RAM",
166001f8613STony Luck [MEM_NVDIMM] = "Non-volatile-RAM",
1673b203693SQiuxu Zhuo [MEM_WIO2] = "Wide-IO-2",
168e1ca90b7SNaveen Krishna Chatradhi [MEM_HBM2] = "High-bandwidth-memory-Gen2",
169239642feSBorislav Petkov };
170239642feSBorislav Petkov EXPORT_SYMBOL_GPL(edac_mem_types);
171239642feSBorislav Petkov
_edac_mc_free(struct mem_ctl_info * mci)172faa2ad09SShaun Ruffell static void _edac_mc_free(struct mem_ctl_info *mci)
173faa2ad09SShaun Ruffell {
174bea1bfd5SRobert Richter put_device(&mci->dev);
175bea1bfd5SRobert Richter }
176bea1bfd5SRobert Richter
mci_release(struct device * dev)177bea1bfd5SRobert Richter static void mci_release(struct device *dev)
178bea1bfd5SRobert Richter {
179bea1bfd5SRobert Richter struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
180faa2ad09SShaun Ruffell struct csrow_info *csr;
181718d5851SRobert Richter int i, chn, row;
182faa2ad09SShaun Ruffell
183faa2ad09SShaun Ruffell if (mci->dimms) {
184718d5851SRobert Richter for (i = 0; i < mci->tot_dimms; i++)
185faa2ad09SShaun Ruffell kfree(mci->dimms[i]);
186faa2ad09SShaun Ruffell kfree(mci->dimms);
187faa2ad09SShaun Ruffell }
188718d5851SRobert Richter
189faa2ad09SShaun Ruffell if (mci->csrows) {
190718d5851SRobert Richter for (row = 0; row < mci->nr_csrows; row++) {
191faa2ad09SShaun Ruffell csr = mci->csrows[row];
192718d5851SRobert Richter if (!csr)
193718d5851SRobert Richter continue;
194718d5851SRobert Richter
195faa2ad09SShaun Ruffell if (csr->channels) {
196718d5851SRobert Richter for (chn = 0; chn < mci->num_cschannel; chn++)
197faa2ad09SShaun Ruffell kfree(csr->channels[chn]);
198faa2ad09SShaun Ruffell kfree(csr->channels);
199faa2ad09SShaun Ruffell }
200faa2ad09SShaun Ruffell kfree(csr);
201faa2ad09SShaun Ruffell }
202faa2ad09SShaun Ruffell kfree(mci->csrows);
203faa2ad09SShaun Ruffell }
2040bbb265fSBorislav Petkov kfree(mci->pvt_info);
2050bbb265fSBorislav Petkov kfree(mci->layers);
206faa2ad09SShaun Ruffell kfree(mci);
207faa2ad09SShaun Ruffell }
208faa2ad09SShaun Ruffell
edac_mc_alloc_csrows(struct mem_ctl_info * mci)209aad28c6fSRobert Richter static int edac_mc_alloc_csrows(struct mem_ctl_info *mci)
210aad28c6fSRobert Richter {
211aad28c6fSRobert Richter unsigned int tot_channels = mci->num_cschannel;
212aad28c6fSRobert Richter unsigned int tot_csrows = mci->nr_csrows;
213aad28c6fSRobert Richter unsigned int row, chn;
214aad28c6fSRobert Richter
215aad28c6fSRobert Richter /*
216aad28c6fSRobert Richter * Alocate and fill the csrow/channels structs
217aad28c6fSRobert Richter */
218aad28c6fSRobert Richter mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL);
219aad28c6fSRobert Richter if (!mci->csrows)
220aad28c6fSRobert Richter return -ENOMEM;
221aad28c6fSRobert Richter
222aad28c6fSRobert Richter for (row = 0; row < tot_csrows; row++) {
223aad28c6fSRobert Richter struct csrow_info *csr;
224aad28c6fSRobert Richter
225aad28c6fSRobert Richter csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL);
226aad28c6fSRobert Richter if (!csr)
227aad28c6fSRobert Richter return -ENOMEM;
228aad28c6fSRobert Richter
229aad28c6fSRobert Richter mci->csrows[row] = csr;
230aad28c6fSRobert Richter csr->csrow_idx = row;
231aad28c6fSRobert Richter csr->mci = mci;
232aad28c6fSRobert Richter csr->nr_channels = tot_channels;
233aad28c6fSRobert Richter csr->channels = kcalloc(tot_channels, sizeof(*csr->channels),
234aad28c6fSRobert Richter GFP_KERNEL);
235aad28c6fSRobert Richter if (!csr->channels)
236aad28c6fSRobert Richter return -ENOMEM;
237aad28c6fSRobert Richter
238aad28c6fSRobert Richter for (chn = 0; chn < tot_channels; chn++) {
239aad28c6fSRobert Richter struct rank_info *chan;
240aad28c6fSRobert Richter
241aad28c6fSRobert Richter chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL);
242aad28c6fSRobert Richter if (!chan)
243aad28c6fSRobert Richter return -ENOMEM;
244aad28c6fSRobert Richter
245aad28c6fSRobert Richter csr->channels[chn] = chan;
246aad28c6fSRobert Richter chan->chan_idx = chn;
247aad28c6fSRobert Richter chan->csrow = csr;
248aad28c6fSRobert Richter }
249aad28c6fSRobert Richter }
250aad28c6fSRobert Richter
251aad28c6fSRobert Richter return 0;
252aad28c6fSRobert Richter }
253aad28c6fSRobert Richter
edac_mc_alloc_dimms(struct mem_ctl_info * mci)254aad28c6fSRobert Richter static int edac_mc_alloc_dimms(struct mem_ctl_info *mci)
255aad28c6fSRobert Richter {
256aad28c6fSRobert Richter unsigned int pos[EDAC_MAX_LAYERS];
257aad28c6fSRobert Richter unsigned int row, chn, idx;
258aad28c6fSRobert Richter int layer;
259aad28c6fSRobert Richter void *p;
260aad28c6fSRobert Richter
261aad28c6fSRobert Richter /*
262aad28c6fSRobert Richter * Allocate and fill the dimm structs
263aad28c6fSRobert Richter */
264aad28c6fSRobert Richter mci->dimms = kcalloc(mci->tot_dimms, sizeof(*mci->dimms), GFP_KERNEL);
265aad28c6fSRobert Richter if (!mci->dimms)
266aad28c6fSRobert Richter return -ENOMEM;
267aad28c6fSRobert Richter
268aad28c6fSRobert Richter memset(&pos, 0, sizeof(pos));
269aad28c6fSRobert Richter row = 0;
270aad28c6fSRobert Richter chn = 0;
271aad28c6fSRobert Richter for (idx = 0; idx < mci->tot_dimms; idx++) {
272aad28c6fSRobert Richter struct dimm_info *dimm;
273aad28c6fSRobert Richter struct rank_info *chan;
274aad28c6fSRobert Richter int n, len;
275aad28c6fSRobert Richter
276aad28c6fSRobert Richter chan = mci->csrows[row]->channels[chn];
277aad28c6fSRobert Richter
278aad28c6fSRobert Richter dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL);
279aad28c6fSRobert Richter if (!dimm)
280aad28c6fSRobert Richter return -ENOMEM;
281aad28c6fSRobert Richter mci->dimms[idx] = dimm;
282aad28c6fSRobert Richter dimm->mci = mci;
283aad28c6fSRobert Richter dimm->idx = idx;
284aad28c6fSRobert Richter
285aad28c6fSRobert Richter /*
286aad28c6fSRobert Richter * Copy DIMM location and initialize it.
287aad28c6fSRobert Richter */
288aad28c6fSRobert Richter len = sizeof(dimm->label);
289aad28c6fSRobert Richter p = dimm->label;
290fca61165SLen Baker n = scnprintf(p, len, "mc#%u", mci->mc_idx);
291aad28c6fSRobert Richter p += n;
292aad28c6fSRobert Richter len -= n;
293aad28c6fSRobert Richter for (layer = 0; layer < mci->n_layers; layer++) {
294fca61165SLen Baker n = scnprintf(p, len, "%s#%u",
295aad28c6fSRobert Richter edac_layer_name[mci->layers[layer].type],
296aad28c6fSRobert Richter pos[layer]);
297aad28c6fSRobert Richter p += n;
298aad28c6fSRobert Richter len -= n;
299aad28c6fSRobert Richter dimm->location[layer] = pos[layer];
300aad28c6fSRobert Richter }
301aad28c6fSRobert Richter
302aad28c6fSRobert Richter /* Link it to the csrows old API data */
303aad28c6fSRobert Richter chan->dimm = dimm;
304aad28c6fSRobert Richter dimm->csrow = row;
305aad28c6fSRobert Richter dimm->cschannel = chn;
306aad28c6fSRobert Richter
307aad28c6fSRobert Richter /* Increment csrow location */
308aad28c6fSRobert Richter if (mci->layers[0].is_virt_csrow) {
309aad28c6fSRobert Richter chn++;
310aad28c6fSRobert Richter if (chn == mci->num_cschannel) {
311aad28c6fSRobert Richter chn = 0;
312aad28c6fSRobert Richter row++;
313aad28c6fSRobert Richter }
314aad28c6fSRobert Richter } else {
315aad28c6fSRobert Richter row++;
316aad28c6fSRobert Richter if (row == mci->nr_csrows) {
317aad28c6fSRobert Richter row = 0;
318aad28c6fSRobert Richter chn++;
319aad28c6fSRobert Richter }
320aad28c6fSRobert Richter }
321aad28c6fSRobert Richter
322aad28c6fSRobert Richter /* Increment dimm location */
323aad28c6fSRobert Richter for (layer = mci->n_layers - 1; layer >= 0; layer--) {
324aad28c6fSRobert Richter pos[layer]++;
325aad28c6fSRobert Richter if (pos[layer] < mci->layers[layer].size)
326aad28c6fSRobert Richter break;
327aad28c6fSRobert Richter pos[layer] = 0;
328aad28c6fSRobert Richter }
329aad28c6fSRobert Richter }
330aad28c6fSRobert Richter
331aad28c6fSRobert Richter return 0;
332aad28c6fSRobert Richter }
333aad28c6fSRobert Richter
edac_mc_alloc(unsigned int mc_num,unsigned int n_layers,struct edac_mc_layer * layers,unsigned int sz_pvt)3341f27c790SRobert Richter struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num,
3351f27c790SRobert Richter unsigned int n_layers,
3361f27c790SRobert Richter struct edac_mc_layer *layers,
3371f27c790SRobert Richter unsigned int sz_pvt)
3381f27c790SRobert Richter {
3391f27c790SRobert Richter struct mem_ctl_info *mci;
3401f27c790SRobert Richter struct edac_mc_layer *layer;
3410bbb265fSBorislav Petkov unsigned int idx, tot_dimms = 1;
3424aa92c86SRobert Richter unsigned int tot_csrows = 1, tot_channels = 1;
3431f27c790SRobert Richter bool per_rank = false;
3441f27c790SRobert Richter
3451f27c790SRobert Richter if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0))
3461f27c790SRobert Richter return NULL;
3471f27c790SRobert Richter
3481f27c790SRobert Richter /*
3491f27c790SRobert Richter * Calculate the total amount of dimms and csrows/cschannels while
3501f27c790SRobert Richter * in the old API emulation mode
3511f27c790SRobert Richter */
3521f27c790SRobert Richter for (idx = 0; idx < n_layers; idx++) {
3531f27c790SRobert Richter tot_dimms *= layers[idx].size;
3541f27c790SRobert Richter
3551f27c790SRobert Richter if (layers[idx].is_virt_csrow)
3561f27c790SRobert Richter tot_csrows *= layers[idx].size;
3571f27c790SRobert Richter else
3581f27c790SRobert Richter tot_channels *= layers[idx].size;
3591f27c790SRobert Richter
3601f27c790SRobert Richter if (layers[idx].type == EDAC_MC_LAYER_CHIP_SELECT)
3611f27c790SRobert Richter per_rank = true;
3621f27c790SRobert Richter }
3631f27c790SRobert Richter
3640bbb265fSBorislav Petkov mci = kzalloc(sizeof(struct mem_ctl_info), GFP_KERNEL);
3650bbb265fSBorislav Petkov if (!mci)
3661f27c790SRobert Richter return NULL;
3671f27c790SRobert Richter
368*13088b65SBorislav Petkov mci->layers = kcalloc(n_layers, sizeof(struct edac_mc_layer), GFP_KERNEL);
3690bbb265fSBorislav Petkov if (!mci->layers)
3700bbb265fSBorislav Petkov goto error;
3710bbb265fSBorislav Petkov
3720bbb265fSBorislav Petkov mci->pvt_info = kzalloc(sz_pvt, GFP_KERNEL);
3730bbb265fSBorislav Petkov if (!mci->pvt_info)
3740bbb265fSBorislav Petkov goto error;
3750bbb265fSBorislav Petkov
3761f27c790SRobert Richter mci->dev.release = mci_release;
3771f27c790SRobert Richter device_initialize(&mci->dev);
3781f27c790SRobert Richter
3791f27c790SRobert Richter /* setup index and various internal pointers */
3801f27c790SRobert Richter mci->mc_idx = mc_num;
3811f27c790SRobert Richter mci->tot_dimms = tot_dimms;
3821f27c790SRobert Richter mci->n_layers = n_layers;
3831f27c790SRobert Richter memcpy(mci->layers, layers, sizeof(*layer) * n_layers);
3841f27c790SRobert Richter mci->nr_csrows = tot_csrows;
3851f27c790SRobert Richter mci->num_cschannel = tot_channels;
3861f27c790SRobert Richter mci->csbased = per_rank;
3871f27c790SRobert Richter
3881f27c790SRobert Richter if (edac_mc_alloc_csrows(mci))
3891f27c790SRobert Richter goto error;
3901f27c790SRobert Richter
3911f27c790SRobert Richter if (edac_mc_alloc_dimms(mci))
3921f27c790SRobert Richter goto error;
3931f27c790SRobert Richter
3941f27c790SRobert Richter mci->op_state = OP_ALLOC;
3951f27c790SRobert Richter
3961f27c790SRobert Richter return mci;
3971f27c790SRobert Richter
3981f27c790SRobert Richter error:
3991f27c790SRobert Richter _edac_mc_free(mci);
4001f27c790SRobert Richter
4011f27c790SRobert Richter return NULL;
4021f27c790SRobert Richter }
4031f27c790SRobert Richter EXPORT_SYMBOL_GPL(edac_mc_alloc);
4041f27c790SRobert Richter
edac_mc_free(struct mem_ctl_info * mci)405da9bb1d2SAlan Cox void edac_mc_free(struct mem_ctl_info *mci)
406da9bb1d2SAlan Cox {
407956b9ba1SJoe Perches edac_dbg(1, "\n");
408bbc560aeSMauro Carvalho Chehab
409216aa145SRobert Richter _edac_mc_free(mci);
410da9bb1d2SAlan Cox }
4119110540fSDave Peterson EXPORT_SYMBOL_GPL(edac_mc_free);
412da9bb1d2SAlan Cox
edac_has_mcs(void)413d7fc9d77SYazen Ghannam bool edac_has_mcs(void)
414d7fc9d77SYazen Ghannam {
415d7fc9d77SYazen Ghannam bool ret;
416d7fc9d77SYazen Ghannam
417d7fc9d77SYazen Ghannam mutex_lock(&mem_ctls_mutex);
418d7fc9d77SYazen Ghannam
419d7fc9d77SYazen Ghannam ret = list_empty(&mc_devices);
420d7fc9d77SYazen Ghannam
421d7fc9d77SYazen Ghannam mutex_unlock(&mem_ctls_mutex);
422d7fc9d77SYazen Ghannam
423d7fc9d77SYazen Ghannam return !ret;
424d7fc9d77SYazen Ghannam }
425d7fc9d77SYazen Ghannam EXPORT_SYMBOL_GPL(edac_has_mcs);
426d7fc9d77SYazen Ghannam
427c73e8833SBorislav Petkov /* Caller must hold mem_ctls_mutex */
__find_mci_by_dev(struct device * dev)428c73e8833SBorislav Petkov static struct mem_ctl_info *__find_mci_by_dev(struct device *dev)
429da9bb1d2SAlan Cox {
430da9bb1d2SAlan Cox struct mem_ctl_info *mci;
431da9bb1d2SAlan Cox struct list_head *item;
432da9bb1d2SAlan Cox
433956b9ba1SJoe Perches edac_dbg(3, "\n");
434da9bb1d2SAlan Cox
435da9bb1d2SAlan Cox list_for_each(item, &mc_devices) {
436da9bb1d2SAlan Cox mci = list_entry(item, struct mem_ctl_info, link);
437da9bb1d2SAlan Cox
438fd687502SMauro Carvalho Chehab if (mci->pdev == dev)
439da9bb1d2SAlan Cox return mci;
440da9bb1d2SAlan Cox }
441da9bb1d2SAlan Cox
442da9bb1d2SAlan Cox return NULL;
443da9bb1d2SAlan Cox }
444c73e8833SBorislav Petkov
445c73e8833SBorislav Petkov /**
446c73e8833SBorislav Petkov * find_mci_by_dev
447c73e8833SBorislav Petkov *
448c73e8833SBorislav Petkov * scan list of controllers looking for the one that manages
449c73e8833SBorislav Petkov * the 'dev' device
450c73e8833SBorislav Petkov * @dev: pointer to a struct device related with the MCI
451c73e8833SBorislav Petkov */
find_mci_by_dev(struct device * dev)452c73e8833SBorislav Petkov struct mem_ctl_info *find_mci_by_dev(struct device *dev)
453c73e8833SBorislav Petkov {
454c73e8833SBorislav Petkov struct mem_ctl_info *ret;
455c73e8833SBorislav Petkov
456c73e8833SBorislav Petkov mutex_lock(&mem_ctls_mutex);
457c73e8833SBorislav Petkov ret = __find_mci_by_dev(dev);
458c73e8833SBorislav Petkov mutex_unlock(&mem_ctls_mutex);
459c73e8833SBorislav Petkov
460c73e8833SBorislav Petkov return ret;
461c73e8833SBorislav Petkov }
462939747bdSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(find_mci_by_dev);
463da9bb1d2SAlan Cox
46481d87cb1SDave Jiang /*
46581d87cb1SDave Jiang * edac_mc_workq_function
46681d87cb1SDave Jiang * performs the operation scheduled by a workq request
46781d87cb1SDave Jiang */
edac_mc_workq_function(struct work_struct * work_req)46881d87cb1SDave Jiang static void edac_mc_workq_function(struct work_struct *work_req)
46981d87cb1SDave Jiang {
470fbeb4384SJean Delvare struct delayed_work *d_work = to_delayed_work(work_req);
47181d87cb1SDave Jiang struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
47281d87cb1SDave Jiang
47381d87cb1SDave Jiang mutex_lock(&mem_ctls_mutex);
47481d87cb1SDave Jiang
47506e912d4SBorislav Petkov if (mci->op_state != OP_RUNNING_POLL) {
476bf52fa4aSDoug Thompson mutex_unlock(&mem_ctls_mutex);
477bf52fa4aSDoug Thompson return;
478bf52fa4aSDoug Thompson }
479bf52fa4aSDoug Thompson
480d3116a08SBorislav Petkov if (edac_op_state == EDAC_OPSTATE_POLL)
48181d87cb1SDave Jiang mci->edac_check(mci);
48281d87cb1SDave Jiang
48381d87cb1SDave Jiang mutex_unlock(&mem_ctls_mutex);
48481d87cb1SDave Jiang
48506e912d4SBorislav Petkov /* Queue ourselves again. */
486c4cf3b45SBorislav Petkov edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec()));
48781d87cb1SDave Jiang }
48881d87cb1SDave Jiang
48981d87cb1SDave Jiang /*
490bce19683SDoug Thompson * edac_mc_reset_delay_period(unsigned long value)
491bce19683SDoug Thompson *
492bce19683SDoug Thompson * user space has updated our poll period value, need to
493bce19683SDoug Thompson * reset our workq delays
49481d87cb1SDave Jiang */
edac_mc_reset_delay_period(unsigned long value)4959da21b15SBorislav Petkov void edac_mc_reset_delay_period(unsigned long value)
49681d87cb1SDave Jiang {
497bce19683SDoug Thompson struct mem_ctl_info *mci;
498bce19683SDoug Thompson struct list_head *item;
49981d87cb1SDave Jiang
500bf52fa4aSDoug Thompson mutex_lock(&mem_ctls_mutex);
501bf52fa4aSDoug Thompson
502bce19683SDoug Thompson list_for_each(item, &mc_devices) {
503bce19683SDoug Thompson mci = list_entry(item, struct mem_ctl_info, link);
504bce19683SDoug Thompson
505fbedcaf4SNicholas Krause if (mci->op_state == OP_RUNNING_POLL)
506c4cf3b45SBorislav Petkov edac_mod_work(&mci->work, value);
507bce19683SDoug Thompson }
50881d87cb1SDave Jiang mutex_unlock(&mem_ctls_mutex);
50981d87cb1SDave Jiang }
51081d87cb1SDave Jiang
511bce19683SDoug Thompson
512bce19683SDoug Thompson
5132d7bbb91SDoug Thompson /* Return 0 on success, 1 on failure.
5142d7bbb91SDoug Thompson * Before calling this function, caller must
5152d7bbb91SDoug Thompson * assign a unique value to mci->mc_idx.
516bf52fa4aSDoug Thompson *
517bf52fa4aSDoug Thompson * locking model:
518bf52fa4aSDoug Thompson *
519bf52fa4aSDoug Thompson * called with the mem_ctls_mutex lock held
5202d7bbb91SDoug Thompson */
add_mc_to_global_list(struct mem_ctl_info * mci)521da9bb1d2SAlan Cox static int add_mc_to_global_list(struct mem_ctl_info *mci)
522da9bb1d2SAlan Cox {
523da9bb1d2SAlan Cox struct list_head *item, *insert_before;
524da9bb1d2SAlan Cox struct mem_ctl_info *p;
525da9bb1d2SAlan Cox
526da9bb1d2SAlan Cox insert_before = &mc_devices;
527da9bb1d2SAlan Cox
528c73e8833SBorislav Petkov p = __find_mci_by_dev(mci->pdev);
529bf52fa4aSDoug Thompson if (unlikely(p != NULL))
5302d7bbb91SDoug Thompson goto fail0;
531da9bb1d2SAlan Cox
532da9bb1d2SAlan Cox list_for_each(item, &mc_devices) {
533da9bb1d2SAlan Cox p = list_entry(item, struct mem_ctl_info, link);
534da9bb1d2SAlan Cox
5352d7bbb91SDoug Thompson if (p->mc_idx >= mci->mc_idx) {
5362d7bbb91SDoug Thompson if (unlikely(p->mc_idx == mci->mc_idx))
5372d7bbb91SDoug Thompson goto fail1;
5382d7bbb91SDoug Thompson
539da9bb1d2SAlan Cox insert_before = item;
540da9bb1d2SAlan Cox break;
541da9bb1d2SAlan Cox }
542da9bb1d2SAlan Cox }
543da9bb1d2SAlan Cox
544da9bb1d2SAlan Cox list_add_tail_rcu(&mci->link, insert_before);
545da9bb1d2SAlan Cox return 0;
5462d7bbb91SDoug Thompson
5472d7bbb91SDoug Thompson fail0:
5482d7bbb91SDoug Thompson edac_printk(KERN_WARNING, EDAC_MC,
549fd687502SMauro Carvalho Chehab "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev),
55017aa7e03SStephen Rothwell edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
5512d7bbb91SDoug Thompson return 1;
5522d7bbb91SDoug Thompson
5532d7bbb91SDoug Thompson fail1:
5542d7bbb91SDoug Thompson edac_printk(KERN_WARNING, EDAC_MC,
5552d7bbb91SDoug Thompson "bug in low-level driver: attempt to assign\n"
5562d7bbb91SDoug Thompson " duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
5572d7bbb91SDoug Thompson return 1;
558da9bb1d2SAlan Cox }
559da9bb1d2SAlan Cox
del_mc_from_global_list(struct mem_ctl_info * mci)56080cc7d87SMauro Carvalho Chehab static int del_mc_from_global_list(struct mem_ctl_info *mci)
561a1d03fccSDave Peterson {
562a1d03fccSDave Peterson list_del_rcu(&mci->link);
563e2e77098SLai Jiangshan
564e2e77098SLai Jiangshan /* these are for safe removal of devices from global list while
565e2e77098SLai Jiangshan * NMI handlers may be traversing list
566e2e77098SLai Jiangshan */
567e2e77098SLai Jiangshan synchronize_rcu();
568e2e77098SLai Jiangshan INIT_LIST_HEAD(&mci->link);
56980cc7d87SMauro Carvalho Chehab
57097bb6c17SBorislav Petkov return list_empty(&mc_devices);
571a1d03fccSDave Peterson }
572a1d03fccSDave Peterson
edac_mc_find(int idx)5735da0831cSDouglas Thompson struct mem_ctl_info *edac_mc_find(int idx)
5745da0831cSDouglas Thompson {
57529a0c843SRobert Richter struct mem_ctl_info *mci;
5765da0831cSDouglas Thompson struct list_head *item;
577c73e8833SBorislav Petkov
578c73e8833SBorislav Petkov mutex_lock(&mem_ctls_mutex);
5795da0831cSDouglas Thompson
5805da0831cSDouglas Thompson list_for_each(item, &mc_devices) {
5815da0831cSDouglas Thompson mci = list_entry(item, struct mem_ctl_info, link);
58229a0c843SRobert Richter if (mci->mc_idx == idx)
583c73e8833SBorislav Petkov goto unlock;
584c73e8833SBorislav Petkov }
5855da0831cSDouglas Thompson
58629a0c843SRobert Richter mci = NULL;
587c73e8833SBorislav Petkov unlock:
588c73e8833SBorislav Petkov mutex_unlock(&mem_ctls_mutex);
589c73e8833SBorislav Petkov return mci;
5905da0831cSDouglas Thompson }
5915da0831cSDouglas Thompson EXPORT_SYMBOL(edac_mc_find);
5925da0831cSDouglas Thompson
edac_get_owner(void)5933877c7d1SToshi Kani const char *edac_get_owner(void)
5943877c7d1SToshi Kani {
5953877c7d1SToshi Kani return edac_mc_owner;
5963877c7d1SToshi Kani }
5973877c7d1SToshi Kani EXPORT_SYMBOL_GPL(edac_get_owner);
598da9bb1d2SAlan Cox
599da9bb1d2SAlan Cox /* FIXME - should a warning be printed if no error detection? correction? */
edac_mc_add_mc_with_groups(struct mem_ctl_info * mci,const struct attribute_group ** groups)6004e8d230dSTakashi Iwai int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
6014e8d230dSTakashi Iwai const struct attribute_group **groups)
602da9bb1d2SAlan Cox {
60380cc7d87SMauro Carvalho Chehab int ret = -EINVAL;
604956b9ba1SJoe Perches edac_dbg(0, "\n");
605b8f6f975SDoug Thompson
606da9bb1d2SAlan Cox #ifdef CONFIG_EDAC_DEBUG
607da9bb1d2SAlan Cox if (edac_debug_level >= 3)
608da9bb1d2SAlan Cox edac_mc_dump_mci(mci);
609e7ecd891SDave Peterson
610da9bb1d2SAlan Cox if (edac_debug_level >= 4) {
611c498afafSRobert Richter struct dimm_info *dimm;
612da9bb1d2SAlan Cox int i;
613da9bb1d2SAlan Cox
614da9bb1d2SAlan Cox for (i = 0; i < mci->nr_csrows; i++) {
6156e84d359SMauro Carvalho Chehab struct csrow_info *csrow = mci->csrows[i];
6166e84d359SMauro Carvalho Chehab u32 nr_pages = 0;
617da9bb1d2SAlan Cox int j;
618e7ecd891SDave Peterson
6196e84d359SMauro Carvalho Chehab for (j = 0; j < csrow->nr_channels; j++)
6206e84d359SMauro Carvalho Chehab nr_pages += csrow->channels[j]->dimm->nr_pages;
6216e84d359SMauro Carvalho Chehab if (!nr_pages)
6226e84d359SMauro Carvalho Chehab continue;
6236e84d359SMauro Carvalho Chehab edac_mc_dump_csrow(csrow);
6246e84d359SMauro Carvalho Chehab for (j = 0; j < csrow->nr_channels; j++)
6256e84d359SMauro Carvalho Chehab if (csrow->channels[j]->dimm->nr_pages)
6266e84d359SMauro Carvalho Chehab edac_mc_dump_channel(csrow->channels[j]);
627da9bb1d2SAlan Cox }
628c498afafSRobert Richter
629c498afafSRobert Richter mci_for_each_dimm(mci, dimm)
630c498afafSRobert Richter edac_mc_dump_dimm(dimm);
631da9bb1d2SAlan Cox }
632da9bb1d2SAlan Cox #endif
63363b7df91SMatthias Kaehlcke mutex_lock(&mem_ctls_mutex);
634da9bb1d2SAlan Cox
63580cc7d87SMauro Carvalho Chehab if (edac_mc_owner && edac_mc_owner != mci->mod_name) {
63680cc7d87SMauro Carvalho Chehab ret = -EPERM;
63780cc7d87SMauro Carvalho Chehab goto fail0;
63880cc7d87SMauro Carvalho Chehab }
63980cc7d87SMauro Carvalho Chehab
640da9bb1d2SAlan Cox if (add_mc_to_global_list(mci))
641028a7b6dSDave Peterson goto fail0;
642da9bb1d2SAlan Cox
643da9bb1d2SAlan Cox /* set load time so that error rate can be tracked */
644da9bb1d2SAlan Cox mci->start_time = jiffies;
645da9bb1d2SAlan Cox
646861e6ed6SBorislav Petkov mci->bus = edac_get_sysfs_subsys();
64788d84ac9SBorislav Petkov
6484e8d230dSTakashi Iwai if (edac_create_sysfs_mci_device(mci, groups)) {
649537fba28SDave Peterson edac_mc_printk(mci, KERN_WARNING,
650537fba28SDave Peterson "failed to create sysfs device\n");
651028a7b6dSDave Peterson goto fail1;
652da9bb1d2SAlan Cox }
653da9bb1d2SAlan Cox
65409667606SBorislav Petkov if (mci->edac_check) {
65581d87cb1SDave Jiang mci->op_state = OP_RUNNING_POLL;
65681d87cb1SDave Jiang
657626a7a4dSBorislav Petkov INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
658626a7a4dSBorislav Petkov edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec()));
659626a7a4dSBorislav Petkov
66081d87cb1SDave Jiang } else {
66181d87cb1SDave Jiang mci->op_state = OP_RUNNING_INTERRUPT;
66281d87cb1SDave Jiang }
66381d87cb1SDave Jiang
664da9bb1d2SAlan Cox /* Report action taken */
6657270a608SRobert Richter edac_mc_printk(mci, KERN_INFO,
6667270a608SRobert Richter "Giving out device to module %s controller %s: DEV %s (%s)\n",
6677270a608SRobert Richter mci->mod_name, mci->ctl_name, mci->dev_name,
6687270a608SRobert Richter edac_op_state_to_string(mci->op_state));
669da9bb1d2SAlan Cox
67080cc7d87SMauro Carvalho Chehab edac_mc_owner = mci->mod_name;
67180cc7d87SMauro Carvalho Chehab
67263b7df91SMatthias Kaehlcke mutex_unlock(&mem_ctls_mutex);
673028a7b6dSDave Peterson return 0;
674028a7b6dSDave Peterson
675028a7b6dSDave Peterson fail1:
676028a7b6dSDave Peterson del_mc_from_global_list(mci);
677028a7b6dSDave Peterson
678028a7b6dSDave Peterson fail0:
67963b7df91SMatthias Kaehlcke mutex_unlock(&mem_ctls_mutex);
68080cc7d87SMauro Carvalho Chehab return ret;
681da9bb1d2SAlan Cox }
6824e8d230dSTakashi Iwai EXPORT_SYMBOL_GPL(edac_mc_add_mc_with_groups);
683da9bb1d2SAlan Cox
edac_mc_del_mc(struct device * dev)68437f04581SDoug Thompson struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
685da9bb1d2SAlan Cox {
68618dbc337SDave Peterson struct mem_ctl_info *mci;
687da9bb1d2SAlan Cox
688956b9ba1SJoe Perches edac_dbg(0, "\n");
689bf52fa4aSDoug Thompson
69063b7df91SMatthias Kaehlcke mutex_lock(&mem_ctls_mutex);
69118dbc337SDave Peterson
692bf52fa4aSDoug Thompson /* find the requested mci struct in the global list */
693c73e8833SBorislav Petkov mci = __find_mci_by_dev(dev);
694bf52fa4aSDoug Thompson if (mci == NULL) {
69563b7df91SMatthias Kaehlcke mutex_unlock(&mem_ctls_mutex);
69618dbc337SDave Peterson return NULL;
69718dbc337SDave Peterson }
69818dbc337SDave Peterson
69909667606SBorislav Petkov /* mark MCI offline: */
70009667606SBorislav Petkov mci->op_state = OP_OFFLINE;
70109667606SBorislav Petkov
70297bb6c17SBorislav Petkov if (del_mc_from_global_list(mci))
70380cc7d87SMauro Carvalho Chehab edac_mc_owner = NULL;
70409667606SBorislav Petkov
70563b7df91SMatthias Kaehlcke mutex_unlock(&mem_ctls_mutex);
706bf52fa4aSDoug Thompson
70709667606SBorislav Petkov if (mci->edac_check)
708626a7a4dSBorislav Petkov edac_stop_work(&mci->work);
709bb31b312SBorislav Petkov
710bb31b312SBorislav Petkov /* remove from sysfs */
711bf52fa4aSDoug Thompson edac_remove_sysfs_mci_device(mci);
712bf52fa4aSDoug Thompson
713537fba28SDave Peterson edac_printk(KERN_INFO, EDAC_MC,
71437f04581SDoug Thompson "Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
71517aa7e03SStephen Rothwell mci->mod_name, mci->ctl_name, edac_dev_name(mci));
716bf52fa4aSDoug Thompson
71718dbc337SDave Peterson return mci;
718da9bb1d2SAlan Cox }
7199110540fSDave Peterson EXPORT_SYMBOL_GPL(edac_mc_del_mc);
720da9bb1d2SAlan Cox
edac_mc_scrub_block(unsigned long page,unsigned long offset,u32 size)7212da1c119SAdrian Bunk static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
7222da1c119SAdrian Bunk u32 size)
723da9bb1d2SAlan Cox {
724da9bb1d2SAlan Cox struct page *pg;
725da9bb1d2SAlan Cox void *virt_addr;
726da9bb1d2SAlan Cox unsigned long flags = 0;
727da9bb1d2SAlan Cox
728956b9ba1SJoe Perches edac_dbg(3, "\n");
729da9bb1d2SAlan Cox
730da9bb1d2SAlan Cox /* ECC error page was not in our memory. Ignore it. */
731da9bb1d2SAlan Cox if (!pfn_valid(page))
732da9bb1d2SAlan Cox return;
733da9bb1d2SAlan Cox
734da9bb1d2SAlan Cox /* Find the actual page structure then map it and fix */
735da9bb1d2SAlan Cox pg = pfn_to_page(page);
736da9bb1d2SAlan Cox
737da9bb1d2SAlan Cox if (PageHighMem(pg))
738da9bb1d2SAlan Cox local_irq_save(flags);
739da9bb1d2SAlan Cox
7404e5df7caSCong Wang virt_addr = kmap_atomic(pg);
741da9bb1d2SAlan Cox
742da9bb1d2SAlan Cox /* Perform architecture specific atomic scrub operation */
743b01aec9bSBorislav Petkov edac_atomic_scrub(virt_addr + offset, size);
744da9bb1d2SAlan Cox
745da9bb1d2SAlan Cox /* Unmap and complete */
7464e5df7caSCong Wang kunmap_atomic(virt_addr);
747da9bb1d2SAlan Cox
748da9bb1d2SAlan Cox if (PageHighMem(pg))
749da9bb1d2SAlan Cox local_irq_restore(flags);
750da9bb1d2SAlan Cox }
751da9bb1d2SAlan Cox
752da9bb1d2SAlan Cox /* FIXME - should return -1 */
edac_mc_find_csrow_by_page(struct mem_ctl_info * mci,unsigned long page)753e7ecd891SDave Peterson int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
754da9bb1d2SAlan Cox {
755de3910ebSMauro Carvalho Chehab struct csrow_info **csrows = mci->csrows;
756a895bf8bSMauro Carvalho Chehab int row, i, j, n;
757da9bb1d2SAlan Cox
758956b9ba1SJoe Perches edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page);
759da9bb1d2SAlan Cox row = -1;
760da9bb1d2SAlan Cox
761da9bb1d2SAlan Cox for (i = 0; i < mci->nr_csrows; i++) {
762de3910ebSMauro Carvalho Chehab struct csrow_info *csrow = csrows[i];
763a895bf8bSMauro Carvalho Chehab n = 0;
764a895bf8bSMauro Carvalho Chehab for (j = 0; j < csrow->nr_channels; j++) {
765de3910ebSMauro Carvalho Chehab struct dimm_info *dimm = csrow->channels[j]->dimm;
766a895bf8bSMauro Carvalho Chehab n += dimm->nr_pages;
767a895bf8bSMauro Carvalho Chehab }
768a895bf8bSMauro Carvalho Chehab if (n == 0)
769da9bb1d2SAlan Cox continue;
770da9bb1d2SAlan Cox
771956b9ba1SJoe Perches edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n",
772956b9ba1SJoe Perches mci->mc_idx,
773537fba28SDave Peterson csrow->first_page, page, csrow->last_page,
774537fba28SDave Peterson csrow->page_mask);
775da9bb1d2SAlan Cox
776da9bb1d2SAlan Cox if ((page >= csrow->first_page) &&
777da9bb1d2SAlan Cox (page <= csrow->last_page) &&
778da9bb1d2SAlan Cox ((page & csrow->page_mask) ==
779da9bb1d2SAlan Cox (csrow->first_page & csrow->page_mask))) {
780da9bb1d2SAlan Cox row = i;
781da9bb1d2SAlan Cox break;
782da9bb1d2SAlan Cox }
783da9bb1d2SAlan Cox }
784da9bb1d2SAlan Cox
785da9bb1d2SAlan Cox if (row == -1)
786537fba28SDave Peterson edac_mc_printk(mci, KERN_ERR,
787537fba28SDave Peterson "could not look up page error address %lx\n",
788537fba28SDave Peterson (unsigned long)page);
789da9bb1d2SAlan Cox
790da9bb1d2SAlan Cox return row;
791da9bb1d2SAlan Cox }
7929110540fSDave Peterson EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
793da9bb1d2SAlan Cox
7944275be63SMauro Carvalho Chehab const char *edac_layer_name[] = {
7954275be63SMauro Carvalho Chehab [EDAC_MC_LAYER_BRANCH] = "branch",
7964275be63SMauro Carvalho Chehab [EDAC_MC_LAYER_CHANNEL] = "channel",
7974275be63SMauro Carvalho Chehab [EDAC_MC_LAYER_SLOT] = "slot",
7984275be63SMauro Carvalho Chehab [EDAC_MC_LAYER_CHIP_SELECT] = "csrow",
799c66b5a79SMauro Carvalho Chehab [EDAC_MC_LAYER_ALL_MEM] = "memory",
8004275be63SMauro Carvalho Chehab };
8014275be63SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(edac_layer_name);
8024275be63SMauro Carvalho Chehab
edac_inc_ce_error(struct edac_raw_error_desc * e)8036ab76179SRobert Richter static void edac_inc_ce_error(struct edac_raw_error_desc *e)
804da9bb1d2SAlan Cox {
8056ab76179SRobert Richter int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer };
8066ab76179SRobert Richter struct mem_ctl_info *mci = error_desc_to_mci(e);
8074aa92c86SRobert Richter struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]);
808da9bb1d2SAlan Cox
8096ab76179SRobert Richter mci->ce_mc += e->error_count;
8104275be63SMauro Carvalho Chehab
8114aa92c86SRobert Richter if (dimm)
8124aa92c86SRobert Richter dimm->ce_count += e->error_count;
8134aa92c86SRobert Richter else
8146ab76179SRobert Richter mci->ce_noinfo_count += e->error_count;
8154275be63SMauro Carvalho Chehab }
8164275be63SMauro Carvalho Chehab
edac_inc_ue_error(struct edac_raw_error_desc * e)8176ab76179SRobert Richter static void edac_inc_ue_error(struct edac_raw_error_desc *e)
8184275be63SMauro Carvalho Chehab {
8196ab76179SRobert Richter int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer };
8206ab76179SRobert Richter struct mem_ctl_info *mci = error_desc_to_mci(e);
8214aa92c86SRobert Richter struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]);
8224275be63SMauro Carvalho Chehab
8236ab76179SRobert Richter mci->ue_mc += e->error_count;
8244275be63SMauro Carvalho Chehab
8254aa92c86SRobert Richter if (dimm)
8264aa92c86SRobert Richter dimm->ue_count += e->error_count;
8274aa92c86SRobert Richter else
8286ab76179SRobert Richter mci->ue_noinfo_count += e->error_count;
8294275be63SMauro Carvalho Chehab }
8304275be63SMauro Carvalho Chehab
edac_ce_error(struct edac_raw_error_desc * e)8311853ee72SRobert Richter static void edac_ce_error(struct edac_raw_error_desc *e)
8324275be63SMauro Carvalho Chehab {
8336ab76179SRobert Richter struct mem_ctl_info *mci = error_desc_to_mci(e);
8344275be63SMauro Carvalho Chehab unsigned long remapped_page;
8354275be63SMauro Carvalho Chehab
8364275be63SMauro Carvalho Chehab if (edac_mc_get_log_ce()) {
8374275be63SMauro Carvalho Chehab edac_mc_printk(mci, KERN_WARNING,
8381853ee72SRobert Richter "%d CE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx%s%s)\n",
8391853ee72SRobert Richter e->error_count, e->msg,
8401853ee72SRobert Richter *e->msg ? " " : "",
8411853ee72SRobert Richter e->label, e->location, e->page_frame_number, e->offset_in_page,
8421853ee72SRobert Richter e->grain, e->syndrome,
8431853ee72SRobert Richter *e->other_detail ? " - " : "",
8441853ee72SRobert Richter e->other_detail);
8454275be63SMauro Carvalho Chehab }
8466ab76179SRobert Richter
8476ab76179SRobert Richter edac_inc_ce_error(e);
848da9bb1d2SAlan Cox
849aa2064d7SLoc Ho if (mci->scrub_mode == SCRUB_SW_SRC) {
850da9bb1d2SAlan Cox /*
8514275be63SMauro Carvalho Chehab * Some memory controllers (called MCs below) can remap
8524275be63SMauro Carvalho Chehab * memory so that it is still available at a different
8534275be63SMauro Carvalho Chehab * address when PCI devices map into memory.
8544275be63SMauro Carvalho Chehab * MC's that can't do this, lose the memory where PCI
8554275be63SMauro Carvalho Chehab * devices are mapped. This mapping is MC-dependent
8564275be63SMauro Carvalho Chehab * and so we call back into the MC driver for it to
8574275be63SMauro Carvalho Chehab * map the MC page to a physical (CPU) page which can
8584275be63SMauro Carvalho Chehab * then be mapped to a virtual page - which can then
8594275be63SMauro Carvalho Chehab * be scrubbed.
860da9bb1d2SAlan Cox */
861da9bb1d2SAlan Cox remapped_page = mci->ctl_page_to_phys ?
8626ab76179SRobert Richter mci->ctl_page_to_phys(mci, e->page_frame_number) :
8636ab76179SRobert Richter e->page_frame_number;
864da9bb1d2SAlan Cox
8656ab76179SRobert Richter edac_mc_scrub_block(remapped_page, e->offset_in_page, e->grain);
866da9bb1d2SAlan Cox }
867da9bb1d2SAlan Cox }
868da9bb1d2SAlan Cox
edac_ue_error(struct edac_raw_error_desc * e)8691853ee72SRobert Richter static void edac_ue_error(struct edac_raw_error_desc *e)
870da9bb1d2SAlan Cox {
8716ab76179SRobert Richter struct mem_ctl_info *mci = error_desc_to_mci(e);
872f430d570SBorislav Petkov
8734275be63SMauro Carvalho Chehab if (edac_mc_get_log_ue()) {
874537fba28SDave Peterson edac_mc_printk(mci, KERN_WARNING,
8751853ee72SRobert Richter "%d UE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld%s%s)\n",
8761853ee72SRobert Richter e->error_count, e->msg,
8771853ee72SRobert Richter *e->msg ? " " : "",
8781853ee72SRobert Richter e->label, e->location, e->page_frame_number, e->offset_in_page,
8791853ee72SRobert Richter e->grain,
8801853ee72SRobert Richter *e->other_detail ? " - " : "",
8811853ee72SRobert Richter e->other_detail);
882da9bb1d2SAlan Cox }
883da9bb1d2SAlan Cox
884e9ff6636SZhenzhong Duan edac_inc_ue_error(e);
885e9ff6636SZhenzhong Duan
8864275be63SMauro Carvalho Chehab if (edac_mc_get_panic_on_ue()) {
8871853ee72SRobert Richter panic("UE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld%s%s)\n",
8881853ee72SRobert Richter e->msg,
8891853ee72SRobert Richter *e->msg ? " " : "",
8901853ee72SRobert Richter e->label, e->location, e->page_frame_number, e->offset_in_page,
8911853ee72SRobert Richter e->grain,
8921853ee72SRobert Richter *e->other_detail ? " - " : "",
8936ab76179SRobert Richter e->other_detail);
8944275be63SMauro Carvalho Chehab }
8954275be63SMauro Carvalho Chehab }
8964275be63SMauro Carvalho Chehab
edac_inc_csrow(struct edac_raw_error_desc * e,int row,int chan)8976334dc4eSRobert Richter static void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan)
8986334dc4eSRobert Richter {
8996334dc4eSRobert Richter struct mem_ctl_info *mci = error_desc_to_mci(e);
9006334dc4eSRobert Richter enum hw_event_mc_err_type type = e->type;
9016334dc4eSRobert Richter u16 count = e->error_count;
9026334dc4eSRobert Richter
9036334dc4eSRobert Richter if (row < 0)
9046334dc4eSRobert Richter return;
9056334dc4eSRobert Richter
9066334dc4eSRobert Richter edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan);
9076334dc4eSRobert Richter
9086334dc4eSRobert Richter if (type == HW_EVENT_ERR_CORRECTED) {
9096334dc4eSRobert Richter mci->csrows[row]->ce_count += count;
9106334dc4eSRobert Richter if (chan >= 0)
9116334dc4eSRobert Richter mci->csrows[row]->channels[chan]->ce_count += count;
9126334dc4eSRobert Richter } else {
9136334dc4eSRobert Richter mci->csrows[row]->ue_count += count;
9146334dc4eSRobert Richter }
9156334dc4eSRobert Richter }
9166334dc4eSRobert Richter
edac_raw_mc_handle_error(struct edac_raw_error_desc * e)91791b327f6SRobert Richter void edac_raw_mc_handle_error(struct edac_raw_error_desc *e)
918e7e24830SMauro Carvalho Chehab {
91991b327f6SRobert Richter struct mem_ctl_info *mci = error_desc_to_mci(e);
920787d8999SRobert Richter u8 grain_bits;
921787d8999SRobert Richter
922787d8999SRobert Richter /* Sanity-check driver-supplied grain value. */
923787d8999SRobert Richter if (WARN_ON_ONCE(!e->grain))
924787d8999SRobert Richter e->grain = 1;
925787d8999SRobert Richter
926787d8999SRobert Richter grain_bits = fls_long(e->grain - 1);
927787d8999SRobert Richter
928787d8999SRobert Richter /* Report the error via the trace interface */
929787d8999SRobert Richter if (IS_ENABLED(CONFIG_RAS))
930672ef0e5SRobert Richter trace_mc_event(e->type, e->msg, e->label, e->error_count,
931787d8999SRobert Richter mci->mc_idx, e->top_layer, e->mid_layer,
932787d8999SRobert Richter e->low_layer,
933787d8999SRobert Richter (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page,
934787d8999SRobert Richter grain_bits, e->syndrome, e->other_detail);
935e7e24830SMauro Carvalho Chehab
9361853ee72SRobert Richter if (e->type == HW_EVENT_ERR_CORRECTED)
9371853ee72SRobert Richter edac_ce_error(e);
9381853ee72SRobert Richter else
9391853ee72SRobert Richter edac_ue_error(e);
940e7e24830SMauro Carvalho Chehab }
941e7e24830SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(edac_raw_mc_handle_error);
94253f2d028SMauro Carvalho Chehab
edac_mc_handle_error(const enum hw_event_mc_err_type type,struct mem_ctl_info * mci,const u16 error_count,const unsigned long page_frame_number,const unsigned long offset_in_page,const unsigned long syndrome,const int top_layer,const int mid_layer,const int low_layer,const char * msg,const char * other_detail)9434275be63SMauro Carvalho Chehab void edac_mc_handle_error(const enum hw_event_mc_err_type type,
9444275be63SMauro Carvalho Chehab struct mem_ctl_info *mci,
9459eb07a7fSMauro Carvalho Chehab const u16 error_count,
9464275be63SMauro Carvalho Chehab const unsigned long page_frame_number,
9474275be63SMauro Carvalho Chehab const unsigned long offset_in_page,
9484275be63SMauro Carvalho Chehab const unsigned long syndrome,
94953f2d028SMauro Carvalho Chehab const int top_layer,
95053f2d028SMauro Carvalho Chehab const int mid_layer,
95153f2d028SMauro Carvalho Chehab const int low_layer,
9524275be63SMauro Carvalho Chehab const char *msg,
95303f7eae8SMauro Carvalho Chehab const char *other_detail)
954da9bb1d2SAlan Cox {
955c498afafSRobert Richter struct dimm_info *dimm;
956fca61165SLen Baker char *p, *end;
9574275be63SMauro Carvalho Chehab int row = -1, chan = -1;
95853f2d028SMauro Carvalho Chehab int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer };
959c7ef7645SMauro Carvalho Chehab int i, n_labels = 0;
960c7ef7645SMauro Carvalho Chehab struct edac_raw_error_desc *e = &mci->error_desc;
96167792cf9SRobert Richter bool any_memory = true;
962fca61165SLen Baker const char *prefix;
963da9bb1d2SAlan Cox
964956b9ba1SJoe Perches edac_dbg(3, "MC%d\n", mci->mc_idx);
965da9bb1d2SAlan Cox
966c7ef7645SMauro Carvalho Chehab /* Fills the error report buffer */
967c7ef7645SMauro Carvalho Chehab memset(e, 0, sizeof (*e));
968c7ef7645SMauro Carvalho Chehab e->error_count = error_count;
969672ef0e5SRobert Richter e->type = type;
970c7ef7645SMauro Carvalho Chehab e->top_layer = top_layer;
971c7ef7645SMauro Carvalho Chehab e->mid_layer = mid_layer;
972c7ef7645SMauro Carvalho Chehab e->low_layer = low_layer;
973c7ef7645SMauro Carvalho Chehab e->page_frame_number = page_frame_number;
974c7ef7645SMauro Carvalho Chehab e->offset_in_page = offset_in_page;
975c7ef7645SMauro Carvalho Chehab e->syndrome = syndrome;
9761853ee72SRobert Richter /* need valid strings here for both: */
9771853ee72SRobert Richter e->msg = msg ?: "";
9781853ee72SRobert Richter e->other_detail = other_detail ?: "";
979c7ef7645SMauro Carvalho Chehab
9804275be63SMauro Carvalho Chehab /*
98167792cf9SRobert Richter * Check if the event report is consistent and if the memory location is
9824aa92c86SRobert Richter * known. If it is, the DIMM(s) label info will be filled and the DIMM's
9834aa92c86SRobert Richter * error counters will be incremented.
9844275be63SMauro Carvalho Chehab */
9854275be63SMauro Carvalho Chehab for (i = 0; i < mci->n_layers; i++) {
9864275be63SMauro Carvalho Chehab if (pos[i] >= (int)mci->layers[i].size) {
9874275be63SMauro Carvalho Chehab
988537fba28SDave Peterson edac_mc_printk(mci, KERN_ERR,
9894275be63SMauro Carvalho Chehab "INTERNAL ERROR: %s value is out of range (%d >= %d)\n",
9904275be63SMauro Carvalho Chehab edac_layer_name[mci->layers[i].type],
9914275be63SMauro Carvalho Chehab pos[i], mci->layers[i].size);
9924275be63SMauro Carvalho Chehab /*
9934275be63SMauro Carvalho Chehab * Instead of just returning it, let's use what's
9944275be63SMauro Carvalho Chehab * known about the error. The increment routines and
9954275be63SMauro Carvalho Chehab * the DIMM filter logic will do the right thing by
9964275be63SMauro Carvalho Chehab * pointing the likely damaged DIMMs.
9974275be63SMauro Carvalho Chehab */
9984275be63SMauro Carvalho Chehab pos[i] = -1;
9994275be63SMauro Carvalho Chehab }
10004275be63SMauro Carvalho Chehab if (pos[i] >= 0)
100167792cf9SRobert Richter any_memory = false;
1002da9bb1d2SAlan Cox }
1003da9bb1d2SAlan Cox
10044275be63SMauro Carvalho Chehab /*
10054275be63SMauro Carvalho Chehab * Get the dimm label/grain that applies to the match criteria.
10064275be63SMauro Carvalho Chehab * As the error algorithm may not be able to point to just one memory
10074275be63SMauro Carvalho Chehab * stick, the logic here will get all possible labels that could
10084275be63SMauro Carvalho Chehab * pottentially be affected by the error.
10094275be63SMauro Carvalho Chehab * On FB-DIMM memory controllers, for uncorrected errors, it is common
10104275be63SMauro Carvalho Chehab * to have only the MC channel and the MC dimm (also called "branch")
10114275be63SMauro Carvalho Chehab * but the channel is not known, as the memory is arranged in pairs,
10124275be63SMauro Carvalho Chehab * where each memory belongs to a separate channel within the same
10134275be63SMauro Carvalho Chehab * branch.
10144275be63SMauro Carvalho Chehab */
1015c7ef7645SMauro Carvalho Chehab p = e->label;
10164275be63SMauro Carvalho Chehab *p = '\0';
1017fca61165SLen Baker end = p + sizeof(e->label);
1018fca61165SLen Baker prefix = "";
10194da1b7bfSBorislav Petkov
1020c498afafSRobert Richter mci_for_each_dimm(mci, dimm) {
102153f2d028SMauro Carvalho Chehab if (top_layer >= 0 && top_layer != dimm->location[0])
10224275be63SMauro Carvalho Chehab continue;
102353f2d028SMauro Carvalho Chehab if (mid_layer >= 0 && mid_layer != dimm->location[1])
10244275be63SMauro Carvalho Chehab continue;
102553f2d028SMauro Carvalho Chehab if (low_layer >= 0 && low_layer != dimm->location[2])
10264275be63SMauro Carvalho Chehab continue;
10274275be63SMauro Carvalho Chehab
10284275be63SMauro Carvalho Chehab /* get the max grain, over the error match range */
1029c7ef7645SMauro Carvalho Chehab if (dimm->grain > e->grain)
1030c7ef7645SMauro Carvalho Chehab e->grain = dimm->grain;
10314275be63SMauro Carvalho Chehab
10324275be63SMauro Carvalho Chehab /*
10334275be63SMauro Carvalho Chehab * If the error is memory-controller wide, there's no need to
103467792cf9SRobert Richter * seek for the affected DIMMs because the whole channel/memory
103567792cf9SRobert Richter * controller/... may be affected. Also, don't show errors for
103667792cf9SRobert Richter * empty DIMM slots.
10374275be63SMauro Carvalho Chehab */
103865bb4d1aSRobert Richter if (!dimm->nr_pages)
10390d8292e0SRobert Richter continue;
10400d8292e0SRobert Richter
1041c7ef7645SMauro Carvalho Chehab n_labels++;
104265bb4d1aSRobert Richter if (n_labels > EDAC_MAX_LABELS) {
104365bb4d1aSRobert Richter p = e->label;
104465bb4d1aSRobert Richter *p = '\0';
104565bb4d1aSRobert Richter } else {
1046fca61165SLen Baker p += scnprintf(p, end - p, "%s%s", prefix, dimm->label);
1047fca61165SLen Baker prefix = OTHER_LABEL;
104865bb4d1aSRobert Richter }
10494275be63SMauro Carvalho Chehab
10504275be63SMauro Carvalho Chehab /*
10514275be63SMauro Carvalho Chehab * get csrow/channel of the DIMM, in order to allow
10524275be63SMauro Carvalho Chehab * incrementing the compat API counters
10534275be63SMauro Carvalho Chehab */
1054956b9ba1SJoe Perches edac_dbg(4, "%s csrows map: (%d,%d)\n",
10559713faecSMauro Carvalho Chehab mci->csbased ? "rank" : "dimm",
10564275be63SMauro Carvalho Chehab dimm->csrow, dimm->cschannel);
10574275be63SMauro Carvalho Chehab if (row == -1)
10584275be63SMauro Carvalho Chehab row = dimm->csrow;
10594275be63SMauro Carvalho Chehab else if (row >= 0 && row != dimm->csrow)
10604275be63SMauro Carvalho Chehab row = -2;
10614275be63SMauro Carvalho Chehab
10624275be63SMauro Carvalho Chehab if (chan == -1)
10634275be63SMauro Carvalho Chehab chan = dimm->cschannel;
10644275be63SMauro Carvalho Chehab else if (chan >= 0 && chan != dimm->cschannel)
10654275be63SMauro Carvalho Chehab chan = -2;
10664275be63SMauro Carvalho Chehab }
1067da9bb1d2SAlan Cox
106867792cf9SRobert Richter if (any_memory)
1069fca61165SLen Baker strscpy(e->label, "any memory", sizeof(e->label));
10706334dc4eSRobert Richter else if (!*e->label)
1071fca61165SLen Baker strscpy(e->label, "unknown memory", sizeof(e->label));
10726334dc4eSRobert Richter
10736334dc4eSRobert Richter edac_inc_csrow(e, row, chan);
1074da9bb1d2SAlan Cox
10754275be63SMauro Carvalho Chehab /* Fill the RAM location data */
1076c7ef7645SMauro Carvalho Chehab p = e->location;
1077fca61165SLen Baker end = p + sizeof(e->location);
1078fca61165SLen Baker prefix = "";
10794da1b7bfSBorislav Petkov
10804275be63SMauro Carvalho Chehab for (i = 0; i < mci->n_layers; i++) {
10814275be63SMauro Carvalho Chehab if (pos[i] < 0)
10824275be63SMauro Carvalho Chehab continue;
1083da9bb1d2SAlan Cox
1084fca61165SLen Baker p += scnprintf(p, end - p, "%s%s:%d", prefix,
1085fca61165SLen Baker edac_layer_name[mci->layers[i].type], pos[i]);
1086fca61165SLen Baker prefix = " ";
10879794f33dSeric wollesen }
108853f2d028SMauro Carvalho Chehab
108991b327f6SRobert Richter edac_raw_mc_handle_error(e);
10909794f33dSeric wollesen }
10914275be63SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(edac_mc_handle_error);
1092