18c2ecf20Sopenharmony_ci
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * edac_device.c
48c2ecf20Sopenharmony_ci * (C) 2007 www.douglaskthompson.com
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the
78c2ecf20Sopenharmony_ci * GNU General Public License.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Written by Doug Thompson <norsk5@xmission.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * edac_device API implementation
128c2ecf20Sopenharmony_ci * 19 Jan 2007
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/page.h>
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <linux/ctype.h>
188c2ecf20Sopenharmony_ci#include <linux/highmem.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/smp.h>
248c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
258c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
268c2ecf20Sopenharmony_ci#include <linux/timer.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "edac_device.h"
298c2ecf20Sopenharmony_ci#include "edac_module.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* lock for the list: 'edac_device_list', manipulation of this list
328c2ecf20Sopenharmony_ci * is protected by the 'device_ctls_mutex' lock
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(device_ctls_mutex);
358c2ecf20Sopenharmony_cistatic LIST_HEAD(edac_device_list);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Default workqueue processing interval on this instance, in msecs */
388c2ecf20Sopenharmony_ci#define DEFAULT_POLL_INTERVAL 1000
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
418c2ecf20Sopenharmony_cistatic void edac_device_dump_device(struct edac_device_ctl_info *edac_dev)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	edac_dbg(3, "\tedac_dev = %p dev_idx=%d\n",
448c2ecf20Sopenharmony_ci		 edac_dev, edac_dev->dev_idx);
458c2ecf20Sopenharmony_ci	edac_dbg(4, "\tedac_dev->edac_check = %p\n", edac_dev->edac_check);
468c2ecf20Sopenharmony_ci	edac_dbg(3, "\tdev = %p\n", edac_dev->dev);
478c2ecf20Sopenharmony_ci	edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
488c2ecf20Sopenharmony_ci		 edac_dev->mod_name, edac_dev->ctl_name);
498c2ecf20Sopenharmony_ci	edac_dbg(3, "\tpvt_info = %p\n\n", edac_dev->pvt_info);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci#endif				/* CONFIG_EDAC_DEBUG */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct edac_device_ctl_info *edac_device_alloc_ctl_info(
548c2ecf20Sopenharmony_ci	unsigned sz_private,
558c2ecf20Sopenharmony_ci	char *edac_device_name, unsigned nr_instances,
568c2ecf20Sopenharmony_ci	char *edac_block_name, unsigned nr_blocks,
578c2ecf20Sopenharmony_ci	unsigned offset_value,		/* zero, 1, or other based offset */
588c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_block_attribute *attrib_spec, unsigned nr_attrib,
598c2ecf20Sopenharmony_ci	int device_index)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *dev_ctl;
628c2ecf20Sopenharmony_ci	struct edac_device_instance *dev_inst, *inst;
638c2ecf20Sopenharmony_ci	struct edac_device_block *dev_blk, *blk_p, *blk;
648c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_block_attribute *dev_attrib, *attrib_p, *attrib;
658c2ecf20Sopenharmony_ci	unsigned total_size;
668c2ecf20Sopenharmony_ci	unsigned count;
678c2ecf20Sopenharmony_ci	unsigned instance, block, attr;
688c2ecf20Sopenharmony_ci	void *pvt, *p;
698c2ecf20Sopenharmony_ci	int err;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	edac_dbg(4, "instances=%d blocks=%d\n", nr_instances, nr_blocks);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Calculate the size of memory we need to allocate AND
748c2ecf20Sopenharmony_ci	 * determine the offsets of the various item arrays
758c2ecf20Sopenharmony_ci	 * (instance,block,attrib) from the start of an  allocated structure.
768c2ecf20Sopenharmony_ci	 * We want the alignment of each item  (instance,block,attrib)
778c2ecf20Sopenharmony_ci	 * to be at least as stringent as what the compiler would
788c2ecf20Sopenharmony_ci	 * provide if we could simply hardcode everything into a single struct.
798c2ecf20Sopenharmony_ci	 */
808c2ecf20Sopenharmony_ci	p = NULL;
818c2ecf20Sopenharmony_ci	dev_ctl = edac_align_ptr(&p, sizeof(*dev_ctl), 1);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Calc the 'end' offset past end of ONE ctl_info structure
848c2ecf20Sopenharmony_ci	 * which will become the start of the 'instance' array
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	dev_inst = edac_align_ptr(&p, sizeof(*dev_inst), nr_instances);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Calc the 'end' offset past the instance array within the ctl_info
898c2ecf20Sopenharmony_ci	 * which will become the start of the block array
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	count = nr_instances * nr_blocks;
928c2ecf20Sopenharmony_ci	dev_blk = edac_align_ptr(&p, sizeof(*dev_blk), count);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Calc the 'end' offset past the dev_blk array
958c2ecf20Sopenharmony_ci	 * which will become the start of the attrib array, if any.
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	/* calc how many nr_attrib we need */
988c2ecf20Sopenharmony_ci	if (nr_attrib > 0)
998c2ecf20Sopenharmony_ci		count *= nr_attrib;
1008c2ecf20Sopenharmony_ci	dev_attrib = edac_align_ptr(&p, sizeof(*dev_attrib), count);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Calc the 'end' offset past the attributes array */
1038c2ecf20Sopenharmony_ci	pvt = edac_align_ptr(&p, sz_private, 1);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* 'pvt' now points to where the private data area is.
1068c2ecf20Sopenharmony_ci	 * At this point 'pvt' (like dev_inst,dev_blk and dev_attrib)
1078c2ecf20Sopenharmony_ci	 * is baselined at ZERO
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	total_size = ((unsigned long)pvt) + sz_private;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Allocate the amount of memory for the set of control structures */
1128c2ecf20Sopenharmony_ci	dev_ctl = kzalloc(total_size, GFP_KERNEL);
1138c2ecf20Sopenharmony_ci	if (dev_ctl == NULL)
1148c2ecf20Sopenharmony_ci		return NULL;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Adjust pointers so they point within the actual memory we
1178c2ecf20Sopenharmony_ci	 * just allocated rather than an imaginary chunk of memory
1188c2ecf20Sopenharmony_ci	 * located at address 0.
1198c2ecf20Sopenharmony_ci	 * 'dev_ctl' points to REAL memory, while the others are
1208c2ecf20Sopenharmony_ci	 * ZERO based and thus need to be adjusted to point within
1218c2ecf20Sopenharmony_ci	 * the allocated memory.
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	dev_inst = (struct edac_device_instance *)
1248c2ecf20Sopenharmony_ci		(((char *)dev_ctl) + ((unsigned long)dev_inst));
1258c2ecf20Sopenharmony_ci	dev_blk = (struct edac_device_block *)
1268c2ecf20Sopenharmony_ci		(((char *)dev_ctl) + ((unsigned long)dev_blk));
1278c2ecf20Sopenharmony_ci	dev_attrib = (struct edac_dev_sysfs_block_attribute *)
1288c2ecf20Sopenharmony_ci		(((char *)dev_ctl) + ((unsigned long)dev_attrib));
1298c2ecf20Sopenharmony_ci	pvt = sz_private ? (((char *)dev_ctl) + ((unsigned long)pvt)) : NULL;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Begin storing the information into the control info structure */
1328c2ecf20Sopenharmony_ci	dev_ctl->dev_idx = device_index;
1338c2ecf20Sopenharmony_ci	dev_ctl->nr_instances = nr_instances;
1348c2ecf20Sopenharmony_ci	dev_ctl->instances = dev_inst;
1358c2ecf20Sopenharmony_ci	dev_ctl->pvt_info = pvt;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Default logging of CEs and UEs */
1388c2ecf20Sopenharmony_ci	dev_ctl->log_ce = 1;
1398c2ecf20Sopenharmony_ci	dev_ctl->log_ue = 1;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Name of this edac device */
1428c2ecf20Sopenharmony_ci	snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	edac_dbg(4, "edac_dev=%p next after end=%p\n",
1458c2ecf20Sopenharmony_ci		 dev_ctl, pvt + sz_private);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* Initialize every Instance */
1488c2ecf20Sopenharmony_ci	for (instance = 0; instance < nr_instances; instance++) {
1498c2ecf20Sopenharmony_ci		inst = &dev_inst[instance];
1508c2ecf20Sopenharmony_ci		inst->ctl = dev_ctl;
1518c2ecf20Sopenharmony_ci		inst->nr_blocks = nr_blocks;
1528c2ecf20Sopenharmony_ci		blk_p = &dev_blk[instance * nr_blocks];
1538c2ecf20Sopenharmony_ci		inst->blocks = blk_p;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		/* name of this instance */
1568c2ecf20Sopenharmony_ci		snprintf(inst->name, sizeof(inst->name),
1578c2ecf20Sopenharmony_ci			 "%s%u", edac_device_name, instance);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		/* Initialize every block in each instance */
1608c2ecf20Sopenharmony_ci		for (block = 0; block < nr_blocks; block++) {
1618c2ecf20Sopenharmony_ci			blk = &blk_p[block];
1628c2ecf20Sopenharmony_ci			blk->instance = inst;
1638c2ecf20Sopenharmony_ci			snprintf(blk->name, sizeof(blk->name),
1648c2ecf20Sopenharmony_ci				 "%s%d", edac_block_name, block+offset_value);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci			edac_dbg(4, "instance=%d inst_p=%p block=#%d block_p=%p name='%s'\n",
1678c2ecf20Sopenharmony_ci				 instance, inst, block, blk, blk->name);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci			/* if there are NO attributes OR no attribute pointer
1708c2ecf20Sopenharmony_ci			 * then continue on to next block iteration
1718c2ecf20Sopenharmony_ci			 */
1728c2ecf20Sopenharmony_ci			if ((nr_attrib == 0) || (attrib_spec == NULL))
1738c2ecf20Sopenharmony_ci				continue;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci			/* setup the attribute array for this block */
1768c2ecf20Sopenharmony_ci			blk->nr_attribs = nr_attrib;
1778c2ecf20Sopenharmony_ci			attrib_p = &dev_attrib[block*nr_instances*nr_attrib];
1788c2ecf20Sopenharmony_ci			blk->block_attributes = attrib_p;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci			edac_dbg(4, "THIS BLOCK_ATTRIB=%p\n",
1818c2ecf20Sopenharmony_ci				 blk->block_attributes);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci			/* Initialize every user specified attribute in this
1848c2ecf20Sopenharmony_ci			 * block with the data the caller passed in
1858c2ecf20Sopenharmony_ci			 * Each block gets its own copy of pointers,
1868c2ecf20Sopenharmony_ci			 * and its unique 'value'
1878c2ecf20Sopenharmony_ci			 */
1888c2ecf20Sopenharmony_ci			for (attr = 0; attr < nr_attrib; attr++) {
1898c2ecf20Sopenharmony_ci				attrib = &attrib_p[attr];
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci				/* populate the unique per attrib
1928c2ecf20Sopenharmony_ci				 * with the code pointers and info
1938c2ecf20Sopenharmony_ci				 */
1948c2ecf20Sopenharmony_ci				attrib->attr = attrib_spec[attr].attr;
1958c2ecf20Sopenharmony_ci				attrib->show = attrib_spec[attr].show;
1968c2ecf20Sopenharmony_ci				attrib->store = attrib_spec[attr].store;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci				attrib->block = blk;	/* up link */
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci				edac_dbg(4, "alloc-attrib=%p attrib_name='%s' attrib-spec=%p spec-name=%s\n",
2018c2ecf20Sopenharmony_ci					 attrib, attrib->attr.name,
2028c2ecf20Sopenharmony_ci					 &attrib_spec[attr],
2038c2ecf20Sopenharmony_ci					 attrib_spec[attr].attr.name
2048c2ecf20Sopenharmony_ci					);
2058c2ecf20Sopenharmony_ci			}
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Mark this instance as merely ALLOCATED */
2108c2ecf20Sopenharmony_ci	dev_ctl->op_state = OP_ALLOC;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Initialize the 'root' kobj for the edac_device controller
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	err = edac_device_register_sysfs_main_kobj(dev_ctl);
2168c2ecf20Sopenharmony_ci	if (err) {
2178c2ecf20Sopenharmony_ci		kfree(dev_ctl);
2188c2ecf20Sopenharmony_ci		return NULL;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* at this point, the root kobj is valid, and in order to
2228c2ecf20Sopenharmony_ci	 * 'free' the object, then the function:
2238c2ecf20Sopenharmony_ci	 *	edac_device_unregister_sysfs_main_kobj() must be called
2248c2ecf20Sopenharmony_ci	 * which will perform kobj unregistration and the actual free
2258c2ecf20Sopenharmony_ci	 * will occur during the kobject callback operation
2268c2ecf20Sopenharmony_ci	 */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return dev_ctl;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_civoid edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	edac_device_unregister_sysfs_main_kobj(ctl_info);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_free_ctl_info);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/*
2398c2ecf20Sopenharmony_ci * find_edac_device_by_dev
2408c2ecf20Sopenharmony_ci *	scans the edac_device list for a specific 'struct device *'
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci *	lock to be held prior to call:	device_ctls_mutex
2438c2ecf20Sopenharmony_ci *
2448c2ecf20Sopenharmony_ci *	Return:
2458c2ecf20Sopenharmony_ci *		pointer to control structure managing 'dev'
2468c2ecf20Sopenharmony_ci *		NULL if not found on list
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_cistatic struct edac_device_ctl_info *find_edac_device_by_dev(struct device *dev)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
2518c2ecf20Sopenharmony_ci	struct list_head *item;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	list_for_each(item, &edac_device_list) {
2568c2ecf20Sopenharmony_ci		edac_dev = list_entry(item, struct edac_device_ctl_info, link);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		if (edac_dev->dev == dev)
2598c2ecf20Sopenharmony_ci			return edac_dev;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return NULL;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/*
2668c2ecf20Sopenharmony_ci * add_edac_dev_to_global_list
2678c2ecf20Sopenharmony_ci *	Before calling this function, caller must
2688c2ecf20Sopenharmony_ci *	assign a unique value to edac_dev->dev_idx.
2698c2ecf20Sopenharmony_ci *
2708c2ecf20Sopenharmony_ci *	lock to be held prior to call:	device_ctls_mutex
2718c2ecf20Sopenharmony_ci *
2728c2ecf20Sopenharmony_ci *	Return:
2738c2ecf20Sopenharmony_ci *		0 on success
2748c2ecf20Sopenharmony_ci *		1 on failure.
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int add_edac_dev_to_global_list(struct edac_device_ctl_info *edac_dev)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct list_head *item, *insert_before;
2798c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *rover;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	insert_before = &edac_device_list;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* Determine if already on the list */
2848c2ecf20Sopenharmony_ci	rover = find_edac_device_by_dev(edac_dev->dev);
2858c2ecf20Sopenharmony_ci	if (unlikely(rover != NULL))
2868c2ecf20Sopenharmony_ci		goto fail0;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Insert in ascending order by 'dev_idx', so find position */
2898c2ecf20Sopenharmony_ci	list_for_each(item, &edac_device_list) {
2908c2ecf20Sopenharmony_ci		rover = list_entry(item, struct edac_device_ctl_info, link);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		if (rover->dev_idx >= edac_dev->dev_idx) {
2938c2ecf20Sopenharmony_ci			if (unlikely(rover->dev_idx == edac_dev->dev_idx))
2948c2ecf20Sopenharmony_ci				goto fail1;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci			insert_before = item;
2978c2ecf20Sopenharmony_ci			break;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	list_add_tail_rcu(&edac_dev->link, insert_before);
3028c2ecf20Sopenharmony_ci	return 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cifail0:
3058c2ecf20Sopenharmony_ci	edac_printk(KERN_WARNING, EDAC_MC,
3068c2ecf20Sopenharmony_ci			"%s (%s) %s %s already assigned %d\n",
3078c2ecf20Sopenharmony_ci			dev_name(rover->dev), edac_dev_name(rover),
3088c2ecf20Sopenharmony_ci			rover->mod_name, rover->ctl_name, rover->dev_idx);
3098c2ecf20Sopenharmony_ci	return 1;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cifail1:
3128c2ecf20Sopenharmony_ci	edac_printk(KERN_WARNING, EDAC_MC,
3138c2ecf20Sopenharmony_ci			"bug in low-level driver: attempt to assign\n"
3148c2ecf20Sopenharmony_ci			"    duplicate dev_idx %d in %s()\n", rover->dev_idx,
3158c2ecf20Sopenharmony_ci			__func__);
3168c2ecf20Sopenharmony_ci	return 1;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci * del_edac_device_from_global_list
3218c2ecf20Sopenharmony_ci */
3228c2ecf20Sopenharmony_cistatic void del_edac_device_from_global_list(struct edac_device_ctl_info
3238c2ecf20Sopenharmony_ci						*edac_device)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	list_del_rcu(&edac_device->link);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* these are for safe removal of devices from global list while
3288c2ecf20Sopenharmony_ci	 * NMI handlers may be traversing list
3298c2ecf20Sopenharmony_ci	 */
3308c2ecf20Sopenharmony_ci	synchronize_rcu();
3318c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&edac_device->link);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/*
3358c2ecf20Sopenharmony_ci * edac_device_workq_function
3368c2ecf20Sopenharmony_ci *	performs the operation scheduled by a workq request
3378c2ecf20Sopenharmony_ci *
3388c2ecf20Sopenharmony_ci *	this workq is embedded within an edac_device_ctl_info
3398c2ecf20Sopenharmony_ci *	structure, that needs to be polled for possible error events.
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci *	This operation is to acquire the list mutex lock
3428c2ecf20Sopenharmony_ci *	(thus preventing insertation or deletion)
3438c2ecf20Sopenharmony_ci *	and then call the device's poll function IFF this device is
3448c2ecf20Sopenharmony_ci *	running polled and there is a poll function defined.
3458c2ecf20Sopenharmony_ci */
3468c2ecf20Sopenharmony_cistatic void edac_device_workq_function(struct work_struct *work_req)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct delayed_work *d_work = to_delayed_work(work_req);
3498c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = to_edac_device_ctl_work(d_work);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	mutex_lock(&device_ctls_mutex);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* If we are being removed, bail out immediately */
3548c2ecf20Sopenharmony_ci	if (edac_dev->op_state == OP_OFFLINE) {
3558c2ecf20Sopenharmony_ci		mutex_unlock(&device_ctls_mutex);
3568c2ecf20Sopenharmony_ci		return;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Only poll controllers that are running polled and have a check */
3608c2ecf20Sopenharmony_ci	if ((edac_dev->op_state == OP_RUNNING_POLL) &&
3618c2ecf20Sopenharmony_ci		(edac_dev->edac_check != NULL)) {
3628c2ecf20Sopenharmony_ci			edac_dev->edac_check(edac_dev);
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	mutex_unlock(&device_ctls_mutex);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* Reschedule the workq for the next time period to start again
3688c2ecf20Sopenharmony_ci	 * if the number of msec is for 1 sec, then adjust to the next
3698c2ecf20Sopenharmony_ci	 * whole one second to save timers firing all over the period
3708c2ecf20Sopenharmony_ci	 * between integral seconds
3718c2ecf20Sopenharmony_ci	 */
3728c2ecf20Sopenharmony_ci	if (edac_dev->poll_msec == DEFAULT_POLL_INTERVAL)
3738c2ecf20Sopenharmony_ci		edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
3748c2ecf20Sopenharmony_ci	else
3758c2ecf20Sopenharmony_ci		edac_queue_work(&edac_dev->work, edac_dev->delay);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/*
3798c2ecf20Sopenharmony_ci * edac_device_workq_setup
3808c2ecf20Sopenharmony_ci *	initialize a workq item for this edac_device instance
3818c2ecf20Sopenharmony_ci *	passing in the new delay period in msec
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_cistatic void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
3848c2ecf20Sopenharmony_ci				    unsigned msec)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* take the arg 'msec' and set it into the control structure
3898c2ecf20Sopenharmony_ci	 * to used in the time period calculation
3908c2ecf20Sopenharmony_ci	 * then calc the number of jiffies that represents
3918c2ecf20Sopenharmony_ci	 */
3928c2ecf20Sopenharmony_ci	edac_dev->poll_msec = msec;
3938c2ecf20Sopenharmony_ci	edac_dev->delay = msecs_to_jiffies(msec);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&edac_dev->work, edac_device_workq_function);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* optimize here for the 1 second case, which will be normal value, to
3988c2ecf20Sopenharmony_ci	 * fire ON the 1 second time event. This helps reduce all sorts of
3998c2ecf20Sopenharmony_ci	 * timers firing on sub-second basis, while they are happy
4008c2ecf20Sopenharmony_ci	 * to fire together on the 1 second exactly
4018c2ecf20Sopenharmony_ci	 */
4028c2ecf20Sopenharmony_ci	if (edac_dev->poll_msec == DEFAULT_POLL_INTERVAL)
4038c2ecf20Sopenharmony_ci		edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
4048c2ecf20Sopenharmony_ci	else
4058c2ecf20Sopenharmony_ci		edac_queue_work(&edac_dev->work, edac_dev->delay);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/*
4098c2ecf20Sopenharmony_ci * edac_device_workq_teardown
4108c2ecf20Sopenharmony_ci *	stop the workq processing on this edac_dev
4118c2ecf20Sopenharmony_ci */
4128c2ecf20Sopenharmony_cistatic void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	if (!edac_dev->edac_check)
4158c2ecf20Sopenharmony_ci		return;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	edac_dev->op_state = OP_OFFLINE;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	edac_stop_work(&edac_dev->work);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/*
4238c2ecf20Sopenharmony_ci * edac_device_reset_delay_period
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci *	need to stop any outstanding workq queued up at this time
4268c2ecf20Sopenharmony_ci *	because we will be resetting the sleep time.
4278c2ecf20Sopenharmony_ci *	Then restart the workq on the new delay
4288c2ecf20Sopenharmony_ci */
4298c2ecf20Sopenharmony_civoid edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
4308c2ecf20Sopenharmony_ci				    unsigned long msec)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	edac_dev->poll_msec = msec;
4338c2ecf20Sopenharmony_ci	edac_dev->delay	    = msecs_to_jiffies(msec);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* See comment in edac_device_workq_setup() above */
4368c2ecf20Sopenharmony_ci	if (edac_dev->poll_msec == DEFAULT_POLL_INTERVAL)
4378c2ecf20Sopenharmony_ci		edac_mod_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
4388c2ecf20Sopenharmony_ci	else
4398c2ecf20Sopenharmony_ci		edac_mod_work(&edac_dev->work, edac_dev->delay);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciint edac_device_alloc_index(void)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	static atomic_t device_indexes = ATOMIC_INIT(0);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	return atomic_inc_return(&device_indexes) - 1;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_alloc_index);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ciint edac_device_add_device(struct edac_device_ctl_info *edac_dev)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
4558c2ecf20Sopenharmony_ci	if (edac_debug_level >= 3)
4568c2ecf20Sopenharmony_ci		edac_device_dump_device(edac_dev);
4578c2ecf20Sopenharmony_ci#endif
4588c2ecf20Sopenharmony_ci	mutex_lock(&device_ctls_mutex);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (add_edac_dev_to_global_list(edac_dev))
4618c2ecf20Sopenharmony_ci		goto fail0;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* set load time so that error rate can be tracked */
4648c2ecf20Sopenharmony_ci	edac_dev->start_time = jiffies;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* create this instance's sysfs entries */
4678c2ecf20Sopenharmony_ci	if (edac_device_create_sysfs(edac_dev)) {
4688c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_WARNING,
4698c2ecf20Sopenharmony_ci					"failed to create sysfs device\n");
4708c2ecf20Sopenharmony_ci		goto fail1;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* If there IS a check routine, then we are running POLLED */
4748c2ecf20Sopenharmony_ci	if (edac_dev->edac_check != NULL) {
4758c2ecf20Sopenharmony_ci		/* This instance is NOW RUNNING */
4768c2ecf20Sopenharmony_ci		edac_dev->op_state = OP_RUNNING_POLL;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		edac_device_workq_setup(edac_dev, edac_dev->poll_msec ?: DEFAULT_POLL_INTERVAL);
4798c2ecf20Sopenharmony_ci	} else {
4808c2ecf20Sopenharmony_ci		edac_dev->op_state = OP_RUNNING_INTERRUPT;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Report action taken */
4848c2ecf20Sopenharmony_ci	edac_device_printk(edac_dev, KERN_INFO,
4858c2ecf20Sopenharmony_ci		"Giving out device to module %s controller %s: DEV %s (%s)\n",
4868c2ecf20Sopenharmony_ci		edac_dev->mod_name, edac_dev->ctl_name, edac_dev->dev_name,
4878c2ecf20Sopenharmony_ci		edac_op_state_to_string(edac_dev->op_state));
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	mutex_unlock(&device_ctls_mutex);
4908c2ecf20Sopenharmony_ci	return 0;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cifail1:
4938c2ecf20Sopenharmony_ci	/* Some error, so remove the entry from the lsit */
4948c2ecf20Sopenharmony_ci	del_edac_device_from_global_list(edac_dev);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cifail0:
4978c2ecf20Sopenharmony_ci	mutex_unlock(&device_ctls_mutex);
4988c2ecf20Sopenharmony_ci	return 1;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_add_device);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistruct edac_device_ctl_info *edac_device_del_device(struct device *dev)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	mutex_lock(&device_ctls_mutex);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* Find the structure on the list, if not there, then leave */
5118c2ecf20Sopenharmony_ci	edac_dev = find_edac_device_by_dev(dev);
5128c2ecf20Sopenharmony_ci	if (edac_dev == NULL) {
5138c2ecf20Sopenharmony_ci		mutex_unlock(&device_ctls_mutex);
5148c2ecf20Sopenharmony_ci		return NULL;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* mark this instance as OFFLINE */
5188c2ecf20Sopenharmony_ci	edac_dev->op_state = OP_OFFLINE;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* deregister from global list */
5218c2ecf20Sopenharmony_ci	del_edac_device_from_global_list(edac_dev);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	mutex_unlock(&device_ctls_mutex);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* clear workq processing on this instance */
5268c2ecf20Sopenharmony_ci	edac_device_workq_teardown(edac_dev);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* Tear down the sysfs entries for this instance */
5298c2ecf20Sopenharmony_ci	edac_device_remove_sysfs(edac_dev);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	edac_printk(KERN_INFO, EDAC_MC,
5328c2ecf20Sopenharmony_ci		"Removed device %d for %s %s: DEV %s\n",
5338c2ecf20Sopenharmony_ci		edac_dev->dev_idx,
5348c2ecf20Sopenharmony_ci		edac_dev->mod_name, edac_dev->ctl_name, edac_dev_name(edac_dev));
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return edac_dev;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_del_device);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic inline int edac_device_get_log_ce(struct edac_device_ctl_info *edac_dev)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	return edac_dev->log_ce;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic inline int edac_device_get_log_ue(struct edac_device_ctl_info *edac_dev)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	return edac_dev->log_ue;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic inline int edac_device_get_panic_on_ue(struct edac_device_ctl_info
5518c2ecf20Sopenharmony_ci					*edac_dev)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	return edac_dev->panic_on_ue;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_civoid edac_device_handle_ce_count(struct edac_device_ctl_info *edac_dev,
5578c2ecf20Sopenharmony_ci				 unsigned int count, int inst_nr, int block_nr,
5588c2ecf20Sopenharmony_ci				 const char *msg)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct edac_device_instance *instance;
5618c2ecf20Sopenharmony_ci	struct edac_device_block *block = NULL;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (!count)
5648c2ecf20Sopenharmony_ci		return;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if ((inst_nr >= edac_dev->nr_instances) || (inst_nr < 0)) {
5678c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_ERR,
5688c2ecf20Sopenharmony_ci				"INTERNAL ERROR: 'instance' out of range "
5698c2ecf20Sopenharmony_ci				"(%d >= %d)\n", inst_nr,
5708c2ecf20Sopenharmony_ci				edac_dev->nr_instances);
5718c2ecf20Sopenharmony_ci		return;
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	instance = edac_dev->instances + inst_nr;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if ((block_nr >= instance->nr_blocks) || (block_nr < 0)) {
5778c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_ERR,
5788c2ecf20Sopenharmony_ci				"INTERNAL ERROR: instance %d 'block' "
5798c2ecf20Sopenharmony_ci				"out of range (%d >= %d)\n",
5808c2ecf20Sopenharmony_ci				inst_nr, block_nr,
5818c2ecf20Sopenharmony_ci				instance->nr_blocks);
5828c2ecf20Sopenharmony_ci		return;
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (instance->nr_blocks > 0) {
5868c2ecf20Sopenharmony_ci		block = instance->blocks + block_nr;
5878c2ecf20Sopenharmony_ci		block->counters.ce_count += count;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* Propagate the count up the 'totals' tree */
5918c2ecf20Sopenharmony_ci	instance->counters.ce_count += count;
5928c2ecf20Sopenharmony_ci	edac_dev->counters.ce_count += count;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (edac_device_get_log_ce(edac_dev))
5958c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_WARNING,
5968c2ecf20Sopenharmony_ci				   "CE: %s instance: %s block: %s count: %d '%s'\n",
5978c2ecf20Sopenharmony_ci				   edac_dev->ctl_name, instance->name,
5988c2ecf20Sopenharmony_ci				   block ? block->name : "N/A", count, msg);
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_handle_ce_count);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_civoid edac_device_handle_ue_count(struct edac_device_ctl_info *edac_dev,
6038c2ecf20Sopenharmony_ci				 unsigned int count, int inst_nr, int block_nr,
6048c2ecf20Sopenharmony_ci				 const char *msg)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct edac_device_instance *instance;
6078c2ecf20Sopenharmony_ci	struct edac_device_block *block = NULL;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (!count)
6108c2ecf20Sopenharmony_ci		return;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if ((inst_nr >= edac_dev->nr_instances) || (inst_nr < 0)) {
6138c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_ERR,
6148c2ecf20Sopenharmony_ci				"INTERNAL ERROR: 'instance' out of range "
6158c2ecf20Sopenharmony_ci				"(%d >= %d)\n", inst_nr,
6168c2ecf20Sopenharmony_ci				edac_dev->nr_instances);
6178c2ecf20Sopenharmony_ci		return;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	instance = edac_dev->instances + inst_nr;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if ((block_nr >= instance->nr_blocks) || (block_nr < 0)) {
6238c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_ERR,
6248c2ecf20Sopenharmony_ci				"INTERNAL ERROR: instance %d 'block' "
6258c2ecf20Sopenharmony_ci				"out of range (%d >= %d)\n",
6268c2ecf20Sopenharmony_ci				inst_nr, block_nr,
6278c2ecf20Sopenharmony_ci				instance->nr_blocks);
6288c2ecf20Sopenharmony_ci		return;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (instance->nr_blocks > 0) {
6328c2ecf20Sopenharmony_ci		block = instance->blocks + block_nr;
6338c2ecf20Sopenharmony_ci		block->counters.ue_count += count;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* Propagate the count up the 'totals' tree */
6378c2ecf20Sopenharmony_ci	instance->counters.ue_count += count;
6388c2ecf20Sopenharmony_ci	edac_dev->counters.ue_count += count;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (edac_device_get_log_ue(edac_dev))
6418c2ecf20Sopenharmony_ci		edac_device_printk(edac_dev, KERN_EMERG,
6428c2ecf20Sopenharmony_ci				   "UE: %s instance: %s block: %s count: %d '%s'\n",
6438c2ecf20Sopenharmony_ci				   edac_dev->ctl_name, instance->name,
6448c2ecf20Sopenharmony_ci				   block ? block->name : "N/A", count, msg);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (edac_device_get_panic_on_ue(edac_dev))
6478c2ecf20Sopenharmony_ci		panic("EDAC %s: UE instance: %s block %s count: %d '%s'\n",
6488c2ecf20Sopenharmony_ci		      edac_dev->ctl_name, instance->name,
6498c2ecf20Sopenharmony_ci		      block ? block->name : "N/A", count, msg);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_device_handle_ue_count);
652