18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * file for managing the edac_device subsystem of devices for EDAC
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * (C) 2007 SoftwareBitMaker
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 Doug Thompson <norsk5@xmission.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/ctype.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/edac.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "edac_device.h"
198c2ecf20Sopenharmony_ci#include "edac_module.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define EDAC_DEVICE_SYMLINK	"device"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define to_edacdev(k) container_of(k, struct edac_device_ctl_info, kobj)
248c2ecf20Sopenharmony_ci#define to_edacdev_attr(a) container_of(a, struct edacdev_attribute, attr)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Set of edac_device_ctl_info attribute store/show functions
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* 'log_ue' */
328c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_log_ue_show(struct edac_device_ctl_info
338c2ecf20Sopenharmony_ci					*ctl_info, char *data)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", ctl_info->log_ue);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_log_ue_store(struct edac_device_ctl_info
398c2ecf20Sopenharmony_ci					*ctl_info, const char *data,
408c2ecf20Sopenharmony_ci					size_t count)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	/* if parameter is zero, turn off flag, if non-zero turn on flag */
438c2ecf20Sopenharmony_ci	ctl_info->log_ue = (simple_strtoul(data, NULL, 0) != 0);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return count;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* 'log_ce' */
498c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_log_ce_show(struct edac_device_ctl_info
508c2ecf20Sopenharmony_ci					*ctl_info, char *data)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", ctl_info->log_ce);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_log_ce_store(struct edac_device_ctl_info
568c2ecf20Sopenharmony_ci					*ctl_info, const char *data,
578c2ecf20Sopenharmony_ci					size_t count)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	/* if parameter is zero, turn off flag, if non-zero turn on flag */
608c2ecf20Sopenharmony_ci	ctl_info->log_ce = (simple_strtoul(data, NULL, 0) != 0);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return count;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* 'panic_on_ue' */
668c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_panic_on_ue_show(struct edac_device_ctl_info
678c2ecf20Sopenharmony_ci						*ctl_info, char *data)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", ctl_info->panic_on_ue);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_panic_on_ue_store(struct edac_device_ctl_info
738c2ecf20Sopenharmony_ci						 *ctl_info, const char *data,
748c2ecf20Sopenharmony_ci						 size_t count)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	/* if parameter is zero, turn off flag, if non-zero turn on flag */
778c2ecf20Sopenharmony_ci	ctl_info->panic_on_ue = (simple_strtoul(data, NULL, 0) != 0);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return count;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* 'poll_msec' show and store functions*/
838c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
848c2ecf20Sopenharmony_ci					*ctl_info, char *data)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", ctl_info->poll_msec);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
908c2ecf20Sopenharmony_ci					*ctl_info, const char *data,
918c2ecf20Sopenharmony_ci					size_t count)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	unsigned long value;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* get the value and enforce that it is non-zero, must be at least
968c2ecf20Sopenharmony_ci	 * one millisecond for the delay period, between scans
978c2ecf20Sopenharmony_ci	 * Then cancel last outstanding delay for the work request
988c2ecf20Sopenharmony_ci	 * and set a new one.
998c2ecf20Sopenharmony_ci	 */
1008c2ecf20Sopenharmony_ci	value = simple_strtoul(data, NULL, 0);
1018c2ecf20Sopenharmony_ci	edac_device_reset_delay_period(ctl_info, value);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return count;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* edac_device_ctl_info specific attribute structure */
1078c2ecf20Sopenharmony_cistruct ctl_info_attribute {
1088c2ecf20Sopenharmony_ci	struct attribute attr;
1098c2ecf20Sopenharmony_ci	ssize_t(*show) (struct edac_device_ctl_info *, char *);
1108c2ecf20Sopenharmony_ci	ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t);
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define to_ctl_info(k) container_of(k, struct edac_device_ctl_info, kobj)
1148c2ecf20Sopenharmony_ci#define to_ctl_info_attr(a) container_of(a,struct ctl_info_attribute,attr)
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* Function to 'show' fields from the edac_dev 'ctl_info' structure */
1178c2ecf20Sopenharmony_cistatic ssize_t edac_dev_ctl_info_show(struct kobject *kobj,
1188c2ecf20Sopenharmony_ci				struct attribute *attr, char *buffer)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj);
1218c2ecf20Sopenharmony_ci	struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (ctl_info_attr->show)
1248c2ecf20Sopenharmony_ci		return ctl_info_attr->show(edac_dev, buffer);
1258c2ecf20Sopenharmony_ci	return -EIO;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/* Function to 'store' fields into the edac_dev 'ctl_info' structure */
1298c2ecf20Sopenharmony_cistatic ssize_t edac_dev_ctl_info_store(struct kobject *kobj,
1308c2ecf20Sopenharmony_ci				struct attribute *attr,
1318c2ecf20Sopenharmony_ci				const char *buffer, size_t count)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj);
1348c2ecf20Sopenharmony_ci	struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (ctl_info_attr->store)
1378c2ecf20Sopenharmony_ci		return ctl_info_attr->store(edac_dev, buffer, count);
1388c2ecf20Sopenharmony_ci	return -EIO;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/* edac_dev file operations for an 'ctl_info' */
1428c2ecf20Sopenharmony_cistatic const struct sysfs_ops device_ctl_info_ops = {
1438c2ecf20Sopenharmony_ci	.show = edac_dev_ctl_info_show,
1448c2ecf20Sopenharmony_ci	.store = edac_dev_ctl_info_store
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci#define CTL_INFO_ATTR(_name,_mode,_show,_store)        \
1488c2ecf20Sopenharmony_cistatic struct ctl_info_attribute attr_ctl_info_##_name = {      \
1498c2ecf20Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },   \
1508c2ecf20Sopenharmony_ci	.show   = _show,                                        \
1518c2ecf20Sopenharmony_ci	.store  = _store,                                       \
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* Declare the various ctl_info attributes here and their respective ops */
1558c2ecf20Sopenharmony_ciCTL_INFO_ATTR(log_ue, S_IRUGO | S_IWUSR,
1568c2ecf20Sopenharmony_ci	edac_device_ctl_log_ue_show, edac_device_ctl_log_ue_store);
1578c2ecf20Sopenharmony_ciCTL_INFO_ATTR(log_ce, S_IRUGO | S_IWUSR,
1588c2ecf20Sopenharmony_ci	edac_device_ctl_log_ce_show, edac_device_ctl_log_ce_store);
1598c2ecf20Sopenharmony_ciCTL_INFO_ATTR(panic_on_ue, S_IRUGO | S_IWUSR,
1608c2ecf20Sopenharmony_ci	edac_device_ctl_panic_on_ue_show,
1618c2ecf20Sopenharmony_ci	edac_device_ctl_panic_on_ue_store);
1628c2ecf20Sopenharmony_ciCTL_INFO_ATTR(poll_msec, S_IRUGO | S_IWUSR,
1638c2ecf20Sopenharmony_ci	edac_device_ctl_poll_msec_show, edac_device_ctl_poll_msec_store);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* Base Attributes of the EDAC_DEVICE ECC object */
1668c2ecf20Sopenharmony_cistatic struct ctl_info_attribute *device_ctrl_attr[] = {
1678c2ecf20Sopenharmony_ci	&attr_ctl_info_panic_on_ue,
1688c2ecf20Sopenharmony_ci	&attr_ctl_info_log_ue,
1698c2ecf20Sopenharmony_ci	&attr_ctl_info_log_ce,
1708c2ecf20Sopenharmony_ci	&attr_ctl_info_poll_msec,
1718c2ecf20Sopenharmony_ci	NULL,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci * edac_device_ctrl_master_release
1768c2ecf20Sopenharmony_ci *
1778c2ecf20Sopenharmony_ci *	called when the reference count for the 'main' kobj
1788c2ecf20Sopenharmony_ci *	for a edac_device control struct reaches zero
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci *	Reference count model:
1818c2ecf20Sopenharmony_ci *		One 'main' kobject for each control structure allocated.
1828c2ecf20Sopenharmony_ci *		That main kobj is initially set to one AND
1838c2ecf20Sopenharmony_ci *		the reference count for the EDAC 'core' module is
1848c2ecf20Sopenharmony_ci *		bumped by one, thus added 'keep in memory' dependency.
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci *		Each new internal kobj (in instances and blocks) then
1878c2ecf20Sopenharmony_ci *		bumps the 'main' kobject.
1888c2ecf20Sopenharmony_ci *
1898c2ecf20Sopenharmony_ci *		When they are released their release functions decrement
1908c2ecf20Sopenharmony_ci *		the 'main' kobj.
1918c2ecf20Sopenharmony_ci *
1928c2ecf20Sopenharmony_ci *		When the main kobj reaches zero (0) then THIS function
1938c2ecf20Sopenharmony_ci *		is called which then decrements the EDAC 'core' module.
1948c2ecf20Sopenharmony_ci *		When the module reference count reaches zero then the
1958c2ecf20Sopenharmony_ci *		module no longer has dependency on keeping the release
1968c2ecf20Sopenharmony_ci *		function code in memory and module can be unloaded.
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci *		This will support several control objects as well, each
1998c2ecf20Sopenharmony_ci *		with its own 'main' kobj.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cistatic void edac_device_ctrl_master_release(struct kobject *kobj)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = to_edacdev(kobj);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	edac_dbg(4, "control index=%d\n", edac_dev->dev_idx);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* decrement the EDAC CORE module ref count */
2088c2ecf20Sopenharmony_ci	module_put(edac_dev->owner);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* free the control struct containing the 'main' kobj
2118c2ecf20Sopenharmony_ci	 * passed in to this routine
2128c2ecf20Sopenharmony_ci	 */
2138c2ecf20Sopenharmony_ci	kfree(edac_dev);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* ktype for the main (master) kobject */
2178c2ecf20Sopenharmony_cistatic struct kobj_type ktype_device_ctrl = {
2188c2ecf20Sopenharmony_ci	.release = edac_device_ctrl_master_release,
2198c2ecf20Sopenharmony_ci	.sysfs_ops = &device_ctl_info_ops,
2208c2ecf20Sopenharmony_ci	.default_attrs = (struct attribute **)device_ctrl_attr,
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*
2248c2ecf20Sopenharmony_ci * edac_device_register_sysfs_main_kobj
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci *	perform the high level setup for the new edac_device instance
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci * Return:  0 SUCCESS
2298c2ecf20Sopenharmony_ci *         !0 FAILURE
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_ciint edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct bus_type *edac_subsys;
2348c2ecf20Sopenharmony_ci	int err;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* get the /sys/devices/system/edac reference */
2398c2ecf20Sopenharmony_ci	edac_subsys = edac_get_sysfs_subsys();
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/* Point to the 'edac_subsys' this instance 'reports' to */
2428c2ecf20Sopenharmony_ci	edac_dev->edac_subsys = edac_subsys;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* Init the devices's kobject */
2458c2ecf20Sopenharmony_ci	memset(&edac_dev->kobj, 0, sizeof(struct kobject));
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Record which module 'owns' this control structure
2488c2ecf20Sopenharmony_ci	 * and bump the ref count of the module
2498c2ecf20Sopenharmony_ci	 */
2508c2ecf20Sopenharmony_ci	edac_dev->owner = THIS_MODULE;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (!try_module_get(edac_dev->owner)) {
2538c2ecf20Sopenharmony_ci		err = -ENODEV;
2548c2ecf20Sopenharmony_ci		goto err_out;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* register */
2588c2ecf20Sopenharmony_ci	err = kobject_init_and_add(&edac_dev->kobj, &ktype_device_ctrl,
2598c2ecf20Sopenharmony_ci				   &edac_subsys->dev_root->kobj,
2608c2ecf20Sopenharmony_ci				   "%s", edac_dev->name);
2618c2ecf20Sopenharmony_ci	if (err) {
2628c2ecf20Sopenharmony_ci		edac_dbg(1, "Failed to register '.../edac/%s'\n",
2638c2ecf20Sopenharmony_ci			 edac_dev->name);
2648c2ecf20Sopenharmony_ci		goto err_kobj_reg;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	kobject_uevent(&edac_dev->kobj, KOBJ_ADD);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* At this point, to 'free' the control struct,
2698c2ecf20Sopenharmony_ci	 * edac_device_unregister_sysfs_main_kobj() must be used
2708c2ecf20Sopenharmony_ci	 */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	edac_dbg(4, "Registered '.../edac/%s' kobject\n", edac_dev->name);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Error exit stack */
2778c2ecf20Sopenharmony_cierr_kobj_reg:
2788c2ecf20Sopenharmony_ci	kobject_put(&edac_dev->kobj);
2798c2ecf20Sopenharmony_ci	module_put(edac_dev->owner);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cierr_out:
2828c2ecf20Sopenharmony_ci	return err;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci/*
2868c2ecf20Sopenharmony_ci * edac_device_unregister_sysfs_main_kobj:
2878c2ecf20Sopenharmony_ci *	the '..../edac/<name>' kobject
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_civoid edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
2928c2ecf20Sopenharmony_ci	edac_dbg(4, "name of kobject is: %s\n", kobject_name(&dev->kobj));
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/*
2958c2ecf20Sopenharmony_ci	 * Unregister the edac device's kobject and
2968c2ecf20Sopenharmony_ci	 * allow for reference count to reach 0 at which point
2978c2ecf20Sopenharmony_ci	 * the callback will be called to:
2988c2ecf20Sopenharmony_ci	 *   a) module_put() this module
2998c2ecf20Sopenharmony_ci	 *   b) 'kfree' the memory
3008c2ecf20Sopenharmony_ci	 */
3018c2ecf20Sopenharmony_ci	kobject_put(&dev->kobj);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/* edac_dev -> instance information */
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/*
3078c2ecf20Sopenharmony_ci * Set of low-level instance attribute show functions
3088c2ecf20Sopenharmony_ci */
3098c2ecf20Sopenharmony_cistatic ssize_t instance_ue_count_show(struct edac_device_instance *instance,
3108c2ecf20Sopenharmony_ci				char *data)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", instance->counters.ue_count);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic ssize_t instance_ce_count_show(struct edac_device_instance *instance,
3168c2ecf20Sopenharmony_ci				char *data)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", instance->counters.ce_count);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci#define to_instance(k) container_of(k, struct edac_device_instance, kobj)
3228c2ecf20Sopenharmony_ci#define to_instance_attr(a) container_of(a,struct instance_attribute,attr)
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/* DEVICE instance kobject release() function */
3258c2ecf20Sopenharmony_cistatic void edac_device_ctrl_instance_release(struct kobject *kobj)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct edac_device_instance *instance;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* map from this kobj to the main control struct
3328c2ecf20Sopenharmony_ci	 * and then dec the main kobj count
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	instance = to_instance(kobj);
3358c2ecf20Sopenharmony_ci	kobject_put(&instance->ctl->kobj);
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci/* instance specific attribute structure */
3398c2ecf20Sopenharmony_cistruct instance_attribute {
3408c2ecf20Sopenharmony_ci	struct attribute attr;
3418c2ecf20Sopenharmony_ci	ssize_t(*show) (struct edac_device_instance *, char *);
3428c2ecf20Sopenharmony_ci	ssize_t(*store) (struct edac_device_instance *, const char *, size_t);
3438c2ecf20Sopenharmony_ci};
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci/* Function to 'show' fields from the edac_dev 'instance' structure */
3468c2ecf20Sopenharmony_cistatic ssize_t edac_dev_instance_show(struct kobject *kobj,
3478c2ecf20Sopenharmony_ci				struct attribute *attr, char *buffer)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct edac_device_instance *instance = to_instance(kobj);
3508c2ecf20Sopenharmony_ci	struct instance_attribute *instance_attr = to_instance_attr(attr);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (instance_attr->show)
3538c2ecf20Sopenharmony_ci		return instance_attr->show(instance, buffer);
3548c2ecf20Sopenharmony_ci	return -EIO;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/* Function to 'store' fields into the edac_dev 'instance' structure */
3588c2ecf20Sopenharmony_cistatic ssize_t edac_dev_instance_store(struct kobject *kobj,
3598c2ecf20Sopenharmony_ci				struct attribute *attr,
3608c2ecf20Sopenharmony_ci				const char *buffer, size_t count)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct edac_device_instance *instance = to_instance(kobj);
3638c2ecf20Sopenharmony_ci	struct instance_attribute *instance_attr = to_instance_attr(attr);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (instance_attr->store)
3668c2ecf20Sopenharmony_ci		return instance_attr->store(instance, buffer, count);
3678c2ecf20Sopenharmony_ci	return -EIO;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/* edac_dev file operations for an 'instance' */
3718c2ecf20Sopenharmony_cistatic const struct sysfs_ops device_instance_ops = {
3728c2ecf20Sopenharmony_ci	.show = edac_dev_instance_show,
3738c2ecf20Sopenharmony_ci	.store = edac_dev_instance_store
3748c2ecf20Sopenharmony_ci};
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci#define INSTANCE_ATTR(_name,_mode,_show,_store)        \
3778c2ecf20Sopenharmony_cistatic struct instance_attribute attr_instance_##_name = {      \
3788c2ecf20Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },   \
3798c2ecf20Sopenharmony_ci	.show   = _show,                                        \
3808c2ecf20Sopenharmony_ci	.store  = _store,                                       \
3818c2ecf20Sopenharmony_ci};
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/*
3848c2ecf20Sopenharmony_ci * Define attributes visible for the edac_device instance object
3858c2ecf20Sopenharmony_ci *	Each contains a pointer to a show and an optional set
3868c2ecf20Sopenharmony_ci *	function pointer that does the low level output/input
3878c2ecf20Sopenharmony_ci */
3888c2ecf20Sopenharmony_ciINSTANCE_ATTR(ce_count, S_IRUGO, instance_ce_count_show, NULL);
3898c2ecf20Sopenharmony_ciINSTANCE_ATTR(ue_count, S_IRUGO, instance_ue_count_show, NULL);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/* list of edac_dev 'instance' attributes */
3928c2ecf20Sopenharmony_cistatic struct instance_attribute *device_instance_attr[] = {
3938c2ecf20Sopenharmony_ci	&attr_instance_ce_count,
3948c2ecf20Sopenharmony_ci	&attr_instance_ue_count,
3958c2ecf20Sopenharmony_ci	NULL,
3968c2ecf20Sopenharmony_ci};
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/* The 'ktype' for each edac_dev 'instance' */
3998c2ecf20Sopenharmony_cistatic struct kobj_type ktype_instance_ctrl = {
4008c2ecf20Sopenharmony_ci	.release = edac_device_ctrl_instance_release,
4018c2ecf20Sopenharmony_ci	.sysfs_ops = &device_instance_ops,
4028c2ecf20Sopenharmony_ci	.default_attrs = (struct attribute **)device_instance_attr,
4038c2ecf20Sopenharmony_ci};
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci/* edac_dev -> instance -> block information */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci#define to_block(k) container_of(k, struct edac_device_block, kobj)
4088c2ecf20Sopenharmony_ci#define to_block_attr(a) \
4098c2ecf20Sopenharmony_ci	container_of(a, struct edac_dev_sysfs_block_attribute, attr)
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/*
4128c2ecf20Sopenharmony_ci * Set of low-level block attribute show functions
4138c2ecf20Sopenharmony_ci */
4148c2ecf20Sopenharmony_cistatic ssize_t block_ue_count_show(struct kobject *kobj,
4158c2ecf20Sopenharmony_ci					struct attribute *attr, char *data)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct edac_device_block *block = to_block(kobj);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", block->counters.ue_count);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic ssize_t block_ce_count_show(struct kobject *kobj,
4238c2ecf20Sopenharmony_ci					struct attribute *attr, char *data)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct edac_device_block *block = to_block(kobj);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return sprintf(data, "%u\n", block->counters.ce_count);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci/* DEVICE block kobject release() function */
4318c2ecf20Sopenharmony_cistatic void edac_device_ctrl_block_release(struct kobject *kobj)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct edac_device_block *block;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	edac_dbg(1, "\n");
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* get the container of the kobj */
4388c2ecf20Sopenharmony_ci	block = to_block(kobj);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* map from 'block kobj' to 'block->instance->controller->main_kobj'
4418c2ecf20Sopenharmony_ci	 * now 'release' the block kobject
4428c2ecf20Sopenharmony_ci	 */
4438c2ecf20Sopenharmony_ci	kobject_put(&block->instance->ctl->kobj);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci/* Function to 'show' fields from the edac_dev 'block' structure */
4488c2ecf20Sopenharmony_cistatic ssize_t edac_dev_block_show(struct kobject *kobj,
4498c2ecf20Sopenharmony_ci				struct attribute *attr, char *buffer)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_block_attribute *block_attr =
4528c2ecf20Sopenharmony_ci						to_block_attr(attr);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (block_attr->show)
4558c2ecf20Sopenharmony_ci		return block_attr->show(kobj, attr, buffer);
4568c2ecf20Sopenharmony_ci	return -EIO;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/* Function to 'store' fields into the edac_dev 'block' structure */
4608c2ecf20Sopenharmony_cistatic ssize_t edac_dev_block_store(struct kobject *kobj,
4618c2ecf20Sopenharmony_ci				struct attribute *attr,
4628c2ecf20Sopenharmony_ci				const char *buffer, size_t count)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_block_attribute *block_attr;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	block_attr = to_block_attr(attr);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (block_attr->store)
4698c2ecf20Sopenharmony_ci		return block_attr->store(kobj, attr, buffer, count);
4708c2ecf20Sopenharmony_ci	return -EIO;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/* edac_dev file operations for a 'block' */
4748c2ecf20Sopenharmony_cistatic const struct sysfs_ops device_block_ops = {
4758c2ecf20Sopenharmony_ci	.show = edac_dev_block_show,
4768c2ecf20Sopenharmony_ci	.store = edac_dev_block_store
4778c2ecf20Sopenharmony_ci};
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci#define BLOCK_ATTR(_name,_mode,_show,_store)        \
4808c2ecf20Sopenharmony_cistatic struct edac_dev_sysfs_block_attribute attr_block_##_name = {	\
4818c2ecf20Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },   \
4828c2ecf20Sopenharmony_ci	.show   = _show,                                        \
4838c2ecf20Sopenharmony_ci	.store  = _store,                                       \
4848c2ecf20Sopenharmony_ci};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ciBLOCK_ATTR(ce_count, S_IRUGO, block_ce_count_show, NULL);
4878c2ecf20Sopenharmony_ciBLOCK_ATTR(ue_count, S_IRUGO, block_ue_count_show, NULL);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/* list of edac_dev 'block' attributes */
4908c2ecf20Sopenharmony_cistatic struct edac_dev_sysfs_block_attribute *device_block_attr[] = {
4918c2ecf20Sopenharmony_ci	&attr_block_ce_count,
4928c2ecf20Sopenharmony_ci	&attr_block_ue_count,
4938c2ecf20Sopenharmony_ci	NULL,
4948c2ecf20Sopenharmony_ci};
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/* The 'ktype' for each edac_dev 'block' */
4978c2ecf20Sopenharmony_cistatic struct kobj_type ktype_block_ctrl = {
4988c2ecf20Sopenharmony_ci	.release = edac_device_ctrl_block_release,
4998c2ecf20Sopenharmony_ci	.sysfs_ops = &device_block_ops,
5008c2ecf20Sopenharmony_ci	.default_attrs = (struct attribute **)device_block_attr,
5018c2ecf20Sopenharmony_ci};
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci/* block ctor/dtor  code */
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/*
5068c2ecf20Sopenharmony_ci * edac_device_create_block
5078c2ecf20Sopenharmony_ci */
5088c2ecf20Sopenharmony_cistatic int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
5098c2ecf20Sopenharmony_ci				struct edac_device_instance *instance,
5108c2ecf20Sopenharmony_ci				struct edac_device_block *block)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	int i;
5138c2ecf20Sopenharmony_ci	int err;
5148c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_block_attribute *sysfs_attrib;
5158c2ecf20Sopenharmony_ci	struct kobject *main_kobj;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	edac_dbg(4, "Instance '%s' inst_p=%p  block '%s'  block_p=%p\n",
5188c2ecf20Sopenharmony_ci		 instance->name, instance, block->name, block);
5198c2ecf20Sopenharmony_ci	edac_dbg(4, "block kobj=%p  block kobj->parent=%p\n",
5208c2ecf20Sopenharmony_ci		 &block->kobj, &block->kobj.parent);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/* init this block's kobject */
5238c2ecf20Sopenharmony_ci	memset(&block->kobj, 0, sizeof(struct kobject));
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* bump the main kobject's reference count for this controller
5268c2ecf20Sopenharmony_ci	 * and this instance is dependent on the main
5278c2ecf20Sopenharmony_ci	 */
5288c2ecf20Sopenharmony_ci	main_kobj = kobject_get(&edac_dev->kobj);
5298c2ecf20Sopenharmony_ci	if (!main_kobj) {
5308c2ecf20Sopenharmony_ci		err = -ENODEV;
5318c2ecf20Sopenharmony_ci		goto err_out;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Add this block's kobject */
5358c2ecf20Sopenharmony_ci	err = kobject_init_and_add(&block->kobj, &ktype_block_ctrl,
5368c2ecf20Sopenharmony_ci				   &instance->kobj,
5378c2ecf20Sopenharmony_ci				   "%s", block->name);
5388c2ecf20Sopenharmony_ci	if (err) {
5398c2ecf20Sopenharmony_ci		edac_dbg(1, "Failed to register instance '%s'\n", block->name);
5408c2ecf20Sopenharmony_ci		kobject_put(main_kobj);
5418c2ecf20Sopenharmony_ci		err = -ENODEV;
5428c2ecf20Sopenharmony_ci		goto err_out;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* If there are driver level block attributes, then added them
5468c2ecf20Sopenharmony_ci	 * to the block kobject
5478c2ecf20Sopenharmony_ci	 */
5488c2ecf20Sopenharmony_ci	sysfs_attrib = block->block_attributes;
5498c2ecf20Sopenharmony_ci	if (sysfs_attrib && block->nr_attribs) {
5508c2ecf20Sopenharmony_ci		for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) {
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci			edac_dbg(4, "creating block attrib='%s' attrib->%p to kobj=%p\n",
5538c2ecf20Sopenharmony_ci				 sysfs_attrib->attr.name,
5548c2ecf20Sopenharmony_ci				 sysfs_attrib, &block->kobj);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci			/* Create each block_attribute file */
5578c2ecf20Sopenharmony_ci			err = sysfs_create_file(&block->kobj,
5588c2ecf20Sopenharmony_ci				&sysfs_attrib->attr);
5598c2ecf20Sopenharmony_ci			if (err)
5608c2ecf20Sopenharmony_ci				goto err_on_attrib;
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	kobject_uevent(&block->kobj, KOBJ_ADD);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	return 0;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* Error unwind stack */
5688c2ecf20Sopenharmony_cierr_on_attrib:
5698c2ecf20Sopenharmony_ci	kobject_put(&block->kobj);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cierr_out:
5728c2ecf20Sopenharmony_ci	return err;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci/*
5768c2ecf20Sopenharmony_ci * edac_device_delete_block(edac_dev,block);
5778c2ecf20Sopenharmony_ci */
5788c2ecf20Sopenharmony_cistatic void edac_device_delete_block(struct edac_device_ctl_info *edac_dev,
5798c2ecf20Sopenharmony_ci				struct edac_device_block *block)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_block_attribute *sysfs_attrib;
5828c2ecf20Sopenharmony_ci	int i;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* if this block has 'attributes' then we need to iterate over the list
5858c2ecf20Sopenharmony_ci	 * and 'remove' the attributes on this block
5868c2ecf20Sopenharmony_ci	 */
5878c2ecf20Sopenharmony_ci	sysfs_attrib = block->block_attributes;
5888c2ecf20Sopenharmony_ci	if (sysfs_attrib && block->nr_attribs) {
5898c2ecf20Sopenharmony_ci		for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) {
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci			/* remove each block_attrib file */
5928c2ecf20Sopenharmony_ci			sysfs_remove_file(&block->kobj,
5938c2ecf20Sopenharmony_ci				(struct attribute *) sysfs_attrib);
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* unregister this block's kobject, SEE:
5988c2ecf20Sopenharmony_ci	 *	edac_device_ctrl_block_release() callback operation
5998c2ecf20Sopenharmony_ci	 */
6008c2ecf20Sopenharmony_ci	kobject_put(&block->kobj);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/* instance ctor/dtor code */
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci/*
6068c2ecf20Sopenharmony_ci * edac_device_create_instance
6078c2ecf20Sopenharmony_ci *	create just one instance of an edac_device 'instance'
6088c2ecf20Sopenharmony_ci */
6098c2ecf20Sopenharmony_cistatic int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
6108c2ecf20Sopenharmony_ci				int idx)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	int i, j;
6138c2ecf20Sopenharmony_ci	int err;
6148c2ecf20Sopenharmony_ci	struct edac_device_instance *instance;
6158c2ecf20Sopenharmony_ci	struct kobject *main_kobj;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	instance = &edac_dev->instances[idx];
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/* Init the instance's kobject */
6208c2ecf20Sopenharmony_ci	memset(&instance->kobj, 0, sizeof(struct kobject));
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	instance->ctl = edac_dev;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* bump the main kobject's reference count for this controller
6258c2ecf20Sopenharmony_ci	 * and this instance is dependent on the main
6268c2ecf20Sopenharmony_ci	 */
6278c2ecf20Sopenharmony_ci	main_kobj = kobject_get(&edac_dev->kobj);
6288c2ecf20Sopenharmony_ci	if (!main_kobj) {
6298c2ecf20Sopenharmony_ci		err = -ENODEV;
6308c2ecf20Sopenharmony_ci		goto err_out;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	/* Formally register this instance's kobject under the edac_device */
6348c2ecf20Sopenharmony_ci	err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl,
6358c2ecf20Sopenharmony_ci				   &edac_dev->kobj, "%s", instance->name);
6368c2ecf20Sopenharmony_ci	if (err != 0) {
6378c2ecf20Sopenharmony_ci		edac_dbg(2, "Failed to register instance '%s'\n",
6388c2ecf20Sopenharmony_ci			 instance->name);
6398c2ecf20Sopenharmony_ci		kobject_put(main_kobj);
6408c2ecf20Sopenharmony_ci		goto err_out;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	edac_dbg(4, "now register '%d' blocks for instance %d\n",
6448c2ecf20Sopenharmony_ci		 instance->nr_blocks, idx);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* register all blocks of this instance */
6478c2ecf20Sopenharmony_ci	for (i = 0; i < instance->nr_blocks; i++) {
6488c2ecf20Sopenharmony_ci		err = edac_device_create_block(edac_dev, instance,
6498c2ecf20Sopenharmony_ci						&instance->blocks[i]);
6508c2ecf20Sopenharmony_ci		if (err) {
6518c2ecf20Sopenharmony_ci			/* If any fail, remove all previous ones */
6528c2ecf20Sopenharmony_ci			for (j = 0; j < i; j++)
6538c2ecf20Sopenharmony_ci				edac_device_delete_block(edac_dev,
6548c2ecf20Sopenharmony_ci							&instance->blocks[j]);
6558c2ecf20Sopenharmony_ci			goto err_release_instance_kobj;
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci	kobject_uevent(&instance->kobj, KOBJ_ADD);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	edac_dbg(4, "Registered instance %d '%s' kobject\n",
6618c2ecf20Sopenharmony_ci		 idx, instance->name);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	return 0;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* error unwind stack */
6668c2ecf20Sopenharmony_cierr_release_instance_kobj:
6678c2ecf20Sopenharmony_ci	kobject_put(&instance->kobj);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cierr_out:
6708c2ecf20Sopenharmony_ci	return err;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/*
6748c2ecf20Sopenharmony_ci * edac_device_remove_instance
6758c2ecf20Sopenharmony_ci *	remove an edac_device instance
6768c2ecf20Sopenharmony_ci */
6778c2ecf20Sopenharmony_cistatic void edac_device_delete_instance(struct edac_device_ctl_info *edac_dev,
6788c2ecf20Sopenharmony_ci					int idx)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct edac_device_instance *instance;
6818c2ecf20Sopenharmony_ci	int i;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	instance = &edac_dev->instances[idx];
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* unregister all blocks in this instance */
6868c2ecf20Sopenharmony_ci	for (i = 0; i < instance->nr_blocks; i++)
6878c2ecf20Sopenharmony_ci		edac_device_delete_block(edac_dev, &instance->blocks[i]);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* unregister this instance's kobject, SEE:
6908c2ecf20Sopenharmony_ci	 *	edac_device_ctrl_instance_release() for callback operation
6918c2ecf20Sopenharmony_ci	 */
6928c2ecf20Sopenharmony_ci	kobject_put(&instance->kobj);
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci/*
6968c2ecf20Sopenharmony_ci * edac_device_create_instances
6978c2ecf20Sopenharmony_ci *	create the first level of 'instances' for this device
6988c2ecf20Sopenharmony_ci *	(ie  'cache' might have 'cache0', 'cache1', 'cache2', etc
6998c2ecf20Sopenharmony_ci */
7008c2ecf20Sopenharmony_cistatic int edac_device_create_instances(struct edac_device_ctl_info *edac_dev)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	int i, j;
7038c2ecf20Sopenharmony_ci	int err;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* iterate over creation of the instances */
7088c2ecf20Sopenharmony_ci	for (i = 0; i < edac_dev->nr_instances; i++) {
7098c2ecf20Sopenharmony_ci		err = edac_device_create_instance(edac_dev, i);
7108c2ecf20Sopenharmony_ci		if (err) {
7118c2ecf20Sopenharmony_ci			/* unwind previous instances on error */
7128c2ecf20Sopenharmony_ci			for (j = 0; j < i; j++)
7138c2ecf20Sopenharmony_ci				edac_device_delete_instance(edac_dev, j);
7148c2ecf20Sopenharmony_ci			return err;
7158c2ecf20Sopenharmony_ci		}
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	return 0;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/*
7228c2ecf20Sopenharmony_ci * edac_device_delete_instances(edac_dev);
7238c2ecf20Sopenharmony_ci *	unregister all the kobjects of the instances
7248c2ecf20Sopenharmony_ci */
7258c2ecf20Sopenharmony_cistatic void edac_device_delete_instances(struct edac_device_ctl_info *edac_dev)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	int i;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* iterate over creation of the instances */
7308c2ecf20Sopenharmony_ci	for (i = 0; i < edac_dev->nr_instances; i++)
7318c2ecf20Sopenharmony_ci		edac_device_delete_instance(edac_dev, i);
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci/* edac_dev sysfs ctor/dtor  code */
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci/*
7378c2ecf20Sopenharmony_ci * edac_device_add_main_sysfs_attributes
7388c2ecf20Sopenharmony_ci *	add some attributes to this instance's main kobject
7398c2ecf20Sopenharmony_ci */
7408c2ecf20Sopenharmony_cistatic int edac_device_add_main_sysfs_attributes(
7418c2ecf20Sopenharmony_ci			struct edac_device_ctl_info *edac_dev)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_attribute *sysfs_attrib;
7448c2ecf20Sopenharmony_ci	int err = 0;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	sysfs_attrib = edac_dev->sysfs_attributes;
7478c2ecf20Sopenharmony_ci	if (sysfs_attrib) {
7488c2ecf20Sopenharmony_ci		/* iterate over the array and create an attribute for each
7498c2ecf20Sopenharmony_ci		 * entry in the list
7508c2ecf20Sopenharmony_ci		 */
7518c2ecf20Sopenharmony_ci		while (sysfs_attrib->attr.name != NULL) {
7528c2ecf20Sopenharmony_ci			err = sysfs_create_file(&edac_dev->kobj,
7538c2ecf20Sopenharmony_ci				(struct attribute*) sysfs_attrib);
7548c2ecf20Sopenharmony_ci			if (err)
7558c2ecf20Sopenharmony_ci				goto err_out;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci			sysfs_attrib++;
7588c2ecf20Sopenharmony_ci		}
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cierr_out:
7628c2ecf20Sopenharmony_ci	return err;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci/*
7668c2ecf20Sopenharmony_ci * edac_device_remove_main_sysfs_attributes
7678c2ecf20Sopenharmony_ci *	remove any attributes to this instance's main kobject
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_cistatic void edac_device_remove_main_sysfs_attributes(
7708c2ecf20Sopenharmony_ci			struct edac_device_ctl_info *edac_dev)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct edac_dev_sysfs_attribute *sysfs_attrib;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	/* if there are main attributes, defined, remove them. First,
7758c2ecf20Sopenharmony_ci	 * point to the start of the array and iterate over it
7768c2ecf20Sopenharmony_ci	 * removing each attribute listed from this device's instance's kobject
7778c2ecf20Sopenharmony_ci	 */
7788c2ecf20Sopenharmony_ci	sysfs_attrib = edac_dev->sysfs_attributes;
7798c2ecf20Sopenharmony_ci	if (sysfs_attrib) {
7808c2ecf20Sopenharmony_ci		while (sysfs_attrib->attr.name != NULL) {
7818c2ecf20Sopenharmony_ci			sysfs_remove_file(&edac_dev->kobj,
7828c2ecf20Sopenharmony_ci					(struct attribute *) sysfs_attrib);
7838c2ecf20Sopenharmony_ci			sysfs_attrib++;
7848c2ecf20Sopenharmony_ci		}
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci/*
7898c2ecf20Sopenharmony_ci * edac_device_create_sysfs() Constructor
7908c2ecf20Sopenharmony_ci *
7918c2ecf20Sopenharmony_ci * accept a created edac_device control structure
7928c2ecf20Sopenharmony_ci * and 'export' it to sysfs. The 'main' kobj should already have been
7938c2ecf20Sopenharmony_ci * created. 'instance' and 'block' kobjects should be registered
7948c2ecf20Sopenharmony_ci * along with any 'block' attributes from the low driver. In addition,
7958c2ecf20Sopenharmony_ci * the main attributes (if any) are connected to the main kobject of
7968c2ecf20Sopenharmony_ci * the control structure.
7978c2ecf20Sopenharmony_ci *
7988c2ecf20Sopenharmony_ci * Return:
7998c2ecf20Sopenharmony_ci *	0	Success
8008c2ecf20Sopenharmony_ci *	!0	Failure
8018c2ecf20Sopenharmony_ci */
8028c2ecf20Sopenharmony_ciint edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	int err;
8058c2ecf20Sopenharmony_ci	struct kobject *edac_kobj = &edac_dev->kobj;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	edac_dbg(0, "idx=%d\n", edac_dev->dev_idx);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/*  go create any main attributes callers wants */
8108c2ecf20Sopenharmony_ci	err = edac_device_add_main_sysfs_attributes(edac_dev);
8118c2ecf20Sopenharmony_ci	if (err) {
8128c2ecf20Sopenharmony_ci		edac_dbg(0, "failed to add sysfs attribs\n");
8138c2ecf20Sopenharmony_ci		goto err_out;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/* create a symlink from the edac device
8178c2ecf20Sopenharmony_ci	 * to the platform 'device' being used for this
8188c2ecf20Sopenharmony_ci	 */
8198c2ecf20Sopenharmony_ci	err = sysfs_create_link(edac_kobj,
8208c2ecf20Sopenharmony_ci				&edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK);
8218c2ecf20Sopenharmony_ci	if (err) {
8228c2ecf20Sopenharmony_ci		edac_dbg(0, "sysfs_create_link() returned err= %d\n", err);
8238c2ecf20Sopenharmony_ci		goto err_remove_main_attribs;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* Create the first level instance directories
8278c2ecf20Sopenharmony_ci	 * In turn, the nested blocks beneath the instances will
8288c2ecf20Sopenharmony_ci	 * be registered as well
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	err = edac_device_create_instances(edac_dev);
8318c2ecf20Sopenharmony_ci	if (err) {
8328c2ecf20Sopenharmony_ci		edac_dbg(0, "edac_device_create_instances() returned err= %d\n",
8338c2ecf20Sopenharmony_ci			 err);
8348c2ecf20Sopenharmony_ci		goto err_remove_link;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	edac_dbg(4, "create-instances done, idx=%d\n", edac_dev->dev_idx);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return 0;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	/* Error unwind stack */
8438c2ecf20Sopenharmony_cierr_remove_link:
8448c2ecf20Sopenharmony_ci	/* remove the sym link */
8458c2ecf20Sopenharmony_ci	sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cierr_remove_main_attribs:
8488c2ecf20Sopenharmony_ci	edac_device_remove_main_sysfs_attributes(edac_dev);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cierr_out:
8518c2ecf20Sopenharmony_ci	return err;
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci/*
8558c2ecf20Sopenharmony_ci * edac_device_remove_sysfs() destructor
8568c2ecf20Sopenharmony_ci *
8578c2ecf20Sopenharmony_ci * given an edac_device struct, tear down the kobject resources
8588c2ecf20Sopenharmony_ci */
8598c2ecf20Sopenharmony_civoid edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* remove any main attributes for this device */
8648c2ecf20Sopenharmony_ci	edac_device_remove_main_sysfs_attributes(edac_dev);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	/* remove the device sym link */
8678c2ecf20Sopenharmony_ci	sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* walk the instance/block kobject tree, deconstructing it */
8708c2ecf20Sopenharmony_ci	edac_device_delete_instances(edac_dev);
8718c2ecf20Sopenharmony_ci}
872