edac_mc.c revision 24f9a7fe3f19f3fd310f556364d01a22911724b3
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
7924f9a7fe3f19f3fd310f556364d01a22911724b3Borislav Petkov#endif				/* CONFIG_EDAC_DEBUG */
8024f9a7fe3f19f3fd310f556364d01a22911724b3Borislav Petkov
81239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov/*
82239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov * keep those in sync with the enum mem_type
83239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov */
84239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkovconst char *edac_mem_types[] = {
85239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Empty csrow",
86239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Reserved csrow type",
87239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Unknown csrow type",
88239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Fast page mode RAM",
89239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Extended data out RAM",
90239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Burst Extended data out RAM",
91239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Single data rate SDRAM",
92239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Registered single data rate SDRAM",
93239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Double data rate SDRAM",
94239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Registered Double data rate SDRAM",
95239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Rambus DRAM",
96239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Unbuffered DDR2 RAM",
97239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Fully buffered DDR2",
98239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Registered DDR2 RAM",
99239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Rambus XDR",
100239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Unbuffered DDR3 RAM",
101239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov	"Registered DDR3 RAM",
102239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov};
103239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav PetkovEXPORT_SYMBOL_GPL(edac_mem_types);
104239642fe19adc19ba0a69e96f3b1904dfd6a3b9fBorislav Petkov
105da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
106da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Adjust 'ptr' so that its alignment is at least as stringent as what the
107da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * compiler would provide for X and return the aligned result.
108da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
109da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * If 'size' is a constant, the compiler will optimize this whole function
110da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * down to either a no-op or the addition of a constant to the value of 'ptr'.
111da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1127391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompsonvoid *edac_align_ptr(void *ptr, unsigned size)
113da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
114da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned align, r;
115da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
116da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Here we assume that the alignment of a "long long" is the most
117da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * stringent alignment that the compiler will ever provide by default.
118da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * As far as I know, this is a reasonable assumption.
119da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
120da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (size > sizeof(long))
121da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(long long);
122da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(int))
123da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(long);
124da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(short))
125da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(int);
126da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(char))
127da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(short);
128da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else
129079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson		return (char *)ptr;
130da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
131da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	r = size % align;
132da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
133da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (r == 0)
134079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson		return (char *)ptr;
135da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1367391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompson	return (void *)(((unsigned long)ptr) + align - r);
137da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
138da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
139da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
140da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_alloc: Allocate a struct mem_ctl_info structure
141da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @size_pvt:	size of private storage needed
142da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @nr_csrows:	Number of CWROWS needed for this MC
143da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @nr_chans:	Number of channels for the MC
144da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
145da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Everything is kmalloc'ed as one big chunk - more efficient.
146da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Only can be used if all structures have the same lifetime - otherwise
147da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * you have to allocate and initialize your own structures.
148da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
149da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Use edac_mc_free() to free mc structures allocated by this function.
150da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
151da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Returns:
152da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	NULL allocation failed
153da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	struct mem_ctl_info pointer
154da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
155da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
156b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson				unsigned nr_chans, int edac_index)
157da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
158da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
159da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csi, *csrow;
160da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct channel_info *chi, *chp, *chan;
161da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *pvt;
162da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned size;
163da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, chn;
1648096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	int err;
165da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
166da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Figure out the offsets of the various items from the start of an mc
167da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * structure.  We want the alignment of each item to be at least as
168da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * stringent as what the compiler would provide if we could simply
169da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * hardcode everything into a single struct.
170da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
171079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	mci = (struct mem_ctl_info *)0;
1727391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompson	csi = edac_align_ptr(&mci[1], sizeof(*csi));
1737391c6dcab3094610cb99bbd559beaa282582eacDouglas Thompson	chi = edac_align_ptr(&csi[nr_csrows], sizeof(*chi));
174e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
175079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	size = ((unsigned long)pvt) + sz_pvt;
176da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1778096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	mci = kzalloc(size, GFP_KERNEL);
1788096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	if (mci == NULL)
179da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return NULL;
180da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
181da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Adjust pointers so they point within the memory we just allocated
182da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * rather than an imaginary chunk of memory located at address 0.
183da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
184079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
185079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	chi = (struct channel_info *)(((char *)mci) + ((unsigned long)chi));
186079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
187da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
188b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson	/* setup index and various internal pointers */
189b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson	mci->mc_idx = edac_index;
190da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows = csi;
191da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->pvt_info = pvt;
192da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->nr_csrows = nr_csrows;
193da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
194da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (row = 0; row < nr_csrows; row++) {
195da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow = &csi[row];
196da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->csrow_idx = row;
197da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->mci = mci;
198da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->nr_channels = nr_chans;
199da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chp = &chi[row * nr_chans];
200da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->channels = chp;
201da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
202da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (chn = 0; chn < nr_chans; chn++) {
203da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan = &chp[chn];
204da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan->chan_idx = chn;
205da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan->csrow = csrow;
206da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
207da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
208da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
20981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mci->op_state = OP_ALLOC;
2106fe1108f14f4f9581af97cab752f37dc8fa9fdecMauro Carvalho Chehab	INIT_LIST_HEAD(&mci->grp_kobj_list);
21181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
2128096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	/*
2138096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * Initialize the 'root' kobj for the edac_mc controller
2148096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 */
2158096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	err = edac_mc_register_sysfs_main_kobj(mci);
2168096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	if (err) {
2178096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson		kfree(mci);
2188096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson		return NULL;
2198096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	}
2208096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson
2218096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	/* at this point, the root kobj is valid, and in order to
2228096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * 'free' the object, then the function:
2238096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 *      edac_mc_unregister_sysfs_main_kobj() must be called
2248096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * which will perform kobj unregistration and the actual free
2258096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 * will occur during the kobject callback operation
2268096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	 */
227da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return mci;
228da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2299110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_alloc);
230da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
231da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
2328096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson * edac_mc_free
2338096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson *	'Free' a previously allocated 'mci' structure
234da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to a struct mem_ctl_info structure
235da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
236da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_free(struct mem_ctl_info *mci)
237da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
238bbc560ae677c0f4d7ff8404a21409c99f35b297bMauro Carvalho Chehab	debugf1("%s()\n", __func__);
239bbc560ae677c0f4d7ff8404a21409c99f35b297bMauro Carvalho Chehab
2408096cfafbb7ad3cb1a286ae7e8086167f4ebb4b6Doug Thompson	edac_mc_unregister_sysfs_main_kobj(mci);
241accf74fff36315a31dc7319dae2927af06e9296fMauro Carvalho Chehab
242accf74fff36315a31dc7319dae2927af06e9296fMauro Carvalho Chehab	/* free the mci instance memory here */
243accf74fff36315a31dc7319dae2927af06e9296fMauro Carvalho Chehab	kfree(mci);
244da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2459110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_free);
246da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
247bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
248939747bd680eb09bb98792b17a5bfd2f525afe9dMauro Carvalho Chehab/**
249bce19683c17485b584b62b984d6dcf5332181588Doug Thompson * find_mci_by_dev
250bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *
251bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	scan list of controllers looking for the one that manages
252bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	the 'dev' device
253939747bd680eb09bb98792b17a5bfd2f525afe9dMauro Carvalho Chehab * @dev: pointer to a struct device related with the MCI
254bce19683c17485b584b62b984d6dcf5332181588Doug Thompson */
255939747bd680eb09bb98792b17a5bfd2f525afe9dMauro Carvalho Chehabstruct mem_ctl_info *find_mci_by_dev(struct device *dev)
256da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
257da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
258da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item;
259da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
260537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
261da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
262da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_for_each(item, &mc_devices) {
263da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci = list_entry(item, struct mem_ctl_info, link);
264da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
26537f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson		if (mci->dev == dev)
266da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			return mci;
267da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
268da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
269da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return NULL;
270da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
271939747bd680eb09bb98792b17a5bfd2f525afe9dMauro Carvalho ChehabEXPORT_SYMBOL_GPL(find_mci_by_dev);
272da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
27381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
27481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * handler for EDAC to check if NMI type handler has asserted interrupt
27581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
27681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic int edac_mc_assert_error_check_and_clear(void)
27781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
27866ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	int old_state;
27981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
280079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	if (edac_op_state == EDAC_OPSTATE_POLL)
28181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		return 1;
28281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
28366ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	old_state = edac_err_assert;
28466ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	edac_err_assert = 0;
28581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
28666ee2f940ac8ab25f0c43a1e717d25dc46bfe74dDave Jiang	return old_state;
28781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
28881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
28981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
29081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_function
29181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	performs the operation scheduled by a workq request
29281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
29381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic void edac_mc_workq_function(struct work_struct *work_req)
29481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
295fbeb4384748abb78531bbe1e80d627412a0abcfaJean Delvare	struct delayed_work *d_work = to_delayed_work(work_req);
29681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
29781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
29881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_lock(&mem_ctls_mutex);
29981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
300bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* if this control struct has movd to offline state, we are done */
301bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (mci->op_state == OP_OFFLINE) {
302bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson		mutex_unlock(&mem_ctls_mutex);
303bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson		return;
304bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	}
305bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
30681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* Only poll controllers that are running polled and have a check */
30781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
30881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->edac_check(mci);
30981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
31081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_unlock(&mem_ctls_mutex);
31181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
31281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* Reschedule */
3134de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	queue_delayed_work(edac_workqueue, &mci->work,
314052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			msecs_to_jiffies(edac_mc_get_poll_msec()));
31581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
31681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
31781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
31881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_setup
31981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	initialize a workq item for this mci
32081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	passing in the new delay period in msec
321bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
322bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *	locking model:
323bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
324bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *		called with the mem_ctls_mutex held
32581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
326bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompsonstatic void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
32781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
32881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	debugf0("%s()\n", __func__);
32981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
330bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* if this instance is not in the POLL state, then simply return */
331bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (mci->op_state != OP_RUNNING_POLL)
332bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson		return;
333bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
33481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
33581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
33681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
33781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
33881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
33981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_mc_workq_teardown
34081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang *	stop the workq processing on this mci
341bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
342bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *	locking model:
343bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
344bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *		called WITHOUT lock held
34581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
346bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompsonstatic void edac_mc_workq_teardown(struct mem_ctl_info *mci)
34781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
34881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	int status;
34981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
35000740c58541b6087d78418cebca1fcb86dc6077dBorislav Petkov	if (mci->op_state != OP_RUNNING_POLL)
35100740c58541b6087d78418cebca1fcb86dc6077dBorislav Petkov		return;
35200740c58541b6087d78418cebca1fcb86dc6077dBorislav Petkov
353bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	status = cancel_delayed_work(&mci->work);
354bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	if (status == 0) {
355bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		debugf0("%s() not canceled, flush the queue\n",
356bce19683c17485b584b62b984d6dcf5332181588Doug Thompson			__func__);
357bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
358bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		/* workq instance might be running, wait for it */
359bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		flush_workqueue(edac_workqueue);
36081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	}
36181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
36281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
36381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang/*
364bce19683c17485b584b62b984d6dcf5332181588Doug Thompson * edac_mc_reset_delay_period(unsigned long value)
365bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *
366bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	user space has updated our poll period value, need to
367bce19683c17485b584b62b984d6dcf5332181588Doug Thompson *	reset our workq delays
36881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang */
369bce19683c17485b584b62b984d6dcf5332181588Doug Thompsonvoid edac_mc_reset_delay_period(int value)
37081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang{
371bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	struct mem_ctl_info *mci;
372bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	struct list_head *item;
373bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
374bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	mutex_lock(&mem_ctls_mutex);
375bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
376bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	/* scan the list and turn off all workq timers, doing so under lock
377bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	 */
378bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	list_for_each(item, &mc_devices) {
379bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		mci = list_entry(item, struct mem_ctl_info, link);
380bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
381bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		if (mci->op_state == OP_RUNNING_POLL)
382bce19683c17485b584b62b984d6dcf5332181588Doug Thompson			cancel_delayed_work(&mci->work);
383bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	}
384bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
385bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	mutex_unlock(&mem_ctls_mutex);
38681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
387bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
388bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	/* re-walk the list, and reset the poll delay */
389bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	mutex_lock(&mem_ctls_mutex);
390bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
391bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	list_for_each(item, &mc_devices) {
392bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		mci = list_entry(item, struct mem_ctl_info, link);
393bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
394bce19683c17485b584b62b984d6dcf5332181588Doug Thompson		edac_mc_workq_setup(mci, (unsigned long) value);
395bce19683c17485b584b62b984d6dcf5332181588Doug Thompson	}
39681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
39781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	mutex_unlock(&mem_ctls_mutex);
39881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang}
39981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
400bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
401bce19683c17485b584b62b984d6dcf5332181588Doug Thompson
4022d7bbb91c8df26c60d223205a087507430024177Doug Thompson/* Return 0 on success, 1 on failure.
4032d7bbb91c8df26c60d223205a087507430024177Doug Thompson * Before calling this function, caller must
4042d7bbb91c8df26c60d223205a087507430024177Doug Thompson * assign a unique value to mci->mc_idx.
405bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
406bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *	locking model:
407bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *
408bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson *		called with the mem_ctls_mutex lock held
4092d7bbb91c8df26c60d223205a087507430024177Doug Thompson */
410079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic int add_mc_to_global_list(struct mem_ctl_info *mci)
411da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
412da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item, *insert_before;
413da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *p;
414da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
4152d7bbb91c8df26c60d223205a087507430024177Doug Thompson	insert_before = &mc_devices;
416da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
417bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	p = find_mci_by_dev(mci->dev);
418bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (unlikely(p != NULL))
4192d7bbb91c8df26c60d223205a087507430024177Doug Thompson		goto fail0;
420da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
4212d7bbb91c8df26c60d223205a087507430024177Doug Thompson	list_for_each(item, &mc_devices) {
4222d7bbb91c8df26c60d223205a087507430024177Doug Thompson		p = list_entry(item, struct mem_ctl_info, link);
423da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
4242d7bbb91c8df26c60d223205a087507430024177Doug Thompson		if (p->mc_idx >= mci->mc_idx) {
4252d7bbb91c8df26c60d223205a087507430024177Doug Thompson			if (unlikely(p->mc_idx == mci->mc_idx))
4262d7bbb91c8df26c60d223205a087507430024177Doug Thompson				goto fail1;
427da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
4282d7bbb91c8df26c60d223205a087507430024177Doug Thompson			insert_before = item;
4292d7bbb91c8df26c60d223205a087507430024177Doug Thompson			break;
430da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
431da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
432da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
433da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_add_tail_rcu(&mci->link, insert_before);
434c0d121720220584bba2876b032e58a076b843fa1Dave Jiang	atomic_inc(&edac_handlers);
435da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
4362d7bbb91c8df26c60d223205a087507430024177Doug Thompson
437052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
4382d7bbb91c8df26c60d223205a087507430024177Doug Thompson	edac_printk(KERN_WARNING, EDAC_MC,
439281efb17d88a91dc3b879bb1d49e3a66daf48797Kay Sievers		"%s (%s) %s %s already assigned %d\n", dev_name(p->dev),
44017aa7e034416e3080bc57a786d09ba0a4a044561Stephen Rothwell		edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
4412d7bbb91c8df26c60d223205a087507430024177Doug Thompson	return 1;
4422d7bbb91c8df26c60d223205a087507430024177Doug Thompson
443052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
4442d7bbb91c8df26c60d223205a087507430024177Doug Thompson	edac_printk(KERN_WARNING, EDAC_MC,
445052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"bug in low-level driver: attempt to assign\n"
446052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"    duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
4472d7bbb91c8df26c60d223205a087507430024177Doug Thompson	return 1;
448da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
449da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
450e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void complete_mc_list_del(struct rcu_head *head)
451a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson{
452a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	struct mem_ctl_info *mci;
453a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
454a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	mci = container_of(head, struct mem_ctl_info, rcu);
455a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	INIT_LIST_HEAD(&mci->link);
456a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson}
457a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
458e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void del_mc_from_global_list(struct mem_ctl_info *mci)
459a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson{
460c0d121720220584bba2876b032e58a076b843fa1Dave Jiang	atomic_dec(&edac_handlers);
461a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	list_del_rcu(&mci->link);
462a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson	call_rcu(&mci->rcu, complete_mc_list_del);
463458e5ff13e1bed050990d97e9aa55bcdafc951a7Jesper Dangaard Brouer	rcu_barrier();
464a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson}
465a1d03fcc1399b1e23922bcc3af1772b128aa6e93Dave Peterson
466da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
4675da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'.
4685da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson *
4695da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * If found, return a pointer to the structure.
4705da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * Else return NULL.
4715da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson *
4725da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson * Caller must hold mem_ctls_mutex.
4735da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson */
474079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct mem_ctl_info *edac_mc_find(int idx)
4755da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson{
4765da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	struct list_head *item;
4775da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	struct mem_ctl_info *mci;
4785da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4795da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	list_for_each(item, &mc_devices) {
4805da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		mci = list_entry(item, struct mem_ctl_info, link);
4815da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4825da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		if (mci->mc_idx >= idx) {
4835da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson			if (mci->mc_idx == idx)
4845da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson				return mci;
4855da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4865da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson			break;
4875da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson		}
4885da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	}
4895da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4905da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson	return NULL;
4915da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson}
4925da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas ThompsonEXPORT_SYMBOL(edac_mc_find);
4935da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson
4945da0831c598f94582bce6bb0a55b8de2f9897cb1Douglas Thompson/**
495472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
496472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson *                 create sysfs entries associated with mci structure
497da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to the mci structure to be added to the list
4982d7bbb91c8df26c60d223205a087507430024177Doug Thompson * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure.
499da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
500da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Return:
501da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	0	Success
502da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	!0	Failure
503da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
504da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
505da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should a warning be printed if no error detection? correction? */
506b8f6f9755248026f21282e25cac49a1af698056cDoug Thompsonint edac_mc_add_mc(struct mem_ctl_info *mci)
507da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
508537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf0("%s()\n", __func__);
509b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson
510da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
511da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 3)
512da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_dump_mci(mci);
513e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
514da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 4) {
515da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		int i;
516da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
517da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (i = 0; i < mci->nr_csrows; i++) {
518da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			int j;
519e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
520da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			edac_mc_dump_csrow(&mci->csrows[i]);
521da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			for (j = 0; j < mci->csrows[i].nr_channels; j++)
522079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				edac_mc_dump_channel(&mci->csrows[i].
523052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson						channels[j]);
524da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
525da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
526da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif
52763b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
528da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
529da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (add_mc_to_global_list(mci))
530028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson		goto fail0;
531da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
532da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* set load time so that error rate can be tracked */
533da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->start_time = jiffies;
534da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
5359794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (edac_create_sysfs_mci_device(mci)) {
5369794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_WARNING,
537052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"failed to create sysfs device\n");
5389794f33ddedd878dd92fcf8b4834391840366919eric wollesen		goto fail1;
5399794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
540da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
54181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	/* If there IS a check routine, then we are running POLLED */
54281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	if (mci->edac_check != NULL) {
54381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		/* This instance is NOW RUNNING */
54481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->op_state = OP_RUNNING_POLL;
54581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
54681d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
54781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	} else {
54881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		mci->op_state = OP_RUNNING_INTERRUPT;
54981d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	}
55081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang
551da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Report action taken */
552bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
55317aa7e034416e3080bc57a786d09ba0a4a044561Stephen Rothwell		" DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci));
554da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
55563b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
556028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	return 0;
557da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
558052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
559028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	del_mc_from_global_list(mci);
560028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson
561052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
56263b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
563028a7b6d3d9fa2cc41d76d45575345cca8d00a4cDave Peterson	return 1;
564da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
5659110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_add_mc);
566da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
567da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
568472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
569472678ebd30d87cbe8d97562dcc0e46d1076040fDave Peterson *                 remove mci structure from global list
57037f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson * @pdev: Pointer to 'struct device' representing mci structure to remove.
571da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
57218dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson * Return pointer to removed mci structure, or NULL if device not found.
573da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
574079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct mem_ctl_info *edac_mc_del_mc(struct device *dev)
575da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
57618dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	struct mem_ctl_info *mci;
577da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
578bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	debugf0("%s()\n", __func__);
579bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
58063b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_lock(&mem_ctls_mutex);
58118dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson
582bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	/* find the requested mci struct in the global list */
583bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	mci = find_mci_by_dev(dev);
584bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	if (mci == NULL) {
58563b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke		mutex_unlock(&mem_ctls_mutex);
58618dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson		return NULL;
58718dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	}
58818dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson
589da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	del_mc_from_global_list(mci);
59063b7df9101895d1f0a259c567b3bab949a23075fMatthias Kaehlcke	mutex_unlock(&mem_ctls_mutex);
591bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
592bb31b3122c0dd07d2d958da17a50ad771ce79e2bBorislav Petkov	/* flush workq processes */
593bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	edac_mc_workq_teardown(mci);
594bb31b3122c0dd07d2d958da17a50ad771ce79e2bBorislav Petkov
595bb31b3122c0dd07d2d958da17a50ad771ce79e2bBorislav Petkov	/* marking MCI offline */
596bb31b3122c0dd07d2d958da17a50ad771ce79e2bBorislav Petkov	mci->op_state = OP_OFFLINE;
597bb31b3122c0dd07d2d958da17a50ad771ce79e2bBorislav Petkov
598bb31b3122c0dd07d2d958da17a50ad771ce79e2bBorislav Petkov	/* remove from sysfs */
599bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson	edac_remove_sysfs_mci_device(mci);
600bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
601537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	edac_printk(KERN_INFO, EDAC_MC,
602052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
60317aa7e034416e3080bc57a786d09ba0a4a044561Stephen Rothwell		mci->mod_name, mci->ctl_name, edac_dev_name(mci));
604bf52fa4a26567bfbf5b1d30f84cf0226e61d26cdDoug Thompson
60518dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson	return mci;
606da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
6079110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_del_mc);
608da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
6092da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunkstatic void edac_mc_scrub_block(unsigned long page, unsigned long offset,
6102da1c119fd999cb834b4fe0c1a5a8c36195df1cbAdrian Bunk				u32 size)
611da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
612da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct page *pg;
613da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *virt_addr;
614da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long flags = 0;
615da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
616537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("%s()\n", __func__);
617da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
618da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* ECC error page was not in our memory. Ignore it. */
619079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	if (!pfn_valid(page))
620da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
621da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
622da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Find the actual page structure then map it and fix */
623da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pg = pfn_to_page(page);
624da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
625da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
626da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_save(flags);
627da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
628da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	virt_addr = kmap_atomic(pg, KM_BOUNCE_READ);
629da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
630da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Perform architecture specific atomic scrub operation */
631da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	atomic_scrub(virt_addr + offset, size);
632da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
633da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Unmap and complete */
634da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kunmap_atomic(virt_addr, KM_BOUNCE_READ);
635da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
636da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
637da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_restore(flags);
638da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
639da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
640da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should return -1 */
641e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonint edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
642da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
643da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrows = mci->csrows;
644da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, i;
645da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
646537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
647da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	row = -1;
648da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
649da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < mci->nr_csrows; i++) {
650da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		struct csrow_info *csrow = &csrows[i];
651da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
652da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (csrow->nr_pages == 0)
653da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			continue;
654da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
655537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
656537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			"mask(0x%lx)\n", mci->mc_idx, __func__,
657537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			csrow->first_page, page, csrow->last_page,
658537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson			csrow->page_mask);
659da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
660da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if ((page >= csrow->first_page) &&
661da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    (page <= csrow->last_page) &&
662da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    ((page & csrow->page_mask) ==
663da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     (csrow->first_page & csrow->page_mask))) {
664da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			row = i;
665da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			break;
666da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
667da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
668da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
669da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row == -1)
670537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
671052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"could not look up page error address %lx\n",
672052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			(unsigned long)page);
673da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
674da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return row;
675da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
6769110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
677da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
678da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - setable log (warning/emerg) levels */
679da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
680da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ce(struct mem_ctl_info *mci,
681052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long page_frame_number,
682052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long offset_in_page, unsigned long syndrome,
683052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		int row, int channel, const char *msg)
684da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
685da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long remapped_page;
686da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
687537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
688da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
689da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
690da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
691da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
692537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
693052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range "
694052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", row, mci->nr_csrows);
695da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
696da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
697da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
698e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
699da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (channel >= mci->csrows[row].nr_channels || channel < 0) {
700da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
701537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
702052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel out of range "
703052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", channel,
704052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].nr_channels);
705da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
706da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
707da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
708da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7094de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
710da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* FIXME - put in DIMM location */
711537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
712052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE page 0x%lx, offset 0x%lx, grain %d, syndrome "
713052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"0x%lx, row %d, channel %d, label \"%s\": %s\n",
714052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number, offset_in_page,
715052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].grain, syndrome, row, channel,
716052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].channels[channel].label, msg);
717da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
718da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
719da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ce_count++;
720da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].channels[channel].ce_count++;
721da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
722da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mci->scrub_mode & SCRUB_SW_SRC) {
723da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/*
724da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * Some MC's can remap memory so that it is still available
725da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * at a different address when PCI devices map into memory.
726da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * MC's that can't do this lose the memory where PCI devices
727da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * are mapped.  This mapping is MC dependant and so we call
728da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * back into the MC driver for it to map the MC page to
729da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * a physical (CPU) page which can then be mapped to a virtual
730da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * page - which can then be scrubbed.
731da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 */
732da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		remapped_page = mci->ctl_page_to_phys ?
733052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->ctl_page_to_phys(mci, page_frame_number) :
734052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number;
735da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
736da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_scrub_block(remapped_page, offset_in_page,
737052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				mci->csrows[row].grain);
738da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
739da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
7409110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ce);
741da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
742e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonvoid edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
743da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
7444de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
745537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
746052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE - no information available: %s\n", msg);
747e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
748da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_noinfo_count++;
749da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
750da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
7519110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
752da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
753da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ue(struct mem_ctl_info *mci,
754052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long page_frame_number,
755052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		unsigned long offset_in_page, int row, const char *msg)
756da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
757da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int len = EDAC_MC_LABEL_LEN * 4;
758da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char labels[len + 1];
759da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *pos = labels;
760da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chan;
761da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chars;
762da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
763537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
764da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
765da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
766da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
767da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
768537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_ERR,
769052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range "
770052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n", row, mci->nr_csrows);
771da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
772da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
773da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
774da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
775da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	chars = snprintf(pos, len + 1, "%s",
776079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson			 mci->csrows[row].channels[0].label);
777da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	len -= chars;
778da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pos += chars;
779e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson
780da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
781052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		chan++) {
782da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chars = snprintf(pos, len + 1, ":%s",
783079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				 mci->csrows[row].channels[chan].label);
784da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		len -= chars;
785da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		pos += chars;
786da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
787da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7884de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
789537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_EMERG,
790052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE page 0x%lx, offset 0x%lx, grain %d, row %d, "
791052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", page_frame_number,
792052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			offset_in_page, mci->csrows[row].grain, row,
793052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			labels, msg);
794da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
7954de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
796e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson		panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, "
797052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"row %d, labels \"%s\": %s\n", mci->mc_idx,
798052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			page_frame_number, offset_in_page,
799052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[row].grain, row, labels, msg);
800da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
801da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
802da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ue_count++;
803da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
8049110540f7f2bbcc3577d2580a696fbb7af68c892Dave PetersonEXPORT_SYMBOL_GPL(edac_mc_handle_ue);
805da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
806e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonvoid edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
807da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
8084de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
809da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
810da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
8114de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
812537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson		edac_mc_printk(mci, KERN_WARNING,
813052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE - no information available: %s\n", msg);
814da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_noinfo_count++;
815da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
816da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
817079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
818da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
8199794f33ddedd878dd92fcf8b4834391840366919eric wollesen/*************************************************************
8209794f33ddedd878dd92fcf8b4834391840366919eric wollesen * On Fully Buffered DIMM modules, this help function is
8219794f33ddedd878dd92fcf8b4834391840366919eric wollesen * called to process UE events
8229794f33ddedd878dd92fcf8b4834391840366919eric wollesen */
8239794f33ddedd878dd92fcf8b4834391840366919eric wollesenvoid edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
824052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int csrow,
825052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int channela,
826052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int channelb, char *msg)
8279794f33ddedd878dd92fcf8b4834391840366919eric wollesen{
8289794f33ddedd878dd92fcf8b4834391840366919eric wollesen	int len = EDAC_MC_LABEL_LEN * 4;
8299794f33ddedd878dd92fcf8b4834391840366919eric wollesen	char labels[len + 1];
8309794f33ddedd878dd92fcf8b4834391840366919eric wollesen	char *pos = labels;
8319794f33ddedd878dd92fcf8b4834391840366919eric wollesen	int chars;
8329794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8339794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (csrow >= mci->nr_csrows) {
8349794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8359794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
836052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range (%d >= %d)\n",
837052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, mci->nr_csrows);
8389794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
8399794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8409794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8419794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8429794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channela >= mci->csrows[csrow].nr_channels) {
8439794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8449794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
845052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel-a out of range "
846052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n",
847052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channela, mci->csrows[csrow].nr_channels);
8489794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
8499794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8509794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8519794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8529794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channelb >= mci->csrows[csrow].nr_channels) {
8539794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8549794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
855052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel-b out of range "
856052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"(%d >= %d)\n",
857052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channelb, mci->csrows[csrow].nr_channels);
8589794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
8599794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
8609794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
8619794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8629794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->ue_count++;
8639794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].ue_count++;
8649794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8659794f33ddedd878dd92fcf8b4834391840366919eric wollesen	/* Generate the DIMM labels from the specified channels */
8669794f33ddedd878dd92fcf8b4834391840366919eric wollesen	chars = snprintf(pos, len + 1, "%s",
8679794f33ddedd878dd92fcf8b4834391840366919eric wollesen			 mci->csrows[csrow].channels[channela].label);
868079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	len -= chars;
869079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pos += chars;
8709794f33ddedd878dd92fcf8b4834391840366919eric wollesen	chars = snprintf(pos, len + 1, "-%s",
8719794f33ddedd878dd92fcf8b4834391840366919eric wollesen			 mci->csrows[csrow].channels[channelb].label);
8729794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8734de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ue())
8749794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_EMERG,
875052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"UE row %d, channel-a= %d channel-b= %d "
876052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", csrow, channela, channelb,
877052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			labels, msg);
8789794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8794de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_panic_on_ue())
8809794f33ddedd878dd92fcf8b4834391840366919eric wollesen		panic("UE row %d, channel-a= %d channel-b= %d "
881052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"labels \"%s\": %s\n", csrow, channela,
882052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channelb, labels, msg);
8839794f33ddedd878dd92fcf8b4834391840366919eric wollesen}
8849794f33ddedd878dd92fcf8b4834391840366919eric wollesenEXPORT_SYMBOL(edac_mc_handle_fbd_ue);
8859794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8869794f33ddedd878dd92fcf8b4834391840366919eric wollesen/*************************************************************
8879794f33ddedd878dd92fcf8b4834391840366919eric wollesen * On Fully Buffered DIMM modules, this help function is
8889794f33ddedd878dd92fcf8b4834391840366919eric wollesen * called to process CE events
8899794f33ddedd878dd92fcf8b4834391840366919eric wollesen */
8909794f33ddedd878dd92fcf8b4834391840366919eric wollesenvoid edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
891052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			unsigned int csrow, unsigned int channel, char *msg)
8929794f33ddedd878dd92fcf8b4834391840366919eric wollesen{
8939794f33ddedd878dd92fcf8b4834391840366919eric wollesen
8949794f33ddedd878dd92fcf8b4834391840366919eric wollesen	/* Ensure boundary values */
8959794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (csrow >= mci->nr_csrows) {
8969794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
8979794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
898052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: row out of range (%d >= %d)\n",
899052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, mci->nr_csrows);
9009794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
9019794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
9029794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
9039794f33ddedd878dd92fcf8b4834391840366919eric wollesen	if (channel >= mci->csrows[csrow].nr_channels) {
9049794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* something is wrong */
9059794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_ERR,
906052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"INTERNAL ERROR: channel out of range (%d >= %d)\n",
907052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			channel, mci->csrows[csrow].nr_channels);
9089794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
9099794f33ddedd878dd92fcf8b4834391840366919eric wollesen		return;
9109794f33ddedd878dd92fcf8b4834391840366919eric wollesen	}
9119794f33ddedd878dd92fcf8b4834391840366919eric wollesen
9124de78c6877ec21142582ac19453c2d453d1ea298Dave Jiang	if (edac_mc_get_log_ce())
9139794f33ddedd878dd92fcf8b4834391840366919eric wollesen		/* FIXME - put in DIMM location */
9149794f33ddedd878dd92fcf8b4834391840366919eric wollesen		edac_mc_printk(mci, KERN_WARNING,
915052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"CE row %d, channel %d, label \"%s\": %s\n",
916052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			csrow, channel,
917052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			mci->csrows[csrow].channels[channel].label, msg);
9189794f33ddedd878dd92fcf8b4834391840366919eric wollesen
9199794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->ce_count++;
9209794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].ce_count++;
9219794f33ddedd878dd92fcf8b4834391840366919eric wollesen	mci->csrows[csrow].channels[channel].ce_count++;
9229794f33ddedd878dd92fcf8b4834391840366919eric wollesen}
923079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL(edac_mc_handle_fbd_ce);
924