blob: 7e1374afd967256054be17b687954151576d60ae [file] [log] [blame]
Dave Jiang81d87cb2007-07-19 01:49:52 -07001/*
2 * edac_module.c
3 *
Doug Thompsonfb3fb202007-07-19 01:50:30 -07004 * (C) 2007 www.softwarebitmaker.com
5 *
Dave Jiang81d87cb2007-07-19 01:49:52 -07006 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 *
Doug Thompsonfb3fb202007-07-19 01:50:30 -070010 * Author: Doug Thompson <dougthompson@xmission.com>
Dave Jiang81d87cb2007-07-19 01:49:52 -070011 *
12 */
Dave Jiangc0d12172007-07-19 01:49:46 -070013#include <linux/edac.h>
Douglas Thompson7c9281d2007-07-19 01:49:33 -070014
Douglas Thompson20bcb7a2007-07-19 01:49:47 -070015#include "edac_core.h"
Douglas Thompson7c9281d2007-07-19 01:49:33 -070016#include "edac_module.h"
17
Doug Thompsonfb3fb202007-07-19 01:50:30 -070018#define EDAC_VERSION "Ver: 2.1.0 " __DATE__
Douglas Thompson7c9281d2007-07-19 01:49:33 -070019
20#ifdef CONFIG_EDAC_DEBUG
21/* Values of 0 to 4 will generate output */
Doug Thompson8096cfa2007-07-19 01:50:27 -070022int edac_debug_level = 2;
Douglas Thompson7c9281d2007-07-19 01:49:33 -070023EXPORT_SYMBOL_GPL(edac_debug_level);
24#endif
25
Douglas Thompsone27e3da2007-07-19 01:49:36 -070026/* scope is to module level only */
27struct workqueue_struct *edac_workqueue;
28
Douglas Thompsone27e3da2007-07-19 01:49:36 -070029/*
30 * sysfs object: /sys/devices/system/edac
31 * need to export to other files in this modules
32 */
33static struct sysdev_class edac_class = {
Kay Sieversaf5ca3f2007-12-20 02:09:39 +010034 .name = "edac",
Douglas Thompsone27e3da2007-07-19 01:49:36 -070035};
Douglas Thompsonf0440912007-07-19 01:50:19 -070036static int edac_class_valid;
Douglas Thompsone27e3da2007-07-19 01:49:36 -070037
38/*
Douglas Thompson494d0d52007-07-19 01:50:21 -070039 * edac_op_state_to_string()
Dave Jiang91b99042007-07-19 01:49:52 -070040 */
Douglas Thompson494d0d52007-07-19 01:50:21 -070041char *edac_op_state_to_string(int opstate)
Dave Jiang91b99042007-07-19 01:49:52 -070042{
43 if (opstate == OP_RUNNING_POLL)
44 return "POLLED";
45 else if (opstate == OP_RUNNING_INTERRUPT)
46 return "INTERRUPT";
47 else if (opstate == OP_RUNNING_POLL_INTR)
48 return "POLL-INTR";
49 else if (opstate == OP_ALLOC)
50 return "ALLOC";
51 else if (opstate == OP_OFFLINE)
52 return "OFFLINE";
53
54 return "UNKNOWN";
55}
56
57/*
Douglas Thompsone27e3da2007-07-19 01:49:36 -070058 * edac_get_edac_class()
59 *
60 * return pointer to the edac class of 'edac'
61 */
62struct sysdev_class *edac_get_edac_class(void)
63{
Douglas Thompson079708b2007-07-19 01:49:58 -070064 struct sysdev_class *classptr = NULL;
Douglas Thompsone27e3da2007-07-19 01:49:36 -070065
66 if (edac_class_valid)
67 classptr = &edac_class;
68
69 return classptr;
70}
71
72/*
73 * edac_register_sysfs_edac_name()
74 *
75 * register the 'edac' into /sys/devices/system
76 *
77 * return:
78 * 0 success
79 * !0 error
80 */
81static int edac_register_sysfs_edac_name(void)
82{
83 int err;
84
85 /* create the /sys/devices/system/edac directory */
86 err = sysdev_class_register(&edac_class);
87
88 if (err) {
89 debugf1("%s() error=%d\n", __func__, err);
90 return err;
91 }
92
93 edac_class_valid = 1;
94 return 0;
95}
96
97/*
98 * sysdev_class_unregister()
99 *
100 * unregister the 'edac' from /sys/devices/system
101 */
102static void edac_unregister_sysfs_edac_name(void)
103{
104 /* only if currently registered, then unregister it */
105 if (edac_class_valid)
106 sysdev_class_unregister(&edac_class);
107
108 edac_class_valid = 0;
109}
110
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700111/*
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700112 * edac_workqueue_setup
113 * initialize the edac work queue for polling operations
114 */
115static int edac_workqueue_setup(void)
116{
117 edac_workqueue = create_singlethread_workqueue("edac-poller");
118 if (edac_workqueue == NULL)
119 return -ENODEV;
120 else
121 return 0;
122}
123
124/*
125 * edac_workqueue_teardown
126 * teardown the edac workqueue
127 */
128static void edac_workqueue_teardown(void)
129{
130 if (edac_workqueue) {
131 flush_workqueue(edac_workqueue);
132 destroy_workqueue(edac_workqueue);
133 edac_workqueue = NULL;
134 }
135}
136
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700137/*
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700138 * edac_init
139 * module initialization entry point
140 */
141static int __init edac_init(void)
142{
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700143 int err = 0;
144
Doug Thompsonfb3fb202007-07-19 01:50:30 -0700145 edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n");
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700146
147 /*
148 * Harvest and clear any boot/initialization PCI parity errors
149 *
150 * FIXME: This only clears errors logged by devices present at time of
Douglas Thompson079708b2007-07-19 01:49:58 -0700151 * module initialization. We should also do an initial clear
152 * of each newly hotplugged device.
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700153 */
154 edac_pci_clear_parity_errors();
155
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700156 /*
Doug Thompson8096cfa2007-07-19 01:50:27 -0700157 * perform the registration of the /sys/devices/system/edac class object
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700158 */
159 if (edac_register_sysfs_edac_name()) {
160 edac_printk(KERN_ERR, EDAC_MC,
Douglas Thompson052dfb42007-07-19 01:50:13 -0700161 "Error initializing 'edac' kobject\n");
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700162 err = -ENODEV;
163 goto error;
164 }
165
Doug Thompson8096cfa2007-07-19 01:50:27 -0700166 /*
167 * now set up the mc_kset under the edac class object
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700168 */
Doug Thompson8096cfa2007-07-19 01:50:27 -0700169 err = edac_sysfs_setup_mc_kset();
170 if (err)
171 goto sysfs_setup_fail;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700172
Doug Thompson8096cfa2007-07-19 01:50:27 -0700173 /* Setup/Initialize the workq for this core */
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700174 err = edac_workqueue_setup();
175 if (err) {
176 edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n");
Doug Thompson8096cfa2007-07-19 01:50:27 -0700177 goto workq_fail;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700178 }
179
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700180 return 0;
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700181
182 /* Error teardown stack */
Doug Thompson8096cfa2007-07-19 01:50:27 -0700183workq_fail:
184 edac_sysfs_teardown_mc_kset();
185
186sysfs_setup_fail:
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700187 edac_unregister_sysfs_edac_name();
Doug Thompson8096cfa2007-07-19 01:50:27 -0700188
Douglas Thompson052dfb42007-07-19 01:50:13 -0700189error:
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700190 return err;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700191}
192
193/*
194 * edac_exit()
195 * module exit/termination function
196 */
197static void __exit edac_exit(void)
198{
199 debugf0("%s()\n", __func__);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700200
Douglas Thompson079708b2007-07-19 01:49:58 -0700201 /* tear down the various subsystems */
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700202 edac_workqueue_teardown();
Doug Thompson8096cfa2007-07-19 01:50:27 -0700203 edac_sysfs_teardown_mc_kset();
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700204 edac_unregister_sysfs_edac_name();
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700205}
206
207/*
208 * Inform the kernel of our entry and exit points
209 */
210module_init(edac_init);
211module_exit(edac_exit);
212
213MODULE_LICENSE("GPL");
214MODULE_AUTHOR("Doug Thompson www.softwarebitmaker.com, et al");
215MODULE_DESCRIPTION("Core library routines for EDAC reporting");
216
217/* refer to *_sysfs.c files for parameters that are exported via sysfs */
218
219#ifdef CONFIG_EDAC_DEBUG
220module_param(edac_debug_level, int, 0644);
221MODULE_PARM_DESC(edac_debug_level, "Debug level");
222#endif