edac_device.c revision 52490c8d07680a7ecc3c1a70a16841455d37e96a
1e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
2e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
3e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device.c
4e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * (C) 2007 www.douglaskthompson.com
5e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
6e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * This file may be distributed under the terms of the
7e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * GNU General Public License.
8e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
9e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * Written by Doug Thompson <norsk5@xmission.com>
10e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
11e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device API implementation
12e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * 19 Jan 2007
13e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
14e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
15e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/module.h>
16e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/types.h>
17e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/smp.h>
18e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/init.h>
19e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/sysctl.h>
20e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/highmem.h>
21e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/timer.h>
22e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/slab.h>
2352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson#include <linux/jiffies.h>
24e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/spinlock.h>
25e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/list.h>
26e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/sysdev.h>
27e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/ctype.h>
28e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <linux/workqueue.h>
29e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <asm/uaccess.h>
30e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include <asm/page.h>
31e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
32e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include "edac_core.h"
33e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#include "edac_module.h"
34e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
3552490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson/* lock to memory controller's control array 'edac_device_list' */
36e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstatic DECLARE_MUTEX(device_ctls_mutex);
37e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstatic struct list_head edac_device_list = LIST_HEAD_INIT(edac_device_list);
38e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
39e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#ifdef CONFIG_EDAC_DEBUG
40e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstatic void edac_device_dump_device(struct edac_device_ctl_info *edac_dev)
41e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
42079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	debugf3("\tedac_dev = %p dev_idx=%d \n", edac_dev, edac_dev->dev_idx);
43e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf4("\tedac_dev->edac_check = %p\n", edac_dev->edac_check);
44e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf3("\tdev = %p\n", edac_dev->dev);
45e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf3("\tmod_name:ctl_name = %s:%s\n",
46e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_dev->mod_name, edac_dev->ctl_name);
47e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf3("\tpvt_info = %p\n\n", edac_dev->pvt_info);
48e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
49079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson#endif				/* CONFIG_EDAC_DEBUG */
50e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
51e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
5252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson * edac_device_alloc_ctl_info()
5352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	Allocate a new edac device control info structure
5452490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
5552490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	The control structure is allocated in complete chunk
5652490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	from the OS. It is in turn sub allocated to the
5752490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	various objects that compose the struture
5852490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
5952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	The structure has a 'nr_instance' array within itself.
6052490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	Each instance represents a major component
6152490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *		Example:  L1 cache and L2 cache are 2 instance components
6252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
6352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	Within each instance is an array of 'nr_blocks' blockoffsets
64e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
65e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstruct edac_device_ctl_info *edac_device_alloc_ctl_info(
66e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	unsigned sz_private,
6752490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	char *edac_device_name, unsigned nr_instances,
6852490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	char *edac_block_name, unsigned nr_blocks,
6952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	unsigned offset_value,		/* zero, 1, or other based offset */
7052490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	struct edac_attrib_spec *attrib_spec, unsigned nr_attribs)
71e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
72e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_ctl_info *dev_ctl;
73e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_instance *dev_inst, *inst;
74e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_block *dev_blk, *blk_p, *blk;
75e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_attrib *dev_attrib, *attrib_p, *attrib;
76e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	unsigned total_size;
77e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	unsigned count;
78e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	unsigned instance, block, attr;
79e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	void *pvt;
80e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
81e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf1("%s() instances=%d blocks=%d\n",
82079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson		__func__, nr_instances, nr_blocks);
83e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
84e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Figure out the offsets of the various items from the start of an
85e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	 * ctl_info structure.  We want the alignment of each item
86e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	 * to be at least as stringent as what the compiler would
87e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	 * provide if we could simply hardcode everything into a single struct.
88e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	 */
8952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	dev_ctl = (struct edac_device_ctl_info *)NULL;
90e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
91e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Calc the 'end' offset past the ctl_info structure */
92e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_inst = (struct edac_device_instance *)
93052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		edac_align_ptr(&dev_ctl[1], sizeof(*dev_inst));
94e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
95e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Calc the 'end' offset past the instance array */
96e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_blk = (struct edac_device_block *)
97052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		edac_align_ptr(&dev_inst[nr_instances], sizeof(*dev_blk));
98e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
99e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Calc the 'end' offset past the dev_blk array */
100e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	count = nr_instances * nr_blocks;
101e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_attrib = (struct edac_attrib *)
102052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib));
103e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
104e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Check for case of NO attributes specified */
105e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (nr_attribs > 0)
106e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		count *= nr_attribs;
107e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
108e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Calc the 'end' offset past the attributes array */
109079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pvt = edac_align_ptr(&dev_attrib[count], sz_private);
110079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	total_size = ((unsigned long)pvt) + sz_private;
111e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
112e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Allocate the amount of memory for the set of control structures */
11352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	dev_ctl = kzalloc(total_size, GFP_KERNEL);
11452490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	if (dev_ctl == NULL)
115e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		return NULL;
116e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
117e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Adjust pointers so they point within the memory we just allocated
118e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	 * rather than an imaginary chunk of memory located at address 0.
119e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	 */
120e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_inst = (struct edac_device_instance *)
121052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		(((char *)dev_ctl) + ((unsigned long)dev_inst));
122e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_blk = (struct edac_device_block *)
123052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		(((char *)dev_ctl) + ((unsigned long)dev_blk));
124e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_attrib = (struct edac_attrib *)
125052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		(((char *)dev_ctl) + ((unsigned long)dev_attrib));
126079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL;
127e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
128e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_ctl->nr_instances = nr_instances;
129e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_ctl->instances = dev_inst;
130e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_ctl->pvt_info = pvt;
131e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
13252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	/* Name of this edac device */
13352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name);
134e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
135e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Initialize every Instance */
136e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	for (instance = 0; instance < nr_instances; instance++) {
137e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		inst = &dev_inst[instance];
138e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		inst->ctl = dev_ctl;
139e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		inst->nr_blocks = nr_blocks;
140e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		blk_p = &dev_blk[instance * nr_blocks];
141e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		inst->blocks = blk_p;
142e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
143e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		/* name of this instance */
144e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		snprintf(inst->name, sizeof(inst->name),
145079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson			 "%s%u", edac_device_name, instance);
146e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
147e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		/* Initialize every block in each instance */
148079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson		for (block = 0; block < nr_blocks; block++) {
149e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			blk = &blk_p[block];
150e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			blk->instance = inst;
151e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			blk->nr_attribs = nr_attribs;
152e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			attrib_p = &dev_attrib[block * nr_attribs];
153e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			blk->attribs = attrib_p;
154e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			snprintf(blk->name, sizeof(blk->name),
155d391a7b8147d12b0e5141fb65829856fb0c289dcDouglas Thompson				 "%s%d", edac_block_name, block+offset_value);
156e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
157e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			debugf1("%s() instance=%d block=%d name=%s\n",
158079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson				__func__, instance, block, blk->name);
159e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
160e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			if (attrib_spec != NULL) {
161e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				/* when there is an attrib_spec passed int then
162e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				 * Initialize every attrib of each block
163e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				 */
164e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				for (attr = 0; attr < nr_attribs; attr++) {
165e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson					attrib = &attrib_p[attr];
166e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson					attrib->block = blk;
167e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
168e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson					/* Link each attribute to the caller's
169e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson					 * spec entry, for name and type
170079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson					 */
171e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson					attrib->spec = &attrib_spec[attr];
172e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				}
173e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			}
174e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		}
175e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
176e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
177e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Mark this instance as merely ALLOCATED */
178e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	dev_ctl->op_state = OP_ALLOC;
179e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
180e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return dev_ctl;
181e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
182e27e3dac651771fe3250f6305dee277bce29fc5dDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info);
183e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
184e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
185e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_free_ctl_info()
186e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	frees the memory allocated by the edac_device_alloc_ctl_info()
187e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	function
188e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
189079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonvoid edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info)
190079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson{
191e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	kfree(ctl_info);
192e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
193079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_free_ctl_info);
194e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
195e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
196e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * find_edac_device_by_dev
197e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	scans the edac_device list for a specific 'struct device *'
19852490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
19952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	lock to be held prior to call:	device_ctls_mutex
20052490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
20152490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	Return:
20252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *		pointer to control structure managing 'dev'
20352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *		NULL if not found on list
204e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
205079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic struct edac_device_ctl_info *find_edac_device_by_dev(struct device *dev)
206e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
207e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_ctl_info *edac_dev;
208e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct list_head *item;
209e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
210e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf3("%s()\n", __func__);
211e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
212e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	list_for_each(item, &edac_device_list) {
213e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_dev = list_entry(item, struct edac_device_ctl_info, link);
214e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
215e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		if (edac_dev->dev == dev)
216e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			return edac_dev;
217e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
218e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
219e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return NULL;
220e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
221e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
222e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
223e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * add_edac_dev_to_global_list
224e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	Before calling this function, caller must
225e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	assign a unique value to edac_dev->dev_idx.
22652490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
22752490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	lock to be held prior to call:	device_ctls_mutex
22852490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
229e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	Return:
230e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *		0 on success
231e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *		1 on failure.
232e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
233079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic int add_edac_dev_to_global_list(struct edac_device_ctl_info *edac_dev)
234e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
235e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct list_head *item, *insert_before;
236e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_ctl_info *rover;
237e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
238e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	insert_before = &edac_device_list;
239e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
240e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Determine if already on the list */
24152490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	rover = find_edac_device_by_dev(edac_dev->dev);
24252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	if (unlikely(rover != NULL))
243e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		goto fail0;
244e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
245e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Insert in ascending order by 'dev_idx', so find position */
246e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	list_for_each(item, &edac_device_list) {
247e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		rover = list_entry(item, struct edac_device_ctl_info, link);
248e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
249e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		if (rover->dev_idx >= edac_dev->dev_idx) {
250e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			if (unlikely(rover->dev_idx == edac_dev->dev_idx))
251e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				goto fail1;
252e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
253e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			insert_before = item;
254e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			break;
255e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		}
256e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
257e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
258e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	list_add_tail_rcu(&edac_dev->link, insert_before);
259e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return 0;
260e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
261052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
262e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_printk(KERN_WARNING, EDAC_MC,
263052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"%s (%s) %s %s already assigned %d\n",
264052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			rover->dev->bus_id, dev_name(rover),
265052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			rover->mod_name, rover->ctl_name, rover->dev_idx);
266e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return 1;
267e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
268052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
269e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_printk(KERN_WARNING, EDAC_MC,
270052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"bug in low-level driver: attempt to assign\n"
271052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			"    duplicate dev_idx %d in %s()\n", rover->dev_idx,
272052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			__func__);
273e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return 1;
274e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
275e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
276e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
277e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * complete_edac_device_list_del
27852490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
27952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	callback function when reference count is zero
280e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
281e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstatic void complete_edac_device_list_del(struct rcu_head *head)
282e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
283e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_ctl_info *edac_dev;
284e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
285e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev = container_of(head, struct edac_device_ctl_info, rcu);
286e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	INIT_LIST_HEAD(&edac_dev->link);
287e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	complete(&edac_dev->complete);
288e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
289e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
290e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
291e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * del_edac_device_from_global_list
29252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *
29352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	remove the RCU, setup for a callback call, then wait for the
29452490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson *	callback to occur
295e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
296079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic void del_edac_device_from_global_list(struct edac_device_ctl_info
297052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson						*edac_device)
298e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
299e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	list_del_rcu(&edac_device->link);
300e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	init_completion(&edac_device->complete);
301e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	call_rcu(&edac_device->rcu, complete_edac_device_list_del);
302e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	wait_for_completion(&edac_device->complete);
303e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
304e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
305e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/**
306e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_find
307e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	Search for a edac_device_ctl_info structure whose index is 'idx'.
308e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
309e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * If found, return a pointer to the structure.
310e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * Else return NULL.
311e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
312e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * Caller must hold device_ctls_mutex.
313e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
314079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct edac_device_ctl_info *edac_device_find(int idx)
315e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
316e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct list_head *item;
317e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_ctl_info *edac_dev;
318e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
319e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Iterate over list, looking for exact match of ID */
320e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	list_for_each(item, &edac_device_list) {
321e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_dev = list_entry(item, struct edac_device_ctl_info, link);
322e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
323e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		if (edac_dev->dev_idx >= idx) {
324e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			if (edac_dev->dev_idx == idx)
325e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson				return edac_dev;
326e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
327e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			/* not on list, so terminate early */
328e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson			break;
329e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		}
330e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
331e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
332e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return NULL;
333e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
33452490c8d07680a7ecc3c1a70a16841455d37e96aDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_find);
335e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
336e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
33781d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_device_workq_function
338e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	performs the operation scheduled by a workq request
339e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
34081d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangstatic void edac_device_workq_function(struct work_struct *work_req)
341e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
342079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	struct delayed_work *d_work = (struct delayed_work *)work_req;
343079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	struct edac_device_ctl_info *edac_dev = to_edac_device_ctl_work(d_work);
344e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
345e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	//debugf0("%s() here and running\n", __func__);
34652490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	down(&device_ctls_mutex);
347e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
348e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Only poll controllers that are running polled and have a check */
349e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if ((edac_dev->op_state == OP_RUNNING_POLL) &&
350052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		(edac_dev->edac_check != NULL)) {
351052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			edac_dev->edac_check(edac_dev);
352e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
353e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
35452490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	up(&device_ctls_mutex);
355e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
356e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Reschedule */
357079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompson	queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay);
358e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
359e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
360e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
36181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_device_workq_setup
362e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	initialize a workq item for this edac_device instance
363e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	passing in the new delay period in msec
364e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
36581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangvoid edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
366052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				unsigned msec)
367e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
368e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf0("%s()\n", __func__);
369e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
370e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev->poll_msec = msec;
37152490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	edac_dev->delay = msecs_to_jiffies(msec);	/* Calc delay jiffies */
372e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
37381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	INIT_DELAYED_WORK(&edac_dev->work, edac_device_workq_function);
37481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay);
375e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
376e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
377e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
37881d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang * edac_device_workq_teardown
379e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	stop the workq processing on this edac_dev
380e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
38181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiangvoid edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
382e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
383e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	int status;
384e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
385e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	status = cancel_delayed_work(&edac_dev->work);
386e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (status == 0) {
387e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		/* workq instance might be running, wait for it */
388e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		flush_workqueue(edac_workqueue);
389e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
390e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
391e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
392e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
393e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_reset_delay_period
394e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
395e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
396079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonvoid edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
397052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson					unsigned long value)
398e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
39952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	down(&device_ctls_mutex);
400e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
401e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* cancel the current workq request */
40281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_device_workq_teardown(edac_dev);
403e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
404e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* restart the workq request, with new delay value */
40581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_device_workq_setup(edac_dev, value);
406e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
40752490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	up(&device_ctls_mutex);
408e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
409e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
410e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/**
411e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_add_device: Insert the 'edac_dev' structure into the
412e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device global list and create sysfs entries associated with
413e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device structure.
414e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * @edac_device: pointer to the edac_device structure to be added to the list
415e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * @edac_idx: A unique numeric identifier to be assigned to the
416e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * 'edac_device' structure.
417e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
418e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * Return:
419e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	0	Success
420e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	!0	Failure
421e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
422e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonint edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx)
423e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
424e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf0("%s()\n", __func__);
425e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
426e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev->dev_idx = edac_idx;
427e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#ifdef CONFIG_EDAC_DEBUG
428e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (edac_debug_level >= 3)
429e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_dump_device(edac_dev);
430e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson#endif
43152490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	down(&device_ctls_mutex);
432e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
433e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (add_edac_dev_to_global_list(edac_dev))
434e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		goto fail0;
435e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
436e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* set load time so that error rate can be tracked */
437e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev->start_time = jiffies;
438e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
439e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* create this instance's sysfs entries */
440e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (edac_device_create_sysfs(edac_dev)) {
441e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_WARNING,
442052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson					"failed to create sysfs device\n");
443e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		goto fail1;
444e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
445e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
446e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* If there IS a check routine, then we are running POLLED */
447e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (edac_dev->edac_check != NULL) {
448e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		/* This instance is NOW RUNNING */
449e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_dev->op_state = OP_RUNNING_POLL;
450e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
45181d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		/*
45281d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		 * enable workq processing on this instance,
45381d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		 * default = 1000 msec
45481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		 */
45581d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang		edac_device_workq_setup(edac_dev, 1000);
456e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	} else {
457e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_dev->op_state = OP_RUNNING_INTERRUPT;
458e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
459e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
460e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Report action taken */
461e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_device_printk(edac_dev, KERN_INFO,
462052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"Giving out device to module '%s' controller "
463052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"'%s': DEV '%s' (%s)\n",
464052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_dev->mod_name,
465052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_dev->ctl_name,
466052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				dev_name(edac_dev),
467052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_op_state_toString(edac_dev->op_state));
468e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
46952490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	up(&device_ctls_mutex);
470e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return 0;
471e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
472052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail1:
473e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Some error, so remove the entry from the lsit */
474e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	del_edac_device_from_global_list(edac_dev);
475e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
476052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail0:
47752490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	up(&device_ctls_mutex);
478e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return 1;
479e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
480e27e3dac651771fe3250f6305dee277bce29fc5dDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_add_device);
481e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
482e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/**
483e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_del_device:
484e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	Remove sysfs entries for specified edac_device structure and
485e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	then remove edac_device structure from global list
486e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
487e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * @pdev:
488e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	Pointer to 'struct device' representing edac_device
489e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	structure to remove.
490e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *
491e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * Return:
492e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	Pointer to removed edac_device structure,
493e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	OR NULL if device not found.
494e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
495079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstruct edac_device_ctl_info *edac_device_del_device(struct device *dev)
496e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
497e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_ctl_info *edac_dev;
498e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
499e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	debugf0("MC: %s()\n", __func__);
500e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
50152490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	down(&device_ctls_mutex);
502e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
50352490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	/* Find the structure on the list, if not there, then leave */
50452490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	edac_dev = find_edac_device_by_dev(dev);
50552490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	if (edac_dev == NULL) {
50652490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson		up(&device_ctls_mutex);
507e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		return NULL;
508e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
509e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
510e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* mark this instance as OFFLINE */
511e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev->op_state = OP_OFFLINE;
512e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
513e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* clear workq processing on this instance */
51481d87cb13e367bb804bf44889ae0de7369705d6cDave Jiang	edac_device_workq_teardown(edac_dev);
515e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
516e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Tear down the sysfs entries for this instance */
517e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_device_remove_sysfs(edac_dev);
518e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
519e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* deregister from global list */
520e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	del_edac_device_from_global_list(edac_dev);
521e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
52252490c8d07680a7ecc3c1a70a16841455d37e96aDouglas Thompson	up(&device_ctls_mutex);
523e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
524e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_printk(KERN_INFO, EDAC_MC,
525052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		"Removed device %d for %s %s: DEV %s\n",
526052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		edac_dev->dev_idx,
527052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson		edac_dev->mod_name, edac_dev->ctl_name, dev_name(edac_dev));
528e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
529e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return edac_dev;
530e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
531079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_del_device);
532e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
533e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstatic inline int edac_device_get_log_ce(struct edac_device_ctl_info *edac_dev)
534e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
535e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return edac_dev->log_ce;
536e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
537e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
538e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonstatic inline int edac_device_get_log_ue(struct edac_device_ctl_info *edac_dev)
539e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
540e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return edac_dev->log_ue;
541e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
542e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
543079708b9173595bf74b31b14c36e946359ae6c7eDouglas Thompsonstatic inline int edac_device_get_panic_on_ue(struct edac_device_ctl_info
544052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson					*edac_dev)
545e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
546e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	return edac_dev->panic_on_ue;
547e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
548e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
549e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
550e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_handle_ce
551e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	perform a common output and handling of an 'edac_dev' CE event
552e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
553e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonvoid edac_device_handle_ce(struct edac_device_ctl_info *edac_dev,
554052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			int inst_nr, int block_nr, const char *msg)
555e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
556e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_instance *instance;
557e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_block *block = NULL;
558e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
559e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if ((inst_nr >= edac_dev->nr_instances) || (inst_nr < 0)) {
560e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_ERR,
561052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"INTERNAL ERROR: 'instance' out of range "
562052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"(%d >= %d)\n", inst_nr,
563052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_dev->nr_instances);
564e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		return;
565e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
566e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
567e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	instance = edac_dev->instances + inst_nr;
568e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
569e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if ((block_nr >= instance->nr_blocks) || (block_nr < 0)) {
570e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_ERR,
571052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"INTERNAL ERROR: instance %d 'block' "
572052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"out of range (%d >= %d)\n",
573052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				inst_nr, block_nr,
574052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				instance->nr_blocks);
575e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		return;
576e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
577e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
578e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (instance->nr_blocks > 0) {
579e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		block = instance->blocks + block_nr;
580e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		block->counters.ce_count++;
581e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
582e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
583e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Propogate the count up the 'totals' tree */
584e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	instance->counters.ce_count++;
585e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev->counters.ce_count++;
586e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
587e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (edac_device_get_log_ce(edac_dev))
588e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_WARNING,
589052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"CE: %s instance: %s block: %s '%s'\n",
590052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_dev->ctl_name, instance->name,
591052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				block ? block->name : "N/A", msg);
592e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
593e27e3dac651771fe3250f6305dee277bce29fc5dDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_handle_ce);
594e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
595e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson/*
596e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson * edac_device_handle_ue
597e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson *	perform a common output and handling of an 'edac_dev' UE event
598e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson */
599e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompsonvoid edac_device_handle_ue(struct edac_device_ctl_info *edac_dev,
600052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			int inst_nr, int block_nr, const char *msg)
601e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson{
602e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_instance *instance;
603e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	struct edac_device_block *block = NULL;
604e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
605e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if ((inst_nr >= edac_dev->nr_instances) || (inst_nr < 0)) {
606e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_ERR,
607052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"INTERNAL ERROR: 'instance' out of range "
608052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"(%d >= %d)\n", inst_nr,
609052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_dev->nr_instances);
610e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		return;
611e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
612e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
613e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	instance = edac_dev->instances + inst_nr;
614e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
615e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if ((block_nr >= instance->nr_blocks) || (block_nr < 0)) {
616e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_ERR,
617052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"INTERNAL ERROR: instance %d 'block' "
618052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"out of range (%d >= %d)\n",
619052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				inst_nr, block_nr,
620052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				instance->nr_blocks);
621e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		return;
622e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
623e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
624e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (instance->nr_blocks > 0) {
625e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		block = instance->blocks + block_nr;
626e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		block->counters.ue_count++;
627e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	}
628e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
629e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	/* Propogate the count up the 'totals' tree */
630e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	instance->counters.ue_count++;
631e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	edac_dev->counters.ue_count++;
632e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
633e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (edac_device_get_log_ue(edac_dev))
634e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson		edac_device_printk(edac_dev, KERN_EMERG,
635052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				"UE: %s instance: %s block: %s '%s'\n",
636052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				edac_dev->ctl_name, instance->name,
637052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson				block ? block->name : "N/A", msg);
638e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson
639e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson	if (edac_device_get_panic_on_ue(edac_dev))
640d391a7b8147d12b0e5141fb65829856fb0c289dcDouglas Thompson		panic("EDAC %s: UE instance: %s block %s '%s'\n",
641052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			edac_dev->ctl_name, instance->name,
642052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson			block ? block->name : "N/A", msg);
643e27e3dac651771fe3250f6305dee277bce29fc5dDouglas Thompson}
644079708b9173595bf74b31b14c36e946359ae6c7eDouglas ThompsonEXPORT_SYMBOL_GPL(edac_device_handle_ue);
645