181d87cb1SDave Jiang /*
281d87cb1SDave Jiang * edac_module.c
381d87cb1SDave Jiang *
4fb3fb206SDoug Thompson * (C) 2007 www.softwarebitmaker.com
5fb3fb206SDoug Thompson *
681d87cb1SDave Jiang * This file is licensed under the terms of the GNU General Public
781d87cb1SDave Jiang * License version 2. This program is licensed "as is" without any
881d87cb1SDave Jiang * warranty of any kind, whether express or implied.
981d87cb1SDave Jiang *
10fb3fb206SDoug Thompson * Author: Doug Thompson <dougthompson@xmission.com>
1181d87cb1SDave Jiang *
1281d87cb1SDave Jiang */
13c0d12172SDave Jiang #include <linux/edac.h>
147c9281d7SDouglas Thompson
1578d88e8aSMauro Carvalho Chehab #include "edac_mc.h"
167c9281d7SDouglas Thompson #include "edac_module.h"
177c9281d7SDouglas Thompson
185156a5f4SMauro Carvalho Chehab #define EDAC_VERSION "Ver: 3.0.0"
197c9281d7SDouglas Thompson
207c9281d7SDouglas Thompson #ifdef CONFIG_EDAC_DEBUG
2137929874SBorislav Petkov
edac_set_debug_level(const char * buf,const struct kernel_param * kp)22*e4dca7b7SKees Cook static int edac_set_debug_level(const char *buf,
23*e4dca7b7SKees Cook const struct kernel_param *kp)
2437929874SBorislav Petkov {
2537929874SBorislav Petkov unsigned long val;
2637929874SBorislav Petkov int ret;
2737929874SBorislav Petkov
2837929874SBorislav Petkov ret = kstrtoul(buf, 0, &val);
2937929874SBorislav Petkov if (ret)
3037929874SBorislav Petkov return ret;
3137929874SBorislav Petkov
326866b390SFabian Frederick if (val > 4)
3337929874SBorislav Petkov return -EINVAL;
3437929874SBorislav Petkov
3537929874SBorislav Petkov return param_set_int(buf, kp);
3637929874SBorislav Petkov }
3737929874SBorislav Petkov
387c9281d7SDouglas Thompson /* Values of 0 to 4 will generate output */
398096cfafSDoug Thompson int edac_debug_level = 2;
407c9281d7SDouglas Thompson EXPORT_SYMBOL_GPL(edac_debug_level);
4137929874SBorislav Petkov
4237929874SBorislav Petkov module_param_call(edac_debug_level, edac_set_debug_level, param_get_int,
4337929874SBorislav Petkov &edac_debug_level, 0644);
4437929874SBorislav Petkov MODULE_PARM_DESC(edac_debug_level, "EDAC debug level: [0-4], default: 2");
457c9281d7SDouglas Thompson #endif
467c9281d7SDouglas Thompson
47e27e3dacSDouglas Thompson /*
48494d0d55SDouglas Thompson * edac_op_state_to_string()
4991b99041SDave Jiang */
edac_op_state_to_string(int opstate)50494d0d55SDouglas Thompson char *edac_op_state_to_string(int opstate)
5191b99041SDave Jiang {
5291b99041SDave Jiang if (opstate == OP_RUNNING_POLL)
5391b99041SDave Jiang return "POLLED";
5491b99041SDave Jiang else if (opstate == OP_RUNNING_INTERRUPT)
5591b99041SDave Jiang return "INTERRUPT";
5691b99041SDave Jiang else if (opstate == OP_RUNNING_POLL_INTR)
5791b99041SDave Jiang return "POLL-INTR";
5891b99041SDave Jiang else if (opstate == OP_ALLOC)
5991b99041SDave Jiang return "ALLOC";
6091b99041SDave Jiang else if (opstate == OP_OFFLINE)
6191b99041SDave Jiang return "OFFLINE";
6291b99041SDave Jiang
6391b99041SDave Jiang return "UNKNOWN";
6491b99041SDave Jiang }
6591b99041SDave Jiang
6691b99041SDave Jiang /*
67733476cfSBorislav Petkov * sysfs object: /sys/devices/system/edac
68733476cfSBorislav Petkov * need to export to other files
69733476cfSBorislav Petkov */
70a97d2627SBorislav Petkov static struct bus_type edac_subsys = {
71733476cfSBorislav Petkov .name = "edac",
72733476cfSBorislav Petkov .dev_name = "edac",
73733476cfSBorislav Petkov };
74733476cfSBorislav Petkov
edac_subsys_init(void)75733476cfSBorislav Petkov static int edac_subsys_init(void)
76733476cfSBorislav Petkov {
77733476cfSBorislav Petkov int err;
78733476cfSBorislav Petkov
79733476cfSBorislav Petkov /* create the /sys/devices/system/edac directory */
80733476cfSBorislav Petkov err = subsys_system_register(&edac_subsys, NULL);
81733476cfSBorislav Petkov if (err)
82733476cfSBorislav Petkov printk(KERN_ERR "Error registering toplevel EDAC sysfs dir\n");
83733476cfSBorislav Petkov
84733476cfSBorislav Petkov return err;
85733476cfSBorislav Petkov }
86733476cfSBorislav Petkov
edac_subsys_exit(void)87733476cfSBorislav Petkov static void edac_subsys_exit(void)
88733476cfSBorislav Petkov {
89733476cfSBorislav Petkov bus_unregister(&edac_subsys);
90733476cfSBorislav Petkov }
91733476cfSBorislav Petkov
92733476cfSBorislav Petkov /* return pointer to the 'edac' node in sysfs */
edac_get_sysfs_subsys(void)93733476cfSBorislav Petkov struct bus_type *edac_get_sysfs_subsys(void)
94733476cfSBorislav Petkov {
95733476cfSBorislav Petkov return &edac_subsys;
96733476cfSBorislav Petkov }
97733476cfSBorislav Petkov EXPORT_SYMBOL_GPL(edac_get_sysfs_subsys);
98733476cfSBorislav Petkov /*
997c9281d7SDouglas Thompson * edac_init
1007c9281d7SDouglas Thompson * module initialization entry point
1017c9281d7SDouglas Thompson */
edac_init(void)1027c9281d7SDouglas Thompson static int __init edac_init(void)
1037c9281d7SDouglas Thompson {
104e27e3dacSDouglas Thompson int err = 0;
105e27e3dacSDouglas Thompson
106fb3fb206SDoug Thompson edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n");
1077c9281d7SDouglas Thompson
108733476cfSBorislav Petkov err = edac_subsys_init();
109733476cfSBorislav Petkov if (err)
110733476cfSBorislav Petkov return err;
111733476cfSBorislav Petkov
1127c9281d7SDouglas Thompson /*
1137c9281d7SDouglas Thompson * Harvest and clear any boot/initialization PCI parity errors
1147c9281d7SDouglas Thompson *
1157c9281d7SDouglas Thompson * FIXME: This only clears errors logged by devices present at time of
1167c9281d7SDouglas Thompson * module initialization. We should also do an initial clear
1177c9281d7SDouglas Thompson * of each newly hotplugged device.
1187c9281d7SDouglas Thompson */
1197c9281d7SDouglas Thompson edac_pci_clear_parity_errors();
1207c9281d7SDouglas Thompson
1217a623c03SMauro Carvalho Chehab err = edac_mc_sysfs_init();
1228096cfafSDoug Thompson if (err)
123c6b97bcfSAlexey Khoroshilov goto err_sysfs;
1247c9281d7SDouglas Thompson
125e7930ba4SRob Herring edac_debugfs_init();
126e7930ba4SRob Herring
127e27e3dacSDouglas Thompson err = edac_workqueue_setup();
128e27e3dacSDouglas Thompson if (err) {
129c6b97bcfSAlexey Khoroshilov edac_printk(KERN_ERR, EDAC_MC, "Failure initializing workqueue\n");
130c6b97bcfSAlexey Khoroshilov goto err_wq;
1317c9281d7SDouglas Thompson }
1327c9281d7SDouglas Thompson
1337c9281d7SDouglas Thompson return 0;
134e27e3dacSDouglas Thompson
135c6b97bcfSAlexey Khoroshilov err_wq:
136c6b97bcfSAlexey Khoroshilov edac_debugfs_exit();
137c6b97bcfSAlexey Khoroshilov edac_mc_sysfs_exit();
138c6b97bcfSAlexey Khoroshilov
139c6b97bcfSAlexey Khoroshilov err_sysfs:
140733476cfSBorislav Petkov edac_subsys_exit();
141733476cfSBorislav Petkov
142e27e3dacSDouglas Thompson return err;
1437c9281d7SDouglas Thompson }
1447c9281d7SDouglas Thompson
1457c9281d7SDouglas Thompson /*
1467c9281d7SDouglas Thompson * edac_exit()
1477c9281d7SDouglas Thompson * module exit/termination function
1487c9281d7SDouglas Thompson */
edac_exit(void)1497c9281d7SDouglas Thompson static void __exit edac_exit(void)
1507c9281d7SDouglas Thompson {
151956b9ba1SJoe Perches edac_dbg(0, "\n");
1527c9281d7SDouglas Thompson
153e27e3dacSDouglas Thompson /* tear down the various subsystems */
154e27e3dacSDouglas Thompson edac_workqueue_teardown();
1557a623c03SMauro Carvalho Chehab edac_mc_sysfs_exit();
156e7930ba4SRob Herring edac_debugfs_exit();
157733476cfSBorislav Petkov edac_subsys_exit();
1587c9281d7SDouglas Thompson }
1597c9281d7SDouglas Thompson
1607c9281d7SDouglas Thompson /*
1617c9281d7SDouglas Thompson * Inform the kernel of our entry and exit points
1627c9281d7SDouglas Thompson */
1634ab19b06SMauro Carvalho Chehab subsys_initcall(edac_init);
1647c9281d7SDouglas Thompson module_exit(edac_exit);
1657c9281d7SDouglas Thompson
1667c9281d7SDouglas Thompson MODULE_LICENSE("GPL");
1677c9281d7SDouglas Thompson MODULE_AUTHOR("Doug Thompson www.softwarebitmaker.com, et al");
1687c9281d7SDouglas Thompson MODULE_DESCRIPTION("Core library routines for EDAC reporting");
169