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)  *	IT8712F "Smart Guardian" Watchdog support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  *	Copyright (c) 2006-2007 Jorge Boncompte - DTI2 <jorge@dti2.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  *	Based on info and code taken from:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  *	drivers/char/watchdog/scx200_wdt.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  *	drivers/hwmon/it87.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  *	IT8712F EC-LPC I/O Preliminary Specification 0.8.2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12)  *	IT8712F EC-LPC I/O Preliminary Specification 0.9.3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14)  *	The author(s) of this software shall not be held liable for damages
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15)  *	of any nature resulting due to the use of this software. This
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16)  *	software is provided AS-IS with no warranties.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) #include <linux/notifier.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) #include <linux/reboot.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) #define DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #define NAME "it8712f_wdt"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) MODULE_DESCRIPTION("IT8712F Watchdog Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) static int max_units = 255;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) static int margin = 60;		/* in seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) module_param(margin, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) static unsigned long wdt_open;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) static unsigned expect_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) static unsigned char revision;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) /* Dog Food address - We use the game port address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) static unsigned short address;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) #define	REG		0x2e	/* The register to read/write */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) #define	VAL		0x2f	/* The value to read/write */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) #define	LDN		0x07	/* Register: Logical device select */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) #define	DEVID		0x20	/* Register: Device ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) #define	DEVREV		0x22	/* Register: Device Revision */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) #define ACT_REG		0x30	/* LDN Register: Activation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) #define BASE_REG	0x60	/* LDN Register: Base address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) #define IT8712F_DEVID	0x8712
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) #define LDN_GPIO	0x07	/* GPIO and Watch Dog Timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) #define LDN_GAME	0x09	/* Game Port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) #define WDT_CONTROL	0x71	/* WDT Register: Control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) #define WDT_CONFIG	0x72	/* WDT Register: Configuration */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) #define WDT_TIMEOUT	0x73	/* WDT Register: Timeout Value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) #define WDT_RESET_GAME	0x10	/* Reset timer on read or write to game port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) #define WDT_RESET_KBD	0x20	/* Reset timer on keyboard interrupt */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) #define WDT_RESET_MOUSE	0x40	/* Reset timer on mouse interrupt */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) #define WDT_RESET_CIR	0x80	/* Reset timer on consumer IR interrupt */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) #define WDT_UNIT_SEC	0x80	/* If 0 in MINUTES */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) #define WDT_OUT_PWROK	0x10	/* Pulse PWROK on timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) #define WDT_OUT_KRST	0x40	/* Pulse reset on timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) static int wdt_control_reg = WDT_RESET_GAME;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) module_param(wdt_control_reg, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) MODULE_PARM_DESC(wdt_control_reg, "Value to write to watchdog control "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 		"register. The default WDT_RESET_GAME resets the timer on "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 		"game port reads that this driver generates. You can also "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 		"use KBD, MOUSE or CIR if you have some external way to "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 		"generate those interrupts.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) static int superio_inb(int reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 	outb(reg, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	return inb(VAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) static void superio_outb(int val, int reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 	outb(reg, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	outb(val, VAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static int superio_inw(int reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 	outb(reg++, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 	val = inb(VAL) << 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 	outb(reg, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	val |= inb(VAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 	return val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) static inline void superio_select(int ldn)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 	outb(LDN, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 	outb(ldn, VAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static inline int superio_enter(void)
^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) 	 * Try to reserve REG and REG + 1 for exclusive access.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) 	if (!request_muxed_region(REG, 2, NAME))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 		return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	outb(0x87, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	outb(0x01, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	outb(0x55, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 	outb(0x55, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) static inline void superio_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	outb(0x02, REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	outb(0x02, VAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 	release_region(REG, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) static inline void it8712f_wdt_ping(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	if (wdt_control_reg & WDT_RESET_GAME)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 		inb(address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) static void it8712f_wdt_update_margin(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 	int config = WDT_OUT_KRST | WDT_OUT_PWROK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 	int units = margin;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 	/* Switch to minutes precision if the configured margin
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 	 * value does not fit within the register width.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 	if (units <= max_units) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 		config |= WDT_UNIT_SEC; /* else UNIT is MINUTES */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 		pr_info("timer margin %d seconds\n", units);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 		units /= 60;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 		pr_info("timer margin %d minutes\n", units);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 	superio_outb(config, WDT_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	if (revision >= 0x08)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 		superio_outb(units >> 8, WDT_TIMEOUT + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	superio_outb(units, WDT_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 int it8712f_wdt_get_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 	if (superio_inb(WDT_CONTROL) & 0x01)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 		return WDIOF_CARDRESET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) static int it8712f_wdt_enable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 	int ret = superio_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	pr_debug("enabling watchdog timer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	superio_select(LDN_GPIO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 	superio_outb(wdt_control_reg, WDT_CONTROL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	it8712f_wdt_update_margin();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	superio_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	it8712f_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) static int it8712f_wdt_disable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 	int ret = superio_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	pr_debug("disabling watchdog timer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	superio_select(LDN_GPIO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 	superio_outb(0, WDT_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 	superio_outb(0, WDT_CONTROL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 	if (revision >= 0x08)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 		superio_outb(0, WDT_TIMEOUT + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) 	superio_outb(0, WDT_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 	superio_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) static int it8712f_wdt_notify(struct notifier_block *this,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 		    unsigned long code, void *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	if (code == SYS_HALT || code == SYS_POWER_OFF)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 		if (!nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 			it8712f_wdt_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 	return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) static struct notifier_block it8712f_wdt_notifier = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 	.notifier_call = it8712f_wdt_notify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) static ssize_t it8712f_wdt_write(struct file *file, const char __user *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 					size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 	/* check for a magic close character */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 	if (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 		size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 		it8712f_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 		expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 		for (i = 0; i < len; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 			char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) 			if (get_user(c, data + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 				return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 			if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 				expect_close = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 	return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 							unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 	void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 	int __user *p = argp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 	static const struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 		.identity = "IT8712F Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 		.firmware_version = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 						WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 	};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 	int value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 	switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) 	case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 		if (copy_to_user(argp, &ident, sizeof(ident)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 	case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) 		ret = superio_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) 		if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 			return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 		superio_select(LDN_GPIO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 		value = it8712f_wdt_get_status();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 		superio_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) 		return put_user(value, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 	case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) 		return put_user(0, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 	case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 		it8712f_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 	case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 		if (get_user(value, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 		if (value < 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 		if (value > (max_units * 60))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) 		margin = value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 		ret = superio_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 		if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 			return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 		superio_select(LDN_GPIO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 		it8712f_wdt_update_margin();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 		superio_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 		it8712f_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) 		fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) 	case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) 		if (put_user(margin, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) 	default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 		return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) static int it8712f_wdt_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) 	/* only allow one at a time */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 	if (test_and_set_bit(0, &wdt_open))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) 		return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) 	ret = it8712f_wdt_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) 	return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) static int it8712f_wdt_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 	if (expect_close != 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 		pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 	} else if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 		if (it8712f_wdt_disable())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) 			pr_warn("Watchdog disable failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) 	expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) 	clear_bit(0, &wdt_open);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) static const struct file_operations it8712f_wdt_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 	.owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) 	.llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 	.write = it8712f_wdt_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) 	.unlocked_ioctl = it8712f_wdt_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 	.compat_ioctl = compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) 	.open = it8712f_wdt_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) 	.release = it8712f_wdt_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) static struct miscdevice it8712f_wdt_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) 	.minor = WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 	.name = "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) 	.fops = &it8712f_wdt_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) static int __init it8712f_wdt_find(unsigned short *address)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) 	int err = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) 	int chip_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) 	int ret = superio_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 	chip_type = superio_inw(DEVID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) 	if (chip_type != IT8712F_DEVID)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 		goto exit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) 	superio_select(LDN_GAME);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 	superio_outb(1, ACT_REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) 	if (!(superio_inb(ACT_REG) & 0x01)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) 		pr_err("Device not activated, skipping\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 		goto exit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) 	*address = superio_inw(BASE_REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) 	if (*address == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) 		pr_err("Base address not set, skipping\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) 		goto exit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) 	err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) 	revision = superio_inb(DEVREV) & 0x0f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) 	/* Later revisions have 16-bit values per datasheet 0.9.1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) 	if (revision >= 0x08)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) 		max_units = 65535;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) 	if (margin > (max_units * 60))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 		margin = (max_units * 60);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 	pr_info("Found IT%04xF chip revision %d - using DogFood address 0x%x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) 		chip_type, revision, *address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) exit:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) 	superio_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) 	return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) static int __init it8712f_wdt_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 	int err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) 	if (it8712f_wdt_find(&address))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) 	if (!request_region(address, 1, "IT8712F Watchdog")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) 		pr_warn("watchdog I/O region busy\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 		return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 	err = it8712f_wdt_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) 	if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) 		pr_err("unable to disable watchdog timer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) 		goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) 	err = register_reboot_notifier(&it8712f_wdt_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) 	if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) 		pr_err("unable to register reboot notifier\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) 		goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) 	err = misc_register(&it8712f_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) 	if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) 		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) 		       WATCHDOG_MINOR, err);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) 		goto reboot_out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) reboot_out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) 	unregister_reboot_notifier(&it8712f_wdt_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) 	release_region(address, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) 	return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) static void __exit it8712f_wdt_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) 	misc_deregister(&it8712f_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) 	unregister_reboot_notifier(&it8712f_wdt_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) 	release_region(address, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) module_init(it8712f_wdt_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) module_exit(it8712f_wdt_exit);