edac_mc.c revision 17aa7e034416e3080bc57a786d09ba0a4a044561
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);
39ff6ac2a616c85d1215899ffda815e29b699cbd3aRobert P. J. Daystatic LIST_HEAD(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
217bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
218bce19683c17485b584b62b984d6dcf5332181588Doug Thompson/*
219bce19683c17485b584b62b984d6dcf5332181588Doug Thompson * find_mci_by_dev
220bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *
221bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	scan list of controllers looking for the one that manages
222bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	the 'dev' device
223bce19683c17485b584b62b984d6dcf5332181588Doug Thompson */
22437f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompsonstatic struct mem_ctl_info *find_mci_by_dev(struct device *dev)
225da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
226da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
227da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item;
228da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
229537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
230da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
231da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_for_each(item, &mc_devices) {
232da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci = list_entry(item, struct mem_ctl_info, link);
233da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
23437f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson		if (mci->dev == dev)
235da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			return mci;
236da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
237da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
238da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return NULL;
239da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
240da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
24181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
24281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * handler for EDAC to check if NMI type handler has asserted interrupt
24381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
24481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic int edac_mc_assert_error_check_and_clear(void)
24581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
24666ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	int old_state;
24781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
248079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	if (edac_op_state == EDAC_OPSTATE_POLL)
24981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		return 1;
25081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
25166ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	old_state = edac_err_assert;
25266ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	edac_err_assert = 0;
25381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
25466ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	return old_state;
25581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
25681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
25781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
25881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_function
25981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	performs the operation scheduled by a workq request
26081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
26181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic void edac_mc_workq_function(struct work_struct *work_req)
26281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
263079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	struct delayed_work *d_work = (struct delayed_work *)work_req;
26481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
26581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
26681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_lock(&mem_ctls_mutex);
26781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
268bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* if this control struct has movd to offline state, we are done */
269bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (mci->op_state == OP_OFFLINE) {
270bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson		mutex_unlock(&mem_ctls_mutex);
271bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson		return;
272bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	}
273bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
27481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* Only poll controllers that are running polled and have a check */
27581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
27681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->edac_check(mci);
27781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
27881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_unlock(&mem_ctls_mutex);
27981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
28081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* Reschedule */
2814de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	queue_delayed_work(edac_workqueue, &mci->work,
282052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			msecs_to_jiffies(edac_mc_get_poll_msec()));
28381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
28481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
28581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
28681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_setup
28781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	initialize a workq item for this mci
28881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	passing in the new delay period in msec
289bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
290bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *	locking model:
291bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
292bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *		called with the mem_ctls_mutex held
29381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
294bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompsonstatic void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
29581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
29681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	debugf0("%s()\n", __func__);
29781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
298bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* if this instance is not in the POLL state, then simply return */
299bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (mci->op_state != OP_RUNNING_POLL)
300bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson		return;
301bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
30281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
30381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
30481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
30581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
30681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
30781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_teardown
30881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	stop the workq processing on this mci
309bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
310bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *	locking model:
311bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
312bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *		called WITHOUT lock held
31381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
314bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompsonstatic void edac_mc_workq_teardown(struct mem_ctl_info *mci)
31581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
31681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	int status;
31781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
318bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	status = cancel_delayed_work(&mci->work);
319bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	if (status == 0) {
320bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		debugf0("%s() not canceled, flush the queue\n",
321bce19683c17485b584b62b984d6dcf5332181588Doug Thompson			__func__);
322bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
323bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		/* workq instance might be running, wait for it */
324bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		flush_workqueue(edac_workqueue);
32581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	}
32681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
32781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
32881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
329bce19683c17485b584b62b984d6dcf5332181588Doug Thompson * edac_mc_reset_delay_period(unsigned long value)
330bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *
331bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	user space has updated our poll period value, need to
332bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	reset our workq delays
33381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
334bce19683c17485b584b62b984d6dcf5332181588Doug Thompsonvoid edac_mc_reset_delay_period(int value)
33581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
336bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	struct mem_ctl_info *mci;
337bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	struct list_head *item;
338bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
339bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	mutex_lock(&mem_ctls_mutex);
340bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
341bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	/* scan the list and turn off all workq timers, doing so under lock
342bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	 */
343bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	list_for_each(item, &mc_devices) {
344bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		mci = list_entry(item, struct mem_ctl_info, link);
345bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
346bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		if (mci->op_state == OP_RUNNING_POLL)
347bce19683c17485b584b62b984d6dcf5332181588Doug Thompson			cancel_delayed_work(&mci->work);
348bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	}
349bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
350bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	mutex_unlock(&mem_ctls_mutex);
35181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
352bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
353bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	/* re-walk the list, and reset the poll delay */
354bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	mutex_lock(&mem_ctls_mutex);
355bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
356bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	list_for_each(item, &mc_devices) {
357bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		mci = list_entry(item, struct mem_ctl_info, link);
358bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
359bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		edac_mc_workq_setup(mci, (unsigned long) value);
360bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	}
36181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
36281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_unlock(&mem_ctls_mutex);
36381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
36481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
365bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
366bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
3672d7bbb91c8df26c60d223205a087507430024177Doug Thompson/* Return 0 on success, 1 on failure.
3682d7bbb91c8df26c60d223205a087507430024177Doug Thompson * Before calling this function, caller must
3692d7bbb91c8df26c60d223205a087507430024177Doug Thompson * assign a unique value to mci->mc_idx.
370bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
371bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *	locking model:
372bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
373bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *		called with the mem_ctls_mutex lock held
3742d7bbb91c8df26c60d223205a087507430024177Doug Thompson */
375079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic int add_mc_to_global_list(struct mem_ctl_info *mci)
376da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
377da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item, *insert_before;
378da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *p;
379da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3802d7bbb91c8df26c60d223205a087507430024177Doug Thompson	insert_before = &mc_devices;
381da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
382bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	p = find_mci_by_dev(mci->dev);
383bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (unlikely(p != NULL))
3842d7bbb91c8df26c60d223205a087507430024177Doug Thompson		goto fail0;
385da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3862d7bbb91c8df26c60d223205a087507430024177Doug Thompson	list_for_each(item, &mc_devices) {
3872d7bbb91c8df26c60d223205a087507430024177Doug Thompson		p = list_entry(item, struct mem_ctl_info, link);
388da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3892d7bbb91c8df26c60d223205a087507430024177Doug Thompson		if (p->mc_idx >= mci->mc_idx) {
3902d7bbb91c8df26c60d223205a087507430024177Doug Thompson			if (unlikely(p->mc_idx == mci->mc_idx))
3912d7bbb91c8df26c60d223205a087507430024177Doug Thompson				goto fail1;
392da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
3932d7bbb91c8df26c60d223205a087507430024177Doug Thompson			insert_before = item;
3942d7bbb91c8df26c60d223205a087507430024177Doug Thompson			break;
395da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
396da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
397da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
398da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_add_tail_rcu(&mci->link, insert_before);
399c0d121720220584bba2876b032e58a076b843fa1Dave Jiang	atomic_inc(&edac_handlers);
400da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
4012d7bbb91c8df26c60d223205a087507430024177Doug Thompson
402052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
4032d7bbb91c8df26c60d223205a087507430024177Doug Thompson	edac_printk(KERN_WARNING, EDAC_MC,
404052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"%s (%s) %s %s already assigned %d\n", p->dev->bus_id,
40517aa7e034416e3080bc57a786d09ba0a4a044561Stephen Rothwell		edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
4062d7bbb91c8df26c60d223205a087507430024177Doug Thompson	return 1;
4072d7bbb91c8df26c60d223205a087507430024177Doug Thompson
408052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
4092d7bbb91c8df26c60d223205a087507430024177Doug Thompson	edac_printk(KERN_WARNING, EDAC_MC,
410052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"bug in low-level driver: attempt to assign\n"
411052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"    duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
4122d7bbb91c8df26c60d223205a087507430024177Doug Thompson	return 1;
413da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
414da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
415e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void complete_mc_list_del(struct rcu_head *head)
416a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson{
417a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	struct mem_ctl_info *mci;
418a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
419a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	mci = container_of(head, struct mem_ctl_info, rcu);
420a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	INIT_LIST_HEAD(&mci->link);
421a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	complete(&mci->complete);
422a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson}
423a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
424e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void del_mc_from_global_list(struct mem_ctl_info *mci)
425a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson{
426c0d121720220584bba2876b032e58a076b843fa1Dave Jiang	atomic_dec(&edac_handlers);
427a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	list_del_rcu(&mci->link);
428a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	init_completion(&mci->complete);
429a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	call_rcu(&mci->rcu, complete_mc_list_del);
430a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	wait_for_completion(&mci->complete);
431a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson}
432a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
433da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
4345da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
4355da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson *
4365da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * If found, return a pointer to the structure.
4375da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * Else return NULL.
4385da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson *
4395da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * Caller must hold mem_ctls_mutex.
4405da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson */
441079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct mem_ctl_info *edac_mc_find(int idx)
4425da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson{
4435da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	struct list_head *item;
4445da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	struct mem_ctl_info *mci;
4455da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4465da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	list_for_each(item, &mc_devices) {
4475da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		mci = list_entry(item, struct mem_ctl_info, link);
4485da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4495da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		if (mci->mc_idx >= idx) {
4505da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson			if (mci->mc_idx == idx)
4515da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson				return mci;
4525da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4535da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson			break;
4545da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		}
4555da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	}
4565da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4575da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	return NULL;
4585da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson}
4595da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas ThompsonEXPORT_SYMBOL(edac_mc_find);
4605da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4615da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson/**
462472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
463472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson *                 create sysfs entries associated with mci structure
464da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to the mci structure to be added to the list
4652d7bbb91c8df26c60d223205a087507430024177Doug Thompson * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure.
466da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
467da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Return:
468da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	0	Success
469da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	!0	Failure
470da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
471da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
472da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should a warning be printed if no error detection? correction? */
473b8f6f9755248026f21282e25cac49a1af698056cDoug Thompsonint edac_mc_add_mc(struct mem_ctl_info *mci)
474da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
475537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf0("%s()\n", __func__);
476b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson
477da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
478da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 3)
479da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_dump_mci(mci);
480e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
481da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 4) {
482da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		int i;
483da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
484da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (i = 0; i < mci->nr_csrows; i++) {
485da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			int j;
486e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
487da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			edac_mc_dump_csrow(&mci->csrows[i]);
488da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			for (j = 0; j < mci->csrows[i].nr_channels; j++)
489079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				edac_mc_dump_channel(&mci->csrows[i].
490052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson						channels[j]);
491da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
492da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
493da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif
49463b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
495da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
496da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (add_mc_to_global_list(mci))
497028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson		goto fail0;
498da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
499da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* set load time so that error rate can be tracked */
500da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->start_time = jiffies;
501da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
5029794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (edac_create_sysfs_mci_device(mci)) {
5039794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_WARNING,
504052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"failed to create sysfs device\n");
5059794f33ddedd878dd92fcf8b4834391840366919eric wollesen		goto fail1;
5069794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
507da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
50881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* If there IS a check routine, then we are running POLLED */
50981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (mci->edac_check != NULL) {
51081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		/* This instance is NOW RUNNING */
51181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->op_state = OP_RUNNING_POLL;
51281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
51381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
51481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	} else {
51581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->op_state = OP_RUNNING_INTERRUPT;
51681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	}
51781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
518da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Report action taken */
519bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
52017aa7e034416e3080bc57a786d09ba0a4a044561Stephen Rothwell		" DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
521da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
52263b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
523028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	return 0;
524da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
525052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
526028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	del_mc_from_global_list(mci);
527028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson
528052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
52963b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
530028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	return 1;
531da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
5329110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_add_mc);
533da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
534da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
535472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
536472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson *                 remove mci structure from global list
53737f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson * @pdev: Pointer to 'struct device' representing mci structure to remove.
538da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
53918dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson * Return pointer to removed mci structure, or NULL if device not found.
540da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
541079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct mem_ctl_info *edac_mc_del_mc(struct device *dev)
542da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
54318dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	struct mem_ctl_info *mci;
544da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
545bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	debugf0("%s()\n", __func__);
546bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
54763b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
54818dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson
549bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* find the requested mci struct in the global list */
550bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	mci = find_mci_by_dev(dev);
551bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (mci == NULL) {
55263b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke		mutex_unlock(&mem_ctls_mutex);
55318dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson		return NULL;
55418dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	}
55518dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson
55681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* marking MCI offline */
55781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mci->op_state = OP_OFFLINE;
55881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
559da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	del_mc_from_global_list(mci);
56063b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
561bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
562bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* flush workq processes and remove sysfs */
563bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	edac_mc_workq_teardown(mci);
564bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	edac_remove_sysfs_mci_device(mci);
565bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
566537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	edac_printk(KERN_INFO, EDAC_MC,
567052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
56817aa7e034416e3080bc57a786d09ba0a4a044561Stephen Rothwell		mci->mod_name, mci->ctl_name, edac_dev_name(mci));
569bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
57018dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	return mci;
571da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
5729110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_del_mc);
573da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
5742da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunkstatic void edac_mc_scrub_block(unsigned long page, unsigned long offset,
5752da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunk				u32 size)
576da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
577da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct page *pg;
578da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *virt_addr;
579da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long flags = 0;
580da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
581537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
582da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
583da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* ECC error page was not in our memory. Ignore it. */
584079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	if (!pfn_valid(page))
585da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
586da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
587da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Find the actual page structure then map it and fix */
588da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pg = pfn_to_page(page);
589da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
590da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
591da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_save(flags);
592da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
593da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	virt_addr = kmap_atomic(pg, KM_BOUNCE_READ);
594da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
595da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Perform architecture specific atomic scrub operation */
596da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	atomic_scrub(virt_addr + offset, size);
597da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
598da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Unmap and complete */
599da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kunmap_atomic(virt_addr, KM_BOUNCE_READ);
600da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
601da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
602da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_restore(flags);
603da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
604da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
605da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should return -1 */
606e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonint edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
607da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
608da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrows = mci->csrows;
609da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, i;
610da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
611537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
612da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	row = -1;
613da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
614da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < mci->nr_csrows; i++) {
615da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		struct csrow_info *csrow = &csrows[i];
616da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
617da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (csrow->nr_pages == 0)
618da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			continue;
619da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
620537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
621537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			"mask(0x%lx)\n", mci->mc_idx, __func__,
622537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			csrow->first_page, page, csrow->last_page,
623537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			csrow->page_mask);
624da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
625da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if ((page >= csrow->first_page) &&
626da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    (page <= csrow->last_page) &&
627da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    ((page & csrow->page_mask) ==
628da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     (csrow->first_page & csrow->page_mask))) {
629da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			row = i;
630da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			break;
631da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
632da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
633da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
634da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row == -1)
635537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
636052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"could not look up page error address %lx\n",
637052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			(unsigned long)page);
638da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
639da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return row;
640da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
6419110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
642da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
643da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - setable log (warning/emerg) levels */
644da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
645da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ce(struct mem_ctl_info *mci,
646052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long page_frame_number,
647052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long offset_in_page, unsigned long syndrome,
648052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		int row, int channel, const char *msg)
649da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
650da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long remapped_page;
651da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
652537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
653da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
654da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
655da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
656da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
657537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
658052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range "
659052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", row, mci->nr_csrows);
660da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
661da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
662da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
663e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
664da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (channel >= mci->csrows[row].nr_channels || channel < 0) {
665da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
666537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
667052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel out of range "
668052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", channel,
669052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].nr_channels);
670da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
671da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
672da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
673da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
6744de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
675da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* FIXME - put in DIMM location */
676537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
677052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE page 0x%lx, offset 0x%lx, grain %d, syndrome "
678052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"0x%lx, row %d, channel %d, label \"%s\": %s\n",
679052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number, offset_in_page,
680052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].grain, syndrome, row, channel,
681052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].channels[channel].label, msg);
682da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
683da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
684da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ce_count++;
685da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].channels[channel].ce_count++;
686da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
687da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mci->scrub_mode & SCRUB_SW_SRC) {
688da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/*
689da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * Some MC's can remap memory so that it is still available
690da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * at a different address when PCI devices map into memory.
691da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * MC's that can't do this lose the memory where PCI devices
692da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * are mapped.  This mapping is MC dependant and so we call
693da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * back into the MC driver for it to map the MC page to
694da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * a physical (CPU) page which can then be mapped to a virtual
695da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * page - which can then be scrubbed.
696da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 */
697da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		remapped_page = mci->ctl_page_to_phys ?
698052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->ctl_page_to_phys(mci, page_frame_number) :
699052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number;
700da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
701da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_scrub_block(remapped_page, offset_in_page,
702052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				mci->csrows[row].grain);
703da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
704da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
7059110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ce);
706da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
707e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonvoid edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
708da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
7094de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
710537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
711052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE - no information available: %s\n", msg);
712e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
713da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_noinfo_count++;
714da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
715da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
7169110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
717da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
718da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ue(struct mem_ctl_info *mci,
719052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long page_frame_number,
720052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long offset_in_page, int row, const char *msg)
721da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
722da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int len = EDAC_MC_LABEL_LEN * 4;
723da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char labels[len + 1];
724da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *pos = labels;
725da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chan;
726da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chars;
727da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
728537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
729da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
730da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
731da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
732da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
733537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
734052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range "
735052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", row, mci->nr_csrows);
736da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
737da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
738da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
739da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
740da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	chars = snprintf(pos, len + 1, "%s",
741079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson			 mci->csrows[row].channels[0].label);
742da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	len -= chars;
743da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pos += chars;
744e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
745da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
746052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		chan++) {
747da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chars = snprintf(pos, len + 1, ":%s",
748079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				 mci->csrows[row].channels[chan].label);
749da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		len -= chars;
750da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		pos += chars;
751da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
752da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7534de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
754537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_EMERG,
755052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE page 0x%lx, offset 0x%lx, grain %d, row %d, "
756052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", page_frame_number,
757052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			offset_in_page, mci->csrows[row].grain, row,
758052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			labels, msg);
759da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7604de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
761e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson		panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, "
762052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"row %d, labels \"%s\": %s\n", mci->mc_idx,
763052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number, offset_in_page,
764052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].grain, row, labels, msg);
765da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
766da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
767da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ue_count++;
768da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
7699110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ue);
770da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
771e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonvoid edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
772da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
7734de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
774da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
775da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7764de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
777537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
778052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE - no information available: %s\n", msg);
779da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_noinfo_count++;
780da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
781da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
782079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
783da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7849794f33ddedd878dd92fcf8b4834391840366919eric wollesen/*************************************************************
7859794f33ddedd878dd92fcf8b4834391840366919eric wollesen * On Fully Buffered DIMM modules, this help function is
7869794f33ddedd878dd92fcf8b4834391840366919eric wollesen * called to process UE events
7879794f33ddedd878dd92fcf8b4834391840366919eric wollesen */
7889794f33ddedd878dd92fcf8b4834391840366919eric wollesenvoid edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
789052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int csrow,
790052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int channela,
791052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int channelb, char *msg)
7929794f33ddedd878dd92fcf8b4834391840366919eric wollesen{
7939794f33ddedd878dd92fcf8b4834391840366919eric wollesen	int len = EDAC_MC_LABEL_LEN * 4;
7949794f33ddedd878dd92fcf8b4834391840366919eric wollesen	char labels[len + 1];
7959794f33ddedd878dd92fcf8b4834391840366919eric wollesen	char *pos = labels;
7969794f33ddedd878dd92fcf8b4834391840366919eric wollesen	int chars;
7979794f33ddedd878dd92fcf8b4834391840366919eric wollesen
7989794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (csrow >= mci->nr_csrows) {
7999794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8009794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
801052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range (%d >= %d)\n",
802052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, mci->nr_csrows);
8039794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
8049794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8059794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8069794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8079794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channela >= mci->csrows[csrow].nr_channels) {
8089794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8099794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
810052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel-a out of range "
811052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n",
812052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channela, mci->csrows[csrow].nr_channels);
8139794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
8149794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8159794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8169794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8179794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channelb >= mci->csrows[csrow].nr_channels) {
8189794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8199794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
820052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel-b out of range "
821052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n",
822052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channelb, mci->csrows[csrow].nr_channels);
8239794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
8249794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8259794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8269794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8279794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->ue_count++;
8289794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].ue_count++;
8299794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8309794f33ddedd878dd92fcf8b4834391840366919eric wollesen	/* Generate the DIMM labels from the specified channels */
8319794f33ddedd878dd92fcf8b4834391840366919eric wollesen	chars = snprintf(pos, len + 1, "%s",
8329794f33ddedd878dd92fcf8b4834391840366919eric wollesen			 mci->csrows[csrow].channels[channela].label);
833079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	len -= chars;
834079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pos += chars;
8359794f33ddedd878dd92fcf8b4834391840366919eric wollesen	chars = snprintf(pos, len + 1, "-%s",
8369794f33ddedd878dd92fcf8b4834391840366919eric wollesen			 mci->csrows[csrow].channels[channelb].label);
8379794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8384de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
8399794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_EMERG,
840052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE row %d, channel-a= %d channel-b= %d "
841052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", csrow, channela, channelb,
842052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			labels, msg);
8439794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8444de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
8459794f33ddedd878dd92fcf8b4834391840366919eric wollesen		panic("UE row %d, channel-a= %d channel-b= %d "
846052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", csrow, channela,
847052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channelb, labels, msg);
8489794f33ddedd878dd92fcf8b4834391840366919eric wollesen}
8499794f33ddedd878dd92fcf8b4834391840366919eric wollesenEXPORT_SYMBOL(edac_mc_handle_fbd_ue);
8509794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8519794f33ddedd878dd92fcf8b4834391840366919eric wollesen/*************************************************************
8529794f33ddedd878dd92fcf8b4834391840366919eric wollesen * On Fully Buffered DIMM modules, this help function is
8539794f33ddedd878dd92fcf8b4834391840366919eric wollesen * called to process CE events
8549794f33ddedd878dd92fcf8b4834391840366919eric wollesen */
8559794f33ddedd878dd92fcf8b4834391840366919eric wollesenvoid edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
856052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int csrow, unsigned int channel, char *msg)
8579794f33ddedd878dd92fcf8b4834391840366919eric wollesen{
8589794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8599794f33ddedd878dd92fcf8b4834391840366919eric wollesen	/* Ensure boundary values */
8609794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (csrow >= mci->nr_csrows) {
8619794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8629794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
863052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range (%d >= %d)\n",
864052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, mci->nr_csrows);
8659794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
8669794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8679794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8689794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channel >= mci->csrows[csrow].nr_channels) {
8699794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8709794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
871052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel out of range (%d >= %d)\n",
872052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channel, mci->csrows[csrow].nr_channels);
8739794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
8749794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8759794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8769794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8774de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
8789794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* FIXME - put in DIMM location */
8799794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_WARNING,
880052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE row %d, channel %d, label \"%s\": %s\n",
881052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, channel,
882052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[csrow].channels[channel].label, msg);
8839794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8849794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->ce_count++;
8859794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].ce_count++;
8869794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].channels[channel].ce_count++;
8879794f33ddedd878dd92fcf8b4834391840366919eric wollesen}
888079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL(edac_mc_handle_fbd_ce);
889