Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  *	SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  *	Based on acquirewdt.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  *	and some other existing drivers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  *	The authors do NOT admit liability nor provide warranty for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  *	any of this software. This material is provided "AS-IS" in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  *	the hope that it may be useful for others.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12)  *	(C) Copyright 2003-2006  Sven Anders <anders@anduras.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14)  *  History:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15)  *	2003 - Created version 1.0 for Linux 2.4.x.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16)  *	2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17)  *	       features. Released version 1.1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19)  *  Theory of operation:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21)  *	A Watchdog Timer (WDT) is a hardware circuit that can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22)  *	reset the computer system in case of a software fault.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23)  *	You probably knew that already.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25)  *	Usually a userspace daemon will notify the kernel WDT driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26)  *	via the /dev/watchdog special device file that userspace is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27)  *	still alive, at regular intervals.  When such a notification
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28)  *	occurs, the driver will usually tell the hardware watchdog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29)  *	that everything is in order, and that the watchdog should wait
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30)  *	for yet another little while to reset the system.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31)  *	If userspace fails (RAM error, kernel bug, whatever), the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32)  *	notifications cease to occur, and the hardware watchdog will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33)  *	reset the system (causing a reboot) after the timeout occurs.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35)  * Create device with:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36)  *  mknod /dev/watchdog c 10 130
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38)  * For an example userspace keep-alive daemon, see:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39)  *   Documentation/watchdog/wdt.rst
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) #include <linux/notifier.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) #include <linux/reboot.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) /* enable support for minutes as units? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) /* (does not always work correctly, so disabled by default!) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) #define SMSC_SUPPORT_MINUTES
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) #undef SMSC_SUPPORT_MINUTES
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) #define MAX_TIMEOUT     255
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) #define UNIT_SECOND     0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) #define UNIT_MINUTE     1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) #define VERSION		"1.1"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) #define IOPORT		0x3F0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) #define IOPORT_SIZE     2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) #define IODEV_NO	8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) static int unit = UNIT_SECOND;	/* timer's unit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) static int timeout = 60;	/* timeout value: default is 60 "units" */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) static unsigned long timer_enabled;   /* is the timer enabled? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) static char expect_close;       /* is the close expected? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) static DEFINE_SPINLOCK(io_lock);/* to guard the watchdog from io races */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) /* -- Low level function ----------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) /* unlock the IO chip */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) static inline void open_io_config(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 	outb(0x55, IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 	mdelay(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 	outb(0x55, IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) /* lock the IO chip */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) static inline void close_io_config(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 	outb(0xAA, IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) /* select the IO device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static inline void select_io_device(unsigned char devno)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 	outb(0x07, IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	outb(devno, IOPORT+1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) /* write to the control register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static inline void write_io_cr(unsigned char reg, unsigned char data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 	outb(reg, IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	outb(data, IOPORT+1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) /* read from the control register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static inline char read_io_cr(unsigned char reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 	outb(reg, IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	return inb(IOPORT+1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) /* -- Medium level functions ------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) static inline void gpio_bit12(unsigned char reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 	/* -- General Purpose I/O Bit 1.2 --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	 * Bit 0,   In/Out: 0 = Output, 1 = Input
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	 * Bit 1,   Polarity: 0 = No Invert, 1 = Invert
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	 * Bit 2,   Group Enable Intr.: 0 = Disable, 1 = Enable
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 	 * Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	 *                           11 = Either Edge Triggered Intr. 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	 * Bit 5/6  (Reserved)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	 * Bit 7,   Output Type: 0 = Push Pull Bit, 1 = Open Drain
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	write_io_cr(0xE2, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static inline void gpio_bit13(unsigned char reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	/* -- General Purpose I/O Bit 1.3 --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	 * Bit 0,  In/Out: 0 = Output, 1 = Input
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	 * Bit 1,  Polarity: 0 = No Invert, 1 = Invert
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	 * Bit 2,  Group Enable Intr.: 0 = Disable, 1 = Enable
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	 * Bit 3,  Function select: 0 = GPI/O, 1 = LED
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	 * Bit 4-6 (Reserved)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	 * Bit 7,  Output Type: 0 = Push Pull Bit, 1 = Open Drain
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 	write_io_cr(0xE3, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) static inline void wdt_timer_units(unsigned char new_units)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 	/* -- Watchdog timer units --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	 * Bit 0-6 (Reserved)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 	 * Bit 7,  WDT Time-out Value Units Select
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 	 *         (0 = Minutes, 1 = Seconds)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	write_io_cr(0xF1, new_units);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) static inline void wdt_timeout_value(unsigned char new_timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	/* -- Watchdog Timer Time-out Value --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	 * Bit 0-7 Binary coded units (0=Disabled, 1..255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	write_io_cr(0xF2, new_timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) static inline void wdt_timer_conf(unsigned char conf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 	/* -- Watchdog timer configuration --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 	 * Bit 0   Joystick enable: 0* = No Reset, 1 = Reset WDT upon
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	 *							Gameport I/O
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 	 * Bit 1   Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	 * Bit 2   Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	 * Bit 3   Reset the timer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 	 *         (Wrong in SMsC documentation? Given as: PowerLED Timout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 	 *							Enabled)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 	 * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 	 *            0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 	write_io_cr(0xF3, conf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) static inline void wdt_timer_ctrl(unsigned char reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 	/* -- Watchdog timer control --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	 * Bit 0   Status Bit: 0 = Timer counting, 1 = Timeout occurred
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 	 * Bit 1   Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	 * Bit 2   Force Timeout: 1 = Forces WD timeout event (self-cleaning)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 	 * Bit 3   P20 Force Timeout enabled:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	 *          0 = P20 activity does not generate the WD timeout event
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 	 *          1 = P20 Allows rising edge of P20, from the keyboard
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	 *              controller, to force the WD timeout event.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	 * Bit 4   (Reserved)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 	 * -- Soft power management --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 	 * Bit 5   Stop Counter: 1 = Stop software power down counter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 	 *            set via register 0xB8, (self-cleaning)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 	 *            (Upon read: 0 = Counter running, 1 = Counter stopped)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	 * Bit 6   Restart Counter: 1 = Restart software power down counter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 	 *            set via register 0xB8, (self-cleaning)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 	 * Bit 7   SPOFF: 1 = Force software power down (self-cleaning)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	write_io_cr(0xF4, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) /* -- Higher level functions ------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) /* initialize watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) static void wb_smsc_wdt_initialize(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	unsigned char old;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 	spin_lock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 	open_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 	select_io_device(IODEV_NO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	/* enable the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 	gpio_bit13(0x08);  /* Select pin 80 = LED not GPIO */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	gpio_bit12(0x0A);  /* Set pin 79 = WDT not
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) 			      GPIO/Output/Polarity=Invert */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 	/* disable the timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) 	wdt_timeout_value(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 	/* reset control register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 	wdt_timer_ctrl(0x00);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 	/* reset configuration register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 	wdt_timer_conf(0x00);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 	/* read old (timer units) register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 	old = read_io_cr(0xF1) & 0x7F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 	if (unit == UNIT_SECOND)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 		old |= 0x80;	/* set to seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	/* set the watchdog timer units */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 	wdt_timer_units(old);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 	close_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 	spin_unlock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) /* shutdown the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) static void wb_smsc_wdt_shutdown(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 	spin_lock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 	open_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 	select_io_device(IODEV_NO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	/* disable the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 	gpio_bit13(0x09);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 	gpio_bit12(0x09);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 	/* reset watchdog config register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 	wdt_timer_conf(0x00);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 	/* reset watchdog control register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 	wdt_timer_ctrl(0x00);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 	/* disable timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 	wdt_timeout_value(0x00);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 	close_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 	spin_unlock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) /* set timeout => enable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) static void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) 	spin_lock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 	open_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 	select_io_device(IODEV_NO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 	/* set Power LED to blink, if we enable the timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 	wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) 	/* set timeout value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) 	wdt_timeout_value(new_timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) 	close_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 	spin_unlock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) /* get timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) static unsigned char wb_smsc_wdt_get_timeout(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 	unsigned char set_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 	spin_lock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) 	open_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 	select_io_device(IODEV_NO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 	set_timeout = read_io_cr(0xF2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 	close_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 	spin_unlock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 	return set_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) /* disable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) static void wb_smsc_wdt_disable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) 	/* set the timeout to 0 to disable the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 	wb_smsc_wdt_set_timeout(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) /* enable watchdog by setting the current timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) static void wb_smsc_wdt_enable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) 	/* set the current timeout... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) 	wb_smsc_wdt_set_timeout(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) /* reset the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) static void wb_smsc_wdt_reset_timer(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 	spin_lock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) 	open_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) 	select_io_device(IODEV_NO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) 	/* reset the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) 	wdt_timeout_value(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 	wdt_timer_conf(0x08);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 	close_io_config();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 	spin_unlock(&io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) /* return, if the watchdog is enabled (timeout is set...) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) static int wb_smsc_wdt_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) 	return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) /* -- File operations -------------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) /* open => enable watchdog and set initial timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) 	/* /dev/watchdog can only be opened once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) 	if (test_and_set_bit(0, &timer_enabled))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) 		return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) 	if (nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) 		__module_get(THIS_MODULE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) 	/* Reload and activate timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) 	wb_smsc_wdt_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) 	pr_info("Watchdog enabled. Timeout set to %d %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) 		timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) 	return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) /* close => shut off the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 	/* Shut off the timer. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) 	if (expect_close == 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 		wb_smsc_wdt_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) 		pr_info("Watchdog disabled, sleeping again...\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 	} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) 		pr_crit("Unexpected close, not stopping watchdog!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) 		wb_smsc_wdt_reset_timer();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) 	clear_bit(0, &timer_enabled);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 	expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) /* write => update the timer to keep the machine alive */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) 				 size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 	/* See if we got the magic character 'V' and reload the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) 	if (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 		if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) 			size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) 			/* reset expect flag */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) 			expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) 			/* scan to see whether or not we got the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) 			   magic character */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) 			for (i = 0; i != len; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) 				char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 				if (get_user(c, data + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 					return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) 				if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) 					expect_close = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) 			}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 		/* someone wrote to us, we should reload the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) 		wb_smsc_wdt_reset_timer();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 	return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) /* ioctl => control interface */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) static long wb_smsc_wdt_ioctl(struct file *file,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) 					unsigned int cmd, unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) 	int new_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) 	union {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) 		struct watchdog_info __user *ident;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) 		int __user *i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) 	} uarg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) 	static const struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) 		.options =		WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) 					WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) 					WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) 		.firmware_version =	0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) 		.identity =		"SMsC 37B787 Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) 	};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) 	uarg.i = (int __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) 	switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) 	case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) 		return copy_to_user(uarg.ident, &ident, sizeof(ident))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) 								? -EFAULT : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) 	case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) 		return put_user(wb_smsc_wdt_status(), uarg.i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) 	case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) 		return put_user(0, uarg.i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) 	case WDIOC_SETOPTIONS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) 	{
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) 		int options, retval = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) 		if (get_user(options, uarg.i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) 		if (options & WDIOS_DISABLECARD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) 			wb_smsc_wdt_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) 			retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) 		if (options & WDIOS_ENABLECARD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) 			wb_smsc_wdt_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) 			retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) 		return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) 	case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) 		wb_smsc_wdt_reset_timer();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) 	case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) 		if (get_user(new_timeout, uarg.i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) 		/* the API states this is given in secs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) 		if (unit == UNIT_MINUTE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) 			new_timeout /= 60;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) 		if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) 		timeout = new_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) 		wb_smsc_wdt_set_timeout(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) 		fallthrough;	/* and return the new timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) 	case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) 		new_timeout = timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) 		if (unit == UNIT_MINUTE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) 			new_timeout *= 60;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) 		return put_user(new_timeout, uarg.i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) 	default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) 		return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) /* -- Notifier funtions -----------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) 					unsigned long code, void *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) 	if (code == SYS_DOWN || code == SYS_HALT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) 		/* set timeout to 0, to avoid possible race-condition */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) 		timeout = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) 		wb_smsc_wdt_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) 	return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) /* -- Module's structures ---------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) static const struct file_operations wb_smsc_wdt_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) 	.owner	  = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) 	.llseek		= no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) 	.write		= wb_smsc_wdt_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) 	.unlocked_ioctl	= wb_smsc_wdt_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) 	.compat_ioctl	= compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) 	.open		= wb_smsc_wdt_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) 	.release	= wb_smsc_wdt_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) static struct notifier_block wb_smsc_wdt_notifier = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) 	.notifier_call  = wb_smsc_wdt_notify_sys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) static struct miscdevice wb_smsc_wdt_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) 	.minor		= WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) 	.name		= "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) 	.fops		= &wb_smsc_wdt_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) /* -- Module init functions -------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) /* module's "constructor" */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) static int __init wb_smsc_wdt_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) 	pr_info("SMsC 37B787 watchdog component driver "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) 		VERSION " initialising...\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) 	if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) 		pr_err("Unable to register IO port %#x\n", IOPORT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) 		ret = -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) 		goto out_pnp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) 	/* set new maximum, if it's too big */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) 	if (timeout > MAX_TIMEOUT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) 		timeout = MAX_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) 	/* init the watchdog timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) 	wb_smsc_wdt_initialize();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) 	ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) 		pr_err("Unable to register reboot notifier err = %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) 		goto out_io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) 	ret = misc_register(&wb_smsc_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) 		pr_err("Unable to register miscdev on minor %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) 		       WATCHDOG_MINOR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) 		goto out_rbt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) 	/* output info */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) 	pr_info("Timeout set to %d %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) 		timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) 	pr_info("Watchdog initialized and sleeping (nowayout=%d)...\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) 		nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) out_clean:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) out_rbt:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) 	unregister_reboot_notifier(&wb_smsc_wdt_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) out_io:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) 	release_region(IOPORT, IOPORT_SIZE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) out_pnp:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) 	goto out_clean;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) /* module's "destructor" */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) static void __exit wb_smsc_wdt_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) 	/* Stop the timer before we leave */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) 	if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) 		wb_smsc_wdt_shutdown();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) 		pr_info("Watchdog disabled\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) 	misc_deregister(&wb_smsc_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) 	unregister_reboot_notifier(&wb_smsc_wdt_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) 	release_region(IOPORT, IOPORT_SIZE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) 	pr_info("SMsC 37B787 watchdog component driver removed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) module_init(wb_smsc_wdt_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) module_exit(wb_smsc_wdt_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) 								VERSION ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) #ifdef SMSC_SUPPORT_MINUTES
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) module_param(unit, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) MODULE_PARM_DESC(unit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) 		"set unit to use, 0=seconds or 1=minutes, default is 0");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) module_param(timeout, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) MODULE_PARM_DESC(nowayout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) 		"Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) 				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");