]>
Commit | Line | Data |
---|---|---|
e27e3dac DT |
1 | /* |
2 | * file for managing the edac_device class of devices for EDAC | |
3 | * | |
4 | * (C) 2007 SoftwareBitMaker(http://www.softwarebitmaker.com) | |
5 | * This file may be distributed under the terms of the | |
6 | * GNU General Public License. | |
7 | * | |
8 | * Written Doug Thompson <norsk5@xmission.com> | |
9 | * | |
10 | */ | |
11 | ||
12 | #include <linux/module.h> | |
13 | #include <linux/sysdev.h> | |
14 | #include <linux/ctype.h> | |
15 | ||
16 | #include "edac_core.h" | |
17 | #include "edac_module.h" | |
18 | ||
e27e3dac DT |
19 | #define EDAC_DEVICE_SYMLINK "device" |
20 | ||
21 | #define to_edacdev(k) container_of(k, struct edac_device_ctl_info, kobj) | |
22 | #define to_edacdev_attr(a) container_of(a, struct edacdev_attribute, attr) | |
23 | ||
24 | #ifdef DKT | |
25 | ||
26 | static ssize_t edac_dev_ue_count_show(struct edac_device_ctl_info *edac_dev, | |
079708b9 | 27 | char *data) |
e27e3dac | 28 | { |
079708b9 | 29 | return sprintf(data, "%d\n", edac_dev->ue_count); |
e27e3dac DT |
30 | } |
31 | ||
32 | static ssize_t edac_dev_ce_count_show(struct edac_device_ctl_info *edac_dev, | |
079708b9 | 33 | char *data) |
e27e3dac | 34 | { |
079708b9 | 35 | return sprintf(data, "%d\n", edac_dev->ce_count); |
e27e3dac DT |
36 | } |
37 | ||
38 | static ssize_t edac_dev_seconds_show(struct edac_device_ctl_info *edac_dev, | |
079708b9 | 39 | char *data) |
e27e3dac | 40 | { |
079708b9 | 41 | return sprintf(data, "%ld\n", (jiffies - edac_dev->start_time) / HZ); |
e27e3dac DT |
42 | } |
43 | ||
44 | static ssize_t edac_dev_ctl_name_show(struct edac_device_ctl_info *edac_dev, | |
079708b9 | 45 | char *data) |
e27e3dac | 46 | { |
079708b9 | 47 | return sprintf(data, "%s\n", edac_dev->ctl_name); |
e27e3dac DT |
48 | } |
49 | ||
e27e3dac DT |
50 | struct edacdev_attribute { |
51 | struct attribute attr; | |
079708b9 DT |
52 | ssize_t(*show) (struct edac_device_ctl_info *, char *); |
53 | ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t); | |
e27e3dac DT |
54 | }; |
55 | ||
e27e3dac DT |
56 | /* EDAC DEVICE show/store functions for top most object */ |
57 | static ssize_t edacdev_show(struct kobject *kobj, struct attribute *attr, | |
079708b9 | 58 | char *buffer) |
e27e3dac DT |
59 | { |
60 | struct edac_device_ctl_info *edac_dev = to_edacdev(kobj); | |
079708b9 | 61 | struct edacdev_attribute *edacdev_attr = to_edacdev_attr(attr); |
e27e3dac DT |
62 | |
63 | if (edacdev_attr->show) | |
64 | return edacdev_attr->show(edac_dev, buffer); | |
65 | ||
66 | return -EIO; | |
67 | } | |
68 | ||
69 | static ssize_t edacdev_store(struct kobject *kobj, struct attribute *attr, | |
079708b9 | 70 | const char *buffer, size_t count) |
e27e3dac DT |
71 | { |
72 | struct edac_device_ctl_info *edac_dev = to_edacdev(kobj); | |
079708b9 | 73 | struct edacdev_attribute *edacdev_attr = to_edacdev_attr(attr); |
e27e3dac DT |
74 | |
75 | if (edacdev_attr->store) | |
76 | return edacdev_attr->store(edac_dev, buffer, count); | |
77 | ||
78 | return -EIO; | |
79 | } | |
80 | ||
81 | static struct sysfs_ops edac_dev_ops = { | |
82 | .show = edacdev_show, | |
83 | .store = edacdev_store | |
84 | }; | |
85 | ||
86 | #define EDACDEV_ATTR(_name,_mode,_show,_store) \ | |
87 | static struct edacdev_attribute edac_dev_attr_##_name = { \ | |
88 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | |
89 | .show = _show, \ | |
90 | .store = _store, \ | |
91 | }; | |
92 | ||
93 | /* default Control file */ | |
079708b9 | 94 | EDACDEV_ATTR(reset_counters, S_IWUSR, NULL, edac_dev_reset_counters_store); |
e27e3dac DT |
95 | |
96 | /* default Attribute files */ | |
079708b9 DT |
97 | EDACDEV_ATTR(mc_name, S_IRUGO, edac_dev_ctl_name_show, NULL); |
98 | EDACDEV_ATTR(seconds_since_reset, S_IRUGO, edac_dev_seconds_show, NULL); | |
99 | EDACDEV_ATTR(ue_count, S_IRUGO, edac_dev_ue_count_show, NULL); | |
100 | EDACDEV_ATTR(ce_count, S_IRUGO, edac_dev_ce_count_show, NULL); | |
e27e3dac DT |
101 | |
102 | static struct edacdev_attribute *edacdev_attr[] = { | |
103 | &edacdev_attr_reset_counters, | |
104 | &edacdev_attr_mc_name, | |
105 | &edacdev_attr_seconds_since_reset, | |
106 | &edacdev_attr_ue_count, | |
107 | &edacdev_attr_ce_count, | |
108 | NULL | |
109 | }; | |
110 | ||
111 | /* | |
112 | * Release of a Edac Device controlling instance | |
113 | */ | |
114 | static void edac_dev_instance_release(struct kobject *kobj) | |
115 | { | |
116 | struct edac_device_ctl_info *edac_dev; | |
117 | ||
118 | edac_dev = to_edacdev(kobj); | |
119 | debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx); | |
120 | complete(&edac_dev->kobj_complete); | |
121 | } | |
122 | ||
123 | static struct kobj_type ktype_device = { | |
124 | .release = edac_dev_instance_release, | |
125 | .sysfs_ops = &edacdev_ops, | |
079708b9 | 126 | .default_attrs = (struct attribute **)edacdev_attr, |
e27e3dac DT |
127 | }; |
128 | ||
129 | #endif | |
130 | ||
131 | /************************** edac_device sysfs code and data **************/ | |
132 | ||
133 | /* | |
134 | * Set of edac_device_ctl_info attribute store/show functions | |
135 | */ | |
136 | ||
137 | /* 'log_ue' */ | |
079708b9 DT |
138 | static ssize_t edac_device_ctl_log_ue_show(struct edac_device_ctl_info |
139 | *ctl_info, char *data) | |
e27e3dac | 140 | { |
079708b9 | 141 | return sprintf(data, "%u\n", ctl_info->log_ue); |
e27e3dac DT |
142 | } |
143 | ||
079708b9 DT |
144 | static ssize_t edac_device_ctl_log_ue_store(struct edac_device_ctl_info |
145 | *ctl_info, const char *data, | |
146 | size_t count) | |
e27e3dac DT |
147 | { |
148 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | |
079708b9 | 149 | ctl_info->log_ue = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac | 150 | |
079708b9 | 151 | return count; |
e27e3dac DT |
152 | } |
153 | ||
154 | /* 'log_ce' */ | |
079708b9 DT |
155 | static ssize_t edac_device_ctl_log_ce_show(struct edac_device_ctl_info |
156 | *ctl_info, char *data) | |
e27e3dac | 157 | { |
079708b9 | 158 | return sprintf(data, "%u\n", ctl_info->log_ce); |
e27e3dac DT |
159 | } |
160 | ||
079708b9 DT |
161 | static ssize_t edac_device_ctl_log_ce_store(struct edac_device_ctl_info |
162 | *ctl_info, const char *data, | |
163 | size_t count) | |
e27e3dac DT |
164 | { |
165 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | |
079708b9 | 166 | ctl_info->log_ce = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac | 167 | |
079708b9 | 168 | return count; |
e27e3dac DT |
169 | } |
170 | ||
e27e3dac | 171 | /* 'panic_on_ue' */ |
079708b9 DT |
172 | static ssize_t edac_device_ctl_panic_on_ue_show(struct edac_device_ctl_info |
173 | *ctl_info, char *data) | |
e27e3dac | 174 | { |
079708b9 | 175 | return sprintf(data, "%u\n", ctl_info->panic_on_ue); |
e27e3dac DT |
176 | } |
177 | ||
079708b9 DT |
178 | static ssize_t edac_device_ctl_panic_on_ue_store(struct edac_device_ctl_info |
179 | *ctl_info, const char *data, | |
180 | size_t count) | |
e27e3dac DT |
181 | { |
182 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | |
079708b9 | 183 | ctl_info->panic_on_ue = (simple_strtoul(data, NULL, 0) != 0); |
e27e3dac DT |
184 | |
185 | return count; | |
186 | } | |
187 | ||
188 | /* 'poll_msec' show and store functions*/ | |
079708b9 DT |
189 | static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info |
190 | *ctl_info, char *data) | |
e27e3dac | 191 | { |
079708b9 | 192 | return sprintf(data, "%u\n", ctl_info->poll_msec); |
e27e3dac DT |
193 | } |
194 | ||
079708b9 DT |
195 | static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info |
196 | *ctl_info, const char *data, | |
197 | size_t count) | |
e27e3dac DT |
198 | { |
199 | unsigned long value; | |
200 | ||
201 | /* get the value and enforce that it is non-zero, must be at least | |
202 | * one millisecond for the delay period, between scans | |
203 | * Then cancel last outstanding delay for the work request | |
204 | * and set a new one. | |
205 | */ | |
079708b9 DT |
206 | value = simple_strtoul(data, NULL, 0); |
207 | edac_device_reset_delay_period(ctl_info, value); | |
e27e3dac | 208 | |
079708b9 | 209 | return count; |
e27e3dac DT |
210 | } |
211 | ||
e27e3dac DT |
212 | /* edac_device_ctl_info specific attribute structure */ |
213 | struct ctl_info_attribute { | |
079708b9 DT |
214 | struct attribute attr; |
215 | ssize_t(*show) (struct edac_device_ctl_info *, char *); | |
216 | ssize_t(*store) (struct edac_device_ctl_info *, const char *, size_t); | |
e27e3dac DT |
217 | }; |
218 | ||
219 | #define to_ctl_info(k) container_of(k, struct edac_device_ctl_info, kobj) | |
220 | #define to_ctl_info_attr(a) container_of(a,struct ctl_info_attribute,attr) | |
221 | ||
222 | /* Function to 'show' fields from the edac_dev 'ctl_info' structure */ | |
223 | static ssize_t edac_dev_ctl_info_show(struct kobject *kobj, | |
079708b9 | 224 | struct attribute *attr, char *buffer) |
e27e3dac | 225 | { |
079708b9 DT |
226 | struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); |
227 | struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); | |
e27e3dac | 228 | |
079708b9 DT |
229 | if (ctl_info_attr->show) |
230 | return ctl_info_attr->show(edac_dev, buffer); | |
231 | return -EIO; | |
e27e3dac DT |
232 | } |
233 | ||
234 | /* Function to 'store' fields into the edac_dev 'ctl_info' structure */ | |
235 | static ssize_t edac_dev_ctl_info_store(struct kobject *kobj, | |
079708b9 DT |
236 | struct attribute *attr, |
237 | const char *buffer, size_t count) | |
e27e3dac | 238 | { |
079708b9 DT |
239 | struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); |
240 | struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); | |
e27e3dac | 241 | |
079708b9 DT |
242 | if (ctl_info_attr->store) |
243 | return ctl_info_attr->store(edac_dev, buffer, count); | |
244 | return -EIO; | |
e27e3dac DT |
245 | } |
246 | ||
247 | /* edac_dev file operations for an 'ctl_info' */ | |
248 | static struct sysfs_ops device_ctl_info_ops = { | |
079708b9 DT |
249 | .show = edac_dev_ctl_info_show, |
250 | .store = edac_dev_ctl_info_store | |
e27e3dac DT |
251 | }; |
252 | ||
253 | #define CTL_INFO_ATTR(_name,_mode,_show,_store) \ | |
254 | static struct ctl_info_attribute attr_ctl_info_##_name = { \ | |
255 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | |
256 | .show = _show, \ | |
257 | .store = _store, \ | |
258 | }; | |
259 | ||
e27e3dac | 260 | /* Declare the various ctl_info attributes here and their respective ops */ |
079708b9 DT |
261 | CTL_INFO_ATTR(log_ue, S_IRUGO | S_IWUSR, |
262 | edac_device_ctl_log_ue_show, edac_device_ctl_log_ue_store); | |
263 | CTL_INFO_ATTR(log_ce, S_IRUGO | S_IWUSR, | |
264 | edac_device_ctl_log_ce_show, edac_device_ctl_log_ce_store); | |
265 | CTL_INFO_ATTR(panic_on_ue, S_IRUGO | S_IWUSR, | |
266 | edac_device_ctl_panic_on_ue_show, | |
267 | edac_device_ctl_panic_on_ue_store); | |
268 | CTL_INFO_ATTR(poll_msec, S_IRUGO | S_IWUSR, | |
269 | edac_device_ctl_poll_msec_show, edac_device_ctl_poll_msec_store); | |
e27e3dac DT |
270 | |
271 | /* Base Attributes of the EDAC_DEVICE ECC object */ | |
272 | static struct ctl_info_attribute *device_ctrl_attr[] = { | |
273 | &attr_ctl_info_panic_on_ue, | |
274 | &attr_ctl_info_log_ue, | |
275 | &attr_ctl_info_log_ce, | |
276 | &attr_ctl_info_poll_msec, | |
277 | NULL, | |
278 | }; | |
279 | ||
280 | /* Main DEVICE kobject release() function */ | |
281 | static void edac_device_ctrl_master_release(struct kobject *kobj) | |
282 | { | |
283 | struct edac_device_ctl_info *edac_dev; | |
284 | ||
285 | edac_dev = to_edacdev(kobj); | |
286 | ||
287 | debugf1("%s()\n", __func__); | |
288 | complete(&edac_dev->kobj_complete); | |
289 | } | |
290 | ||
291 | static struct kobj_type ktype_device_ctrl = { | |
292 | .release = edac_device_ctrl_master_release, | |
293 | .sysfs_ops = &device_ctl_info_ops, | |
079708b9 | 294 | .default_attrs = (struct attribute **)device_ctrl_attr, |
e27e3dac DT |
295 | }; |
296 | ||
e27e3dac DT |
297 | /**************** edac_device main kobj ctor/dtor code *********************/ |
298 | ||
299 | /* | |
300 | * edac_device_register_main_kobj | |
301 | * | |
302 | * perform the high level setup for the new edac_device instance | |
303 | * | |
304 | * Return: 0 SUCCESS | |
305 | * !0 FAILURE | |
306 | */ | |
079708b9 | 307 | static int edac_device_register_main_kobj(struct edac_device_ctl_info *edac_dev) |
e27e3dac DT |
308 | { |
309 | int err = 0; | |
310 | struct sysdev_class *edac_class; | |
311 | ||
312 | debugf1("%s()\n", __func__); | |
313 | ||
079708b9 DT |
314 | /* get the /sys/devices/system/edac reference */ |
315 | edac_class = edac_get_edac_class(); | |
316 | if (edac_class == NULL) { | |
317 | debugf1("%s() no edac_class error=%d\n", __func__, err); | |
318 | return err; | |
319 | } | |
e27e3dac DT |
320 | |
321 | /* Point to the 'edac_class' this instance 'reports' to */ | |
322 | edac_dev->edac_class = edac_class; | |
323 | ||
324 | /* Init the devices's kobject */ | |
079708b9 | 325 | memset(&edac_dev->kobj, 0, sizeof(struct kobject)); |
e27e3dac DT |
326 | edac_dev->kobj.ktype = &ktype_device_ctrl; |
327 | ||
328 | /* set this new device under the edac_class kobject */ | |
329 | edac_dev->kobj.parent = &edac_class->kset.kobj; | |
330 | ||
331 | /* generate sysfs "..../edac/<name>" */ | |
079708b9 DT |
332 | debugf1("%s() set name of kobject to: %s\n", __func__, edac_dev->name); |
333 | err = kobject_set_name(&edac_dev->kobj, "%s", edac_dev->name); | |
e27e3dac DT |
334 | if (err) |
335 | return err; | |
336 | err = kobject_register(&edac_dev->kobj); | |
337 | if (err) { | |
338 | debugf1("%s()Failed to register '.../edac/%s'\n", | |
079708b9 | 339 | __func__, edac_dev->name); |
e27e3dac DT |
340 | return err; |
341 | } | |
342 | ||
343 | debugf1("%s() Registered '.../edac/%s' kobject\n", | |
344 | __func__, edac_dev->name); | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | /* | |
350 | * edac_device_unregister_main_kobj: | |
351 | * the '..../edac/<name>' kobject | |
352 | */ | |
079708b9 DT |
353 | static void edac_device_unregister_main_kobj(struct edac_device_ctl_info |
354 | *edac_dev) | |
e27e3dac DT |
355 | { |
356 | debugf0("%s()\n", __func__); | |
357 | debugf1("%s() name of kobject is: %s\n", | |
358 | __func__, kobject_name(&edac_dev->kobj)); | |
359 | ||
360 | init_completion(&edac_dev->kobj_complete); | |
361 | ||
362 | /* | |
363 | * Unregister the edac device's kobject and | |
364 | * wait for reference count to reach 0. | |
365 | */ | |
366 | kobject_unregister(&edac_dev->kobj); | |
367 | wait_for_completion(&edac_dev->kobj_complete); | |
368 | } | |
369 | ||
e27e3dac DT |
370 | /*************** edac_dev -> instance information ***********/ |
371 | ||
372 | /* | |
373 | * Set of low-level instance attribute show functions | |
374 | */ | |
079708b9 DT |
375 | static ssize_t instance_ue_count_show(struct edac_device_instance *instance, |
376 | char *data) | |
e27e3dac | 377 | { |
079708b9 | 378 | return sprintf(data, "%u\n", instance->counters.ue_count); |
e27e3dac DT |
379 | } |
380 | ||
079708b9 DT |
381 | static ssize_t instance_ce_count_show(struct edac_device_instance *instance, |
382 | char *data) | |
e27e3dac | 383 | { |
079708b9 | 384 | return sprintf(data, "%u\n", instance->counters.ce_count); |
e27e3dac DT |
385 | } |
386 | ||
e27e3dac DT |
387 | #define to_instance(k) container_of(k, struct edac_device_instance, kobj) |
388 | #define to_instance_attr(a) container_of(a,struct instance_attribute,attr) | |
389 | ||
390 | /* DEVICE instance kobject release() function */ | |
391 | static void edac_device_ctrl_instance_release(struct kobject *kobj) | |
392 | { | |
393 | struct edac_device_instance *instance; | |
394 | ||
395 | debugf1("%s()\n", __func__); | |
396 | ||
397 | instance = to_instance(kobj); | |
398 | complete(&instance->kobj_complete); | |
399 | } | |
400 | ||
e27e3dac DT |
401 | /* instance specific attribute structure */ |
402 | struct instance_attribute { | |
079708b9 DT |
403 | struct attribute attr; |
404 | ssize_t(*show) (struct edac_device_instance *, char *); | |
405 | ssize_t(*store) (struct edac_device_instance *, const char *, size_t); | |
e27e3dac DT |
406 | }; |
407 | ||
e27e3dac DT |
408 | /* Function to 'show' fields from the edac_dev 'instance' structure */ |
409 | static ssize_t edac_dev_instance_show(struct kobject *kobj, | |
079708b9 | 410 | struct attribute *attr, char *buffer) |
e27e3dac | 411 | { |
079708b9 DT |
412 | struct edac_device_instance *instance = to_instance(kobj); |
413 | struct instance_attribute *instance_attr = to_instance_attr(attr); | |
e27e3dac | 414 | |
079708b9 DT |
415 | if (instance_attr->show) |
416 | return instance_attr->show(instance, buffer); | |
417 | return -EIO; | |
e27e3dac DT |
418 | } |
419 | ||
e27e3dac DT |
420 | /* Function to 'store' fields into the edac_dev 'instance' structure */ |
421 | static ssize_t edac_dev_instance_store(struct kobject *kobj, | |
079708b9 DT |
422 | struct attribute *attr, |
423 | const char *buffer, size_t count) | |
e27e3dac | 424 | { |
079708b9 DT |
425 | struct edac_device_instance *instance = to_instance(kobj); |
426 | struct instance_attribute *instance_attr = to_instance_attr(attr); | |
e27e3dac | 427 | |
079708b9 DT |
428 | if (instance_attr->store) |
429 | return instance_attr->store(instance, buffer, count); | |
430 | return -EIO; | |
e27e3dac DT |
431 | } |
432 | ||
e27e3dac DT |
433 | /* edac_dev file operations for an 'instance' */ |
434 | static struct sysfs_ops device_instance_ops = { | |
079708b9 DT |
435 | .show = edac_dev_instance_show, |
436 | .store = edac_dev_instance_store | |
e27e3dac DT |
437 | }; |
438 | ||
439 | #define INSTANCE_ATTR(_name,_mode,_show,_store) \ | |
440 | static struct instance_attribute attr_instance_##_name = { \ | |
441 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | |
442 | .show = _show, \ | |
443 | .store = _store, \ | |
444 | }; | |
445 | ||
446 | /* | |
447 | * Define attributes visible for the edac_device instance object | |
448 | * Each contains a pointer to a show and an optional set | |
449 | * function pointer that does the low level output/input | |
450 | */ | |
079708b9 DT |
451 | INSTANCE_ATTR(ce_count, S_IRUGO, instance_ce_count_show, NULL); |
452 | INSTANCE_ATTR(ue_count, S_IRUGO, instance_ue_count_show, NULL); | |
e27e3dac DT |
453 | |
454 | /* list of edac_dev 'instance' attributes */ | |
455 | static struct instance_attribute *device_instance_attr[] = { | |
456 | &attr_instance_ce_count, | |
457 | &attr_instance_ue_count, | |
458 | NULL, | |
459 | }; | |
460 | ||
461 | /* The 'ktype' for each edac_dev 'instance' */ | |
462 | static struct kobj_type ktype_instance_ctrl = { | |
463 | .release = edac_device_ctrl_instance_release, | |
464 | .sysfs_ops = &device_instance_ops, | |
079708b9 | 465 | .default_attrs = (struct attribute **)device_instance_attr, |
e27e3dac DT |
466 | }; |
467 | ||
e27e3dac DT |
468 | /*************** edac_dev -> instance -> block information *********/ |
469 | ||
470 | /* | |
471 | * Set of low-level block attribute show functions | |
472 | */ | |
079708b9 | 473 | static ssize_t block_ue_count_show(struct edac_device_block *block, char *data) |
e27e3dac | 474 | { |
079708b9 | 475 | return sprintf(data, "%u\n", block->counters.ue_count); |
e27e3dac DT |
476 | } |
477 | ||
079708b9 | 478 | static ssize_t block_ce_count_show(struct edac_device_block *block, char *data) |
e27e3dac | 479 | { |
079708b9 | 480 | return sprintf(data, "%u\n", block->counters.ce_count); |
e27e3dac DT |
481 | } |
482 | ||
e27e3dac DT |
483 | #define to_block(k) container_of(k, struct edac_device_block, kobj) |
484 | #define to_block_attr(a) container_of(a,struct block_attribute,attr) | |
485 | ||
486 | /* DEVICE block kobject release() function */ | |
487 | static void edac_device_ctrl_block_release(struct kobject *kobj) | |
488 | { | |
489 | struct edac_device_block *block; | |
490 | ||
491 | debugf1("%s()\n", __func__); | |
492 | ||
493 | block = to_block(kobj); | |
494 | complete(&block->kobj_complete); | |
495 | } | |
496 | ||
497 | /* block specific attribute structure */ | |
498 | struct block_attribute { | |
079708b9 DT |
499 | struct attribute attr; |
500 | ssize_t(*show) (struct edac_device_block *, char *); | |
501 | ssize_t(*store) (struct edac_device_block *, const char *, size_t); | |
e27e3dac DT |
502 | }; |
503 | ||
504 | /* Function to 'show' fields from the edac_dev 'block' structure */ | |
505 | static ssize_t edac_dev_block_show(struct kobject *kobj, | |
079708b9 | 506 | struct attribute *attr, char *buffer) |
e27e3dac | 507 | { |
079708b9 DT |
508 | struct edac_device_block *block = to_block(kobj); |
509 | struct block_attribute *block_attr = to_block_attr(attr); | |
e27e3dac | 510 | |
079708b9 DT |
511 | if (block_attr->show) |
512 | return block_attr->show(block, buffer); | |
513 | return -EIO; | |
e27e3dac DT |
514 | } |
515 | ||
e27e3dac DT |
516 | /* Function to 'store' fields into the edac_dev 'block' structure */ |
517 | static ssize_t edac_dev_block_store(struct kobject *kobj, | |
079708b9 DT |
518 | struct attribute *attr, |
519 | const char *buffer, size_t count) | |
e27e3dac | 520 | { |
079708b9 DT |
521 | struct edac_device_block *block = to_block(kobj); |
522 | struct block_attribute *block_attr = to_block_attr(attr); | |
e27e3dac | 523 | |
079708b9 DT |
524 | if (block_attr->store) |
525 | return block_attr->store(block, buffer, count); | |
526 | return -EIO; | |
e27e3dac DT |
527 | } |
528 | ||
e27e3dac DT |
529 | /* edac_dev file operations for a 'block' */ |
530 | static struct sysfs_ops device_block_ops = { | |
079708b9 DT |
531 | .show = edac_dev_block_show, |
532 | .store = edac_dev_block_store | |
e27e3dac DT |
533 | }; |
534 | ||
e27e3dac DT |
535 | #define BLOCK_ATTR(_name,_mode,_show,_store) \ |
536 | static struct block_attribute attr_block_##_name = { \ | |
537 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | |
538 | .show = _show, \ | |
539 | .store = _store, \ | |
540 | }; | |
541 | ||
079708b9 DT |
542 | BLOCK_ATTR(ce_count, S_IRUGO, block_ce_count_show, NULL); |
543 | BLOCK_ATTR(ue_count, S_IRUGO, block_ue_count_show, NULL); | |
e27e3dac DT |
544 | |
545 | /* list of edac_dev 'block' attributes */ | |
546 | static struct block_attribute *device_block_attr[] = { | |
547 | &attr_block_ce_count, | |
548 | &attr_block_ue_count, | |
549 | NULL, | |
550 | }; | |
551 | ||
552 | /* The 'ktype' for each edac_dev 'block' */ | |
553 | static struct kobj_type ktype_block_ctrl = { | |
554 | .release = edac_device_ctrl_block_release, | |
555 | .sysfs_ops = &device_block_ops, | |
079708b9 | 556 | .default_attrs = (struct attribute **)device_block_attr, |
e27e3dac DT |
557 | }; |
558 | ||
e27e3dac DT |
559 | /************** block ctor/dtor code ************/ |
560 | ||
561 | /* | |
562 | * edac_device_create_block | |
563 | */ | |
079708b9 DT |
564 | static int edac_device_create_block(struct edac_device_ctl_info *edac_dev, |
565 | struct edac_device_instance *instance, | |
566 | int idx) | |
e27e3dac DT |
567 | { |
568 | int err; | |
569 | struct edac_device_block *block; | |
570 | ||
571 | block = &instance->blocks[idx]; | |
572 | ||
573 | debugf1("%s() Instance '%s' block[%d] '%s'\n", | |
079708b9 | 574 | __func__, instance->name, idx, block->name); |
e27e3dac DT |
575 | |
576 | /* init this block's kobject */ | |
079708b9 | 577 | memset(&block->kobj, 0, sizeof(struct kobject)); |
e27e3dac DT |
578 | block->kobj.parent = &instance->kobj; |
579 | block->kobj.ktype = &ktype_block_ctrl; | |
580 | ||
079708b9 | 581 | err = kobject_set_name(&block->kobj, "%s", block->name); |
e27e3dac DT |
582 | if (err) |
583 | return err; | |
584 | ||
585 | err = kobject_register(&block->kobj); | |
586 | if (err) { | |
587 | debugf1("%s()Failed to register instance '%s'\n", | |
079708b9 | 588 | __func__, block->name); |
e27e3dac DT |
589 | return err; |
590 | } | |
591 | ||
592 | return 0; | |
593 | } | |
594 | ||
595 | /* | |
596 | * edac_device_delete_block(edac_dev,j); | |
597 | */ | |
079708b9 DT |
598 | static void edac_device_delete_block(struct edac_device_ctl_info *edac_dev, |
599 | struct edac_device_instance *instance, | |
600 | int idx) | |
e27e3dac DT |
601 | { |
602 | struct edac_device_block *block; | |
603 | ||
604 | block = &instance->blocks[idx]; | |
605 | ||
606 | /* unregister this block's kobject */ | |
607 | init_completion(&block->kobj_complete); | |
608 | kobject_unregister(&block->kobj); | |
609 | wait_for_completion(&block->kobj_complete); | |
610 | } | |
611 | ||
612 | /************** instance ctor/dtor code ************/ | |
613 | ||
614 | /* | |
615 | * edac_device_create_instance | |
616 | * create just one instance of an edac_device 'instance' | |
617 | */ | |
079708b9 DT |
618 | static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev, |
619 | int idx) | |
e27e3dac DT |
620 | { |
621 | int i, j; | |
622 | int err; | |
623 | struct edac_device_instance *instance; | |
624 | ||
625 | instance = &edac_dev->instances[idx]; | |
626 | ||
627 | /* Init the instance's kobject */ | |
079708b9 | 628 | memset(&instance->kobj, 0, sizeof(struct kobject)); |
e27e3dac DT |
629 | |
630 | /* set this new device under the edac_device main kobject */ | |
631 | instance->kobj.parent = &edac_dev->kobj; | |
632 | instance->kobj.ktype = &ktype_instance_ctrl; | |
633 | ||
079708b9 | 634 | err = kobject_set_name(&instance->kobj, "%s", instance->name); |
e27e3dac DT |
635 | if (err) |
636 | return err; | |
637 | ||
638 | err = kobject_register(&instance->kobj); | |
639 | if (err != 0) { | |
640 | debugf2("%s() Failed to register instance '%s'\n", | |
079708b9 | 641 | __func__, instance->name); |
e27e3dac DT |
642 | return err; |
643 | } | |
644 | ||
645 | debugf1("%s() now register '%d' blocks for instance %d\n", | |
079708b9 | 646 | __func__, instance->nr_blocks, idx); |
e27e3dac DT |
647 | |
648 | /* register all blocks of this instance */ | |
079708b9 DT |
649 | for (i = 0; i < instance->nr_blocks; i++) { |
650 | err = edac_device_create_block(edac_dev, instance, i); | |
e27e3dac DT |
651 | if (err) { |
652 | for (j = 0; j < i; j++) { | |
079708b9 | 653 | edac_device_delete_block(edac_dev, instance, j); |
e27e3dac DT |
654 | } |
655 | return err; | |
656 | } | |
657 | } | |
658 | ||
659 | debugf1("%s() Registered instance %d '%s' kobject\n", | |
660 | __func__, idx, instance->name); | |
661 | ||
662 | return 0; | |
663 | } | |
664 | ||
665 | /* | |
666 | * edac_device_remove_instance | |
667 | * remove an edac_device instance | |
668 | */ | |
079708b9 DT |
669 | static void edac_device_delete_instance(struct edac_device_ctl_info *edac_dev, |
670 | int idx) | |
e27e3dac DT |
671 | { |
672 | int i; | |
673 | struct edac_device_instance *instance; | |
674 | ||
675 | instance = &edac_dev->instances[idx]; | |
676 | ||
677 | /* unregister all blocks in this instance */ | |
678 | for (i = 0; i < instance->nr_blocks; i++) { | |
079708b9 | 679 | edac_device_delete_block(edac_dev, instance, i); |
e27e3dac DT |
680 | } |
681 | ||
682 | /* unregister this instance's kobject */ | |
683 | init_completion(&instance->kobj_complete); | |
684 | kobject_unregister(&instance->kobj); | |
685 | wait_for_completion(&instance->kobj_complete); | |
686 | } | |
687 | ||
688 | /* | |
689 | * edac_device_create_instances | |
690 | * create the first level of 'instances' for this device | |
691 | * (ie 'cache' might have 'cache0', 'cache1', 'cache2', etc | |
692 | */ | |
693 | static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev) | |
694 | { | |
695 | int i, j; | |
696 | int err; | |
697 | ||
698 | debugf0("%s()\n", __func__); | |
699 | ||
700 | /* iterate over creation of the instances */ | |
079708b9 DT |
701 | for (i = 0; i < edac_dev->nr_instances; i++) { |
702 | err = edac_device_create_instance(edac_dev, i); | |
e27e3dac DT |
703 | if (err) { |
704 | /* unwind previous instances on error */ | |
705 | for (j = 0; j < i; j++) { | |
079708b9 | 706 | edac_device_delete_instance(edac_dev, j); |
e27e3dac DT |
707 | } |
708 | return err; | |
709 | } | |
710 | } | |
711 | ||
712 | return 0; | |
713 | } | |
714 | ||
715 | /* | |
716 | * edac_device_delete_instances(edac_dev); | |
717 | * unregister all the kobjects of the instances | |
718 | */ | |
719 | static void edac_device_delete_instances(struct edac_device_ctl_info *edac_dev) | |
720 | { | |
721 | int i; | |
722 | ||
723 | /* iterate over creation of the instances */ | |
079708b9 DT |
724 | for (i = 0; i < edac_dev->nr_instances; i++) { |
725 | edac_device_delete_instance(edac_dev, i); | |
e27e3dac DT |
726 | } |
727 | } | |
728 | ||
729 | /******************* edac_dev sysfs ctor/dtor code *************/ | |
730 | ||
731 | /* | |
732 | * edac_device_create_sysfs() Constructor | |
733 | * | |
734 | * Create a new edac_device kobject instance, | |
735 | * | |
736 | * Return: | |
737 | * 0 Success | |
738 | * !0 Failure | |
739 | */ | |
740 | int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) | |
741 | { | |
742 | int err; | |
079708b9 | 743 | struct kobject *edac_kobj = &edac_dev->kobj; |
e27e3dac DT |
744 | |
745 | /* register this instance's main kobj with the edac class kobj */ | |
746 | err = edac_device_register_main_kobj(edac_dev); | |
747 | if (err) | |
748 | return err; | |
749 | ||
750 | debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx); | |
751 | ||
752 | /* create a symlink from the edac device | |
753 | * to the platform 'device' being used for this | |
754 | */ | |
755 | err = sysfs_create_link(edac_kobj, | |
079708b9 | 756 | &edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK); |
e27e3dac DT |
757 | if (err) { |
758 | debugf0("%s() sysfs_create_link() returned err= %d\n", | |
759 | __func__, err); | |
760 | return err; | |
761 | } | |
762 | ||
763 | debugf0("%s() calling create-instances, idx=%d\n", | |
764 | __func__, edac_dev->dev_idx); | |
765 | ||
766 | /* Create the first level instance directories */ | |
767 | err = edac_device_create_instances(edac_dev); | |
768 | if (err) { | |
769 | goto error0; | |
770 | } | |
771 | ||
772 | return 0; | |
773 | ||
774 | /* Error unwind stack */ | |
775 | ||
079708b9 | 776 | error0: |
e27e3dac DT |
777 | edac_device_unregister_main_kobj(edac_dev); |
778 | ||
779 | return err; | |
780 | } | |
781 | ||
782 | /* | |
783 | * edac_device_remove_sysfs() destructor | |
784 | * | |
785 | * remove a edac_device instance | |
786 | */ | |
787 | void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev) | |
788 | { | |
789 | debugf0("%s()\n", __func__); | |
790 | ||
791 | edac_device_delete_instances(edac_dev); | |
792 | ||
793 | /* remove the sym link */ | |
794 | sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); | |
795 | ||
796 | /* unregister the instance's main kobj */ | |
797 | edac_device_unregister_main_kobj(edac_dev); | |
798 | } |