// SPDX-License-Identifier: GPL-2.0+ /* * Industrial Computer Source PCI-WDT500/501 driver * * (c) Copyright 1996-1997 Alan Cox <[email protected]>, * All Rights Reserved. * * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. * * (c) Copyright 1995 Alan Cox <[email protected]> * * Release 0.10. * * Fixes * Dave Gregorich : Modularisation and minor bugs * Alan Cox : Added the watchdog ioctl() stuff * Alan Cox : Fixed the reboot problem (as noted by * Matt Crocker). * Alan Cox : Added wdt= boot option * Alan Cox : Cleaned up copy/user stuff * Tim Hockin : Added insmod parameters, comment cleanup * Parameterized timeout * JP Nollmann : Added support for PCI wdt501p * Alan Cox : Split ISA and PCI cards into two drivers * Jeff Garzik : PCI cleanups * Tigran Aivazian : Restructured wdtpci_init_one() to handle * failures * Joel Becker : Added WDIOC_GET/SETTIMEOUT * Zwane Mwaikambo : Magic char closing, locking changes, * cleanups * Matt Domsch : nowayout module option */ #define pr_fmt(fmt) … #include <linux/interrupt.h> #include <linux/module.h> #include <linux/moduleparam.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/reboot.h> #include <linux/fs.h> #include <linux/pci.h> #include <linux/io.h> #include <linux/uaccess.h> #define WDT_IS_PCI #include "wd501p.h" /* We can only use 1 card due to the /dev/watchdog restriction */ static int dev_count; static unsigned long open_lock; static DEFINE_SPINLOCK(wdtpci_lock); static char expect_close; static resource_size_t io; static int irq; /* Default timeout */ #define WD_TIMO … static int heartbeat = …; static int wd_heartbeat; module_param(heartbeat, int, 0); MODULE_PARM_DESC(…) …; static bool nowayout = … WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(…) …; /* Support for the Fan Tachometer on the PCI-WDT501 */ static int tachometer; module_param(tachometer, int, 0); MODULE_PARM_DESC(…) …; static int type = …; module_param(type, int, 0); MODULE_PARM_DESC(…) …; /* * Programming support */ static void wdtpci_ctr_mode(int ctr, int mode) { … } static void wdtpci_ctr_load(int ctr, int val) { … } /** * wdtpci_start: * * Start the watchdog driver. */ static int wdtpci_start(void) { … } /** * wdtpci_stop: * * Stop the watchdog driver. */ static int wdtpci_stop(void) { … } /** * wdtpci_ping: * * Reload counter one with the watchdog heartbeat. We don't bother * reloading the cascade counter. */ static int wdtpci_ping(void) { … } /** * wdtpci_set_heartbeat: * @t: the new heartbeat value that needs to be set. * * Set a new heartbeat value for the watchdog device. If the heartbeat * value is incorrect we keep the old value and return -EINVAL. * If successful we return 0. */ static int wdtpci_set_heartbeat(int t) { … } /** * wdtpci_get_status: * @status: the new status. * * Extract the status information from a WDT watchdog device. There are * several board variants so we have to know which bits are valid. Some * bits default to one and some to zero in order to be maximally painful. * * we then map the bits onto the status ioctl flags. */ static int wdtpci_get_status(int *status) { … } /** * wdtpci_get_temperature: * * Reports the temperature in degrees Fahrenheit. The API is in * farenheit. It was designed by an imperial measurement luddite. */ static int wdtpci_get_temperature(int *temperature) { … } /** * wdtpci_interrupt: * @irq: Interrupt number * @dev_id: Unused as we don't allow multiple devices. * * Handle an interrupt from the board. These are raised when the status * map changes in what the board considers an interesting way. That means * a failure condition occurring. */ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) { … } /** * wdtpci_write: * @file: file handle to the watchdog * @buf: buffer to write (unused as data does not matter here * @count: count of 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 wdtpci_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { … } /** * wdtpci_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 wdtpci_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } /** * wdtpci_open: * @inode: inode of device * @file: file handle to device * * The watchdog device has been opened. The watchdog device is single * open and on opening we load the counters. Counter zero is a 100Hz * cascade, into counter 1 which downcounts to reboot. When the counter * triggers counter 2 downcounts the length of the reset pulse which * set set to be as long as possible. */ static int wdtpci_open(struct inode *inode, struct file *file) { … } /** * wdtpci_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 wdtpci_release(struct inode *inode, struct file *file) { … } /** * wdtpci_temp_read: * @file: file handle to the watchdog board * @buf: buffer to write 1 byte into * @count: length of buffer * @ptr: offset (no seek allowed) * * Read reports the temperature in degrees Fahrenheit. The API is in * fahrenheit. It was designed by an imperial measurement luddite. */ static ssize_t wdtpci_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) { … } /** * wdtpci_temp_open: * @inode: inode of device * @file: file handle to device * * The temperature device has been opened. */ static int wdtpci_temp_open(struct inode *inode, struct file *file) { … } /** * wdtpci_temp_release: * @inode: inode to board * @file: file handle to board * * The temperature device has been closed. */ static int wdtpci_temp_release(struct inode *inode, struct file *file) { … } /** * wdtpci_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 wdtpci_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { … } /* * Kernel Interfaces */ static const struct file_operations wdtpci_fops = …; static struct miscdevice wdtpci_miscdev = …; static const struct file_operations wdtpci_temp_fops = …; static struct miscdevice temp_miscdev = …; /* * The WDT card needs to learn about soft shutdowns in order to * turn the timebomb registers off. */ static struct notifier_block wdtpci_notifier = …; static int wdtpci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { … } static void wdtpci_remove_one(struct pci_dev *pdev) { … } static const struct pci_device_id wdtpci_pci_tbl[] = …; MODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl); static struct pci_driver wdtpci_driver = …; module_pci_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;