Blame view

drivers/edac/edac_device_sysfs.c 23.7 KB
e27e3dac6   Douglas Thompson   drivers/edac: add...
1
2
3
  /*
   * file for managing the edac_device class of devices for EDAC
   *
1c3631ff1   Douglas Thompson   drivers/edac: fix...
4
5
   * (C) 2007 SoftwareBitMaker (http://www.softwarebitmaker.com)
   *
e27e3dac6   Douglas Thompson   drivers/edac: add...
6
7
8
9
10
11
   * This file may be distributed under the terms of the
   * GNU General Public License.
   *
   * Written Doug Thompson <norsk5@xmission.com>
   *
   */
e27e3dac6   Douglas Thompson   drivers/edac: add...
12
  #include <linux/ctype.h>
1c3631ff1   Douglas Thompson   drivers/edac: fix...
13
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
30e1f7a81   Borislav Petkov   EDAC: Export edac...
15
  #include <linux/edac.h>
e27e3dac6   Douglas Thompson   drivers/edac: add...
16
17
18
  
  #include "edac_core.h"
  #include "edac_module.h"
e27e3dac6   Douglas Thompson   drivers/edac: add...
19
20
21
22
  #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)
e27e3dac6   Douglas Thompson   drivers/edac: add...
23
24
25
26
27
28
  
  /*
   * Set of edac_device_ctl_info attribute store/show functions
   */
  
  /* 'log_ue' */
