edac_mc.c revision 8096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6
1da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc kernel module
349c0dab7e6000888b616bedcbbc8cd4710331610Doug Thompson * (C) 2005, 2006 Linux Networx (http://lnxi.com)
4da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * This file may be distributed under the terms of the
5da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * GNU General Public License.
6da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
7da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Written by Thayne Harbaugh
8da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Based on work by Dan Hollis <goemon at anime dot net> and others.
9da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	http://www.anime.net/~goemon/linux-ecc/
10da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
11da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Modified by Dave Peterson and Doug Thompson
12da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
13da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
14da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
15da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/module.h>
16da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/proc_fs.h>
17da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/kernel.h>
18da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/types.h>
19da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/smp.h>
20da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/init.h>
21da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/sysctl.h>
22da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/highmem.h>
23da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/timer.h>
24da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/slab.h>
25da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/jiffies.h>
26da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/spinlock.h>
27da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/list.h>
28da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/sysdev.h>
29da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/ctype.h>
30c0d121720220584bba2876b032e58a076b843fa1Dave Jiang#include <linux/edac.h>
31da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <asm/uaccess.h>
32da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <asm/page.h>
33da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <asm/edac.h>
3420bcb7a81dee21bfa3408f03f46b2891c9b5c84bDouglas Thompson#include "edac_core.h"
357c9281d76c1c0b130f79d5fc021084e9749959d4Douglas Thompson#include "edac_module.h"
36da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
37da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* lock to memory controller's control array */
3863b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlckestatic DEFINE_MUTEX(mem_ctls_mutex);
39da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct list_head mc_devices = LIST_HEAD_INIT(mc_devices);
40da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
41da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
42da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
432da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunkstatic void edac_mc_dump_channel(struct channel_info *chan)
44da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
45da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel = %p\n", chan);
46da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
47da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->ce_count = %d\n", chan->ce_count);
48da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->label = '%s'\n", chan->label);
49da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
50da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
51da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
522da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunkstatic void edac_mc_dump_csrow(struct csrow_info *csrow)
53da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
54da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow = %p\n", csrow);
55da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx);
56079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
57da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
58da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
59da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->nr_pages = 0x%x\n", csrow->nr_pages);
60079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels);
61da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->channels = %p\n", csrow->channels);
62da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
63da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
64da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
652da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunkstatic void edac_mc_dump_mci(struct mem_ctl_info *mci)
66da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
67da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci = %p\n", mci);
68da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->mtype_cap = %lx\n", mci->mtype_cap);
69da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
70da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->edac_cap = %lx\n", mci->edac_cap);
71da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tmci->edac_check = %p\n", mci->edac_check);
72da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->nr_csrows = %d, csrows = %p\n",
73da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci->nr_csrows, mci->csrows);
7437f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson	debugf3("\tdev = %p\n", mci->dev);
75079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name);
76da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
77da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
78da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
79079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson#endif				/* CONFIG_EDAC_DEBUG */
80da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
81da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
82da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Adjust 'ptr' so that its alignment is at least as stringent as what the
83da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * compiler would provide for X and return the aligned result.
84da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
85da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * If 'size' is a constant, the compiler will optimize this whole function
86da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * down to either a no-op or the addition of a constant to the value of 'ptr'.
87da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
887391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompsonvoid *edac_align_ptr(void *ptr, unsigned size)
89da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
90da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned align, r;
91da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
92da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Here we assume that the alignment of a "long long" is the most
93da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * stringent alignment that the compiler will ever provide by default.
94da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * As far as I know, this is a reasonable assumption.
95da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
96da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (size > sizeof(long))
97da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(long long);
98da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(int))
99da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(long);
100da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(short))
101da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(int);
102da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(char))
103da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(short);
104da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else
105079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson		return (char *)ptr;
106da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
107da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	r = size % align;
108da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
109da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (r == 0)
110079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson		return (char *)ptr;
111da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1127391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompson	return (void *)(((unsigned long)ptr) + align - r);
113da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
114da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
115da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
116da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_alloc: Allocate a struct mem_ctl_info structure
117da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @size_pvt:	size of private storage needed
118da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @nr_csrows:	Number of CWROWS needed for this MC
119da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @nr_chans:	Number of channels for the MC
120da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
121da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Everything is kmalloc'ed as one big chunk - more efficient.
122da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Only can be used if all structures have the same lifetime - otherwise
123da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * you have to allocate and initialize your own structures.
124da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
125da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Use edac_mc_free() to free mc structures allocated by this function.
126da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
127da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Returns:
128da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	NULL allocation failed
129da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	struct mem_ctl_info pointer
130da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
131da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
132b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson				unsigned nr_chans, int edac_index)
133da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
134da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
135da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csi, *csrow;
136da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct channel_info *chi, *chp, *chan;
137da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *pvt;
138da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned size;
139da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, chn;
1408096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	int err;
141da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
142da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Figure out the offsets of the various items from the start of an mc
143da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * structure.  We want the alignment of each item to be at least as
144da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * stringent as what the compiler would provide if we could simply
145da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * hardcode everything into a single struct.
146da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
147079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	mci = (struct mem_ctl_info *)0;
1487391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompson	csi = edac_align_ptr(&mci[1], sizeof(*csi));
1497391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompson	chi = edac_align_ptr(&csi[nr_csrows], sizeof(*chi));
150e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
151079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	size = ((unsigned long)pvt) + sz_pvt;
152da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1538096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	mci = kzalloc(size, GFP_KERNEL);
1548096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	if (mci == NULL)
155da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return NULL;
156da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
157da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Adjust pointers so they point within the memory we just allocated
158da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * rather than an imaginary chunk of memory located at address 0.
159da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
160079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
161079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	chi = (struct channel_info *)(((char *)mci) + ((unsigned long)chi));
162079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
163da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
164b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson	/* setup index and various internal pointers */
165b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson	mci->mc_idx = edac_index;
166da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows = csi;
167da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->pvt_info = pvt;
168da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->nr_csrows = nr_csrows;
169da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
170da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (row = 0; row < nr_csrows; row++) {
171da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow = &csi[row];
172da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->csrow_idx = row;
173da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->mci = mci;
174da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->nr_channels = nr_chans;
175da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chp = &chi[row * nr_chans];
176da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->channels = chp;
177da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
178da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (chn = 0; chn < nr_chans; chn++) {
179da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan = &chp[chn];
180da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan->chan_idx = chn;
181da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan->csrow = csrow;
182da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
183da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
184da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
18581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mci->op_state = OP_ALLOC;
18681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
1878096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	/*
1888096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * Initialize the 'root' kobj for the edac_mc controller
1898096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 */
1908096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	err = edac_mc_register_sysfs_main_kobj(mci);
1918096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	if (err) {
1928096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson		kfree(mci);
1938096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson		return NULL;
1948096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	}
1958096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson
1968096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	/* at this point, the root kobj is valid, and in order to
1978096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * 'free' the object, then the function:
1988096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 *      edac_mc_unregister_sysfs_main_kobj() must be called
1998096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * which will perform kobj unregistration and the actual free
2008096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * will occur during the kobject callback operation
2018096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 */
202da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return mci;
203da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2049110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_alloc);
205da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
206da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
2078096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson * edac_mc_free
2088096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson *	'Free' a previously allocated 'mci' structure
209da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to a struct mem_ctl_info structure
210da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
211da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_free(struct mem_ctl_info *mci)
212da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2138096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	edac_mc_unregister_sysfs_main_kobj(mci);
214da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2159110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_free);
216da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
21737f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompsonstatic struct mem_ctl_info *find_mci_by_dev(struct device *dev)
218da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
219da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
220da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item;
221da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
222537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
223da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
224da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_for_each(item, &mc_devices) {
225da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci = list_entry(item, struct mem_ctl_info, link);
226da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
22737f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson		if (mci->dev == dev)
228da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			return mci;
229da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
230da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
231da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return NULL;
232da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
233da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
23481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
23581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * handler for EDAC to check if NMI type handler has asserted interrupt
23681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
23781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic int edac_mc_assert_error_check_and_clear(void)
23881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
23966ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	int old_state;
24081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
241079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	if (edac_op_state == EDAC_OPSTATE_POLL)
24281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		return 1;
24381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
24466ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	old_state = edac_err_assert;
24566ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	edac_err_assert = 0;
24681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
24766ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	return old_state;
24881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
24981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
25081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
25181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_function
25281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	performs the operation scheduled by a workq request
25381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
25481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic void edac_mc_workq_function(struct work_struct *work_req)
25581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
256079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	struct delayed_work *d_work = (struct delayed_work *)work_req;
25781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
25881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
25981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_lock(&mem_ctls_mutex);
26081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
26181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* Only poll controllers that are running polled and have a check */
26281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
26381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->edac_check(mci);
26481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
26581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/*
26681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	 * FIXME: temp place holder for PCI checks,
26781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	 * goes away when we break out PCI
26881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	 */
26981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_pci_do_parity_check();
27081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
27181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_unlock(&mem_ctls_mutex);
27281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
27381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* Reschedule */
2744de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	queue_delayed_work(edac_workqueue, &mci->work,
275052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			msecs_to_jiffies(edac_mc_get_poll_msec()));
27681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
27781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
27881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
27981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_setup
28081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	initialize a workq item for this mci
28181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	passing in the new delay period in msec
28281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
28381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangvoid edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
28481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
28581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	debugf0("%s()\n", __func__);
28681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
28781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
28881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
28981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
29081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
29181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
29281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_teardown
29381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	stop the workq processing on this mci
29481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
29581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangvoid edac_mc_workq_teardown(struct mem_ctl_info *mci)
29681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
29781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	int status;
29881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
29981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	status = cancel_delayed_work(&mci->work);
30081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (status == 0) {
30181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		/* workq instance might be running, wait for it */
30281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		flush_workqueue(edac_workqueue);
30381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	}
30481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
30581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
30681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
30781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_reset_delay_period
30881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
30981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
31081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangvoid edac_reset_delay_period(struct mem_ctl_info *mci, unsigned long value)
31181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
31281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_lock(&mem_ctls_mutex);
31381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
31481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* cancel the current workq request */
31581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_mc_workq_teardown(mci);
31681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
31781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* restart the workq request, with new delay value */
31881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_mc_workq_setup(mci, value);
31981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
32081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_unlock(&mem_ctls_mutex);
32181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
32281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
3232d7bbb91c8df26c60d223205a087507430024177Doug Thompson/* Return 0 on success, 1 on failure.
3242d7bbb91c8df26c60d223205a087507430024177Doug Thompson * Before calling this function, caller must
3252d7bbb91c8df26c60d223205a087507430024177Doug Thompson * assign a unique value to mci->mc_idx.
3262d7bbb91c8df26c60d223205a087507430024177Doug Thompson */
327079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic int add_mc_to_global_list(struct mem_ctl_info *mci)
328da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
329da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item, *insert_before;
330da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *p;
331da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3322d7bbb91c8df26c60d223205a087507430024177Doug Thompson	insert_before = &mc_devices;
333da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3342d7bbb91c8df26c60d223205a087507430024177Doug Thompson	if (unlikely((p = find_mci_by_dev(mci->dev)) != NULL))
3352d7bbb91c8df26c60d223205a087507430024177Doug Thompson		goto fail0;
336da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3372d7bbb91c8df26c60d223205a087507430024177Doug Thompson	list_for_each(item, &mc_devices) {
3382d7bbb91c8df26c60d223205a087507430024177Doug Thompson		p = list_entry(item, struct mem_ctl_info, link);
339da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3402d7bbb91c8df26c60d223205a087507430024177Doug Thompson		if (p->mc_idx >= mci->mc_idx) {
3412d7bbb91c8df26c60d223205a087507430024177Doug Thompson			if (unlikely(p->mc_idx == mci->mc_idx))
3422d7bbb91c8df26c60d223205a087507430024177Doug Thompson				goto fail1;
343da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3442d7bbb91c8df26c60d223205a087507430024177Doug Thompson			insert_before = item;
3452d7bbb91c8df26c60d223205a087507430024177Doug Thompson			break;
346da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
347da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
348da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
349da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_add_tail_rcu(&mci->link, insert_before);
350c0d121720220584bba2876b032e58a076b843fa1Dave Jiang	atomic_inc(&edac_handlers);
351da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
3522d7bbb91c8df26c60d223205a087507430024177Doug Thompson
353052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
3542d7bbb91c8df26c60d223205a087507430024177Doug Thompson	edac_printk(KERN_WARNING, EDAC_MC,
355052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"%s (%s) %s %s already assigned %d\n", p->dev->bus_id,
356052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
3572d7bbb91c8df26c60d223205a087507430024177Doug Thompson	return 1;
3582d7bbb91c8df26c60d223205a087507430024177Doug Thompson
359052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
3602d7bbb91c8df26c60d223205a087507430024177Doug Thompson	edac_printk(KERN_WARNING, EDAC_MC,
361052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"bug in low-level driver: attempt to assign\n"
362052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"    duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
3632d7bbb91c8df26c60d223205a087507430024177Doug Thompson	return 1;
364da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
365da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
366e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void complete_mc_list_del(struct rcu_head *head)
367a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson{
368a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	struct mem_ctl_info *mci;
369a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
370a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	mci = container_of(head, struct mem_ctl_info, rcu);
371a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	INIT_LIST_HEAD(&mci->link);
372a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	complete(&mci->complete);
373a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson}
374a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
375e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void del_mc_from_global_list(struct mem_ctl_info *mci)
376a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson{
377c0d121720220584bba2876b032e58a076b843fa1Dave Jiang	atomic_dec(&edac_handlers);
378a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	list_del_rcu(&mci->link);
379a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	init_completion(&mci->complete);
380a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	call_rcu(&mci->rcu, complete_mc_list_del);
381a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	wait_for_completion(&mci->complete);
382a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson}
383a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
384da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
3855da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
3865da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson *
3875da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * If found, return a pointer to the structure.
3885da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * Else return NULL.
3895da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson *
3905da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * Caller must hold mem_ctls_mutex.
3915da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson */
392079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct mem_ctl_info *edac_mc_find(int idx)
3935da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson{
3945da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	struct list_head *item;
3955da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	struct mem_ctl_info *mci;
3965da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
3975da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	list_for_each(item, &mc_devices) {
3985da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		mci = list_entry(item, struct mem_ctl_info, link);
3995da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4005da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		if (mci->mc_idx >= idx) {
4015da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson			if (mci->mc_idx == idx)
4025da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson				return mci;
4035da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4045da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson			break;
4055da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		}
4065da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	}
4075da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4085da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	return NULL;
4095da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson}
4105da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas ThompsonEXPORT_SYMBOL(edac_mc_find);
4115da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4125da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson/**
413472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
414472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson *                 create sysfs entries associated with mci structure
415da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to the mci structure to be added to the list
4162d7bbb91c8df26c60d223205a087507430024177Doug Thompson * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure.
417da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
418da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Return:
419da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	0	Success
420da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	!0	Failure
421da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
422da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
423da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should a warning be printed if no error detection? correction? */
424b8f6f9755248026f21282e25cac49a1af698056cDoug Thompsonint edac_mc_add_mc(struct mem_ctl_info *mci)
425da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
426537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf0("%s()\n", __func__);
427b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson
428da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
429da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 3)
430da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_dump_mci(mci);
431e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
432da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 4) {
433da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		int i;
434da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
435da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (i = 0; i < mci->nr_csrows; i++) {
436da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			int j;
437e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
438da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			edac_mc_dump_csrow(&mci->csrows[i]);
439da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			for (j = 0; j < mci->csrows[i].nr_channels; j++)
440079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				edac_mc_dump_channel(&mci->csrows[i].
441052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson						channels[j]);
442da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
443da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
444da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif
44563b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
446da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
447da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (add_mc_to_global_list(mci))
448028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson		goto fail0;
449da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
450da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* set load time so that error rate can be tracked */
451da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->start_time = jiffies;
452da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
4539794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (edac_create_sysfs_mci_device(mci)) {
4549794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_WARNING,
455052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"failed to create sysfs device\n");
4569794f33ddedd878dd92fcf8b4834391840366919eric wollesen		goto fail1;
4579794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
458da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
45981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* If there IS a check routine, then we are running POLLED */
46081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (mci->edac_check != NULL) {
46181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		/* This instance is NOW RUNNING */
46281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->op_state = OP_RUNNING_POLL;
46381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
46481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
46581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	} else {
46681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->op_state = OP_RUNNING_INTERRUPT;
46781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	}
46881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
469da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Report action taken */
47037f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson	edac_mc_printk(mci, KERN_INFO, "Giving out device to %s %s: DEV %s\n",
471052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		mci->mod_name, mci->ctl_name, dev_name(mci));
472da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
47363b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
474028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	return 0;
475da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
476052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
477028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	del_mc_from_global_list(mci);
478028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson
479052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
48063b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
481028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	return 1;
482da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
4839110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_add_mc);
484da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
485da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
486472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
487472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson *                 remove mci structure from global list
48837f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson * @pdev: Pointer to 'struct device' representing mci structure to remove.
489da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
49018dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson * Return pointer to removed mci structure, or NULL if device not found.
491da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
492079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct mem_ctl_info *edac_mc_del_mc(struct device *dev)
493da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
49418dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	struct mem_ctl_info *mci;
495da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
49618dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	debugf0("MC: %s()\n", __func__);
49763b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
49818dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson
49937f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson	if ((mci = find_mci_by_dev(dev)) == NULL) {
50063b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke		mutex_unlock(&mem_ctls_mutex);
50118dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson		return NULL;
50218dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	}
50318dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson
50481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* marking MCI offline */
50581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mci->op_state = OP_OFFLINE;
50681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
50781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* flush workq processes */
50881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_mc_workq_teardown(mci);
50981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
51018dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	edac_remove_sysfs_mci_device(mci);
511da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	del_mc_from_global_list(mci);
51263b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
513537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	edac_printk(KERN_INFO, EDAC_MC,
514052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
515052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		mci->mod_name, mci->ctl_name, dev_name(mci));
51618dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	return mci;
517da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
5189110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_del_mc);
519da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
5202da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunkstatic void edac_mc_scrub_block(unsigned long page, unsigned long offset,
5212da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunk				u32 size)
522da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
523da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct page *pg;
524da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *virt_addr;
525da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long flags = 0;
526da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
527537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
528da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
529da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* ECC error page was not in our memory. Ignore it. */
530079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	if (!pfn_valid(page))
531da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
532da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
533da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Find the actual page structure then map it and fix */
534da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pg = pfn_to_page(page);
535da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
536da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
537da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_save(flags);
538da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
539da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	virt_addr = kmap_atomic(pg, KM_BOUNCE_READ);
540da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
541da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Perform architecture specific atomic scrub operation */
542da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	atomic_scrub(virt_addr + offset, size);
543da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
544da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Unmap and complete */
545da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kunmap_atomic(virt_addr, KM_BOUNCE_READ);
546da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
547da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
548da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_restore(flags);
549da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
550da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
551da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should return -1 */
552e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonint edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
553da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
554da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrows = mci->csrows;
555da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, i;
556da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
557537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
558da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	row = -1;
559da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
560da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < mci->nr_csrows; i++) {
561da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		struct csrow_info *csrow = &csrows[i];
562da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
563da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (csrow->nr_pages == 0)
564da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			continue;
565da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
566537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
567537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			"mask(0x%lx)\n", mci->mc_idx, __func__,
568537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			csrow->first_page, page, csrow->last_page,
569537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			csrow->page_mask);
570da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
571da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if ((page >= csrow->first_page) &&
572da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    (page <= csrow->last_page) &&
573da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    ((page & csrow->page_mask) ==
574da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     (csrow->first_page & csrow->page_mask))) {
575da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			row = i;
576da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			break;
577da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
578da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
579da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
580da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row == -1)
581537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
582052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"could not look up page error address %lx\n",
583052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			(unsigned long)page);
584da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
585da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return row;
586da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
5879110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
588da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
589da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - setable log (warning/emerg) levels */
590da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
591da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ce(struct mem_ctl_info *mci,
592052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long page_frame_number,
593052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long offset_in_page, unsigned long syndrome,
594052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		int row, int channel, const char *msg)
595da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
596da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long remapped_page;
597da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
598537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
599da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
600da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
601da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
602da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
603537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
604052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range "
605052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", row, mci->nr_csrows);
606da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
607da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
608da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
609e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
610da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (channel >= mci->csrows[row].nr_channels || channel < 0) {
611da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
612537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
613052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel out of range "
614052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", channel,
615052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].nr_channels);
616da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
617da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
618da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
619da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
6204de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
621da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* FIXME - put in DIMM location */
622537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
623052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE page 0x%lx, offset 0x%lx, grain %d, syndrome "
624052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"0x%lx, row %d, channel %d, label \"%s\": %s\n",
625052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number, offset_in_page,
626052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].grain, syndrome, row, channel,
627052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].channels[channel].label, msg);
628da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
629da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
630da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ce_count++;
631da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].channels[channel].ce_count++;
632da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
633da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mci->scrub_mode & SCRUB_SW_SRC) {
634da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/*
635da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * Some MC's can remap memory so that it is still available
636da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * at a different address when PCI devices map into memory.
637da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * MC's that can't do this lose the memory where PCI devices
638da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * are mapped.  This mapping is MC dependant and so we call
639da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * back into the MC driver for it to map the MC page to
640da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * a physical (CPU) page which can then be mapped to a virtual
641da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * page - which can then be scrubbed.
642da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 */
643da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		remapped_page = mci->ctl_page_to_phys ?
644052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->ctl_page_to_phys(mci, page_frame_number) :
645052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number;
646da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
647da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_scrub_block(remapped_page, offset_in_page,
648052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				mci->csrows[row].grain);
649da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
650da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
6519110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ce);
652da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
653e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonvoid edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
654da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
6554de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
656537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
657052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE - no information available: %s\n", msg);
658e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
659da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_noinfo_count++;
660da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
661da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
6629110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
663da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
664da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ue(struct mem_ctl_info *mci,
665052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long page_frame_number,
666052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long offset_in_page, int row, const char *msg)
667da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
668da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int len = EDAC_MC_LABEL_LEN * 4;
669da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char labels[len + 1];
670da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *pos = labels;
671da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chan;
672da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chars;
673da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
674537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
675da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
676da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
677da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
678da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
679537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
680052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range "
681052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", row, mci->nr_csrows);
682da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
683da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
684da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
685da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
686da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	chars = snprintf(pos, len + 1, "%s",
687079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson			 mci->csrows[row].channels[0].label);
688da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	len -= chars;
689da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pos += chars;
690e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
691da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
692052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		chan++) {
693da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chars = snprintf(pos, len + 1, ":%s",
694079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				 mci->csrows[row].channels[chan].label);
695da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		len -= chars;
696da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		pos += chars;
697da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
698da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
6994de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
700537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_EMERG,
701052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE page 0x%lx, offset 0x%lx, grain %d, row %d, "
702052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", page_frame_number,
703052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			offset_in_page, mci->csrows[row].grain, row,
704052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			labels, msg);
705da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7064de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
707e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson		panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, "
708052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"row %d, labels \"%s\": %s\n", mci->mc_idx,
709052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number, offset_in_page,
710052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].grain, row, labels, msg);
711da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
712da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
713da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ue_count++;
714da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
7159110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ue);
716da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
717e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonvoid edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
718da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
7194de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
720da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
721da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7224de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
723537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
724052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE - no information available: %s\n", msg);
725da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_noinfo_count++;
726da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
727da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
728079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
729da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7309794f33ddedd878dd92fcf8b4834391840366919eric wollesen/*************************************************************
7319794f33ddedd878dd92fcf8b4834391840366919eric wollesen * On Fully Buffered DIMM modules, this help function is
7329794f33ddedd878dd92fcf8b4834391840366919eric wollesen * called to process UE events
7339794f33ddedd878dd92fcf8b4834391840366919eric wollesen */
7349794f33ddedd878dd92fcf8b4834391840366919eric wollesenvoid edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
735052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int csrow,
736052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int channela,
737052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int channelb, char *msg)
7389794f33ddedd878dd92fcf8b4834391840366919eric wollesen{
7399794f33ddedd878dd92fcf8b4834391840366919eric wollesen	int len = EDAC_MC_LABEL_LEN * 4;
7409794f33ddedd878dd92fcf8b4834391840366919eric wollesen	char labels[len + 1];
7419794f33ddedd878dd92fcf8b4834391840366919eric wollesen	char *pos = labels;
7429794f33ddedd878dd92fcf8b4834391840366919eric wollesen	int chars;
7439794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7449794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (csrow >= mci->nr_csrows) {
7459794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
7469794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
747052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range (%d >= %d)\n",
748052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, mci->nr_csrows);
7499794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
7509794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
7519794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
7529794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7539794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channela >= mci->csrows[csrow].nr_channels) {
7549794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
7559794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
756052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel-a out of range "
757052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n",
758052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channela, mci->csrows[csrow].nr_channels);
7599794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
7609794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
7619794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
7629794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7639794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channelb >= mci->csrows[csrow].nr_channels) {
7649794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
7659794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
766052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel-b out of range "
767052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n",
768052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channelb, mci->csrows[csrow].nr_channels);
7699794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
7709794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
7719794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
7729794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7739794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->ue_count++;
7749794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].ue_count++;
7759794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7769794f33ddedd878dd92fcf8b4834391840366919eric wollesen	/* Generate the DIMM labels from the specified channels */
7779794f33ddedd878dd92fcf8b4834391840366919eric wollesen	chars = snprintf(pos, len + 1, "%s",
7789794f33ddedd878dd92fcf8b4834391840366919eric wollesen			 mci->csrows[csrow].channels[channela].label);
779079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	len -= chars;
780079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pos += chars;
7819794f33ddedd878dd92fcf8b4834391840366919eric wollesen	chars = snprintf(pos, len + 1, "-%s",
7829794f33ddedd878dd92fcf8b4834391840366919eric wollesen			 mci->csrows[csrow].channels[channelb].label);
7839794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7844de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
7859794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_EMERG,
786052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE row %d, channel-a= %d channel-b= %d "
787052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", csrow, channela, channelb,
788052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			labels, msg);
7899794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7904de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
7919794f33ddedd878dd92fcf8b4834391840366919eric wollesen		panic("UE row %d, channel-a= %d channel-b= %d "
792052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", csrow, channela,
793052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channelb, labels, msg);
7949794f33ddedd878dd92fcf8b4834391840366919eric wollesen}
7959794f33ddedd878dd92fcf8b4834391840366919eric wollesenEXPORT_SYMBOL(edac_mc_handle_fbd_ue);
7969794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7979794f33ddedd878dd92fcf8b4834391840366919eric wollesen/*************************************************************
7989794f33ddedd878dd92fcf8b4834391840366919eric wollesen * On Fully Buffered DIMM modules, this help function is
7999794f33ddedd878dd92fcf8b4834391840366919eric wollesen * called to process CE events
8009794f33ddedd878dd92fcf8b4834391840366919eric wollesen */
8019794f33ddedd878dd92fcf8b4834391840366919eric wollesenvoid edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
802052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int csrow, unsigned int channel, char *msg)
8039794f33ddedd878dd92fcf8b4834391840366919eric wollesen{
8049794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8059794f33ddedd878dd92fcf8b4834391840366919eric wollesen	/* Ensure boundary values */
8069794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (csrow >= mci->nr_csrows) {
8079794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8089794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
809052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range (%d >= %d)\n",
810052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, mci->nr_csrows);
8119794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
8129794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8139794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8149794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channel >= mci->csrows[csrow].nr_channels) {
8159794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8169794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
817052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel out of range (%d >= %d)\n",
818052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channel, mci->csrows[csrow].nr_channels);
8199794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
8209794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8219794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8229794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8234de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
8249794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* FIXME - put in DIMM location */
8259794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_WARNING,
826052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE row %d, channel %d, label \"%s\": %s\n",
827052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, channel,
828052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[csrow].channels[channel].label, msg);
8299794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8309794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->ce_count++;
8319794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].ce_count++;
8329794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].channels[channel].ce_count++;
8339794f33ddedd878dd92fcf8b4834391840366919eric wollesen}
834079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL(edac_mc_handle_fbd_ce);
8359794f33ddedd878dd92fcf8b4834391840366919eric wollesen
836da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
837da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Iterate over all MC instances and check for ECC, et al, errors
838da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
8397c9281d76c1c0b130f79d5fc021084e9749959d4Douglas Thompsonvoid edac_check_mc_devices(void)
840da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
841da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item;
842da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
843da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
844537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
84563b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
846da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
847da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_for_each(item, &mc_devices) {
848da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci = list_entry(item, struct mem_ctl_info, link);
849da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
850da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (mci->edac_check != NULL)
851da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			mci->edac_check(mci);
852da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
853da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
85463b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
855da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
856