// SPDX-License-Identifier: GPL-2.0+ /* * NS pc87413-wdt Watchdog Timer driver for Linux 2.6.x.x * * This code is based on wdt.c with original copyright. * * (C) Copyright 2006 Sven Anders, <[email protected]> * and Marcus Junker, <[email protected]> * * Neither Sven Anders, Marcus Junker nor ANDURAS AG * admit liability nor provide warranty for any of this software. * This material is provided "AS-IS" and at no charge. * * Release 1.1 */ #define pr_fmt(fmt) … #include <linux/module.h> #include <linux/types.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/notifier.h> #include <linux/fs.h> #include <linux/reboot.h> #include <linux/init.h> #include <linux/spinlock.h> #include <linux/moduleparam.h> #include <linux/io.h> #include <linux/uaccess.h> /* #define DEBUG 1 */ #define DEFAULT_TIMEOUT … #define MAX_TIMEOUT … #define VERSION … #define MODNAME … #define DPFX … #define WDT_INDEX_IO_PORT … #define WDT_DATA_IO_PORT … #define SWC_LDN … #define SIOCFG2 … #define WDCTL … #define WDTO … #define WDCFG … #define IO_DEFAULT … static int io = …; static int swc_base_addr = …; static int timeout = …; /* timeout value */ static unsigned long timer_enabled; /* is the timer enabled? */ static char expect_close; /* is the close expected? */ static DEFINE_SPINLOCK(io_lock); /* to guard us from io races */ static bool nowayout = … WATCHDOG_NOWAYOUT; /* -- Low level function ----------------------------------------*/ /* Select pins for Watchdog output */ static inline void pc87413_select_wdt_out(void) { … } /* Enable SWC functions */ static inline void pc87413_enable_swc(void) { … } /* Read SWC I/O base address */ static void pc87413_get_swc_base_addr(void) { … } /* Select Bank 3 of SWC */ static inline void pc87413_swc_bank3(void) { … } /* Set watchdog timeout to x minutes */ static inline void pc87413_programm_wdto(char pc87413_time) { … } /* Enable WDEN */ static inline void pc87413_enable_wden(void) { … } /* Enable SW_WD_TREN */ static inline void pc87413_enable_sw_wd_tren(void) { … } /* Disable SW_WD_TREN */ static inline void pc87413_disable_sw_wd_tren(void) { … } /* Enable SW_WD_TRG */ static inline void pc87413_enable_sw_wd_trg(void) { … } /* Disable SW_WD_TRG */ static inline void pc87413_disable_sw_wd_trg(void) { … } /* -- Higher level functions ------------------------------------*/ /* Enable the watchdog */ static void pc87413_enable(void) { … } /* Disable the watchdog */ static void pc87413_disable(void) { … } /* Refresh the watchdog */ static void pc87413_refresh(void) { … } /* -- File operations -------------------------------------------*/ /** * pc87413_open: * @inode: inode of device * @file: file handle to device * */ static int pc87413_open(struct inode *inode, struct file *file) { … } /** * pc87413_release: * @inode: inode to board * @file: file handle to board * * The watchdog has a configurable API. There is a religious dispute * between people who want their watchdog to be able to shut down and * those who want to be sure if the watchdog manager dies the machine * reboots. In the former case we disable the counters, in the latter * case you have to open it again very soon. */ static int pc87413_release(struct inode *inode, struct file *file) { … } /** * pc87413_status: * * return, if the watchdog is enabled (timeout is set...) */ static int pc87413_status(void) { … } /** * pc87413_write: * @file: file handle to the watchdog * @data: data buffer to write * @len: length in bytes * @ppos: pointer to the position to write. No seeks allowed * * A write to a watchdog device is defined as a keepalive signal. Any * write of data will do, as we we don't define content meaning. */ static ssize_t pc87413_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { … } /** * pc87413_ioctl: * @file: file handle to the device * @cmd: watchdog command * @arg: argument pointer * * The watchdog API defines a common set of functions for all watchdogs * according to their available features. We only actually usefully support * querying capabilities and current status. */ static long pc87413_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } /* -- Notifier functions -----------------------------------------*/ /** * pc87413_notify_sys: * @this: our notifier block * @code: the event being reported * @unused: unused * * Our notifier is called on system shutdowns. We want to turn the card * off at reboot otherwise the machine will reboot again during memory * test or worse yet during the following fsck. This would suck, in fact * trust me - if it happens it does suck. */ static int pc87413_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { … } /* -- Module's structures ---------------------------------------*/ static const struct file_operations pc87413_fops = …; static struct notifier_block pc87413_notifier = …; static struct miscdevice pc87413_miscdev = …; /* -- Module init functions -------------------------------------*/ /** * pc87413_init: module's "constructor" * * Set up the WDT watchdog board. All we have to do is grab the * resources we require and bitch if anyone beat us to them. * The open() function will actually kick the board off. */ static int __init pc87413_init(void) { … } /** * pc87413_exit: module's "destructor" * * Unload the watchdog. You cannot do this with any file handles open. * If your watchdog is set to continue ticking on close and you unload * it, well it keeps ticking. We won't get the interrupt but the board * will not touch PC memory so all is fine. You just have to load a new * module in 60 seconds or reboot. */ static void __exit pc87413_exit(void) { … } module_init(…) …; module_exit(pc87413_exit); MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; module_param_hw(io, int, ioport, 0); MODULE_PARM_DESC(…) …; module_param(timeout, int, 0); MODULE_PARM_DESC(…) …; module_param(nowayout, bool, 0); MODULE_PARM_DESC(…) …;