aboutsummaryrefslogtreecommitdiff
path: root/drivers/edac/edac_device_sysfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/edac/edac_device_sysfs.c')
-rw-r--r--drivers/edac/edac_device_sysfs.c837
1 files changed, 837 insertions, 0 deletions
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c
new file mode 100644
index 00000000000..afb19050264
--- /dev/null
+++ b/drivers/edac/edac_device_sysfs.c
@@ -0,0 +1,837 @@
+/*
+ * file for managing the edac_device class of devices for EDAC
+ *
+ * (C) 2007 SoftwareBitMaker(http://www.softwarebitmaker.com)
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ *
+ * Written Doug Thompson <norsk5@xmission.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sysdev.h>
+#include <linux/ctype.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+
+#define EDAC_DEVICE_SYMLINK "device"
+
+#define to_edacdev(k) container_of(k, struct edac_device_ctl_info, kobj)
+#define to_edacdev_attr(a) container_of(a, struct edacdev_attribute, attr)
+
+#ifdef DKT
+
+static ssize_t edac_dev_ue_count_show(struct edac_device_ctl_info *edac_dev,
+ char *data)
+{
+ return sprintf(data,"%d\n", edac_dev->ue_count);
+}
+
+static ssize_t edac_dev_ce_count_show(struct edac_device_ctl_info *edac_dev,
+ char *data)
+{
+ return sprintf(data,"%d\n", edac_dev->ce_count);
+}
+
+static ssize_t edac_dev_seconds_show(struct edac_device_ctl_info *edac_dev,
+ char *data)
+{
+ return sprintf(data,"%ld\n", (jiffies - edac_dev->start_time) / HZ);
+}
+
+static ssize_t edac_dev_ctl_name_show(struct edac_device_ctl_info *edac_dev,
+ char *data)
+{
+ return sprintf(data,"%s\n", edac_dev->ctl_name);
+}
+
+
+struct edacdev_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct edac_device_ctl_info *,char *);
+ ssize_t (*store)(struct edac_device_ctl_info *, const char *,size_t);
+};
+
+
+/* EDAC DEVICE show/store functions for top most object */
+static ssize_t edacdev_show(struct kobject *kobj, struct attribute *attr,
+ char *buffer)
+{
+ struct edac_device_ctl_info *edac_dev = to_edacdev(kobj);
+ struct edacdev_attribute * edacdev_attr = to_edacdev_attr(attr);
+
+ if (edacdev_attr->show)
+ return edacdev_attr->show(edac_dev, buffer);
+
+ return -EIO;
+}
+
+static ssize_t edacdev_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct edac_device_ctl_info *edac_dev = to_edacdev(kobj);
+ struct edacdev_attribute * edacdev_attr = to_edacdev_attr(attr);
+
+ if (edacdev_attr->store)
+ return edacdev_attr->store(edac_dev, buffer, count);
+
+ return -EIO;
+}
+
+static struct sysfs_ops edac_dev_ops = {
+ .show = edacdev_show,
+ .store = edacdev_store
+};
+
+#define EDACDEV_ATTR(_name,_mode,_show,_store) \
+static struct edacdev_attribute edac_dev_attr_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
+/* default Control file */
+EDACDEV_ATTR(reset_counters,S_IWUSR,NULL,edac_dev_reset_counters_store);
+
+/* default Attribute files */
+EDACDEV_ATTR(mc_name,S_IRUGO,edac_dev_ctl_name_show,NULL);
+EDACDEV_ATTR(seconds_since_reset,S_IRUGO,edac_dev_seconds_show,NULL);
+EDACDEV_ATTR(ue_count,S_IRUGO,edac_dev_ue_count_show,NULL);
+EDACDEV_ATTR(ce_count,S_IRUGO,edac_dev_ce_count_show,NULL);
+
+
+static struct edacdev_attribute *edacdev_attr[] = {
+ &edacdev_attr_reset_counters,
+ &edacdev_attr_mc_name,
+ &edacdev_attr_seconds_since_reset,
+ &edacdev_attr_ue_count,
+ &edacdev_attr_ce_count,
+ NULL
+};
+
+/*
+ * Release of a Edac Device controlling instance
+ */
+static void edac_dev_instance_release(struct kobject *kobj)
+{
+ struct edac_device_ctl_info *edac_dev;
+
+ edac_dev = to_edacdev(kobj);
+ debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx);
+ complete(&edac_dev->kobj_complete);
+}
+
+static struct kobj_type ktype_device = {
+ .release = edac_dev_instance_release,
+ .sysfs_ops = &edacdev_ops,
+ .default_attrs = (struct attribute **) edacdev_attr,
+};
+
+#endif
+
+/************************** edac_device sysfs code and data **************/
+
+/*
+ * Set of edac_device_ctl_info attribute store/show functions
+ */
+
+/* 'log_ue' */
+static ssize_t edac_device_ctl_log_ue_show(
+ struct edac_device_ctl_info *ctl_info, char *data)
+{
+ return sprintf(data,"%u\n", ctl_info->log_ue);
+}
+
+static ssize_t edac_device_ctl_log_ue_store(
+ struct edac_device_ctl_info *ctl_info,
+ const char *data,size_t count)
+{
+ /* if parameter is zero, turn off flag, if non-zero turn on flag */
+ ctl_info->log_ue = (simple_strtoul(data,NULL,0) != 0);
+
+ return count;
+}
+
+/* 'log_ce' */
+static ssize_t edac_device_ctl_log_ce_show(
+ struct edac_device_ctl_info *ctl_info,char *data)
+{
+ return sprintf(data,"%u\n", ctl_info->log_ce);
+}
+
+static ssize_t edac_device_ctl_log_ce_store(
+ struct edac_device_ctl_info *ctl_info,
+ const char *data,size_t count)
+{
+ /* if parameter is zero, turn off flag, if non-zero turn on flag */
+ ctl_info->log_ce = (simple_strtoul(data,NULL,0) != 0);
+
+ return count;
+}
+
+
+/* 'panic_on_ue' */
+static ssize_t edac_device_ctl_panic_on_ue_show(
+ struct edac_device_ctl_info *ctl_info, char *data)
+{
+ return sprintf(data,"%u\n", ctl_info->panic_on_ue);
+}
+
+static ssize_t edac_device_ctl_panic_on_ue_store(
+ struct edac_device_ctl_info *ctl_info,
+ const char *data,size_t count)
+{
+ /* if parameter is zero, turn off flag, if non-zero turn on flag */
+ ctl_info->panic_on_ue = (simple_strtoul(data,NULL,0) != 0);
+
+ return count;
+}
+
+/* 'poll_msec' show and store functions*/
+static ssize_t edac_device_ctl_poll_msec_show(
+ struct edac_device_ctl_info *ctl_info, char *data)
+{
+ return sprintf(data,"%u\n", ctl_info->poll_msec);
+}
+
+static ssize_t edac_device_ctl_poll_msec_store(
+ struct edac_device_ctl_info *ctl_info,
+ const char *data,size_t count)
+{
+ unsigned long value;
+
+ /* get the value and enforce that it is non-zero, must be at least
+ * one millisecond for the delay period, between scans
+ * Then cancel last outstanding delay for the work request
+ * and set a new one.
+ */
+ value = simple_strtoul(data,NULL,0);
+ edac_device_reset_delay_period(ctl_info,value);
+
+ return count;
+}
+
+
+/* edac_device_ctl_info specific attribute structure */
+struct ctl_info_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct edac_device_ctl_info *,char *);
+ ssize_t (*store)(struct edac_device_ctl_info *,const char *,size_t);
+};
+
+#define to_ctl_info(k) container_of(k, struct edac_device_ctl_info, kobj)
+#define to_ctl_info_attr(a) container_of(a,struct ctl_info_attribute,attr)
+
+/* Function to 'show' fields from the edac_dev 'ctl_info' structure */
+static ssize_t edac_dev_ctl_info_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buffer)
+{
+ struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj);
+ struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr);
+
+ if (ctl_info_attr->show)
+ return ctl_info_attr->show(edac_dev,buffer);
+ return -EIO;
+}
+
+/* Function to 'store' fields into the edac_dev 'ctl_info' structure */
+static ssize_t edac_dev_ctl_info_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj);
+ struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr);
+
+ if (ctl_info_attr->store)
+ return ctl_info_attr->store(edac_dev, buffer, count);
+ return -EIO;
+}
+
+/* edac_dev file operations for an 'ctl_info' */
+static struct sysfs_ops device_ctl_info_ops = {
+ .show = edac_dev_ctl_info_show,
+ .store = edac_dev_ctl_info_store
+};
+
+#define CTL_INFO_ATTR(_name,_mode,_show,_store) \
+static struct ctl_info_attribute attr_ctl_info_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
+
+/* Declare the various ctl_info attributes here and their respective ops */
+CTL_INFO_ATTR(log_ue,S_IRUGO|S_IWUSR,
+ edac_device_ctl_log_ue_show,
+ edac_device_ctl_log_ue_store);
+CTL_INFO_ATTR(log_ce,S_IRUGO|S_IWUSR,
+ edac_device_ctl_log_ce_show,
+ edac_device_ctl_log_ce_store);
+CTL_INFO_ATTR(panic_on_ue,S_IRUGO|S_IWUSR,
+ edac_device_ctl_panic_on_ue_show,
+ edac_device_ctl_panic_on_ue_store);
+CTL_INFO_ATTR(poll_msec,S_IRUGO|S_IWUSR,
+ edac_device_ctl_poll_msec_show,
+ edac_device_ctl_poll_msec_store);
+
+
+/* Base Attributes of the EDAC_DEVICE ECC object */
+static struct ctl_info_attribute *device_ctrl_attr[] = {
+ &attr_ctl_info_panic_on_ue,
+ &attr_ctl_info_log_ue,
+ &attr_ctl_info_log_ce,
+ &attr_ctl_info_poll_msec,
+ NULL,
+};
+
+/* Main DEVICE kobject release() function */
+static void edac_device_ctrl_master_release(struct kobject *kobj)
+{
+ struct edac_device_ctl_info *edac_dev;
+
+ edac_dev = to_edacdev(kobj);
+
+ debugf1("%s()\n", __func__);
+ complete(&edac_dev->kobj_complete);
+}
+
+static struct kobj_type ktype_device_ctrl = {
+ .release = edac_device_ctrl_master_release,
+ .sysfs_ops = &device_ctl_info_ops,
+ .default_attrs = (struct attribute **) device_ctrl_attr,
+};
+
+
+/**************** edac_device main kobj ctor/dtor code *********************/
+
+/*
+ * edac_device_register_main_kobj
+ *
+ * perform the high level setup for the new edac_device instance
+ *
+ * Return: 0 SUCCESS
+ * !0 FAILURE
+ */
+static int edac_device_register_main_kobj(
+ struct edac_device_ctl_info *edac_dev)
+{
+ int err = 0;
+ struct sysdev_class *edac_class;
+
+ debugf1("%s()\n", __func__);
+
+ /* get the /sys/devices/system/edac reference */
+ edac_class = edac_get_edac_class();
+ if (edac_class == NULL) {
+ debugf1("%s() no edac_class error=%d\n", __func__, err);
+ return err;
+ }
+
+ /* Point to the 'edac_class' this instance 'reports' to */
+ edac_dev->edac_class = edac_class;
+
+ /* Init the devices's kobject */
+ memset(&edac_dev->kobj, 0, sizeof (struct kobject));
+ edac_dev->kobj.ktype = &ktype_device_ctrl;
+
+ /* set this new device under the edac_class kobject */
+ edac_dev->kobj.parent = &edac_class->kset.kobj;
+
+ /* generate sysfs "..../edac/<name>" */
+ debugf1("%s() set name of kobject to: %s\n",
+ __func__, edac_dev->name);
+ err = kobject_set_name(&edac_dev->kobj,"%s",edac_dev->name);
+ if (err)
+ return err;
+ err = kobject_register(&edac_dev->kobj);
+ if (err) {
+ debugf1("%s()Failed to register '.../edac/%s'\n",
+ __func__,edac_dev->name);
+ return err;
+ }
+
+ debugf1("%s() Registered '.../edac/%s' kobject\n",
+ __func__, edac_dev->name);
+
+ return 0;
+}
+
+/*
+ * edac_device_unregister_main_kobj:
+ * the '..../edac/<name>' kobject
+ */
+static void edac_device_unregister_main_kobj(
+ struct edac_device_ctl_info *edac_dev)
+{
+ debugf0("%s()\n", __func__);
+ debugf1("%s() name of kobject is: %s\n",
+ __func__, kobject_name(&edac_dev->kobj));
+
+ init_completion(&edac_dev->kobj_complete);
+
+ /*
+ * Unregister the edac device's kobject and
+ * wait for reference count to reach 0.
+ */
+ kobject_unregister(&edac_dev->kobj);
+ wait_for_completion(&edac_dev->kobj_complete);
+}
+
+
+/*************** edac_dev -> instance information ***********/
+
+/*
+ * Set of low-level instance attribute show functions
+ */
+static ssize_t instance_ue_count_show(
+ struct edac_device_instance *instance, char *data)
+{
+ return sprintf(data,"%u\n", instance->counters.ue_count);
+}
+
+static ssize_t instance_ce_count_show(
+ struct edac_device_instance *instance, char *data)
+{
+ return sprintf(data,"%u\n", instance->counters.ce_count);
+}
+
+
+
+#define to_instance(k) container_of(k, struct edac_device_instance, kobj)
+#define to_instance_attr(a) container_of(a,struct instance_attribute,attr)
+
+/* DEVICE instance kobject release() function */
+static void edac_device_ctrl_instance_release(struct kobject *kobj)
+{
+ struct edac_device_instance *instance;
+
+ debugf1("%s()\n", __func__);
+
+ instance = to_instance(kobj);
+ complete(&instance->kobj_complete);
+}
+
+
+/* instance specific attribute structure */
+struct instance_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct edac_device_instance *,char *);
+ ssize_t (*store)(struct edac_device_instance *,const char *,size_t);
+};
+
+
+/* Function to 'show' fields from the edac_dev 'instance' structure */
+static ssize_t edac_dev_instance_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buffer)
+{
+ struct edac_device_instance *instance = to_instance(kobj);
+ struct instance_attribute *instance_attr = to_instance_attr(attr);
+
+ if (instance_attr->show)
+ return instance_attr->show(instance,buffer);
+ return -EIO;
+}
+
+
+/* Function to 'store' fields into the edac_dev 'instance' structure */
+static ssize_t edac_dev_instance_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct edac_device_instance *instance = to_instance(kobj);
+ struct instance_attribute *instance_attr = to_instance_attr(attr);
+
+ if (instance_attr->store)
+ return instance_attr->store(instance, buffer, count);
+ return -EIO;
+}
+
+
+
+/* edac_dev file operations for an 'instance' */
+static struct sysfs_ops device_instance_ops = {
+ .show = edac_dev_instance_show,
+ .store = edac_dev_instance_store
+};
+
+#define INSTANCE_ATTR(_name,_mode,_show,_store) \
+static struct instance_attribute attr_instance_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
+/*
+ * Define attributes visible for the edac_device instance object
+ * Each contains a pointer to a show and an optional set
+ * function pointer that does the low level output/input
+ */
+INSTANCE_ATTR(ce_count,S_IRUGO,instance_ce_count_show,NULL);
+INSTANCE_ATTR(ue_count,S_IRUGO,instance_ue_count_show,NULL);
+
+/* list of edac_dev 'instance' attributes */
+static struct instance_attribute *device_instance_attr[] = {
+ &attr_instance_ce_count,
+ &attr_instance_ue_count,
+ NULL,
+};
+
+/* The 'ktype' for each edac_dev 'instance' */
+static struct kobj_type ktype_instance_ctrl = {
+ .release = edac_device_ctrl_instance_release,
+ .sysfs_ops = &device_instance_ops,
+ .default_attrs = (struct attribute **) device_instance_attr,
+};
+
+
+/*************** edac_dev -> instance -> block information *********/
+
+/*
+ * Set of low-level block attribute show functions
+ */
+static ssize_t block_ue_count_show(
+ struct edac_device_block *block, char *data)
+{
+ return sprintf(data,"%u\n", block->counters.ue_count);
+}
+
+static ssize_t block_ce_count_show(
+ struct edac_device_block *block, char *data)
+{
+ return sprintf(data,"%u\n", block->counters.ce_count);
+}
+
+
+
+#define to_block(k) container_of(k, struct edac_device_block, kobj)
+#define to_block_attr(a) container_of(a,struct block_attribute,attr)
+
+/* DEVICE block kobject release() function */
+static void edac_device_ctrl_block_release(struct kobject *kobj)
+{
+ struct edac_device_block *block;
+
+ debugf1("%s()\n", __func__);
+
+ block = to_block(kobj);
+ complete(&block->kobj_complete);
+}
+
+/* block specific attribute structure */
+struct block_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct edac_device_block *,char *);
+ ssize_t (*store)(struct edac_device_block *,const char *,size_t);
+};
+
+/* Function to 'show' fields from the edac_dev 'block' structure */
+static ssize_t edac_dev_block_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buffer)
+{
+ struct edac_device_block *block = to_block(kobj);
+ struct block_attribute *block_attr = to_block_attr(attr);
+
+ if (block_attr->show)
+ return block_attr->show(block,buffer);
+ return -EIO;
+}
+
+
+/* Function to 'store' fields into the edac_dev 'block' structure */
+static ssize_t edac_dev_block_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct edac_device_block *block = to_block(kobj);
+ struct block_attribute *block_attr = to_block_attr(attr);
+
+ if (block_attr->store)
+ return block_attr->store(block, buffer, count);
+ return -EIO;
+}
+
+
+/* edac_dev file operations for a 'block' */
+static struct sysfs_ops device_block_ops = {
+ .show = edac_dev_block_show,
+ .store = edac_dev_block_store
+};
+
+
+#define BLOCK_ATTR(_name,_mode,_show,_store) \
+static struct block_attribute attr_block_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
+BLOCK_ATTR(ce_count,S_IRUGO,block_ce_count_show,NULL);
+BLOCK_ATTR(ue_count,S_IRUGO,block_ue_count_show,NULL);
+
+
+/* list of edac_dev 'block' attributes */
+static struct block_attribute *device_block_attr[] = {
+ &attr_block_ce_count,
+ &attr_block_ue_count,
+ NULL,
+};
+
+/* The 'ktype' for each edac_dev 'block' */
+static struct kobj_type ktype_block_ctrl = {
+ .release = edac_device_ctrl_block_release,
+ .sysfs_ops = &device_block_ops,
+ .default_attrs = (struct attribute **) device_block_attr,
+};
+
+
+/************** block ctor/dtor code ************/
+
+/*
+ * edac_device_create_block
+ */
+static int edac_device_create_block(
+ struct edac_device_ctl_info *edac_dev,
+ struct edac_device_instance *instance,
+ int idx)
+{
+ int err;
+ struct edac_device_block *block;
+
+ block = &instance->blocks[idx];
+
+ debugf1("%s() Instance '%s' block[%d] '%s'\n",
+ __func__,instance->name, idx, block->name);
+
+ /* init this block's kobject */
+ memset(&block->kobj, 0, sizeof (struct kobject));
+ block->kobj.parent = &instance->kobj;
+ block->kobj.ktype = &ktype_block_ctrl;
+
+ err = kobject_set_name(&block->kobj,"%s",block->name);
+ if (err)
+ return err;
+
+ err = kobject_register(&block->kobj);
+ if (err) {
+ debugf1("%s()Failed to register instance '%s'\n",
+ __func__,block->name);
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * edac_device_delete_block(edac_dev,j);
+ */
+static void edac_device_delete_block(
+ struct edac_device_ctl_info *edac_dev,
+ struct edac_device_instance *instance,
+ int idx)
+{
+ struct edac_device_block *block;
+
+ block = &instance->blocks[idx];
+
+ /* unregister this block's kobject */
+ init_completion(&block->kobj_complete);
+ kobject_unregister(&block->kobj);
+ wait_for_completion(&block->kobj_complete);
+}
+
+/************** instance ctor/dtor code ************/
+
+/*
+ * edac_device_create_instance
+ * create just one instance of an edac_device 'instance'
+ */
+static int edac_device_create_instance(
+ struct edac_device_ctl_info *edac_dev, int idx)
+{
+ int i, j;
+ int err;
+ struct edac_device_instance *instance;
+
+ instance = &edac_dev->instances[idx];
+
+ /* Init the instance's kobject */
+ memset(&instance->kobj, 0, sizeof (struct kobject));
+
+ /* set this new device under the edac_device main kobject */
+ instance->kobj.parent = &edac_dev->kobj;
+ instance->kobj.ktype = &ktype_instance_ctrl;
+
+ err = kobject_set_name(&instance->kobj,"%s",instance->name);
+ if (err)
+ return err;
+
+ err = kobject_register(&instance->kobj);
+ if (err != 0) {
+ debugf2("%s() Failed to register instance '%s'\n",
+ __func__,instance->name);
+ return err;
+ }
+
+ debugf1("%s() now register '%d' blocks for instance %d\n",
+ __func__,instance->nr_blocks,idx);
+
+ /* register all blocks of this instance */
+ for (i = 0; i < instance->nr_blocks; i++ ) {
+ err = edac_device_create_block(edac_dev,instance,i);
+ if (err) {
+ for (j = 0; j < i; j++) {
+ edac_device_delete_block(edac_dev,instance,j);
+ }
+ return err;
+ }
+ }
+
+ debugf1("%s() Registered instance %d '%s' kobject\n",
+ __func__, idx, instance->name);
+
+ return 0;
+}
+
+/*
+ * edac_device_remove_instance
+ * remove an edac_device instance
+ */
+static void edac_device_delete_instance(
+ struct edac_device_ctl_info *edac_dev, int idx)
+{
+ int i;
+ struct edac_device_instance *instance;
+
+ instance = &edac_dev->instances[idx];
+
+ /* unregister all blocks in this instance */
+ for (i = 0; i < instance->nr_blocks; i++) {
+ edac_device_delete_block(edac_dev,instance,i);
+ }
+
+ /* unregister this instance's kobject */
+ init_completion(&instance->kobj_complete);
+ kobject_unregister(&instance->kobj);
+ wait_for_completion(&instance->kobj_complete);
+}
+
+/*
+ * edac_device_create_instances
+ * create the first level of 'instances' for this device
+ * (ie 'cache' might have 'cache0', 'cache1', 'cache2', etc
+ */
+static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev)
+{
+ int i, j;
+ int err;
+
+ debugf0("%s()\n", __func__);
+
+ /* iterate over creation of the instances */
+ for (i = 0; i < edac_dev->nr_instances; i++ ) {
+ err = edac_device_create_instance(edac_dev,i);
+ if (err) {
+ /* unwind previous instances on error */
+ for (j = 0; j < i; j++) {
+ edac_device_delete_instance(edac_dev,j);
+ }
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * edac_device_delete_instances(edac_dev);
+ * unregister all the kobjects of the instances
+ */
+static void edac_device_delete_instances(struct edac_device_ctl_info *edac_dev)
+{
+ int i;
+
+ /* iterate over creation of the instances */
+ for (i = 0; i < edac_dev->nr_instances; i++ ) {
+ edac_device_delete_instance(edac_dev,i);
+ }
+}
+
+/******************* edac_dev sysfs ctor/dtor code *************/
+
+/*
+ * edac_device_create_sysfs() Constructor
+ *
+ * Create a new edac_device kobject instance,
+ *
+ * Return:
+ * 0 Success
+ * !0 Failure
+ */
+int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
+{
+ int err;
+ struct kobject *edac_kobj=&edac_dev->kobj;
+
+ /* register this instance's main kobj with the edac class kobj */
+ err = edac_device_register_main_kobj(edac_dev);
+ if (err)
+ return err;
+
+ debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx);
+
+ /* create a symlink from the edac device
+ * to the platform 'device' being used for this
+ */
+ err = sysfs_create_link(edac_kobj,
+ &edac_dev->dev->kobj,
+ EDAC_DEVICE_SYMLINK);
+ if (err) {
+ debugf0("%s() sysfs_create_link() returned err= %d\n",
+ __func__, err);
+ return err;
+ }
+
+ debugf0("%s() calling create-instances, idx=%d\n",
+ __func__, edac_dev->dev_idx);
+
+ /* Create the first level instance directories */
+ err = edac_device_create_instances(edac_dev);
+ if (err) {
+ goto error0;
+ }
+
+ return 0;
+
+ /* Error unwind stack */
+
+error0:
+ edac_device_unregister_main_kobj(edac_dev);
+
+ return err;
+}
+
+/*
+ * edac_device_remove_sysfs() destructor
+ *
+ * remove a edac_device instance
+ */
+void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev)
+{
+ debugf0("%s()\n", __func__);
+
+ edac_device_delete_instances(edac_dev);
+
+ /* remove the sym link */
+ sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK);
+
+ /* unregister the instance's main kobj */
+ edac_device_unregister_main_kobj(edac_dev);
+}
+