079708b91   Douglas Thompson   drivers/edac: cor...
29
  static ssize_t edac_device_ctl_log_ue_show(struct edac_device_ctl_info
052dfb45c   Douglas Thompson   drivers/edac: cle...
30
  					*ctl_info, char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
31
  {
079708b91   Douglas Thompson   drivers/edac: cor...
32
33
  	return sprintf(data, "%u
  ", ctl_info->log_ue);
e27e3dac6   Douglas Thompson   drivers/edac: add...
34
  }
079708b91   Douglas Thompson   drivers/edac: cor...
35
  static ssize_t edac_device_ctl_log_ue_store(struct edac_device_ctl_info
052dfb45c   Douglas Thompson   drivers/edac: cle...
36
37
  					*ctl_info, const char *data,
  					size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
38
39
  {
  	/* if parameter is zero, turn off flag, if non-zero turn on flag */
079708b91   Douglas Thompson   drivers/edac: cor...
40
  	ctl_info->log_ue = (simple_strtoul(data, NULL, 0) != 0);
e27e3dac6   Douglas Thompson   drivers/edac: add...
41

079708b91   Douglas Thompson   drivers/edac: cor...
42
  	return count;
e27e3dac6   Douglas Thompson   drivers/edac: add...
43
44
45
  }
  
  /* 'log_ce' */
079708b91   Douglas Thompson   drivers/edac: cor...
46
  static ssize_t edac_device_ctl_log_ce_show(struct edac_device_ctl_info
052dfb45c   Douglas Thompson   drivers/edac: cle...
47
  					*ctl_info, char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
48
  {
079708b91   Douglas Thompson   drivers/edac: cor...
49
50
  	return sprintf(data, "%u
  ", ctl_info->log_ce);
e27e3dac6   Douglas Thompson   drivers/edac: add...
51
  }
079708b91   Douglas Thompson   drivers/edac: cor...
52
  static ssize_t edac_device_ctl_log_ce_store(struct edac_device_ctl_info
052dfb45c   Douglas Thompson   drivers/edac: cle...
53
54
  					*ctl_info, const char *data,
  					size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
55
56
  {
  	/* if parameter is zero, turn off flag, if non-zero turn on flag */
079708b91   Douglas Thompson   drivers/edac: cor...
57
  	ctl_info->log_ce = (simple_strtoul(data, NULL, 0) != 0);
e27e3dac6   Douglas Thompson   drivers/edac: add...
58

079708b91   Douglas Thompson   drivers/edac: cor...
59
  	return count;
e27e3dac6   Douglas Thompson   drivers/edac: add...
60
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
61
  /* 'panic_on_ue' */
079708b91   Douglas Thompson   drivers/edac: cor...
62
63
  static ssize_t edac_device_ctl_panic_on_ue_show(struct edac_device_ctl_info
  						*ctl_info, char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
64
  {
079708b91   Douglas Thompson   drivers/edac: cor...
65
66
  	return sprintf(data, "%u
  ", ctl_info->panic_on_ue);
e27e3dac6   Douglas Thompson   drivers/edac: add...
67
  }
079708b91   Douglas Thompson   drivers/edac: cor...
68
69
70
  static ssize_t edac_device_ctl_panic_on_ue_store(struct edac_device_ctl_info
  						 *ctl_info, const char *data,
  						 size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
71
72
  {
  	/* if parameter is zero, turn off flag, if non-zero turn on flag */
079708b91   Douglas Thompson   drivers/edac: cor...
73
  	ctl_info->panic_on_ue = (simple_strtoul(data, NULL, 0) != 0);
e27e3dac6   Douglas Thompson   drivers/edac: add...
74
75
76
77
78
  
  	return count;
  }
  
  /* 'poll_msec' show and store functions*/
079708b91   Douglas Thompson   drivers/edac: cor...
79
  static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
052dfb45c   Douglas Thompson   drivers/edac: cle...
80
  					*ctl_info, char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
81
  {
079708b91   Douglas Thompson   drivers/edac: cor...
82
83
  	return sprintf(data, "%u
  ", ctl_info->poll_msec);
e27e3dac6   Douglas Thompson   drivers/edac: add...
84
  }
079708b91   Douglas Thompson   drivers/edac: cor...
85
  static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
052dfb45c   Douglas Thompson   drivers/edac: cle...
86
87
  					*ctl_info, const char *data,
  					size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
88
89
90
91
92
93
94
95
  {
  	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.
  	 */
079708b91   Douglas Thompson   drivers/edac: cor...
96
97
  	value = simple_strtoul(data, NULL, 0);
  	edac_device_reset_delay_period(ctl_info, value);
e27e3dac6   Douglas Thompson   drivers/edac: add...
98

079708b91   Douglas Thompson   drivers/edac: cor...
99
  	return count;
e27e3dac6   Douglas Thompson   drivers/edac: add...
100
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
101
102
  /* edac_device_ctl_info specific attribute structure */
  struct ctl_info_attribute {
079708b91   Douglas Thompson   drivers/edac: cor...
103
  	struct attribute attr;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
104
105
  	ssize_t(*show) (struct edac_device_ctl_info *, char *);
  	ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t);
e27e3dac6   Douglas Thompson   drivers/edac: add...
106
107
108
109
110
111
112
  };
  
  #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,
052dfb45c   Douglas Thompson   drivers/edac: cle...
113
  				struct attribute *attr, char *buffer)
e27e3dac6   Douglas Thompson   drivers/edac: add...
114
  {
079708b91   Douglas Thompson   drivers/edac: cor...
115
116
  	struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj);
  	struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr);
e27e3dac6   Douglas Thompson   drivers/edac: add...
117

079708b91   Douglas Thompson   drivers/edac: cor...
118
119
120
  	if (ctl_info_attr->show)
  		return ctl_info_attr->show(edac_dev, buffer);
  	return -EIO;
e27e3dac6   Douglas Thompson   drivers/edac: add...
121
122
123
124
  }
  
  /* Function to 'store' fields into the edac_dev 'ctl_info' structure */
  static ssize_t edac_dev_ctl_info_store(struct kobject *kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
125
126
  				struct attribute *attr,
  				const char *buffer, size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
127
  {
079708b91   Douglas Thompson   drivers/edac: cor...
128
129
  	struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj);
  	struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr);
e27e3dac6   Douglas Thompson   drivers/edac: add...
130

079708b91   Douglas Thompson   drivers/edac: cor...
131
132
133
  	if (ctl_info_attr->store)
  		return ctl_info_attr->store(edac_dev, buffer, count);
  	return -EIO;
e27e3dac6   Douglas Thompson   drivers/edac: add...
134
135
136
  }
  
  /* edac_dev file operations for an 'ctl_info' */
52cf25d0a   Emese Revfy   Driver core: Cons...
137
  static const struct sysfs_ops device_ctl_info_ops = {
079708b91   Douglas Thompson   drivers/edac: cor...
138
139
  	.show = edac_dev_ctl_info_show,
  	.store = edac_dev_ctl_info_store
e27e3dac6   Douglas Thompson   drivers/edac: add...
140
141
142
143
  };
  
  #define CTL_INFO_ATTR(_name,_mode,_show,_store)        \
  static struct ctl_info_attribute attr_ctl_info_##_name = {      \
052dfb45c   Douglas Thompson   drivers/edac: cle...
144
145
146
  	.attr = {.name = __stringify(_name), .mode = _mode },   \
  	.show   = _show,                                        \
  	.store  = _store,                                       \
e27e3dac6   Douglas Thompson   drivers/edac: add...
147
  };
e27e3dac6   Douglas Thompson   drivers/edac: add...
148
  /* Declare the various ctl_info attributes here and their respective ops */
079708b91   Douglas Thompson   drivers/edac: cor...
149
  CTL_INFO_ATTR(log_ue, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
150
  	edac_device_ctl_log_ue_show, edac_device_ctl_log_ue_store);
079708b91   Douglas Thompson   drivers/edac: cor...
151
  CTL_INFO_ATTR(log_ce, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
152
  	edac_device_ctl_log_ce_show, edac_device_ctl_log_ce_store);
079708b91   Douglas Thompson   drivers/edac: cor...
153
  CTL_INFO_ATTR(panic_on_ue, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
154
155
  	edac_device_ctl_panic_on_ue_show,
  	edac_device_ctl_panic_on_ue_store);
079708b91   Douglas Thompson   drivers/edac: cor...
156
  CTL_INFO_ATTR(poll_msec, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
157
  	edac_device_ctl_poll_msec_show, edac_device_ctl_poll_msec_store);
e27e3dac6   Douglas Thompson   drivers/edac: add...
158
159
160
161
162
163
164
165
166
  
  /* 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,
  };
1c3631ff1   Douglas Thompson   drivers/edac: fix...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  /*
   * edac_device_ctrl_master_release
   *
   *	called when the reference count for the 'main' kobj
   *	for a edac_device control struct reaches zero
   *
   *	Reference count model:
   *		One 'main' kobject for each control structure allocated.
   *		That main kobj is initially set to one AND
   *		the reference count for the EDAC 'core' module is
   *		bumped by one, thus added 'keep in memory' dependency.
   *
   *		Each new internal kobj (in instances and blocks) then
   *		bumps the 'main' kobject.
   *
   *		When they are released their release functions decrement
   *		the 'main' kobj.
   *
   *		When the main kobj reaches zero (0) then THIS function
   *		is called which then decrements the EDAC 'core' module.
   *		When the module reference count reaches zero then the
   *		module no longer has dependency on keeping the release
   *		function code in memory and module can be unloaded.
   *
   *		This will support several control objects as well, each
   *		with its own 'main' kobj.
   */
e27e3dac6   Douglas Thompson   drivers/edac: add...
194
195
  static void edac_device_ctrl_master_release(struct kobject *kobj)
  {
1c3631ff1   Douglas Thompson   drivers/edac: fix...
196
  	struct edac_device_ctl_info *edac_dev = to_edacdev(kobj);
e27e3dac6   Douglas Thompson   drivers/edac: add...
197

b2a4ac0c2   Doug Thompson   drivers/edac: fix...
198
199
  	debugf4("%s() control index=%d
  ", __func__, edac_dev->dev_idx);
e27e3dac6   Douglas Thompson   drivers/edac: add...
200

1c3631ff1   Douglas Thompson   drivers/edac: fix...
201
202
203
204
205
206
207
  	/* decrement the EDAC CORE module ref count */
  	module_put(edac_dev->owner);
  
  	/* free the control struct containing the 'main' kobj
  	 * passed in to this routine
  	 */
  	kfree(edac_dev);
e27e3dac6   Douglas Thompson   drivers/edac: add...
208
  }
1c3631ff1   Douglas Thompson   drivers/edac: fix...
209
  /* ktype for the main (master) kobject */
e27e3dac6   Douglas Thompson   drivers/edac: add...
210
211
212
  static struct kobj_type ktype_device_ctrl = {
  	.release = edac_device_ctrl_master_release,
  	.sysfs_ops = &device_ctl_info_ops,
079708b91   Douglas Thompson   drivers/edac: cor...
213
  	.default_attrs = (struct attribute **)device_ctrl_attr,
e27e3dac6   Douglas Thompson   drivers/edac: add...
214
  };
e27e3dac6   Douglas Thompson   drivers/edac: add...
215
  /*
1c3631ff1   Douglas Thompson   drivers/edac: fix...
216
   * edac_device_register_sysfs_main_kobj
e27e3dac6   Douglas Thompson   drivers/edac: add...
217
218
219
220
221
222
   *
   *	perform the high level setup for the new edac_device instance
   *
   * Return:  0 SUCCESS
   *         !0 FAILURE
   */
1c3631ff1   Douglas Thompson   drivers/edac: fix...
223
  int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
e27e3dac6   Douglas Thompson   drivers/edac: add...
224
  {
e27e3dac6   Douglas Thompson   drivers/edac: add...
225
  	struct sysdev_class *edac_class;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
226
  	int err;
e27e3dac6   Douglas Thompson   drivers/edac: add...
227
228
229
  
  	debugf1("%s()
  ", __func__);
079708b91   Douglas Thompson   drivers/edac: cor...
230
  	/* get the /sys/devices/system/edac reference */
30e1f7a81   Borislav Petkov   EDAC: Export edac...
231
  	edac_class = edac_get_sysfs_class();
079708b91   Douglas Thompson   drivers/edac: cor...
232
  	if (edac_class == NULL) {
1c3631ff1   Douglas Thompson   drivers/edac: fix...
233
234
235
236
  		debugf1("%s() no edac_class error
  ", __func__);
  		err = -ENODEV;
  		goto err_out;
079708b91   Douglas Thompson   drivers/edac: cor...
237
  	}
e27e3dac6   Douglas Thompson   drivers/edac: add...
238
239
240
241
242
  
  	/* Point to the 'edac_class' this instance 'reports' to */
  	edac_dev->edac_class = edac_class;
  
  	/* Init the devices's kobject */
079708b91   Douglas Thompson   drivers/edac: cor...
243
  	memset(&edac_dev->kobj, 0, sizeof(struct kobject));
1c3631ff1   Douglas Thompson   drivers/edac: fix...
244
245
246
247
248
249
250
251
  
  	/* Record which module 'owns' this control structure
  	 * and bump the ref count of the module
  	 */
  	edac_dev->owner = THIS_MODULE;
  
  	if (!try_module_get(edac_dev->owner)) {
  		err = -ENODEV;
30e1f7a81   Borislav Petkov   EDAC: Export edac...
252
  		goto err_mod_get;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
253
254
255
  	}
  
  	/* register */
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
256
257
258
  	err = kobject_init_and_add(&edac_dev->kobj, &ktype_device_ctrl,
  				   &edac_class->kset.kobj,
  				   "%s", edac_dev->name);
e27e3dac6   Douglas Thompson   drivers/edac: add...
259
260
261
  	if (err) {
  		debugf1("%s()Failed to register '.../edac/%s'
  ",
079708b91   Douglas Thompson   drivers/edac: cor...
262
  			__func__, edac_dev->name);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
263
  		goto err_kobj_reg;
e27e3dac6   Douglas Thompson   drivers/edac: add...
264
  	}
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
265
  	kobject_uevent(&edac_dev->kobj, KOBJ_ADD);
e27e3dac6   Douglas Thompson   drivers/edac: add...
266

1c3631ff1   Douglas Thompson   drivers/edac: fix...
267
268
269
  	/* At this point, to 'free' the control struct,
  	 * edac_device_unregister_sysfs_main_kobj() must be used
  	 */
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
270
271
  	debugf4("%s() Registered '.../edac/%s' kobject
  ",
e27e3dac6   Douglas Thompson   drivers/edac: add...
272
273
274
  		__func__, edac_dev->name);
  
  	return 0;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
275
276
277
278
  
  	/* Error exit stack */
  err_kobj_reg:
  	module_put(edac_dev->owner);
30e1f7a81   Borislav Petkov   EDAC: Export edac...
279
280
  err_mod_get:
  	edac_put_sysfs_class();
1c3631ff1   Douglas Thompson   drivers/edac: fix...
281
282
  err_out:
  	return err;
e27e3dac6   Douglas Thompson   drivers/edac: add...
283
284
285
  }
  
  /*
1c3631ff1   Douglas Thompson   drivers/edac: fix...
286
   * edac_device_unregister_sysfs_main_kobj:
e27e3dac6   Douglas Thompson   drivers/edac: add...
287
288
   *	the '..../edac/<name>' kobject
   */
30e1f7a81   Borislav Petkov   EDAC: Export edac...
289
  void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev)
e27e3dac6   Douglas Thompson   drivers/edac: add...
290
291
292
  {
  	debugf0("%s()
  ", __func__);
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
293
294
  	debugf4("%s() name of kobject is: %s
  ",
30e1f7a81   Borislav Petkov   EDAC: Export edac...
295
  		__func__, kobject_name(&dev->kobj));
e27e3dac6   Douglas Thompson   drivers/edac: add...
296

e27e3dac6   Douglas Thompson   drivers/edac: add...
297
298
  	/*
  	 * Unregister the edac device's kobject and
1c3631ff1   Douglas Thompson   drivers/edac: fix...
299
300
301
302
  	 * allow for reference count to reach 0 at which point
  	 * the callback will be called to:
  	 *   a) module_put() this module
  	 *   b) 'kfree' the memory
e27e3dac6   Douglas Thompson   drivers/edac: add...
303
  	 */
30e1f7a81   Borislav Petkov   EDAC: Export edac...
304
305
  	kobject_put(&dev->kobj);
  	edac_put_sysfs_class();
e27e3dac6   Douglas Thompson   drivers/edac: add...
306
  }
1c3631ff1   Douglas Thompson   drivers/edac: fix...
307
  /* edac_dev -> instance information */
e27e3dac6   Douglas Thompson   drivers/edac: add...
308
309
310
311
  
  /*
   * Set of low-level instance attribute show functions
   */
079708b91   Douglas Thompson   drivers/edac: cor...
312
  static ssize_t instance_ue_count_show(struct edac_device_instance *instance,
052dfb45c   Douglas Thompson   drivers/edac: cle...
313
  				char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
314
  {
079708b91   Douglas Thompson   drivers/edac: cor...
315
316
  	return sprintf(data, "%u
  ", instance->counters.ue_count);
e27e3dac6   Douglas Thompson   drivers/edac: add...
317
  }
079708b91   Douglas Thompson   drivers/edac: cor...
318
  static ssize_t instance_ce_count_show(struct edac_device_instance *instance,
052dfb45c   Douglas Thompson   drivers/edac: cle...
319
  				char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
320
  {
079708b91   Douglas Thompson   drivers/edac: cor...
321
322
  	return sprintf(data, "%u
  ", instance->counters.ce_count);
e27e3dac6   Douglas Thompson   drivers/edac: add...
323
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
324
325
326
327
328
329
330
331
332
333
  #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()
  ", __func__);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
334
335
336
  	/* map from this kobj to the main control struct
  	 * and then dec the main kobj count
  	 */
e27e3dac6   Douglas Thompson   drivers/edac: add...
337
  	instance = to_instance(kobj);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
338
  	kobject_put(&instance->ctl->kobj);
e27e3dac6   Douglas Thompson   drivers/edac: add...
339
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
340
341
  /* instance specific attribute structure */
  struct instance_attribute {
079708b91   Douglas Thompson   drivers/edac: cor...
342
  	struct attribute attr;
52490c8d0   Douglas Thompson   drivers/edac: eda...
343
344
  	ssize_t(*show) (struct edac_device_instance *, char *);
  	ssize_t(*store) (struct edac_device_instance *, const char *, size_t);
e27e3dac6   Douglas Thompson   drivers/edac: add...
345
  };
e27e3dac6   Douglas Thompson   drivers/edac: add...
346
347
  /* Function to 'show' fields from the edac_dev 'instance' structure */
  static ssize_t edac_dev_instance_show(struct kobject *kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
348
  				struct attribute *attr, char *buffer)
e27e3dac6   Douglas Thompson   drivers/edac: add...
349
  {
079708b91   Douglas Thompson   drivers/edac: cor...
350
351
  	struct edac_device_instance *instance = to_instance(kobj);
  	struct instance_attribute *instance_attr = to_instance_attr(attr);
e27e3dac6   Douglas Thompson   drivers/edac: add...
352

079708b91   Douglas Thompson   drivers/edac: cor...
353
354
355
  	if (instance_attr->show)
  		return instance_attr->show(instance, buffer);
  	return -EIO;
e27e3dac6   Douglas Thompson   drivers/edac: add...
356
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
357
358
  /* Function to 'store' fields into the edac_dev 'instance' structure */
  static ssize_t edac_dev_instance_store(struct kobject *kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
359
360
  				struct attribute *attr,
  				const char *buffer, size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
361
  {
079708b91   Douglas Thompson   drivers/edac: cor...
362
363
  	struct edac_device_instance *instance = to_instance(kobj);
  	struct instance_attribute *instance_attr = to_instance_attr(attr);
e27e3dac6   Douglas Thompson   drivers/edac: add...
364

079708b91   Douglas Thompson   drivers/edac: cor...
365
366
367
  	if (instance_attr->store)
  		return instance_attr->store(instance, buffer, count);
  	return -EIO;
e27e3dac6   Douglas Thompson   drivers/edac: add...
368
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
369
  /* edac_dev file operations for an 'instance' */
52cf25d0a   Emese Revfy   Driver core: Cons...
370
  static const struct sysfs_ops device_instance_ops = {
079708b91   Douglas Thompson   drivers/edac: cor...
371
372
  	.show = edac_dev_instance_show,
  	.store = edac_dev_instance_store
e27e3dac6   Douglas Thompson   drivers/edac: add...
373
374
375
376
  };
  
  #define INSTANCE_ATTR(_name,_mode,_show,_store)        \
  static struct instance_attribute attr_instance_##_name = {      \
052dfb45c   Douglas Thompson   drivers/edac: cle...
377
378
379
  	.attr = {.name = __stringify(_name), .mode = _mode },   \
  	.show   = _show,                                        \
  	.store  = _store,                                       \
e27e3dac6   Douglas Thompson   drivers/edac: add...
380
381
382
383
384
385
386
  };
  
  /*
   * 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
   */
079708b91   Douglas Thompson   drivers/edac: cor...
387
388
  INSTANCE_ATTR(ce_count, S_IRUGO, instance_ce_count_show, NULL);
  INSTANCE_ATTR(ue_count, S_IRUGO, instance_ue_count_show, NULL);
e27e3dac6   Douglas Thompson   drivers/edac: add...
389
390
391
392
393
394
395
396
397
398
399
400
  
  /* 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,
079708b91   Douglas Thompson   drivers/edac: cor...
401
  	.default_attrs = (struct attribute **)device_instance_attr,
e27e3dac6   Douglas Thompson   drivers/edac: add...
402
  };
1c3631ff1   Douglas Thompson   drivers/edac: fix...
403
  /* edac_dev -> instance -> block information */
e27e3dac6   Douglas Thompson   drivers/edac: add...
404

b2a4ac0c2   Doug Thompson   drivers/edac: fix...
405
406
407
  #define to_block(k) container_of(k, struct edac_device_block, kobj)
  #define to_block_attr(a) \
  	container_of(a, struct edac_dev_sysfs_block_attribute, attr)
e27e3dac6   Douglas Thompson   drivers/edac: add...
408
409
410
  /*
   * Set of low-level block attribute show functions
   */
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
411
412
  static ssize_t block_ue_count_show(struct kobject *kobj,
  					struct attribute *attr, char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
413
  {
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
414
  	struct edac_device_block *block = to_block(kobj);
079708b91   Douglas Thompson   drivers/edac: cor...
415
416
  	return sprintf(data, "%u
  ", block->counters.ue_count);
e27e3dac6   Douglas Thompson   drivers/edac: add...
417
  }
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
418
419
  static ssize_t block_ce_count_show(struct kobject *kobj,
  					struct attribute *attr, char *data)
e27e3dac6   Douglas Thompson   drivers/edac: add...
420
  {
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
421
  	struct edac_device_block *block = to_block(kobj);
079708b91   Douglas Thompson   drivers/edac: cor...
422
423
  	return sprintf(data, "%u
  ", block->counters.ce_count);
e27e3dac6   Douglas Thompson   drivers/edac: add...
424
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
425
426
427
428
429
430
431
  /* DEVICE block kobject release() function */
  static void edac_device_ctrl_block_release(struct kobject *kobj)
  {
  	struct edac_device_block *block;
  
  	debugf1("%s()
  ", __func__);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
432
  	/* get the container of the kobj */
e27e3dac6   Douglas Thompson   drivers/edac: add...
433
  	block = to_block(kobj);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
434
435
436
437
438
  
  	/* map from 'block kobj' to 'block->instance->controller->main_kobj'
  	 * now 'release' the block kobject
  	 */
  	kobject_put(&block->instance->ctl->kobj);
e27e3dac6   Douglas Thompson   drivers/edac: add...
439
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
440
441
442
  
  /* Function to 'show' fields from the edac_dev 'block' structure */
  static ssize_t edac_dev_block_show(struct kobject *kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
443
  				struct attribute *attr, char *buffer)
e27e3dac6   Douglas Thompson   drivers/edac: add...
444
  {
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
445
446
  	struct edac_dev_sysfs_block_attribute *block_attr =
  						to_block_attr(attr);
e27e3dac6   Douglas Thompson   drivers/edac: add...
447

079708b91   Douglas Thompson   drivers/edac: cor...
448
  	if (block_attr->show)
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
449
  		return block_attr->show(kobj, attr, buffer);
079708b91   Douglas Thompson   drivers/edac: cor...
450
  	return -EIO;
e27e3dac6   Douglas Thompson   drivers/edac: add...
451
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
452
453
  /* Function to 'store' fields into the edac_dev 'block' structure */
  static ssize_t edac_dev_block_store(struct kobject *kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
454
455
  				struct attribute *attr,
  				const char *buffer, size_t count)
e27e3dac6   Douglas Thompson   drivers/edac: add...
456
  {
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
457
458
459
  	struct edac_dev_sysfs_block_attribute *block_attr;
  
  	block_attr = to_block_attr(attr);
e27e3dac6   Douglas Thompson   drivers/edac: add...
460

079708b91   Douglas Thompson   drivers/edac: cor...
461
  	if (block_attr->store)
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
462
  		return block_attr->store(kobj, attr, buffer, count);
079708b91   Douglas Thompson   drivers/edac: cor...
463
  	return -EIO;
e27e3dac6   Douglas Thompson   drivers/edac: add...
464
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
465
  /* edac_dev file operations for a 'block' */
52cf25d0a   Emese Revfy   Driver core: Cons...
466
  static const struct sysfs_ops device_block_ops = {
079708b91   Douglas Thompson   drivers/edac: cor...
467
468
  	.show = edac_dev_block_show,
  	.store = edac_dev_block_store
e27e3dac6   Douglas Thompson   drivers/edac: add...
469
  };
e27e3dac6   Douglas Thompson   drivers/edac: add...
470
  #define BLOCK_ATTR(_name,_mode,_show,_store)        \
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
471
  static struct edac_dev_sysfs_block_attribute attr_block_##_name = {	\
052dfb45c   Douglas Thompson   drivers/edac: cle...
472
473
474
  	.attr = {.name = __stringify(_name), .mode = _mode },   \
  	.show   = _show,                                        \
  	.store  = _store,                                       \
e27e3dac6   Douglas Thompson   drivers/edac: add...
475
  };
079708b91   Douglas Thompson   drivers/edac: cor...
476
477
  BLOCK_ATTR(ce_count, S_IRUGO, block_ce_count_show, NULL);
  BLOCK_ATTR(ue_count, S_IRUGO, block_ue_count_show, NULL);
e27e3dac6   Douglas Thompson   drivers/edac: add...
478
479
  
  /* list of edac_dev 'block' attributes */
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
480
  static struct edac_dev_sysfs_block_attribute *device_block_attr[] = {
e27e3dac6   Douglas Thompson   drivers/edac: add...
481
482
483
484
485
486
487
488
489
  	&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,
079708b91   Douglas Thompson   drivers/edac: cor...
490
  	.default_attrs = (struct attribute **)device_block_attr,
e27e3dac6   Douglas Thompson   drivers/edac: add...
491
  };
1c3631ff1   Douglas Thompson   drivers/edac: fix...
492
  /* block ctor/dtor  code */
e27e3dac6   Douglas Thompson   drivers/edac: add...
493
494
495
496
  
  /*
   * edac_device_create_block
   */
079708b91   Douglas Thompson   drivers/edac: cor...
497
  static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
052dfb45c   Douglas Thompson   drivers/edac: cle...
498
  				struct edac_device_instance *instance,
1c3631ff1   Douglas Thompson   drivers/edac: fix...
499
  				struct edac_device_block *block)
e27e3dac6   Douglas Thompson   drivers/edac: add...
500
  {
fd309a9d8   Douglas Thompson   drivers/edac: fix...
501
  	int i;
e27e3dac6   Douglas Thompson   drivers/edac: add...
502
  	int err;
fd309a9d8   Douglas Thompson   drivers/edac: fix...
503
  	struct edac_dev_sysfs_block_attribute *sysfs_attrib;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
504
  	struct kobject *main_kobj;
e27e3dac6   Douglas Thompson   drivers/edac: add...
505

b2a4ac0c2   Doug Thompson   drivers/edac: fix...
506
507
508
509
510
511
  	debugf4("%s() Instance '%s' inst_p=%p  block '%s'  block_p=%p
  ",
  		__func__, instance->name, instance, block->name, block);
  	debugf4("%s() block kobj=%p  block kobj->parent=%p
  ",
  		__func__, &block->kobj, &block->kobj.parent);
e27e3dac6   Douglas Thompson   drivers/edac: add...
512
513
  
  	/* init this block's kobject */
079708b91   Douglas Thompson   drivers/edac: cor...
514
  	memset(&block->kobj, 0, sizeof(struct kobject));
e27e3dac6   Douglas Thompson   drivers/edac: add...
515

1c3631ff1   Douglas Thompson   drivers/edac: fix...
516
517
518
519
520
521
522
523
524
525
  	/* bump the main kobject's reference count for this controller
  	 * and this instance is dependant on the main
  	 */
  	main_kobj = kobject_get(&edac_dev->kobj);
  	if (!main_kobj) {
  		err = -ENODEV;
  		goto err_out;
  	}
  
  	/* Add this block's kobject */
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
526
527
528
  	err = kobject_init_and_add(&block->kobj, &ktype_block_ctrl,
  				   &instance->kobj,
  				   "%s", block->name);
e27e3dac6   Douglas Thompson   drivers/edac: add...
529
  	if (err) {
1c3631ff1   Douglas Thompson   drivers/edac: fix...
530
531
  		debugf1("%s() Failed to register instance '%s'
  ",
079708b91   Douglas Thompson   drivers/edac: cor...
532
  			__func__, block->name);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
533
534
535
  		kobject_put(main_kobj);
  		err = -ENODEV;
  		goto err_out;
e27e3dac6   Douglas Thompson   drivers/edac: add...
536
  	}
fd309a9d8   Douglas Thompson   drivers/edac: fix...
537
538
539
540
  	/* If there are driver level block attributes, then added them
  	 * to the block kobject
  	 */
  	sysfs_attrib = block->block_attributes;
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
541
542
543
544
545
546
547
548
549
550
551
  	if (sysfs_attrib && block->nr_attribs) {
  		for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) {
  
  			debugf4("%s() creating block attrib='%s' "
  				"attrib->%p to kobj=%p
  ",
  				__func__,
  				sysfs_attrib->attr.name,
  				sysfs_attrib, &block->kobj);
  
  			/* Create each block_attribute file */
fd309a9d8   Douglas Thompson   drivers/edac: fix...
552
  			err = sysfs_create_file(&block->kobj,
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
553
  				&sysfs_attrib->attr);
fd309a9d8   Douglas Thompson   drivers/edac: fix...
554
555
  			if (err)
  				goto err_on_attrib;
fd309a9d8   Douglas Thompson   drivers/edac: fix...
556
557
  		}
  	}
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
558
  	kobject_uevent(&block->kobj, KOBJ_ADD);
fd309a9d8   Douglas Thompson   drivers/edac: fix...
559

e27e3dac6   Douglas Thompson   drivers/edac: add...
560
  	return 0;
fd309a9d8   Douglas Thompson   drivers/edac: fix...
561

1c3631ff1   Douglas Thompson   drivers/edac: fix...
562
  	/* Error unwind stack */
fd309a9d8   Douglas Thompson   drivers/edac: fix...
563
  err_on_attrib:
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
564
  	kobject_put(&block->kobj);
fd309a9d8   Douglas Thompson   drivers/edac: fix...
565

1c3631ff1   Douglas Thompson   drivers/edac: fix...
566
  err_out:
fd309a9d8   Douglas Thompson   drivers/edac: fix...
567
  	return err;
e27e3dac6   Douglas Thompson   drivers/edac: add...
568
569
570
  }
  
  /*
1c3631ff1   Douglas Thompson   drivers/edac: fix...
571
   * edac_device_delete_block(edac_dev,block);
e27e3dac6   Douglas Thompson   drivers/edac: add...
572
   */
079708b91   Douglas Thompson   drivers/edac: cor...
573
  static void edac_device_delete_block(struct edac_device_ctl_info *edac_dev,
1c3631ff1   Douglas Thompson   drivers/edac: fix...
574
  				struct edac_device_block *block)
e27e3dac6   Douglas Thompson   drivers/edac: add...
575
  {
1c3631ff1   Douglas Thompson   drivers/edac: fix...
576
577
  	struct edac_dev_sysfs_block_attribute *sysfs_attrib;
  	int i;
e27e3dac6   Douglas Thompson   drivers/edac: add...
578

1c3631ff1   Douglas Thompson   drivers/edac: fix...
579
580
581
582
583
  	/* if this block has 'attributes' then we need to iterate over the list
  	 * and 'remove' the attributes on this block
  	 */
  	sysfs_attrib = block->block_attributes;
  	if (sysfs_attrib && block->nr_attribs) {
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
584
585
586
  		for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) {
  
  			/* remove each block_attrib file */
1c3631ff1   Douglas Thompson   drivers/edac: fix...
587
588
589
590
  			sysfs_remove_file(&block->kobj,
  				(struct attribute *) sysfs_attrib);
  		}
  	}
e27e3dac6   Douglas Thompson   drivers/edac: add...
591

1c3631ff1   Douglas Thompson   drivers/edac: fix...
592
593
594
  	/* unregister this block's kobject, SEE:
  	 *	edac_device_ctrl_block_release() callback operation
  	 */
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
595
  	kobject_put(&block->kobj);
e27e3dac6   Douglas Thompson   drivers/edac: add...
596
  }
1c3631ff1   Douglas Thompson   drivers/edac: fix...
597
  /* instance ctor/dtor code */
e27e3dac6   Douglas Thompson   drivers/edac: add...
598
599
600
601
602
  
  /*
   * edac_device_create_instance
   *	create just one instance of an edac_device 'instance'
   */
079708b91   Douglas Thompson   drivers/edac: cor...
603
  static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
052dfb45c   Douglas Thompson   drivers/edac: cle...
604
  				int idx)
e27e3dac6   Douglas Thompson   drivers/edac: add...
605
606
607
608
  {
  	int i, j;
  	int err;
  	struct edac_device_instance *instance;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
609
  	struct kobject *main_kobj;
e27e3dac6   Douglas Thompson   drivers/edac: add...
610
611
612
613
  
  	instance = &edac_dev->instances[idx];
  
  	/* Init the instance's kobject */
079708b91   Douglas Thompson   drivers/edac: cor...
614
  	memset(&instance->kobj, 0, sizeof(struct kobject));
e27e3dac6   Douglas Thompson   drivers/edac: add...
615

1c3631ff1   Douglas Thompson   drivers/edac: fix...
616
  	instance->ctl = edac_dev;
e27e3dac6   Douglas Thompson   drivers/edac: add...
617

1c3631ff1   Douglas Thompson   drivers/edac: fix...
618
619
620
621
622
623
624
625
  	/* bump the main kobject's reference count for this controller
  	 * and this instance is dependant on the main
  	 */
  	main_kobj = kobject_get(&edac_dev->kobj);
  	if (!main_kobj) {
  		err = -ENODEV;
  		goto err_out;
  	}
e27e3dac6   Douglas Thompson   drivers/edac: add...
626

b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
627
628
629
  	/* Formally register this instance's kobject under the edac_device */
  	err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl,
  				   &edac_dev->kobj, "%s", instance->name);
e27e3dac6   Douglas Thompson   drivers/edac: add...
630
631
632
  	if (err != 0) {
  		debugf2("%s() Failed to register instance '%s'
  ",
079708b91   Douglas Thompson   drivers/edac: cor...
633
  			__func__, instance->name);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
634
635
  		kobject_put(main_kobj);
  		goto err_out;
e27e3dac6   Douglas Thompson   drivers/edac: add...
636
  	}
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
637
638
  	debugf4("%s() now register '%d' blocks for instance %d
  ",
079708b91   Douglas Thompson   drivers/edac: cor...
639
  		__func__, instance->nr_blocks, idx);
e27e3dac6   Douglas Thompson   drivers/edac: add...
640
641
  
  	/* register all blocks of this instance */
079708b91   Douglas Thompson   drivers/edac: cor...
642
  	for (i = 0; i < instance->nr_blocks; i++) {
1c3631ff1   Douglas Thompson   drivers/edac: fix...
643
644
  		err = edac_device_create_block(edac_dev, instance,
  						&instance->blocks[i]);
e27e3dac6   Douglas Thompson   drivers/edac: add...
645
  		if (err) {
1c3631ff1   Douglas Thompson   drivers/edac: fix...
646
  			/* If any fail, remove all previous ones */
52490c8d0   Douglas Thompson   drivers/edac: eda...
647
  			for (j = 0; j < i; j++)
1c3631ff1   Douglas Thompson   drivers/edac: fix...
648
649
650
  				edac_device_delete_block(edac_dev,
  							&instance->blocks[j]);
  			goto err_release_instance_kobj;
e27e3dac6   Douglas Thompson   drivers/edac: add...
651
652
  		}
  	}
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
653
  	kobject_uevent(&instance->kobj, KOBJ_ADD);
e27e3dac6   Douglas Thompson   drivers/edac: add...
654

b2a4ac0c2   Doug Thompson   drivers/edac: fix...
655
656
  	debugf4("%s() Registered instance %d '%s' kobject
  ",
e27e3dac6   Douglas Thompson   drivers/edac: add...
657
658
659
  		__func__, idx, instance->name);
  
  	return 0;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
660
661
662
  
  	/* error unwind stack */
  err_release_instance_kobj:
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
663
  	kobject_put(&instance->kobj);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
664
665
666
  
  err_out:
  	return err;
e27e3dac6   Douglas Thompson   drivers/edac: add...
667
668
669
670
671
672
  }
  
  /*
   * edac_device_remove_instance
   *	remove an edac_device instance
   */
079708b91   Douglas Thompson   drivers/edac: cor...
673
674
  static void edac_device_delete_instance(struct edac_device_ctl_info *edac_dev,
  					int idx)
e27e3dac6   Douglas Thompson   drivers/edac: add...
675
  {
e27e3dac6   Douglas Thompson   drivers/edac: add...
676
  	struct edac_device_instance *instance;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
677
  	int i;
e27e3dac6   Douglas Thompson   drivers/edac: add...
678
679
680
681
  
  	instance = &edac_dev->instances[idx];
  
  	/* unregister all blocks in this instance */
52490c8d0   Douglas Thompson   drivers/edac: eda...
682
  	for (i = 0; i < instance->nr_blocks; i++)
1c3631ff1   Douglas Thompson   drivers/edac: fix...
683
  		edac_device_delete_block(edac_dev, &instance->blocks[i]);
e27e3dac6   Douglas Thompson   drivers/edac: add...
684

1c3631ff1   Douglas Thompson   drivers/edac: fix...
685
686
687
  	/* unregister this instance's kobject, SEE:
  	 *	edac_device_ctrl_instance_release() for callback operation
  	 */
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
688
  	kobject_put(&instance->kobj);
e27e3dac6   Douglas Thompson   drivers/edac: add...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  }
  
  /*
   * 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()
  ", __func__);
  
  	/* iterate over creation of the instances */
079708b91   Douglas Thompson   drivers/edac: cor...
705
706
  	for (i = 0; i < edac_dev->nr_instances; i++) {
  		err = edac_device_create_instance(edac_dev, i);
e27e3dac6   Douglas Thompson   drivers/edac: add...
707
708
  		if (err) {
  			/* unwind previous instances on error */
52490c8d0   Douglas Thompson   drivers/edac: eda...
709
  			for (j = 0; j < i; j++)
079708b91   Douglas Thompson   drivers/edac: cor...
710
  				edac_device_delete_instance(edac_dev, j);
e27e3dac6   Douglas Thompson   drivers/edac: add...
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
  			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 */
52490c8d0   Douglas Thompson   drivers/edac: eda...
727
  	for (i = 0; i < edac_dev->nr_instances; i++)
079708b91   Douglas Thompson   drivers/edac: cor...
728
  		edac_device_delete_instance(edac_dev, i);
e27e3dac6   Douglas Thompson   drivers/edac: add...
729
  }
1c3631ff1   Douglas Thompson   drivers/edac: fix...
730
  /* edac_dev sysfs ctor/dtor  code */
e27e3dac6   Douglas Thompson   drivers/edac: add...
731
732
  
  /*
1c3631ff1   Douglas Thompson   drivers/edac: fix...
733
   * edac_device_add_main_sysfs_attributes
42a8e397a   Douglas Thompson   drivers/edac: add...
734
735
   *	add some attributes to this instance's main kobject
   */
1c3631ff1   Douglas Thompson   drivers/edac: fix...
736
  static int edac_device_add_main_sysfs_attributes(
42a8e397a   Douglas Thompson   drivers/edac: add...
737
738
  			struct edac_device_ctl_info *edac_dev)
  {
052dfb45c   Douglas Thompson   drivers/edac: cle...
739
  	struct edac_dev_sysfs_attribute *sysfs_attrib;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
740
  	int err = 0;
052dfb45c   Douglas Thompson   drivers/edac: cle...
741

052dfb45c   Douglas Thompson   drivers/edac: cle...
742
  	sysfs_attrib = edac_dev->sysfs_attributes;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
743
744
745
746
747
748
  	if (sysfs_attrib) {
  		/* iterate over the array and create an attribute for each
  		 * entry in the list
  		 */
  		while (sysfs_attrib->attr.name != NULL) {
  			err = sysfs_create_file(&edac_dev->kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
749
  				(struct attribute*) sysfs_attrib);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
750
751
  			if (err)
  				goto err_out;
052dfb45c   Douglas Thompson   drivers/edac: cle...
752

1c3631ff1   Douglas Thompson   drivers/edac: fix...
753
754
  			sysfs_attrib++;
  		}
052dfb45c   Douglas Thompson   drivers/edac: cle...
755
  	}
42a8e397a   Douglas Thompson   drivers/edac: add...
756

1c3631ff1   Douglas Thompson   drivers/edac: fix...
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  err_out:
  	return err;
  }
  
  /*
   * edac_device_remove_main_sysfs_attributes
   *	remove any attributes to this instance's main kobject
   */
  static void edac_device_remove_main_sysfs_attributes(
  			struct edac_device_ctl_info *edac_dev)
  {
  	struct edac_dev_sysfs_attribute *sysfs_attrib;
  
  	/* if there are main attributes, defined, remove them. First,
  	 * point to the start of the array and iterate over it
  	 * removing each attribute listed from this device's instance's kobject
  	 */
  	sysfs_attrib = edac_dev->sysfs_attributes;
  	if (sysfs_attrib) {
  		while (sysfs_attrib->attr.name != NULL) {
  			sysfs_remove_file(&edac_dev->kobj,
  					(struct attribute *) sysfs_attrib);
  			sysfs_attrib++;
  		}
  	}
42a8e397a   Douglas Thompson   drivers/edac: add...
782
783
784
  }
  
  /*
e27e3dac6   Douglas Thompson   drivers/edac: add...
785
786
   * edac_device_create_sysfs() Constructor
   *
1c3631ff1   Douglas Thompson   drivers/edac: fix...
787
788
789
790
791
792
   * accept a created edac_device control structure
   * and 'export' it to sysfs. The 'main' kobj should already have been
   * created. 'instance' and 'block' kobjects should be registered
   * along with any 'block' attributes from the low driver. In addition,
   * the main attributes (if any) are connected to the main kobject of
   * the control structure.
e27e3dac6   Douglas Thompson   drivers/edac: add...
793
794
795
796
797
798
799
800
   *
   * Return:
   *	0	Success
   *	!0	Failure
   */
  int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
  {
  	int err;
079708b91   Douglas Thompson   drivers/edac: cor...
801
  	struct kobject *edac_kobj = &edac_dev->kobj;
e27e3dac6   Douglas Thompson   drivers/edac: add...
802

e27e3dac6   Douglas Thompson   drivers/edac: add...
803
804
  	debugf0("%s() idx=%d
  ", __func__, edac_dev->dev_idx);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
805
806
807
808
809
810
  	/*  go create any main attributes callers wants */
  	err = edac_device_add_main_sysfs_attributes(edac_dev);
  	if (err) {
  		debugf0("%s() failed to add sysfs attribs
  ", __func__);
  		goto err_out;
42a8e397a   Douglas Thompson   drivers/edac: add...
811
  	}
e27e3dac6   Douglas Thompson   drivers/edac: add...
812
813
814
815
  	/* create a symlink from the edac device
  	 * to the platform 'device' being used for this
  	 */
  	err = sysfs_create_link(edac_kobj,
079708b91   Douglas Thompson   drivers/edac: cor...
816
  				&edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK);
e27e3dac6   Douglas Thompson   drivers/edac: add...
817
818
819
820
  	if (err) {
  		debugf0("%s() sysfs_create_link() returned err= %d
  ",
  			__func__, err);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
821
  		goto err_remove_main_attribs;
e27e3dac6   Douglas Thompson   drivers/edac: add...
822
  	}
1c3631ff1   Douglas Thompson   drivers/edac: fix...
823
824
825
826
  	/* Create the first level instance directories
  	 * In turn, the nested blocks beneath the instances will
  	 * be registered as well
  	 */
e27e3dac6   Douglas Thompson   drivers/edac: add...
827
  	err = edac_device_create_instances(edac_dev);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
828
829
830
831
  	if (err) {
  		debugf0("%s() edac_device_create_instances() "
  			"returned err= %d
  ", __func__, err);
42a8e397a   Douglas Thompson   drivers/edac: add...
832
  		goto err_remove_link;
1c3631ff1   Douglas Thompson   drivers/edac: fix...
833
  	}
b2a4ac0c2   Doug Thompson   drivers/edac: fix...
834
835
  	debugf4("%s() create-instances done, idx=%d
  ",
1c3631ff1   Douglas Thompson   drivers/edac: fix...
836
  		__func__, edac_dev->dev_idx);
e27e3dac6   Douglas Thompson   drivers/edac: add...
837
838
839
840
  
  	return 0;
  
  	/* Error unwind stack */
42a8e397a   Douglas Thompson   drivers/edac: add...
841
842
843
  err_remove_link:
  	/* remove the sym link */
  	sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK);
e27e3dac6   Douglas Thompson   drivers/edac: add...
844

1c3631ff1   Douglas Thompson   drivers/edac: fix...
845
846
  err_remove_main_attribs:
  	edac_device_remove_main_sysfs_attributes(edac_dev);
e27e3dac6   Douglas Thompson   drivers/edac: add...
847

1c3631ff1   Douglas Thompson   drivers/edac: fix...
848
  err_out:
e27e3dac6   Douglas Thompson   drivers/edac: add...
849
850
851
852
853
854
  	return err;
  }
  
  /*
   * edac_device_remove_sysfs() destructor
   *
1c3631ff1   Douglas Thompson   drivers/edac: fix...
855
   * given an edac_device struct, tear down the kobject resources
e27e3dac6   Douglas Thompson   drivers/edac: add...
856
857
858
859
860
   */
  void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev)
  {
  	debugf0("%s()
  ", __func__);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
861
862
  	/* remove any main attributes for this device */
  	edac_device_remove_main_sysfs_attributes(edac_dev);
e27e3dac6   Douglas Thompson   drivers/edac: add...
863

1c3631ff1   Douglas Thompson   drivers/edac: fix...
864
  	/* remove the device sym link */
e27e3dac6   Douglas Thompson   drivers/edac: add...
865
  	sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK);
1c3631ff1   Douglas Thompson   drivers/edac: fix...
866
867
  	/* walk the instance/block kobject tree, deconstructing it */
  	edac_device_delete_instances(edac_dev);
e27e3dac6   Douglas Thompson   drivers/edac: add...
868
  }