18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * edac_module.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * (C) 2007 www.softwarebitmaker.com
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Author: Doug Thompson <dougthompson@xmission.com>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/edac.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "edac_mc.h"
168c2ecf20Sopenharmony_ci#include "edac_module.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define EDAC_VERSION "Ver: 3.0.0"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int edac_set_debug_level(const char *buf,
238c2ecf20Sopenharmony_ci				const struct kernel_param *kp)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	unsigned long val;
268c2ecf20Sopenharmony_ci	int ret;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &val);
298c2ecf20Sopenharmony_ci	if (ret)
308c2ecf20Sopenharmony_ci		return ret;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (val > 4)
338c2ecf20Sopenharmony_ci		return -EINVAL;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return param_set_int(buf, kp);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Values of 0 to 4 will generate output */
398c2ecf20Sopenharmony_ciint edac_debug_level = 2;
408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_debug_level);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cimodule_param_call(edac_debug_level, edac_set_debug_level, param_get_int,
438c2ecf20Sopenharmony_ci		  &edac_debug_level, 0644);
448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_debug_level, "EDAC debug level: [0-4], default: 2");
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * edac_op_state_to_string()
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cichar *edac_op_state_to_string(int opstate)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	if (opstate == OP_RUNNING_POLL)
538c2ecf20Sopenharmony_ci		return "POLLED";
548c2ecf20Sopenharmony_ci	else if (opstate == OP_RUNNING_INTERRUPT)
558c2ecf20Sopenharmony_ci		return "INTERRUPT";
568c2ecf20Sopenharmony_ci	else if (opstate == OP_RUNNING_POLL_INTR)
578c2ecf20Sopenharmony_ci		return "POLL-INTR";
588c2ecf20Sopenharmony_ci	else if (opstate == OP_ALLOC)
598c2ecf20Sopenharmony_ci		return "ALLOC";
608c2ecf20Sopenharmony_ci	else if (opstate == OP_OFFLINE)
618c2ecf20Sopenharmony_ci		return "OFFLINE";
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return "UNKNOWN";
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * sysfs object: /sys/devices/system/edac
688c2ecf20Sopenharmony_ci *	need to export to other files
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic struct bus_type edac_subsys = {
718c2ecf20Sopenharmony_ci	.name = "edac",
728c2ecf20Sopenharmony_ci	.dev_name = "edac",
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int edac_subsys_init(void)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	int err;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* create the /sys/devices/system/edac directory */
808c2ecf20Sopenharmony_ci	err = subsys_system_register(&edac_subsys, NULL);
818c2ecf20Sopenharmony_ci	if (err)
828c2ecf20Sopenharmony_ci		printk(KERN_ERR "Error registering toplevel EDAC sysfs dir\n");
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return err;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void edac_subsys_exit(void)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	bus_unregister(&edac_subsys);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* return pointer to the 'edac' node in sysfs */
938c2ecf20Sopenharmony_cistruct bus_type *edac_get_sysfs_subsys(void)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	return &edac_subsys;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(edac_get_sysfs_subsys);
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * edac_init
1008c2ecf20Sopenharmony_ci *      module initialization entry point
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic int __init edac_init(void)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	int err = 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n");
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	err = edac_subsys_init();
1098c2ecf20Sopenharmony_ci	if (err)
1108c2ecf20Sopenharmony_ci		return err;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * Harvest and clear any boot/initialization PCI parity errors
1148c2ecf20Sopenharmony_ci	 *
1158c2ecf20Sopenharmony_ci	 * FIXME: This only clears errors logged by devices present at time of
1168c2ecf20Sopenharmony_ci	 *      module initialization.  We should also do an initial clear
1178c2ecf20Sopenharmony_ci	 *      of each newly hotplugged device.
1188c2ecf20Sopenharmony_ci	 */
1198c2ecf20Sopenharmony_ci	edac_pci_clear_parity_errors();
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	err = edac_mc_sysfs_init();
1228c2ecf20Sopenharmony_ci	if (err)
1238c2ecf20Sopenharmony_ci		goto err_sysfs;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	edac_debugfs_init();
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	err = edac_workqueue_setup();
1288c2ecf20Sopenharmony_ci	if (err) {
1298c2ecf20Sopenharmony_ci		edac_printk(KERN_ERR, EDAC_MC, "Failure initializing workqueue\n");
1308c2ecf20Sopenharmony_ci		goto err_wq;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cierr_wq:
1368c2ecf20Sopenharmony_ci	edac_debugfs_exit();
1378c2ecf20Sopenharmony_ci	edac_mc_sysfs_exit();
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cierr_sysfs:
1408c2ecf20Sopenharmony_ci	edac_subsys_exit();
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return err;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * edac_exit()
1478c2ecf20Sopenharmony_ci *      module exit/termination function
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistatic void __exit edac_exit(void)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* tear down the various subsystems */
1548c2ecf20Sopenharmony_ci	edac_workqueue_teardown();
1558c2ecf20Sopenharmony_ci	edac_mc_sysfs_exit();
1568c2ecf20Sopenharmony_ci	edac_debugfs_exit();
1578c2ecf20Sopenharmony_ci	edac_subsys_exit();
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/*
1618c2ecf20Sopenharmony_ci * Inform the kernel of our entry and exit points
1628c2ecf20Sopenharmony_ci */
1638c2ecf20Sopenharmony_cisubsys_initcall(edac_init);
1648c2ecf20Sopenharmony_cimodule_exit(edac_exit);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Doug Thompson www.softwarebitmaker.com, et al");
1688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Core library routines for EDAC reporting");
169