219 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * SBC EPX C3 0.1 A Hardware Watchdog Device for the Winsystems EPX-C34 * single board computer5 *6 * (c) Copyright 2006 Calin A. Culianu <calin@ajvar.org>, All Rights7 * Reserved.8 *9 * based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>10 */11 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt13 14#include <linux/module.h>15#include <linux/moduleparam.h>16#include <linux/types.h>17#include <linux/kernel.h>18#include <linux/fs.h>19#include <linux/mm.h>20#include <linux/miscdevice.h>21#include <linux/watchdog.h>22#include <linux/notifier.h>23#include <linux/reboot.h>24#include <linux/init.h>25#include <linux/ioport.h>26#include <linux/uaccess.h>27#include <linux/io.h>28 29static int epx_c3_alive;30 31#define WATCHDOG_TIMEOUT 1 /* 1 sec default timeout */32 33static bool nowayout = WATCHDOG_NOWAYOUT;34module_param(nowayout, bool, 0);35MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="36 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");37 38#define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */39#define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */40 41static void epx_c3_start(void)42{43 outb(1, EPXC3_WATCHDOG_CTL_REG);44}45 46static void epx_c3_stop(void)47{48 49 outb(0, EPXC3_WATCHDOG_CTL_REG);50 51 pr_info("Stopped watchdog timer\n");52}53 54static void epx_c3_pet(void)55{56 outb(1, EPXC3_WATCHDOG_PET_REG);57}58 59/*60 * Allow only one person to hold it open61 */62static int epx_c3_open(struct inode *inode, struct file *file)63{64 if (epx_c3_alive)65 return -EBUSY;66 67 if (nowayout)68 __module_get(THIS_MODULE);69 70 /* Activate timer */71 epx_c3_start();72 epx_c3_pet();73 74 epx_c3_alive = 1;75 pr_info("Started watchdog timer\n");76 77 return stream_open(inode, file);78}79 80static int epx_c3_release(struct inode *inode, struct file *file)81{82 /* Shut off the timer.83 * Lock it in if it's a module and we defined ...NOWAYOUT */84 if (!nowayout)85 epx_c3_stop(); /* Turn the WDT off */86 87 epx_c3_alive = 0;88 89 return 0;90}91 92static ssize_t epx_c3_write(struct file *file, const char __user *data,93 size_t len, loff_t *ppos)94{95 /* Refresh the timer. */96 if (len)97 epx_c3_pet();98 return len;99}100 101static long epx_c3_ioctl(struct file *file, unsigned int cmd,102 unsigned long arg)103{104 int options, retval = -EINVAL;105 int __user *argp = (void __user *)arg;106 static const struct watchdog_info ident = {107 .options = WDIOF_KEEPALIVEPING,108 .firmware_version = 0,109 .identity = "Winsystems EPX-C3 H/W Watchdog",110 };111 112 switch (cmd) {113 case WDIOC_GETSUPPORT:114 if (copy_to_user(argp, &ident, sizeof(ident)))115 return -EFAULT;116 return 0;117 case WDIOC_GETSTATUS:118 case WDIOC_GETBOOTSTATUS:119 return put_user(0, argp);120 case WDIOC_SETOPTIONS:121 if (get_user(options, argp))122 return -EFAULT;123 124 if (options & WDIOS_DISABLECARD) {125 epx_c3_stop();126 retval = 0;127 }128 129 if (options & WDIOS_ENABLECARD) {130 epx_c3_start();131 retval = 0;132 }133 134 return retval;135 case WDIOC_KEEPALIVE:136 epx_c3_pet();137 return 0;138 case WDIOC_GETTIMEOUT:139 return put_user(WATCHDOG_TIMEOUT, argp);140 default:141 return -ENOTTY;142 }143}144 145static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code,146 void *unused)147{148 if (code == SYS_DOWN || code == SYS_HALT)149 epx_c3_stop(); /* Turn the WDT off */150 151 return NOTIFY_DONE;152}153 154static const struct file_operations epx_c3_fops = {155 .owner = THIS_MODULE,156 .write = epx_c3_write,157 .unlocked_ioctl = epx_c3_ioctl,158 .compat_ioctl = compat_ptr_ioctl,159 .open = epx_c3_open,160 .release = epx_c3_release,161};162 163static struct miscdevice epx_c3_miscdev = {164 .minor = WATCHDOG_MINOR,165 .name = "watchdog",166 .fops = &epx_c3_fops,167};168 169static struct notifier_block epx_c3_notifier = {170 .notifier_call = epx_c3_notify_sys,171};172 173static int __init watchdog_init(void)174{175 int ret;176 177 if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))178 return -EBUSY;179 180 ret = register_reboot_notifier(&epx_c3_notifier);181 if (ret) {182 pr_err("cannot register reboot notifier (err=%d)\n", ret);183 goto out;184 }185 186 ret = misc_register(&epx_c3_miscdev);187 if (ret) {188 pr_err("cannot register miscdev on minor=%d (err=%d)\n",189 WATCHDOG_MINOR, ret);190 unregister_reboot_notifier(&epx_c3_notifier);191 goto out;192 }193 194 pr_info("Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n");195 196 return 0;197 198out:199 release_region(EPXC3_WATCHDOG_CTL_REG, 2);200 return ret;201}202 203static void __exit watchdog_exit(void)204{205 misc_deregister(&epx_c3_miscdev);206 unregister_reboot_notifier(&epx_c3_notifier);207 release_region(EPXC3_WATCHDOG_CTL_REG, 2);208}209 210module_init(watchdog_init);211module_exit(watchdog_exit);212 213MODULE_AUTHOR("Calin A. Culianu <calin@ajvar.org>");214MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. "215 "Note that there is no way to probe for this device -- "216 "so only use it if you are *sure* you are running on this specific "217 "SBC system from Winsystems! It writes to IO ports 0x1ee and 0x1ef!");218MODULE_LICENSE("GPL");219