edac_mc.c revision da9bb1d27b21cb24cbb6a2efb5d3c464d357a01e
1da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc kernel module
3da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * (C) 2005 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
16da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/config.h>
17da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/version.h>
18da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/module.h>
19da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/proc_fs.h>
20da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/kernel.h>
21da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/types.h>
22da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/smp.h>
23da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/init.h>
24da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/sysctl.h>
25da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/highmem.h>
26da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/timer.h>
27da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/slab.h>
28da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/jiffies.h>
29da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/spinlock.h>
30da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/list.h>
31da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/sysdev.h>
32da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <linux/ctype.h>
33da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
34da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <asm/uaccess.h>
35da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <asm/page.h>
36da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include <asm/edac.h>
37da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
38da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#include "edac_mc.h"
39da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
40da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define	EDAC_MC_VERSION	"edac_mc  Ver: 2.0.0 " __DATE__
41da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
42da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
43da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Values of 0 to 4 will generate output */
44da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxint edac_debug_level = 1;
45da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_debug_level);
46da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif
47da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
48da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* EDAC Controls, setable by module parameter, and sysfs */
49da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int log_ue = 1;
50da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int log_ce = 1;
51da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int panic_on_ue = 1;
52da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int poll_msec = 1000;
53da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
54da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int check_pci_parity = 0;	/* default YES check PCI parity */
55da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int panic_on_pci_parity;		/* default no panic on PCI Parity */
56da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic atomic_t pci_parity_count = ATOMIC_INIT(0);
57da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
58da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* lock to memory controller's control array */
59da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic DECLARE_MUTEX(mem_ctls_mutex);
60da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct list_head mc_devices = LIST_HEAD_INIT(mc_devices);
61da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
62da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Structure of the whitelist and blacklist arrays */
63da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct edac_pci_device_list {
64da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned int  vendor;		/* Vendor ID */
65da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned int  device;		/* Deviice ID */
66da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
67da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
68da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
69da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define MAX_LISTED_PCI_DEVICES		32
70da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
71da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* List of PCI devices (vendor-id:device-id) that should be skipped */
72da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct edac_pci_device_list pci_blacklist[MAX_LISTED_PCI_DEVICES];
73da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int pci_blacklist_count;
74da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
75da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* List of PCI devices (vendor-id:device-id) that should be scanned */
76da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct edac_pci_device_list pci_whitelist[MAX_LISTED_PCI_DEVICES];
77da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int pci_whitelist_count ;
78da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
79da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*  START sysfs data and methods */
80da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
81da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic const char *mem_types[] = {
82da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_EMPTY] = "Empty",
83da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_RESERVED] = "Reserved",
84da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_UNKNOWN] = "Unknown",
85da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_FPM] = "FPM",
86da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_EDO] = "EDO",
87da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_BEDO] = "BEDO",
88da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_SDR] = "Unbuffered-SDR",
89da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_RDR] = "Registered-SDR",
90da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_DDR] = "Unbuffered-DDR",
91da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_RDDR] = "Registered-DDR",
92da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[MEM_RMBS] = "RMBS"
93da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
94da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
95da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic const char *dev_types[] = {
96da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_UNKNOWN] = "Unknown",
97da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X1] = "x1",
98da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X2] = "x2",
99da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X4] = "x4",
100da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X8] = "x8",
101da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X16] = "x16",
102da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X32] = "x32",
103da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[DEV_X64] = "x64"
104da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
105da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
106da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic const char *edac_caps[] = {
107da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_UNKNOWN] = "Unknown",
108da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_NONE] = "None",
109da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_RESERVED] = "Reserved",
110da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_PARITY] = "PARITY",
111da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_EC] = "EC",
112da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_SECDED] = "SECDED",
113da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_S2ECD2ED] = "S2ECD2ED",
114da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_S4ECD4ED] = "S4ECD4ED",
115da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_S8ECD8ED] = "S8ECD8ED",
116da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	[EDAC_S16ECD16ED] = "S16ECD16ED"
117da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
118da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
119da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
120da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* sysfs object: /sys/devices/system/edac */
121da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct sysdev_class edac_class = {
122da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	set_kset_name("edac"),
123da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
124da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
125da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* sysfs objects:
126da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	/sys/devices/system/edac/mc
127da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	/sys/devices/system/edac/pci
128da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
129da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct kobject edac_memctrl_kobj;
130da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct kobject edac_pci_kobj;
131da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
132da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
133da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * /sys/devices/system/edac/mc;
134da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * 	data structures and methods
135da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
136da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t memctrl_string_show(void *ptr, char *buffer)
137da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
138da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *value = (char*) ptr;
139da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(buffer, "%s\n", value);
140da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
141da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
142da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t memctrl_int_show(void *ptr, char *buffer)
143da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
144da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int *value = (int*) ptr;
145da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(buffer, "%d\n", *value);
146da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
147da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
148da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count)
149da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
150da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int *value = (int*) ptr;
151da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
152da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (isdigit(*buffer))
153da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		*value = simple_strtoul(buffer, NULL, 0);
154da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
155da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return count;
156da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
157da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
158da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct memctrl_dev_attribute {
159da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct attribute	attr;
160da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void	*value;
161da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*show)(void *,char *);
162da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*store)(void *, const char *, size_t);
163da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
164da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
165da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Set of show/store abstract level functions for memory control object */
166da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t
167da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmemctrl_dev_show(struct kobject *kobj, struct attribute *attr, char *buffer)
168da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
169da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct memctrl_dev_attribute *memctrl_dev;
170da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	memctrl_dev = (struct memctrl_dev_attribute*)attr;
171da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
172da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (memctrl_dev->show)
173da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return memctrl_dev->show(memctrl_dev->value, buffer);
174da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
175da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
176da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
177da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t
178da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmemctrl_dev_store(struct kobject *kobj, struct attribute *attr,
179da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			const char *buffer, size_t count)
180da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
181da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct memctrl_dev_attribute *memctrl_dev;
182da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	memctrl_dev = (struct memctrl_dev_attribute*)attr;
183da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
184da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (memctrl_dev->store)
185da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return memctrl_dev->store(memctrl_dev->value, buffer, count);
186da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
187da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
188da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
189da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct sysfs_ops memctrlfs_ops = {
190da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = memctrl_dev_show,
191da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = memctrl_dev_store
192da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
193da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
194da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define MEMCTRL_ATTR(_name,_mode,_show,_store)			\
195da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct memctrl_dev_attribute attr_##_name = {			\
196da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.attr = {.name = __stringify(_name), .mode = _mode },	\
197da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.value  = &_name,					\
198da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = _show,					\
199da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = _store,					\
200da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
201da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
202da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define MEMCTRL_STRING_ATTR(_name,_data,_mode,_show,_store)	\
203da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct memctrl_dev_attribute attr_##_name = {			\
204da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.attr = {.name = __stringify(_name), .mode = _mode },	\
205da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.value  = _data,					\
206da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = _show,					\
207da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = _store,					\
208da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
209da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
210da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* cwrow<id> attribute f*/
211da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMEMCTRL_STRING_ATTR(mc_version,EDAC_MC_VERSION,S_IRUGO,memctrl_string_show,NULL);
212da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
213da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* csrow<id> control files */
214da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMEMCTRL_ATTR(panic_on_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store);
215da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMEMCTRL_ATTR(log_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store);
216da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMEMCTRL_ATTR(log_ce,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store);
217da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMEMCTRL_ATTR(poll_msec,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store);
218da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
219da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
220da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Base Attributes of the memory ECC object */
221da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct memctrl_dev_attribute *memctrl_attr[] = {
222da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_panic_on_ue,
223da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_log_ue,
224da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_log_ce,
225da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_poll_msec,
226da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_mc_version,
227da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	NULL,
228da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
229da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
230da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Main MC kobject release() function */
231da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_memctrl_master_release(struct kobject *kobj)
232da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
233da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf1("EDAC MC: " __FILE__ ": %s()\n", __func__);
234da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
235da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
236da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct kobj_type ktype_memctrl = {
237da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.release	= edac_memctrl_master_release,
238da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.sysfs_ops	= &memctrlfs_ops,
239da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.default_attrs	= (struct attribute **) memctrl_attr,
240da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
241da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
242da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
243da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Initialize the main sysfs entries for edac:
244da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *   /sys/devices/system/edac
245da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
246da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * and children
247da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
248da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Return:  0 SUCCESS
249da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *         !0 FAILURE
250da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
251da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int edac_sysfs_memctrl_setup(void)
252da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
253da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int err=0;
254da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
255da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf1("MC: " __FILE__ ": %s()\n", __func__);
256da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
257da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* create the /sys/devices/system/edac directory */
258da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	err = sysdev_class_register(&edac_class);
259da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!err) {
260da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* Init the MC's kobject */
261da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		memset(&edac_memctrl_kobj, 0, sizeof (edac_memctrl_kobj));
262da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		kobject_init(&edac_memctrl_kobj);
263da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
264da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_memctrl_kobj.parent = &edac_class.kset.kobj;
265da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_memctrl_kobj.ktype = &ktype_memctrl;
266da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
267da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* generate sysfs "..../edac/mc"   */
268da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		err = kobject_set_name(&edac_memctrl_kobj,"mc");
269da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (!err) {
270da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			/* FIXME: maybe new sysdev_create_subdir() */
271da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			err = kobject_register(&edac_memctrl_kobj);
272da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			if (err) {
273da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				debugf1("Failed to register '.../edac/mc'\n");
274da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			} else {
275da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				debugf1("Registered '.../edac/mc' kobject\n");
276da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			}
277da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
278da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	} else {
279da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		debugf1(KERN_WARNING "__FILE__ %s() error=%d\n", __func__,err);
280da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
281da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
282da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return err;
283da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
284da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
285da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
286da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * MC teardown:
287da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	the '..../edac/mc' kobject followed by '..../edac' itself
288da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
289da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_sysfs_memctrl_teardown(void)
290da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
291da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s()\n", __func__);
292da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
293da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Unregister the MC's kobject */
294da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_unregister(&edac_memctrl_kobj);
295da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
296da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* release the master edac mc kobject */
297da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_put(&edac_memctrl_kobj);
298da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
299da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Unregister the 'edac' object */
300da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	sysdev_class_unregister(&edac_class);
301da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
302da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
303da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
304da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * /sys/devices/system/edac/pci;
305da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * 	data structures and methods
306da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
307da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
308da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct list_control {
309da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct edac_pci_device_list *list;
310da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int *count;
311da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
312da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
313da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Output the list as:  vendor_id:device:id<,vendor_id:device_id> */
314da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t edac_pci_list_string_show(void *ptr, char *buffer)
315da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
316da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_control *listctl;
317da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct edac_pci_device_list *list;
318da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *p = buffer;
319da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int len=0;
320da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int i;
321da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
322da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	listctl = ptr;
323da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list = listctl->list;
324da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
325da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < *(listctl->count); i++, list++ ) {
326da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (len > 0)
327da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			len += snprintf(p + len, (PAGE_SIZE-len), ",");
328da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
329da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		len += snprintf(p + len,
330da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				(PAGE_SIZE-len),
331da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				"%x:%x",
332da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				list->vendor,list->device);
333da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
334da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
335da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	len += snprintf(p + len,(PAGE_SIZE-len), "\n");
336da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
337da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return (ssize_t) len;
338da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
339da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
340da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
341da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
342da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Scan string from **s to **e looking for one 'vendor:device' tuple
343da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * where each field is a hex value
344da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
345da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * return 0 if an entry is NOT found
346da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * return 1 if an entry is found
347da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	fill in *vendor_id and *device_id with values found
348da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
349da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * In both cases, make sure *s has been moved forward toward *e
350da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
351da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int parse_one_device(const char **s,const char **e,
352da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned int *vendor_id, unsigned int *device_id)
353da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
354da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	const char *runner, *p;
355da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
356da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* if null byte, we are done */
357da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!**s) {
358da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		(*s)++;	/* keep *s moving */
359da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return 0;
360da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
361da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
362da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* skip over newlines & whitespace */
363da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if ((**s == '\n') || isspace(**s)) {
364da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		(*s)++;
365da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return 0;
366da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
367da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
368da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!isxdigit(**s)) {
369da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		(*s)++;
370da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return 0;
371da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
372da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
373da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* parse vendor_id */
374da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	runner = *s;
375da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	while (runner < *e) {
376da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* scan for vendor:device delimiter */
377da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (*runner == ':') {
378da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			*vendor_id = simple_strtol((char*) *s, (char**) &p, 16);
379da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			runner = p + 1;
380da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			break;
381da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
382da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		runner++;
383da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
384da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
385da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!isxdigit(*runner)) {
386da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		*s = ++runner;
387da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return 0;
388da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
389da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
390da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* parse device_id */
391da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (runner < *e) {
392da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		*device_id = simple_strtol((char*)runner, (char**)&p, 16);
393da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		runner = p;
394da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
395da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
396da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	*s = runner;
397da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
398da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 1;
399da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
400da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
401da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t edac_pci_list_string_store(void *ptr, const char *buffer,
402da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					size_t count)
403da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
404da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_control *listctl;
405da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct edac_pci_device_list *list;
406da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned int vendor_id, device_id;
407da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	const char *s, *e;
408da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int *index;
409da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
410da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	s = (char*)buffer;
411da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	e = s + count;
412da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
413da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	listctl = ptr;
414da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list = listctl->list;
415da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	index = listctl->count;
416da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
417da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	*index = 0;
418da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	while (*index < MAX_LISTED_PCI_DEVICES) {
419da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
420da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (parse_one_device(&s,&e,&vendor_id,&device_id)) {
421da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			list[ *index ].vendor = vendor_id;
422da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			list[ *index ].device = device_id;
423da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			(*index)++;
424da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
425da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
426da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* check for all data consume */
427da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (s >= e)
428da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			break;
429da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
430da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
431da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return count;
432da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
433da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
434da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t edac_pci_int_show(void *ptr, char *buffer)
435da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
436da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int *value = ptr;
437da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(buffer,"%d\n",*value);
438da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
439da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
440da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t edac_pci_int_store(void *ptr, const char *buffer, size_t count)
441da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
442da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int *value = ptr;
443da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
444da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (isdigit(*buffer))
445da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		*value = simple_strtoul(buffer,NULL,0);
446da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
447da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return count;
448da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
449da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
450da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct edac_pci_dev_attribute {
451da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct attribute	attr;
452da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void	*value;
453da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*show)(void *,char *);
454da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*store)(void *, const char *,size_t);
455da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
456da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
457da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Set of show/store abstract level functions for PCI Parity object */
458da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t edac_pci_dev_show(struct kobject *kobj, struct attribute *attr,
459da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				char *buffer)
460da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
461da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct edac_pci_dev_attribute *edac_pci_dev;
462da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_dev= (struct edac_pci_dev_attribute*)attr;
463da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
464da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_pci_dev->show)
465da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return edac_pci_dev->show(edac_pci_dev->value, buffer);
466da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
467da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
468da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
469da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t edac_pci_dev_store(struct kobject *kobj, struct attribute *attr,
470da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				const char *buffer, size_t count)
471da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
472da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct edac_pci_dev_attribute *edac_pci_dev;
473da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_dev= (struct edac_pci_dev_attribute*)attr;
474da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
475da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_pci_dev->show)
476da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return edac_pci_dev->store(edac_pci_dev->value, buffer, count);
477da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
478da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
479da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
480da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct sysfs_ops edac_pci_sysfs_ops = {
481da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = edac_pci_dev_show,
482da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = edac_pci_dev_store
483da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
484da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
485da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
486da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define EDAC_PCI_ATTR(_name,_mode,_show,_store)			\
487da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct edac_pci_dev_attribute edac_pci_attr_##_name = {		\
488da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.attr = {.name = __stringify(_name), .mode = _mode },	\
489da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.value  = &_name,					\
490da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = _show,					\
491da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = _store,					\
492da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
493da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
494da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define EDAC_PCI_STRING_ATTR(_name,_data,_mode,_show,_store)	\
495da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct edac_pci_dev_attribute edac_pci_attr_##_name = {		\
496da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.attr = {.name = __stringify(_name), .mode = _mode },	\
497da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.value  = _data,					\
498da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = _show,					\
499da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = _store,					\
500da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
501da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
502da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct list_control pci_whitelist_control = {
503da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.list = pci_whitelist,
504da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.count = &pci_whitelist_count
505da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
506da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
507da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct list_control pci_blacklist_control = {
508da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.list = pci_blacklist,
509da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.count = &pci_blacklist_count
510da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
511da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
512da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* whitelist attribute */
513da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEDAC_PCI_STRING_ATTR(pci_parity_whitelist,
514da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&pci_whitelist_control,
515da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	S_IRUGO|S_IWUSR,
516da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_list_string_show,
517da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_list_string_store);
518da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
519da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEDAC_PCI_STRING_ATTR(pci_parity_blacklist,
520da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&pci_blacklist_control,
521da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	S_IRUGO|S_IWUSR,
522da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_list_string_show,
523da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_list_string_store);
524da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
525da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* PCI Parity control files */
526da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEDAC_PCI_ATTR(check_pci_parity,S_IRUGO|S_IWUSR,edac_pci_int_show,edac_pci_int_store);
527da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEDAC_PCI_ATTR(panic_on_pci_parity,S_IRUGO|S_IWUSR,edac_pci_int_show,edac_pci_int_store);
528da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEDAC_PCI_ATTR(pci_parity_count,S_IRUGO,edac_pci_int_show,NULL);
529da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
530da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Base Attributes of the memory ECC object */
531da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct edac_pci_dev_attribute *edac_pci_attr[] = {
532da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&edac_pci_attr_check_pci_parity,
533da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&edac_pci_attr_panic_on_pci_parity,
534da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&edac_pci_attr_pci_parity_count,
535da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&edac_pci_attr_pci_parity_whitelist,
536da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&edac_pci_attr_pci_parity_blacklist,
537da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	NULL,
538da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
539da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
540da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* No memory to release */
541da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_pci_release(struct kobject *kobj)
542da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
543da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf1("EDAC PCI: " __FILE__ ": %s()\n", __func__);
544da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
545da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
546da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct kobj_type ktype_edac_pci = {
547da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.release	= edac_pci_release,
548da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.sysfs_ops	= &edac_pci_sysfs_ops,
549da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.default_attrs	= (struct attribute **) edac_pci_attr,
550da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
551da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
552da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
553da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_sysfs_pci_setup()
554da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
555da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
556da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int edac_sysfs_pci_setup(void)
557da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
558da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int err;
559da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
560da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf1("MC: " __FILE__ ": %s()\n", __func__);
561da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
562da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	memset(&edac_pci_kobj, 0, sizeof(edac_pci_kobj));
563da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
564da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_init(&edac_pci_kobj);
565da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_kobj.parent = &edac_class.kset.kobj;
566da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_kobj.ktype = &ktype_edac_pci;
567da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
568da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	err = kobject_set_name(&edac_pci_kobj, "pci");
569da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!err) {
570da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* Instanstiate the csrow object */
571da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* FIXME: maybe new sysdev_create_subdir() */
572da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		err = kobject_register(&edac_pci_kobj);
573da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (err)
574da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			debugf1("Failed to register '.../edac/pci'\n");
575da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		else
576da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			debugf1("Registered '.../edac/pci' kobject\n");
577da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
578da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return err;
579da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
580da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
581da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
582da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_sysfs_pci_teardown(void)
583da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
584da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s()\n", __func__);
585da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
586da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_unregister(&edac_pci_kobj);
587da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_put(&edac_pci_kobj);
588da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
589da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
590da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* EDAC sysfs CSROW data structures and methods */
591da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
592da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Set of more detailed csrow<id> attribute show/store functions */
593da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ch0_dimm_label_show(struct csrow_info *csrow, char *data)
594da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
595da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t size = 0;
596da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
597da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrow->nr_channels > 0) {
598da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		size = snprintf(data, EDAC_MC_LABEL_LEN,"%s\n",
599da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			csrow->channels[0].label);
600da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
601da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return size;
602da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
603da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
604da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ch1_dimm_label_show(struct csrow_info *csrow, char *data)
605da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
606da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t size = 0;
607da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
608da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrow->nr_channels > 0) {
609da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		size = snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
610da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			csrow->channels[1].label);
611da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
612da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return size;
613da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
614da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
615da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ch0_dimm_label_store(struct csrow_info *csrow,
616da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			const char *data, size_t size)
617da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
618da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t max_size = 0;
619da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
620da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrow->nr_channels > 0) {
621da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		max_size = min((ssize_t)size,(ssize_t)EDAC_MC_LABEL_LEN-1);
622da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		strncpy(csrow->channels[0].label, data, max_size);
623da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->channels[0].label[max_size] = '\0';
624da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
625da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return size;
626da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
627da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
628da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ch1_dimm_label_store(struct csrow_info *csrow,
629da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			const char *data, size_t size)
630da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
631da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t max_size = 0;
632da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
633da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrow->nr_channels > 1) {
634da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		max_size = min((ssize_t)size,(ssize_t)EDAC_MC_LABEL_LEN-1);
635da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		strncpy(csrow->channels[1].label, data, max_size);
636da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->channels[1].label[max_size] = '\0';
637da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
638da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return max_size;
639da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
640da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
641da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data)
642da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
643da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%u\n", csrow->ue_count);
644da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
645da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
646da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data)
647da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
648da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%u\n", csrow->ce_count);
649da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
650da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
651da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ch0_ce_count_show(struct csrow_info *csrow, char *data)
652da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
653da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t size = 0;
654da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
655da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrow->nr_channels > 0) {
656da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		size = sprintf(data,"%u\n", csrow->channels[0].ce_count);
657da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
658da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return size;
659da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
660da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
661da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_ch1_ce_count_show(struct csrow_info *csrow, char *data)
662da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
663da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t size = 0;
664da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
665da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrow->nr_channels > 1) {
666da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		size = sprintf(data,"%u\n", csrow->channels[1].ce_count);
667da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
668da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return size;
669da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
670da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
671da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_size_show(struct csrow_info *csrow, char *data)
672da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
673da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%u\n", PAGES_TO_MiB(csrow->nr_pages));
674da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
675da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
676da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data)
677da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
678da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%s\n", mem_types[csrow->mtype]);
679da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
680da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
681da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data)
682da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
683da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%s\n", dev_types[csrow->dtype]);
684da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
685da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
686da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data)
687da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
688da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%s\n", edac_caps[csrow->edac_mode]);
689da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
690da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
691da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct csrowdev_attribute {
692da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct attribute	attr;
693da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*show)(struct csrow_info *,char *);
694da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*store)(struct csrow_info *, const char *,size_t);
695da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
696da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
697da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define to_csrow(k) container_of(k, struct csrow_info, kobj)
698da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr)
699da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
700da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Set of show/store higher level functions for csrow objects */
701da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrowdev_show(struct kobject *kobj, struct attribute *attr,
702da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				char *buffer)
703da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
704da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrow = to_csrow(kobj);
705da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
706da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
707da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrowdev_attr->show)
708da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return csrowdev_attr->show(csrow, buffer);
709da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
710da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
711da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
712da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr,
713da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				const char *buffer, size_t count)
714da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
715da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrow = to_csrow(kobj);
716da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrowdev_attribute * csrowdev_attr = to_csrowdev_attr(attr);
717da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
718da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (csrowdev_attr->store)
719da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return csrowdev_attr->store(csrow, buffer, count);
720da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
721da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
722da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
723da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct sysfs_ops csrowfs_ops = {
724da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = csrowdev_show,
725da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = csrowdev_store
726da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
727da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
728da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define CSROWDEV_ATTR(_name,_mode,_show,_store)			\
729da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct csrowdev_attribute attr_##_name = {			\
730da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.attr = {.name = __stringify(_name), .mode = _mode },	\
731da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = _show,					\
732da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = _store,					\
733da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
734da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
735da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* cwrow<id>/attribute files */
736da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(size_mb,S_IRUGO,csrow_size_show,NULL);
737da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(dev_type,S_IRUGO,csrow_dev_type_show,NULL);
738da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(mem_type,S_IRUGO,csrow_mem_type_show,NULL);
739da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(edac_mode,S_IRUGO,csrow_edac_mode_show,NULL);
740da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(ue_count,S_IRUGO,csrow_ue_count_show,NULL);
741da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(ce_count,S_IRUGO,csrow_ce_count_show,NULL);
742da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(ch0_ce_count,S_IRUGO,csrow_ch0_ce_count_show,NULL);
743da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(ch1_ce_count,S_IRUGO,csrow_ch1_ce_count_show,NULL);
744da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
745da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* control/attribute files */
746da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(ch0_dimm_label,S_IRUGO|S_IWUSR,
747da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow_ch0_dimm_label_show,
748da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow_ch0_dimm_label_store);
749da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxCSROWDEV_ATTR(ch1_dimm_label,S_IRUGO|S_IWUSR,
750da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow_ch1_dimm_label_show,
751da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow_ch1_dimm_label_store);
752da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
753da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
754da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Attributes of the CSROW<id> object */
755da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct csrowdev_attribute *csrow_attr[] = {
756da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_dev_type,
757da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_mem_type,
758da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_edac_mode,
759da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_size_mb,
760da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_ue_count,
761da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_ce_count,
762da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_ch0_ce_count,
763da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_ch1_ce_count,
764da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_ch0_dimm_label,
765da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&attr_ch1_dimm_label,
766da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	NULL,
767da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
768da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
769da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
770da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* No memory to release */
771da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_csrow_instance_release(struct kobject *kobj)
772da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
773da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf1("EDAC MC: " __FILE__ ": %s()\n", __func__);
774da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
775da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
776da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct kobj_type ktype_csrow = {
777da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.release	= edac_csrow_instance_release,
778da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.sysfs_ops	= &csrowfs_ops,
779da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.default_attrs	= (struct attribute **) csrow_attr,
780da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
781da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
782da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Create a CSROW object under specifed edac_mc_device */
783da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int edac_create_csrow_object(struct kobject *edac_mci_kobj,
784da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				struct csrow_info *csrow, int index )
785da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
786da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int err = 0;
787da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
788da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s()\n", __func__);
789da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
790da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	memset(&csrow->kobj, 0, sizeof(csrow->kobj));
791da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
792da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* generate ..../edac/mc/mc<id>/csrow<index>   */
793da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
794da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_init(&csrow->kobj);
795da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	csrow->kobj.parent = edac_mci_kobj;
796da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	csrow->kobj.ktype = &ktype_csrow;
797da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
798da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* name this instance of csrow<id> */
799da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	err = kobject_set_name(&csrow->kobj,"csrow%d",index);
800da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!err) {
801da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* Instanstiate the csrow object */
802da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		err = kobject_register(&csrow->kobj);
803da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (err)
804da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			debugf0("Failed to register CSROW%d\n",index);
805da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		else
806da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			debugf0("Registered CSROW%d\n",index);
807da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
808da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
809da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return err;
810da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
811da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
812da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* sysfs data structures and methods for the MCI kobjects */
813da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
814da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_reset_counters_store(struct mem_ctl_info  *mci,
815da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					const char *data, size_t count )
816da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
817da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, chan;
818da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
819da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_noinfo_count = 0;
820da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_noinfo_count = 0;
821da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count = 0;
822da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count = 0;
823da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (row = 0; row < mci->nr_csrows; row++) {
824da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		struct csrow_info *ri = &mci->csrows[row];
825da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
826da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		ri->ue_count = 0;
827da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		ri->ce_count = 0;
828da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (chan = 0; chan < ri->nr_channels; chan++)
829da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			ri->channels[chan].ce_count = 0;
830da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
831da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->start_time = jiffies;
832da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
833da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return count;
834da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
835da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
836da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
837da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
838da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%d\n", mci->ue_count);
839da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
840da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
841da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data)
842da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
843da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%d\n", mci->ce_count);
844da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
845da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
846da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data)
847da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
848da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%d\n", mci->ce_noinfo_count);
849da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
850da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
851da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data)
852da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
853da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%d\n", mci->ue_noinfo_count);
854da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
855da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
856da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data)
857da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
858da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%ld\n", (jiffies - mci->start_time) / HZ);
859da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
860da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
861da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_mod_name_show(struct mem_ctl_info *mci, char *data)
862da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
863da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%s %s\n", mci->mod_name, mci->mod_ver);
864da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
865da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
866da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data)
867da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
868da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%s\n", mci->ctl_name);
869da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
870da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
871da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int mci_output_edac_cap(char *buf, unsigned long edac_cap)
872da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
873da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *p = buf;
874da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int bit_idx;
875da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
876da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (bit_idx = 0; bit_idx < 8 * sizeof(edac_cap); bit_idx++) {
877da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if ((edac_cap >> bit_idx) & 0x1)
878da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			p += sprintf(p, "%s ", edac_caps[bit_idx]);
879da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
880da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
881da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return p - buf;
882da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
883da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
884da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_edac_capability_show(struct mem_ctl_info *mci, char *data)
885da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
886da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *p = data;
887da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
888da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	p += mci_output_edac_cap(p,mci->edac_ctl_cap);
889da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	p += sprintf(p, "\n");
890da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
891da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return p - data;
892da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
893da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
894da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_edac_current_capability_show(struct mem_ctl_info *mci,
895da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox						char *data)
896da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
897da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *p = data;
898da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
899da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	p += mci_output_edac_cap(p,mci->edac_cap);
900da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	p += sprintf(p, "\n");
901da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
902da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return p - data;
903da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
904da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
905da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int mci_output_mtype_cap(char *buf, unsigned long mtype_cap)
906da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
907da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *p = buf;
908da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int bit_idx;
909da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
910da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (bit_idx = 0; bit_idx < 8 * sizeof(mtype_cap); bit_idx++) {
911da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if ((mtype_cap >> bit_idx) & 0x1)
912da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			p += sprintf(p, "%s ", mem_types[bit_idx]);
913da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
914da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
915da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return p - buf;
916da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
917da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
918da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_supported_mem_type_show(struct mem_ctl_info *mci, char *data)
919da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
920da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *p = data;
921da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
922da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	p += mci_output_mtype_cap(p,mci->mtype_cap);
923da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	p += sprintf(p, "\n");
924da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
925da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return p - data;
926da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
927da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
928da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
929da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
930da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int total_pages, csrow_idx;
931da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
932da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows;
933da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			csrow_idx++) {
934da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		struct csrow_info *csrow = &mci->csrows[csrow_idx];
935da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
936da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (!csrow->nr_pages)
937da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			continue;
938da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		total_pages += csrow->nr_pages;
939da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
940da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
941da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return sprintf(data,"%u\n", PAGES_TO_MiB(total_pages));
942da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
943da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
944da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct mcidev_attribute {
945da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct attribute	attr;
946da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*show)(struct mem_ctl_info *,char *);
947da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
948da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
949da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
950da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj)
951da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define to_mcidev_attr(a) container_of(a, struct mcidev_attribute, attr)
952da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
953da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr,
954da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			char *buffer)
955da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
956da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
957da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr);
958da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
959da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mcidev_attr->show)
960da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return mcidev_attr->show(mem_ctl_info, buffer);
961da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
962da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
963da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
964da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr,
965da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				const char *buffer, size_t count)
966da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
967da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
968da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr);
969da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
970da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mcidev_attr->store)
971da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return mcidev_attr->store(mem_ctl_info, buffer, count);
972da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return -EIO;
973da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
974da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
975da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct sysfs_ops mci_ops = {
976da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = mcidev_show,
977da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = mcidev_store
978da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
979da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
980da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define MCIDEV_ATTR(_name,_mode,_show,_store)			\
981da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct mcidev_attribute mci_attr_##_name = {			\
982da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.attr = {.name = __stringify(_name), .mode = _mode },	\
983da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.show   = _show,					\
984da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.store  = _store,					\
985da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
986da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
987da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Control file */
988da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(reset_counters,S_IWUSR,NULL,mci_reset_counters_store);
989da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
990da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Attribute files */
991da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(mc_name,S_IRUGO,mci_ctl_name_show,NULL);
992da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(module_name,S_IRUGO,mci_mod_name_show,NULL);
993da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(edac_capability,S_IRUGO,mci_edac_capability_show,NULL);
994da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(size_mb,S_IRUGO,mci_size_mb_show,NULL);
995da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(seconds_since_reset,S_IRUGO,mci_seconds_show,NULL);
996da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(ue_noinfo_count,S_IRUGO,mci_ue_noinfo_show,NULL);
997da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL);
998da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL);
999da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL);
1000da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(edac_current_capability,S_IRUGO,
1001da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci_edac_current_capability_show,NULL);
1002da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMCIDEV_ATTR(supported_mem_type,S_IRUGO,
1003da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci_supported_mem_type_show,NULL);
1004da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1005da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1006da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct mcidev_attribute *mci_attr[] = {
1007da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_reset_counters,
1008da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_module_name,
1009da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_mc_name,
1010da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_edac_capability,
1011da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_edac_current_capability,
1012da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_supported_mem_type,
1013da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_size_mb,
1014da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_seconds_since_reset,
1015da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_ue_noinfo_count,
1016da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_ce_noinfo_count,
1017da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_ue_count,
1018da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	&mci_attr_ce_count,
1019da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	NULL
1020da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
1021da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1022da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1023da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1024da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Release of a MC controlling instance
1025da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1026da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_mci_instance_release(struct kobject *kobj)
1027da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1028da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
1029da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci = container_of(kobj,struct mem_ctl_info,edac_mci_kobj);
1030da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1031da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s() idx=%d calling kfree\n",
1032da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		__func__, mci->mc_idx);
1033da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1034da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kfree(mci);
1035da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1036da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1037da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct kobj_type ktype_mci = {
1038da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.release	= edac_mci_instance_release,
1039da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.sysfs_ops	= &mci_ops,
1040da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	.default_attrs	= (struct attribute **) mci_attr,
1041da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
1042da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1043da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#define EDAC_DEVICE_SYMLINK	"device"
1044da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1045da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1046da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Create a new Memory Controller kobject instance,
1047da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	mc<id> under the 'mc' directory
1048da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1049da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Return:
1050da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	0	Success
1051da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	!0	Failure
1052da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1053da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
1054da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1055da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int i;
1056da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int err;
1057da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrow;
1058da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct kobject *edac_mci_kobj=&mci->edac_mci_kobj;
1059da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1060da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s() idx=%d\n", __func__, mci->mc_idx);
1061da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1062da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	memset(edac_mci_kobj, 0, sizeof(*edac_mci_kobj));
1063da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_init(edac_mci_kobj);
1064da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1065da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* set the name of the mc<id> object */
1066da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	err = kobject_set_name(edac_mci_kobj,"mc%d",mci->mc_idx);
1067da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (err)
1068da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return err;
1069da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1070da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* link to our parent the '..../edac/mc' object */
1071da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_mci_kobj->parent = &edac_memctrl_kobj;
1072da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_mci_kobj->ktype = &ktype_mci;
1073da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1074da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* register the mc<id> kobject */
1075da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	err = kobject_register(edac_mci_kobj);
1076da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (err)
1077da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return err;
1078da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1079da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* create a symlink for the device */
1080da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	err = sysfs_create_link(edac_mci_kobj, &mci->pdev->dev.kobj,
1081da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				EDAC_DEVICE_SYMLINK);
1082da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (err) {
1083da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		kobject_unregister(edac_mci_kobj);
1084da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return err;
1085da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1086da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1087da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Make directories for each CSROW object
1088da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * under the mc<id> kobject
1089da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1090da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < mci->nr_csrows; i++) {
1091da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1092da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow = &mci->csrows[i];
1093da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1094da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* Only expose populated CSROWs */
1095da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (csrow->nr_pages > 0) {
1096da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			err = edac_create_csrow_object(edac_mci_kobj,csrow,i);
1097da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			if (err)
1098da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				goto fail;
1099da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1100da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1101da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1102da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Mark this MCI instance as having sysfs entries */
1103da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->sysfs_active = MCI_SYSFS_ACTIVE;
1104da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1105da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
1106da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1107da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1108da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* CSROW error: backout what has already been registered,  */
1109da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxfail:
1110da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for ( i--; i >= 0; i--) {
1111da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (csrow->nr_pages > 0) {
1112da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			kobject_unregister(&mci->csrows[i].kobj);
1113da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			kobject_put(&mci->csrows[i].kobj);
1114da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1115da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1116da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1117da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_unregister(edac_mci_kobj);
1118da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_put(edac_mci_kobj);
1119da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1120da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return err;
1121da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1122da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1123da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1124da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * remove a Memory Controller instance
1125da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1126da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
1127da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1128da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int i;
1129da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1130da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s()\n", __func__);
1131da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1132da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* remove all csrow kobjects */
1133da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < mci->nr_csrows; i++) {
1134da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (mci->csrows[i].nr_pages > 0)  {
1135da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			kobject_unregister(&mci->csrows[i].kobj);
1136da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			kobject_put(&mci->csrows[i].kobj);
1137da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1138da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1139da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1140da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
1141da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1142da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_unregister(&mci->edac_mci_kobj);
1143da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kobject_put(&mci->edac_mci_kobj);
1144da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1145da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1146da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* END OF sysfs data and methods */
1147da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1148da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
1149da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1150da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_dump_channel);
1151da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1152da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_dump_channel(struct channel_info *chan)
1153da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1154da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel = %p\n", chan);
1155da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
1156da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->ce_count = %d\n", chan->ce_count);
1157da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->label = '%s'\n", chan->label);
1158da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
1159da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1160da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1161da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1162da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_dump_csrow);
1163da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1164da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_dump_csrow(struct csrow_info *csrow)
1165da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1166da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow = %p\n", csrow);
1167da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx);
1168da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->first_page = 0x%lx\n",
1169da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->first_page);
1170da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
1171da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
1172da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->nr_pages = 0x%x\n", csrow->nr_pages);
1173da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->nr_channels = %d\n",
1174da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->nr_channels);
1175da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->channels = %p\n", csrow->channels);
1176da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
1177da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1178da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1179da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1180da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_dump_mci);
1181da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1182da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_dump_mci(struct mem_ctl_info *mci)
1183da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1184da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci = %p\n", mci);
1185da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->mtype_cap = %lx\n", mci->mtype_cap);
1186da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
1187da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->edac_cap = %lx\n", mci->edac_cap);
1188da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf4("\tmci->edac_check = %p\n", mci->edac_check);
1189da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmci->nr_csrows = %d, csrows = %p\n",
1190da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci->nr_csrows, mci->csrows);
1191da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tpdev = %p\n", mci->pdev);
1192da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tmod_name:ctl_name = %s:%s\n",
1193da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci->mod_name, mci->ctl_name);
1194da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
1195da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1196da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1197da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1198da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif				/* CONFIG_EDAC_DEBUG */
1199da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1200da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
1201da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Adjust 'ptr' so that its alignment is at least as stringent as what the
1202da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * compiler would provide for X and return the aligned result.
1203da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1204da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * If 'size' is a constant, the compiler will optimize this whole function
1205da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * down to either a no-op or the addition of a constant to the value of 'ptr'.
1206da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1207da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic inline char * align_ptr (void *ptr, unsigned size)
1208da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1209da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned align, r;
1210da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1211da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Here we assume that the alignment of a "long long" is the most
1212da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * stringent alignment that the compiler will ever provide by default.
1213da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * As far as I know, this is a reasonable assumption.
1214da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1215da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (size > sizeof(long))
1216da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(long long);
1217da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(int))
1218da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(long);
1219da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(short))
1220da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(int);
1221da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else if (size > sizeof(char))
1222da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		align = sizeof(short);
1223da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else
1224da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return (char *) ptr;
1225da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1226da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	r = size % align;
1227da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1228da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (r == 0)
1229da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return (char *) ptr;
1230da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1231da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return (char *) (((unsigned long) ptr) + align - r);
1232da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1233da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1234da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1235da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_alloc);
1236da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1237da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
1238da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_alloc: Allocate a struct mem_ctl_info structure
1239da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @size_pvt:	size of private storage needed
1240da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @nr_csrows:	Number of CWROWS needed for this MC
1241da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @nr_chans:	Number of channels for the MC
1242da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1243da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Everything is kmalloc'ed as one big chunk - more efficient.
1244da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Only can be used if all structures have the same lifetime - otherwise
1245da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * you have to allocate and initialize your own structures.
1246da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1247da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Use edac_mc_free() to free mc structures allocated by this function.
1248da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1249da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Returns:
1250da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	NULL allocation failed
1251da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	struct mem_ctl_info pointer
1252da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1253da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
1254da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					unsigned nr_chans)
1255da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1256da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
1257da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csi, *csrow;
1258da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct channel_info *chi, *chp, *chan;
1259da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *pvt;
1260da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned size;
1261da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, chn;
1262da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1263da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Figure out the offsets of the various items from the start of an mc
1264da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * structure.  We want the alignment of each item to be at least as
1265da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * stringent as what the compiler would provide if we could simply
1266da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * hardcode everything into a single struct.
1267da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1268da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci = (struct mem_ctl_info *) 0;
1269da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	csi = (struct csrow_info *)align_ptr(&mci[1], sizeof(*csi));
1270da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	chi = (struct channel_info *)
1271da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			align_ptr(&csi[nr_csrows], sizeof(*chi));
1272da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pvt = align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
1273da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	size = ((unsigned long) pvt) + sz_pvt;
1274da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1275da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if ((mci = kmalloc(size, GFP_KERNEL)) == NULL)
1276da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return NULL;
1277da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1278da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Adjust pointers so they point within the memory we just allocated
1279da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * rather than an imaginary chunk of memory located at address 0.
1280da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1281da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	csi = (struct csrow_info *) (((char *) mci) + ((unsigned long) csi));
1282da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	chi = (struct channel_info *) (((char *) mci) + ((unsigned long) chi));
1283da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pvt = sz_pvt ? (((char *) mci) + ((unsigned long) pvt)) : NULL;
1284da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1285da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	memset(mci, 0, size);	/* clear all fields */
1286da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1287da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows = csi;
1288da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->pvt_info = pvt;
1289da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->nr_csrows = nr_csrows;
1290da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1291da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (row = 0; row < nr_csrows; row++) {
1292da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow = &csi[row];
1293da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->csrow_idx = row;
1294da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->mci = mci;
1295da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->nr_channels = nr_chans;
1296da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chp = &chi[row * nr_chans];
1297da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		csrow->channels = chp;
1298da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1299da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (chn = 0; chn < nr_chans; chn++) {
1300da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan = &chp[chn];
1301da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan->chan_idx = chn;
1302da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			chan->csrow = csrow;
1303da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1304da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1305da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1306da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return mci;
1307da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1308da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1309da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1310da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_free);
1311da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1312da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
1313da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_free:  Free a previously allocated 'mci' structure
1314da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to a struct mem_ctl_info structure
1315da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1316da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Free up a previously allocated mci structure
1317da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * A MCI structure can be in 2 states after being allocated
1318da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * by edac_mc_alloc().
1319da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	1) Allocated in a MC driver's probe, but not yet committed
1320da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	2) Allocated and committed, by a call to  edac_mc_add_mc()
1321da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_add_mc() is the function that adds the sysfs entries
1322da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * thus, this free function must determine which state the 'mci'
1323da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * structure is in, then either free it directly or
1324da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * perform kobject cleanup by calling edac_remove_sysfs_mci_device().
1325da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1326da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * VOID Return
1327da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1328da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_free(struct mem_ctl_info *mci)
1329da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1330da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* only if sysfs entries for this mci instance exist
1331da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * do we remove them and defer the actual kfree via
1332da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * the kobject 'release()' callback.
1333da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox 	 *
1334da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * Otherwise, do a straight kfree now.
1335da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1336da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mci->sysfs_active == MCI_SYSFS_ACTIVE)
1337da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_remove_sysfs_mci_device(mci);
1338da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	else
1339da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		kfree(mci);
1340da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1341da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1342da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1343da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1344da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_find_mci_by_pdev);
1345da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1346da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct mem_ctl_info *edac_mc_find_mci_by_pdev(struct pci_dev *pdev)
1347da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1348da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
1349da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item;
1350da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1351da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC: " __FILE__ ": %s()\n", __func__);
1352da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1353da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_for_each(item, &mc_devices) {
1354da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci = list_entry(item, struct mem_ctl_info, link);
1355da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1356da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (mci->pdev == pdev)
1357da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			return mci;
1358da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1359da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1360da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return NULL;
1361da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1362da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1363da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int add_mc_to_global_list (struct mem_ctl_info *mci)
1364da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1365da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item, *insert_before;
1366da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *p;
1367da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int i;
1368da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1369da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (list_empty(&mc_devices)) {
1370da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci->mc_idx = 0;
1371da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		insert_before = &mc_devices;
1372da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	} else {
1373da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (edac_mc_find_mci_by_pdev(mci->pdev)) {
1374da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			printk(KERN_WARNING
1375da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				"EDAC MC: %s (%s) %s %s already assigned %d\n",
1376da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				mci->pdev->dev.bus_id, pci_name(mci->pdev),
1377da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				mci->mod_name, mci->ctl_name, mci->mc_idx);
1378da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			return 1;
1379da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1380da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1381da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		insert_before = NULL;
1382da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		i = 0;
1383da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1384da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		list_for_each(item, &mc_devices) {
1385da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			p = list_entry(item, struct mem_ctl_info, link);
1386da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1387da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			if (p->mc_idx != i) {
1388da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				insert_before = item;
1389da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				break;
1390da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			}
1391da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1392da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			i++;
1393da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1394da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1395da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci->mc_idx = i;
1396da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1397da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (insert_before == NULL)
1398da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			insert_before = &mc_devices;
1399da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1400da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1401da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_add_tail_rcu(&mci->link, insert_before);
1402da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
1403da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1404da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1405da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1406da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1407da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_add_mc);
1408da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1409da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
1410da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_add_mc: Insert the 'mci' structure into the mci global list
1411da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci: pointer to the mci structure to be added to the list
1412da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1413da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Return:
1414da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	0	Success
1415da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	!0	Failure
1416da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1417da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1418da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should a warning be printed if no error detection? correction? */
1419da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxint edac_mc_add_mc(struct mem_ctl_info *mci)
1420da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1421da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int rc = 1;
1422da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1423da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s()\n", __func__);
1424da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
1425da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 3)
1426da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_dump_mci(mci);
1427da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_debug_level >= 4) {
1428da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		int i;
1429da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1430da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		for (i = 0; i < mci->nr_csrows; i++) {
1431da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			int j;
1432da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			edac_mc_dump_csrow(&mci->csrows[i]);
1433da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			for (j = 0; j < mci->csrows[i].nr_channels; j++)
1434da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				edac_mc_dump_channel(&mci->csrows[i].
1435da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox							  channels[j]);
1436da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1437da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1438da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif
1439da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	down(&mem_ctls_mutex);
1440da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1441da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (add_mc_to_global_list(mci))
1442da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		goto finish;
1443da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1444da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* set load time so that error rate can be tracked */
1445da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->start_time = jiffies;
1446da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1447da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        if (edac_create_sysfs_mci_device(mci)) {
1448da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                printk(KERN_WARNING
1449da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                       "EDAC MC%d: failed to create sysfs device\n",
1450da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                       mci->mc_idx);
1451da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* FIXME - should there be an error code and unwind? */
1452da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                goto finish;
1453da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        }
1454da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1455da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Report action taken */
1456da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	printk(KERN_INFO
1457da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	       "EDAC MC%d: Giving out device to %s %s: PCI %s\n",
1458da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	       mci->mc_idx, mci->mod_name, mci->ctl_name,
1459da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	       pci_name(mci->pdev));
1460da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1461da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1462da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	rc = 0;
1463da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1464da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxfinish:
1465da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	up(&mem_ctls_mutex);
1466da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return rc;
1467da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1468da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1469da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1470da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1471da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void complete_mc_list_del (struct rcu_head *head)
1472da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1473da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
1474da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1475da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci = container_of(head, struct mem_ctl_info, rcu);
1476da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	INIT_LIST_HEAD(&mci->link);
1477da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	complete(&mci->complete);
1478da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1479da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1480da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void del_mc_from_global_list (struct mem_ctl_info *mci)
1481da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1482da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_del_rcu(&mci->link);
1483da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	init_completion(&mci->complete);
1484da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	call_rcu(&mci->rcu, complete_mc_list_del);
1485da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	wait_for_completion(&mci->complete);
1486da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1487da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1488da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_del_mc);
1489da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1490da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/**
1491da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_del_mc:  Remove the specified mci structure from global list
1492da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @mci:	Pointer to struct mem_ctl_info structure
1493da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1494da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Returns:
1495da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	0	Success
1496da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	1 	Failure
1497da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1498da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxint edac_mc_del_mc(struct mem_ctl_info *mci)
1499da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1500da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int rc = 1;
1501da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1502da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC%d: " __FILE__ ": %s()\n", mci->mc_idx, __func__);
1503da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	down(&mem_ctls_mutex);
1504da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	del_mc_from_global_list(mci);
1505da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	printk(KERN_INFO
1506da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	       "EDAC MC%d: Removed device %d for %s %s: PCI %s\n",
1507da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	       mci->mc_idx, mci->mc_idx, mci->mod_name, mci->ctl_name,
1508da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	       pci_name(mci->pdev));
1509da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	rc = 0;
1510da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	up(&mem_ctls_mutex);
1511da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1512da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return rc;
1513da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1514da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1515da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1516da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_scrub_block);
1517da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1518da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_scrub_block(unsigned long page, unsigned long offset,
1519da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			      u32 size)
1520da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1521da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct page *pg;
1522da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void *virt_addr;
1523da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long flags = 0;
1524da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1525da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC: " __FILE__ ": %s()\n", __func__);
1526da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1527da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* ECC error page was not in our memory. Ignore it. */
1528da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if(!pfn_valid(page))
1529da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
1530da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1531da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Find the actual page structure then map it and fix */
1532da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pg = pfn_to_page(page);
1533da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1534da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
1535da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_save(flags);
1536da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1537da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	virt_addr = kmap_atomic(pg, KM_BOUNCE_READ);
1538da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1539da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Perform architecture specific atomic scrub operation */
1540da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	atomic_scrub(virt_addr + offset, size);
1541da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1542da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Unmap and complete */
1543da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	kunmap_atomic(virt_addr, KM_BOUNCE_READ);
1544da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1545da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (PageHighMem(pg))
1546da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		local_irq_restore(flags);
1547da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1548da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1549da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1550da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - should return -1 */
1551da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_find_csrow_by_page);
1552da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1553da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxint edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
1554da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				    unsigned long page)
1555da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1556da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct csrow_info *csrows = mci->csrows;
1557da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int row, i;
1558da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1559da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf1("MC%d: " __FILE__ ": %s(): 0x%lx\n", mci->mc_idx, __func__,
1560da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		page);
1561da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	row = -1;
1562da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1563da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (i = 0; i < mci->nr_csrows; i++) {
1564da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		struct csrow_info *csrow = &csrows[i];
1565da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1566da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (csrow->nr_pages == 0)
1567da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			continue;
1568da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1569da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		debugf3("MC%d: " __FILE__
1570da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			": %s(): first(0x%lx) page(0x%lx)"
1571da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			" last(0x%lx) mask(0x%lx)\n", mci->mc_idx,
1572da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			__func__, csrow->first_page, page,
1573da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			csrow->last_page, csrow->page_mask);
1574da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1575da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if ((page >= csrow->first_page) &&
1576da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    (page <= csrow->last_page) &&
1577da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    ((page & csrow->page_mask) ==
1578da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     (csrow->first_page & csrow->page_mask))) {
1579da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			row = i;
1580da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			break;
1581da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1582da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1583da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1584da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row == -1)
1585da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_ERR
1586da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: could not look up page error address %lx\n",
1587da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->mc_idx, (unsigned long) page);
1588da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1589da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return row;
1590da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1591da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1592da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1593da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_handle_ce);
1594da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1595da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - setable log (warning/emerg) levels */
1596da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
1597da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ce(struct mem_ctl_info *mci,
1598da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    unsigned long page_frame_number,
1599da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    unsigned long offset_in_page,
1600da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    unsigned long syndrome, int row, int channel,
1601da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    const char *msg)
1602da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1603da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long remapped_page;
1604da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1605da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC%d: " __FILE__ ": %s()\n", mci->mc_idx, __func__);
1606da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1607da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
1608da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
1609da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
1610da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_ERR
1611da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: INTERNAL ERROR: row out of range (%d >= %d)\n",
1612da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->mc_idx, row, mci->nr_csrows);
1613da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
1614da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
1615da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1616da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (channel >= mci->csrows[row].nr_channels || channel < 0) {
1617da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
1618da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_ERR
1619da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: INTERNAL ERROR: channel out of range "
1620da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "(%d >= %d)\n",
1621da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->mc_idx, channel, mci->csrows[row].nr_channels);
1622da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
1623da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
1624da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1625da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1626da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (log_ce)
1627da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* FIXME - put in DIMM location */
1628da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_WARNING
1629da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: CE page 0x%lx, offset 0x%lx,"
1630da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       " grain %d, syndrome 0x%lx, row %d, channel %d,"
1631da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       " label \"%s\": %s\n", mci->mc_idx,
1632da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       page_frame_number, offset_in_page,
1633da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->csrows[row].grain, syndrome, row, channel,
1634da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->csrows[row].channels[channel].label, msg);
1635da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1636da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
1637da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ce_count++;
1638da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].channels[channel].ce_count++;
1639da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1640da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (mci->scrub_mode & SCRUB_SW_SRC) {
1641da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/*
1642da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * Some MC's can remap memory so that it is still available
1643da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * at a different address when PCI devices map into memory.
1644da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * MC's that can't do this lose the memory where PCI devices
1645da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * are mapped.  This mapping is MC dependant and so we call
1646da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * back into the MC driver for it to map the MC page to
1647da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * a physical (CPU) page which can then be mapped to a virtual
1648da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 * page - which can then be scrubbed.
1649da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		 */
1650da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		remapped_page = mci->ctl_page_to_phys ?
1651da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    mci->ctl_page_to_phys(mci, page_frame_number) :
1652da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    page_frame_number;
1653da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1654da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_scrub_block(remapped_page, offset_in_page,
1655da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					 mci->csrows[row].grain);
1656da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1657da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1658da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1659da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1660da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_handle_ce_no_info);
1661da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1662da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ce_no_info(struct mem_ctl_info *mci,
1663da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				    const char *msg)
1664da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1665da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (log_ce)
1666da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_WARNING
1667da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: CE - no information available: %s\n",
1668da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->mc_idx, msg);
1669da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_noinfo_count++;
1670da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ce_count++;
1671da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1672da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1673da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1674da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_handle_ue);
1675da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1676da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ue(struct mem_ctl_info *mci,
1677da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    unsigned long page_frame_number,
1678da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    unsigned long offset_in_page, int row,
1679da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			    const char *msg)
1680da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1681da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int len = EDAC_MC_LABEL_LEN * 4;
1682da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char labels[len + 1];
1683da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *pos = labels;
1684da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chan;
1685da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int chars;
1686da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1687da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC%d: " __FILE__ ": %s()\n", mci->mc_idx, __func__);
1688da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1689da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* FIXME - maybe make panic on INTERNAL ERROR an option */
1690da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (row >= mci->nr_csrows || row < 0) {
1691da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* something is wrong */
1692da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_ERR
1693da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: INTERNAL ERROR: row out of range (%d >= %d)\n",
1694da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->mc_idx, row, mci->nr_csrows);
1695da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
1696da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
1697da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1698da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1699da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	chars = snprintf(pos, len + 1, "%s",
1700da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			 mci->csrows[row].channels[0].label);
1701da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	len -= chars;
1702da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pos += chars;
1703da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
1704da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	     chan++) {
1705da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		chars = snprintf(pos, len + 1, ":%s",
1706da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				 mci->csrows[row].channels[chan].label);
1707da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		len -= chars;
1708da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		pos += chars;
1709da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1710da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1711da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (log_ue)
1712da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_EMERG
1713da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, row %d,"
1714da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       " labels \"%s\": %s\n", mci->mc_idx,
1715da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       page_frame_number, offset_in_page,
1716da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->csrows[row].grain, row, labels, msg);
1717da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1718da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (panic_on_ue)
1719da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		panic
1720da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		    ("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, row %d,"
1721da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     " labels \"%s\": %s\n", mci->mc_idx,
1722da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     page_frame_number, offset_in_page,
1723da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		     mci->csrows[row].grain, row, labels, msg);
1724da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1725da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
1726da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->csrows[row].ue_count++;
1727da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1728da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1729da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1730da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxEXPORT_SYMBOL(edac_mc_handle_ue_no_info);
1731da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1732da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxvoid edac_mc_handle_ue_no_info(struct mem_ctl_info *mci,
1733da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				    const char *msg)
1734da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1735da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (panic_on_ue)
1736da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
1737da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1738da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (log_ue)
1739da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_WARNING
1740da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       "EDAC MC%d: UE - no information available: %s\n",
1741da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		       mci->mc_idx, msg);
1742da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_noinfo_count++;
1743da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	mci->ue_count++;
1744da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1745da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1746da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1747da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_PCI
1748da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1749da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic u16 get_pci_parity_status(struct pci_dev *dev, int secondary)
1750da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1751da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int where;
1752da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	u16 status;
1753da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1754da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	where = secondary ? PCI_SEC_STATUS : PCI_STATUS;
1755da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pci_read_config_word(dev, where, &status);
1756da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1757da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* If we get back 0xFFFF then we must suspect that the card has been pulled but
1758da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	   the Linux PCI layer has not yet finished cleaning up. We don't want to report
1759da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	   on such devices */
1760da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1761da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (status == 0xFFFF) {
1762da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		u32 sanity;
1763da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		pci_read_config_dword(dev, 0, &sanity);
1764da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (sanity == 0xFFFFFFFF)
1765da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			return 0;
1766da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1767da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	status &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR |
1768da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		  PCI_STATUS_PARITY;
1769da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1770da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (status)
1771da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* reset only the bits we are interested in */
1772da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		pci_write_config_word(dev, where, status);
1773da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1774da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return status;
1775da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1776da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1777da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxtypedef void (*pci_parity_check_fn_t) (struct pci_dev *dev);
1778da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1779da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/* Clear any PCI parity errors logged by this device. */
1780da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_pci_dev_parity_clear( struct pci_dev *dev )
1781da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1782da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	u8 header_type;
1783da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1784da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	get_pci_parity_status(dev, 0);
1785da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1786da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* read the device TYPE, looking for bridges */
1787da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type);
1788da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1789da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE)
1790da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		get_pci_parity_status(dev, 1);
1791da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1792da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1793da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1794da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *  PCI Parity polling
1795da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1796da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1797da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void edac_pci_dev_parity_test(struct pci_dev *dev)
1798da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1799da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	u16 status;
1800da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	u8  header_type;
1801da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1802da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* read the STATUS register on this device
1803da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1804da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	status = get_pci_parity_status(dev, 0);
1805da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1806da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf2("PCI STATUS= 0x%04x %s\n", status, dev->dev.bus_id );
1807da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1808da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* check the status reg for errors */
1809da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (status) {
1810da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (status & (PCI_STATUS_SIG_SYSTEM_ERROR))
1811da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			printk(KERN_CRIT
1812da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			   	"EDAC PCI- "
1813da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				"Signaled System Error on %s\n",
1814da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				pci_name (dev));
1815da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1816da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (status & (PCI_STATUS_PARITY)) {
1817da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			printk(KERN_CRIT
1818da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			   	"EDAC PCI- "
1819da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				"Master Data Parity Error on %s\n",
1820da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				pci_name (dev));
1821da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1822da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			atomic_inc(&pci_parity_count);
1823da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1824da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1825da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (status & (PCI_STATUS_DETECTED_PARITY)) {
1826da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			printk(KERN_CRIT
1827da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			   	"EDAC PCI- "
1828da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				"Detected Parity Error on %s\n",
1829da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				pci_name (dev));
1830da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1831da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			atomic_inc(&pci_parity_count);
1832da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1833da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1834da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1835da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* read the device TYPE, looking for bridges */
1836da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type);
1837da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1838da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf2("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev->dev.bus_id );
1839da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1840da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
1841da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* On bridges, need to examine secondary status register  */
1842da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		status = get_pci_parity_status(dev, 1);
1843da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1844da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		debugf2("PCI SEC_STATUS= 0x%04x %s\n",
1845da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				status, dev->dev.bus_id );
1846da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1847da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* check the secondary status reg for errors */
1848da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (status) {
1849da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			if (status & (PCI_STATUS_SIG_SYSTEM_ERROR))
1850da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				printk(KERN_CRIT
1851da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					"EDAC PCI-Bridge- "
1852da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					"Signaled System Error on %s\n",
1853da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					pci_name (dev));
1854da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1855da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			if (status & (PCI_STATUS_PARITY)) {
1856da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				printk(KERN_CRIT
1857da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					"EDAC PCI-Bridge- "
1858da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					"Master Data Parity Error on %s\n",
1859da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					pci_name (dev));
1860da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1861da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				atomic_inc(&pci_parity_count);
1862da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			}
1863da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1864da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			if (status & (PCI_STATUS_DETECTED_PARITY)) {
1865da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				printk(KERN_CRIT
1866da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					"EDAC PCI-Bridge- "
1867da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					"Detected Parity Error on %s\n",
1868da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					pci_name (dev));
1869da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1870da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				atomic_inc(&pci_parity_count);
1871da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			}
1872da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		}
1873da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1874da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1875da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1876da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1877da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * check_dev_on_list: Scan for a PCI device on a white/black list
1878da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @list:	an EDAC  &edac_pci_device_list  white/black list pointer
1879da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @free_index:	index of next free entry on the list
1880da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * @pci_dev:	PCI Device pointer
1881da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1882da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * see if list contains the device.
1883da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
1884da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Returns:  	0 not found
1885da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *		1 found on list
1886da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1887da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int check_dev_on_list(struct edac_pci_device_list *list, int free_index,
1888da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				struct pci_dev *dev)
1889da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1890da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        int i;
1891da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        int rc = 0;     /* Assume not found */
1892da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        unsigned short vendor=dev->vendor;
1893da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        unsigned short device=dev->device;
1894da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1895da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        /* Scan the list, looking for a vendor/device match
1896da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox         */
1897da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        for (i = 0; i < free_index; i++, list++ ) {
1898da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                if (    (list->vendor == vendor ) &&
1899da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                        (list->device == device )) {
1900da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                        rc = 1;
1901da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                        break;
1902da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                }
1903da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        }
1904da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1905da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        return rc;
1906da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1907da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1908da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1909da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * pci_dev parity list iterator
1910da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * 	Scan the PCI device list for one iteration, looking for SERRORs
1911da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *	Master Parity ERRORS or Parity ERRORs on primary or secondary devices
1912da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1913da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic inline void edac_pci_dev_parity_iterator(pci_parity_check_fn_t fn)
1914da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1915da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct pci_dev *dev=NULL;
1916da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1917da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* request for kernel access to the next PCI device, if any,
1918da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * and while we are looking at it have its reference count
1919da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * bumped until we are done with it
1920da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1921da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	while((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
1922da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1923da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                /* if whitelist exists then it has priority, so only scan those
1924da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                 * devices on the whitelist
1925da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                 */
1926da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                if (pci_whitelist_count > 0 ) {
1927da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                        if (check_dev_on_list(pci_whitelist,
1928da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					pci_whitelist_count, dev))
1929da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				fn(dev);
1930da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                } else {
1931da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			/*
1932da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			 * if no whitelist, then check if this devices is
1933da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			 * blacklisted
1934da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			 */
1935da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                        if (!check_dev_on_list(pci_blacklist,
1936da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox					pci_blacklist_count, dev))
1937da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox				fn(dev);
1938da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox                }
1939da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1940da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1941da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1942da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void do_pci_parity_check(void)
1943da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1944da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long flags;
1945da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int before_count;
1946da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1947da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC: " __FILE__ ": %s()\n", __func__);
1948da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1949da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (!check_pci_parity)
1950da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return;
1951da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1952da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	before_count = atomic_read(&pci_parity_count);
1953da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1954da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* scan all PCI devices looking for a Parity Error on devices and
1955da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * bridges
1956da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1957da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	local_irq_save(flags);
1958da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_dev_parity_iterator(edac_pci_dev_parity_test);
1959da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	local_irq_restore(flags);
1960da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1961da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Only if operator has selected panic on PCI Error */
1962da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (panic_on_pci_parity) {
1963da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* If the count is different 'after' from 'before' */
1964da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (before_count != atomic_read(&pci_parity_count))
1965da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			panic("EDAC: PCI Parity Error");
1966da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
1967da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1968da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1969da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1970da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic inline void clear_pci_parity_errors(void)
1971da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1972da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Clear any PCI bus parity errors that devices initially have logged
1973da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * in their registers.
1974da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
1975da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_pci_dev_parity_iterator(edac_pci_dev_parity_clear);
1976da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1977da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1978da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1979da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#else  /* CONFIG_PCI */
1980da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1981da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1982da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic inline void do_pci_parity_check(void)
1983da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1984da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* no-op */
1985da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1986da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1987da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1988da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic inline void clear_pci_parity_errors(void)
1989da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
1990da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* no-op */
1991da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
1992da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1993da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1994da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif  /* CONFIG_PCI */
1995da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
1996da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
1997da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Iterate over all MC instances and check for ECC, et al, errors
1998da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
1999da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic inline void check_mc_devices (void)
2000da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2001da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	unsigned long flags;
2002da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct list_head *item;
2003da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct mem_ctl_info *mci;
2004da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2005da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC: " __FILE__ ": %s()\n", __func__);
2006da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2007da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* during poll, have interrupts off */
2008da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	local_irq_save(flags);
2009da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2010da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	list_for_each(item, &mc_devices) {
2011da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		mci = list_entry(item, struct mem_ctl_info, link);
2012da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2013da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (mci->edac_check != NULL)
2014da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			mci->edac_check(mci);
2015da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
2016da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2017da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	local_irq_restore(flags);
2018da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2019da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2020da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2021da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2022da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Check MC status every poll_msec.
2023da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * Check PCI status every poll_msec as well.
2024da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
2025da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * This where the work gets done for edac.
2026da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *
2027da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * SMP safe, doesn't use NMI, and auto-rate-limits.
2028da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
2029da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void do_edac_check(void)
2030da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2031da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2032da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf3("MC: " __FILE__ ": %s()\n", __func__);
2033da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2034da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	check_mc_devices();
2035da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2036da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	do_pci_parity_check();
2037da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2038da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2039da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2040da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2041da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * EDAC thread state information
2042da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
2043da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstruct bs_thread_info
2044da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2045da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct task_struct *task;
2046da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct completion *event;
2047da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	char *name;
2048da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	void (*run)(void);
2049da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox};
2050da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2051da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic struct bs_thread_info bs_thread;
2052da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2053da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2054da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *  edac_kernel_thread
2055da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *      This the kernel thread that processes edac operations
2056da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *      in a normal thread environment
2057da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
2058da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int edac_kernel_thread(void *arg)
2059da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2060da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct bs_thread_info *thread = (struct bs_thread_info *) arg;
2061da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2062da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* detach thread */
2063da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	daemonize(thread->name);
2064da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2065da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	current->exit_signal = SIGCHLD;
2066da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	allow_signal(SIGKILL);
2067da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	thread->task = current;
2068da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2069da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* indicate to starting task we have started */
2070da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	complete(thread->event);
2071da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2072da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* loop forever, until we are told to stop */
2073da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	while(thread->run != NULL) {
2074da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		void (*run)(void);
2075da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2076da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* call the function to check the memory controllers */
2077da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		run = thread->run;
2078da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (run)
2079da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			run();
2080da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2081da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		if (signal_pending(current))
2082da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox			flush_signals(current);
2083da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2084da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* ensure we are interruptable */
2085da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		set_current_state(TASK_INTERRUPTIBLE);
2086da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2087da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* goto sleep for the interval */
2088da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		schedule_timeout((HZ * poll_msec) / 1000);
2089da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		try_to_freeze();
2090da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
2091da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2092da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* notify waiter that we are exiting */
2093da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	complete(thread->event);
2094da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2095da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
2096da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2097da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2098da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2099da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_init
2100da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *      module initialization entry point
2101da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
2102da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int __init edac_mc_init(void)
2103da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2104da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	int ret;
2105da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct completion event;
2106da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2107da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	printk(KERN_INFO "MC: " __FILE__ " version " EDAC_MC_VERSION "\n");
2108da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2109da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/*
2110da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * Harvest and clear any boot/initialization PCI parity errors
2111da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 *
2112da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * FIXME: This only clears errors logged by devices present at time of
2113da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * 	module initialization.  We should also do an initial clear
2114da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 *	of each newly hotplugged device.
2115da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
2116da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	clear_pci_parity_errors();
2117da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2118da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* perform check for first time to harvest boot leftovers */
2119da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	do_edac_check();
2120da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2121da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Create the MC sysfs entires */
2122da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_sysfs_memctrl_setup()) {
2123da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_ERR "EDAC MC: Error initializing sysfs code\n");
2124da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return -ENODEV;
2125da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
2126da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2127da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Create the PCI parity sysfs entries */
2128da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (edac_sysfs_pci_setup()) {
2129da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_sysfs_memctrl_teardown();
2130da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		printk(KERN_ERR "EDAC PCI: Error initializing sysfs code\n");
2131da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return -ENODEV;
2132da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
2133da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2134da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* Create our kernel thread */
2135da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	init_completion(&event);
2136da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	bs_thread.event = &event;
2137da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	bs_thread.name = "kedac";
2138da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	bs_thread.run = do_edac_check;
2139da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2140da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* create our kernel thread */
2141da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	ret = kernel_thread(edac_kernel_thread, &bs_thread, CLONE_KERNEL);
2142da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	if (ret < 0) {
2143da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		/* remove the sysfs entries */
2144da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_sysfs_memctrl_teardown();
2145da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		edac_sysfs_pci_teardown();
2146da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox		return -ENOMEM;
2147da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	}
2148da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2149da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* wait for our kernel theard ack that it is up and running */
2150da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	wait_for_completion(&event);
2151da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2152da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	return 0;
2153da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2154da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2155da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2156da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox/*
2157da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox * edac_mc_exit()
2158da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox *      module exit/termination functioni
2159da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox */
2160da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic void __exit edac_mc_exit(void)
2161da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox{
2162da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	struct completion event;
2163da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2164da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	debugf0("MC: " __FILE__ ": %s()\n", __func__);
2165da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2166da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	init_completion(&event);
2167da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	bs_thread.event = &event;
2168da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2169da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	/* As soon as ->run is set to NULL, the task could disappear,
2170da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 * so we need to hold tasklist_lock until we have sent the signal
2171da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	 */
2172da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	read_lock(&tasklist_lock);
2173da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	bs_thread.run = NULL;
2174da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	send_sig(SIGKILL, bs_thread.task, 1);
2175da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	read_unlock(&tasklist_lock);
2176da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	wait_for_completion(&event);
2177da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2178da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox        /* tear down the sysfs device */
2179da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_sysfs_memctrl_teardown();
2180da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	edac_sysfs_pci_teardown();
2181da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox}
2182da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2183da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2184da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2185da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2186da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_init(edac_mc_init);
2187da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_exit(edac_mc_exit);
2188da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2189da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_LICENSE("GPL");
2190da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh et al\n"
2191da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox	      "Based on.work by Dan Hollis et al");
2192da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_DESCRIPTION("Core library routines for MC reporting");
2193da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox
2194da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(panic_on_ue, int, 0644);
2195da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
2196da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(check_pci_parity, int, 0644);
2197da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(check_pci_parity, "Check for PCI bus parity errors: 0=off 1=on");
2198da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(panic_on_pci_parity, int, 0644);
2199da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(panic_on_pci_parity, "Panic on PCI Bus Parity error: 0=off 1=on");
2200da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(log_ue, int, 0644);
2201da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(log_ue, "Log uncorrectable error to console: 0=off 1=on");
2202da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(log_ce, int, 0644);
2203da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(log_ce, "Log correctable error to console: 0=off 1=on");
2204da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(poll_msec, int, 0644);
2205da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(poll_msec, "Polling period in milliseconds");
2206da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#ifdef CONFIG_EDAC_DEBUG
2207da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxmodule_param(edac_debug_level, int, 0644);
2208da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan CoxMODULE_PARM_DESC(edac_debug_level, "Debug level");
2209da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Cox#endif
2210