0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0013
0014 #include <linux/module.h>
0015 #include <linux/moduleparam.h>
0016 #include <linux/types.h>
0017 #include <linux/kernel.h>
0018 #include <linux/fs.h>
0019 #include <linux/mm.h>
0020 #include <linux/miscdevice.h>
0021 #include <linux/watchdog.h>
0022 #include <linux/notifier.h>
0023 #include <linux/reboot.h>
0024 #include <linux/init.h>
0025 #include <linux/ioport.h>
0026 #include <linux/uaccess.h>
0027 #include <linux/io.h>
0028
0029 static int epx_c3_alive;
0030
0031 #define WATCHDOG_TIMEOUT 1
0032
0033 static bool nowayout = WATCHDOG_NOWAYOUT;
0034 module_param(nowayout, bool, 0);
0035 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0036 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0037
0038 #define EPXC3_WATCHDOG_CTL_REG 0x1ee
0039 #define EPXC3_WATCHDOG_PET_REG 0x1ef
0040
0041 static void epx_c3_start(void)
0042 {
0043 outb(1, EPXC3_WATCHDOG_CTL_REG);
0044 }
0045
0046 static void epx_c3_stop(void)
0047 {
0048
0049 outb(0, EPXC3_WATCHDOG_CTL_REG);
0050
0051 pr_info("Stopped watchdog timer\n");
0052 }
0053
0054 static void epx_c3_pet(void)
0055 {
0056 outb(1, EPXC3_WATCHDOG_PET_REG);
0057 }
0058
0059
0060
0061
0062 static int epx_c3_open(struct inode *inode, struct file *file)
0063 {
0064 if (epx_c3_alive)
0065 return -EBUSY;
0066
0067 if (nowayout)
0068 __module_get(THIS_MODULE);
0069
0070
0071 epx_c3_start();
0072 epx_c3_pet();
0073
0074 epx_c3_alive = 1;
0075 pr_info("Started watchdog timer\n");
0076
0077 return stream_open(inode, file);
0078 }
0079
0080 static int epx_c3_release(struct inode *inode, struct file *file)
0081 {
0082
0083
0084 if (!nowayout)
0085 epx_c3_stop();
0086
0087 epx_c3_alive = 0;
0088
0089 return 0;
0090 }
0091
0092 static ssize_t epx_c3_write(struct file *file, const char __user *data,
0093 size_t len, loff_t *ppos)
0094 {
0095
0096 if (len)
0097 epx_c3_pet();
0098 return len;
0099 }
0100
0101 static long epx_c3_ioctl(struct file *file, unsigned int cmd,
0102 unsigned long arg)
0103 {
0104 int options, retval = -EINVAL;
0105 int __user *argp = (void __user *)arg;
0106 static const struct watchdog_info ident = {
0107 .options = WDIOF_KEEPALIVEPING,
0108 .firmware_version = 0,
0109 .identity = "Winsystems EPX-C3 H/W Watchdog",
0110 };
0111
0112 switch (cmd) {
0113 case WDIOC_GETSUPPORT:
0114 if (copy_to_user(argp, &ident, sizeof(ident)))
0115 return -EFAULT;
0116 return 0;
0117 case WDIOC_GETSTATUS:
0118 case WDIOC_GETBOOTSTATUS:
0119 return put_user(0, argp);
0120 case WDIOC_SETOPTIONS:
0121 if (get_user(options, argp))
0122 return -EFAULT;
0123
0124 if (options & WDIOS_DISABLECARD) {
0125 epx_c3_stop();
0126 retval = 0;
0127 }
0128
0129 if (options & WDIOS_ENABLECARD) {
0130 epx_c3_start();
0131 retval = 0;
0132 }
0133
0134 return retval;
0135 case WDIOC_KEEPALIVE:
0136 epx_c3_pet();
0137 return 0;
0138 case WDIOC_GETTIMEOUT:
0139 return put_user(WATCHDOG_TIMEOUT, argp);
0140 default:
0141 return -ENOTTY;
0142 }
0143 }
0144
0145 static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code,
0146 void *unused)
0147 {
0148 if (code == SYS_DOWN || code == SYS_HALT)
0149 epx_c3_stop();
0150
0151 return NOTIFY_DONE;
0152 }
0153
0154 static const struct file_operations epx_c3_fops = {
0155 .owner = THIS_MODULE,
0156 .llseek = no_llseek,
0157 .write = epx_c3_write,
0158 .unlocked_ioctl = epx_c3_ioctl,
0159 .compat_ioctl = compat_ptr_ioctl,
0160 .open = epx_c3_open,
0161 .release = epx_c3_release,
0162 };
0163
0164 static struct miscdevice epx_c3_miscdev = {
0165 .minor = WATCHDOG_MINOR,
0166 .name = "watchdog",
0167 .fops = &epx_c3_fops,
0168 };
0169
0170 static struct notifier_block epx_c3_notifier = {
0171 .notifier_call = epx_c3_notify_sys,
0172 };
0173
0174 static int __init watchdog_init(void)
0175 {
0176 int ret;
0177
0178 if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))
0179 return -EBUSY;
0180
0181 ret = register_reboot_notifier(&epx_c3_notifier);
0182 if (ret) {
0183 pr_err("cannot register reboot notifier (err=%d)\n", ret);
0184 goto out;
0185 }
0186
0187 ret = misc_register(&epx_c3_miscdev);
0188 if (ret) {
0189 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0190 WATCHDOG_MINOR, ret);
0191 unregister_reboot_notifier(&epx_c3_notifier);
0192 goto out;
0193 }
0194
0195 pr_info("Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n");
0196
0197 return 0;
0198
0199 out:
0200 release_region(EPXC3_WATCHDOG_CTL_REG, 2);
0201 return ret;
0202 }
0203
0204 static void __exit watchdog_exit(void)
0205 {
0206 misc_deregister(&epx_c3_miscdev);
0207 unregister_reboot_notifier(&epx_c3_notifier);
0208 release_region(EPXC3_WATCHDOG_CTL_REG, 2);
0209 }
0210
0211 module_init(watchdog_init);
0212 module_exit(watchdog_exit);
0213
0214 MODULE_AUTHOR("Calin A. Culianu <calin@ajvar.org>");
0215 MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. "
0216 "Note that there is no way to probe for this device -- "
0217 "so only use it if you are *sure* you are running on this specific "
0218 "SBC system from Winsystems! It writes to IO ports 0x1ee and 0x1ef!");
0219 MODULE_LICENSE("GPL");