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-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * PIKA FPGA based Watchdog Timer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * Copyright (c) 2008 PIKA Technologies
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  *   Sean MacLennan <smaclennan@pikatech.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) #include <linux/reboot.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #include <linux/jiffies.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) #include <linux/timer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) #include <linux/bitops.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) #define DRV_NAME "PIKA-WDT"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) /* Hardware timeout in seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) #define WDT_HW_TIMEOUT 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) /* Timer heartbeat (500ms) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #define WDT_TIMEOUT	(HZ/2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) /* User land timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) #define WDT_HEARTBEAT 15
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) static int heartbeat = WDT_HEARTBEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) module_param(heartbeat, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) 	"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) static struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) 	void __iomem *fpga;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) 	unsigned long open;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 	char expect_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) 	int bootstatus;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	struct timer_list timer;	/* The timer that pings the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) } pikawdt_private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) static struct watchdog_info ident __ro_after_init = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 	.identity	= DRV_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 	.options	= WDIOF_CARDRESET |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) 			  WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) 			  WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 			  WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67)  * Reload the watchdog timer.  (ie, pat the watchdog)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) static inline void pikawdt_reset(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	/* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	 * Bit 7,    WTCHDG_EN: When set to 1, the watchdog timer is enabled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	 *           Once enabled, it cannot be disabled. The watchdog can be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	 *           kicked by performing any write access to the reset
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	 *           control register (this register).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	 * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	 *           seconds. Valid ranges are 1 to 15 seconds. The value can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 	 *           be modified dynamically.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 	unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 	/* enable with max timeout - 15 seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 	reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 	out_be32(pikawdt_private.fpga + 0x14, reset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87)  * Timer tick
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) static void pikawdt_ping(struct timer_list *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 	if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 			(!nowayout && !pikawdt_private.open)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 		pikawdt_reset();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 		mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 	} else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 		pr_crit("I will reset your machine !\n");
^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) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static void pikawdt_keepalive(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
^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 void pikawdt_start(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	pikawdt_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 	mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)  * Watchdog device is opened, and watchdog starts running.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) static int pikawdt_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	/* /dev/watchdog can only be opened once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 	if (test_and_set_bit(0, &pikawdt_private.open))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 		return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 	pikawdt_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 	return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)  * Close the watchdog device.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) static int pikawdt_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	/* stop internal ping */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	if (!pikawdt_private.expect_close)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 		del_timer(&pikawdt_private.timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	clear_bit(0, &pikawdt_private.open);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	pikawdt_private.expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^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)  * Pat the watchdog whenever device is written to.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) static ssize_t pikawdt_write(struct file *file, const char __user *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 			     size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	if (!len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	/* Scan for magic character */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 		size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 		pikawdt_private.expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 		for (i = 0; i < len; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 			char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 			if (get_user(c, data + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 				return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 			if (c == 'V') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 				pikawdt_private.expect_close = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 				break;
^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) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	pikawdt_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) }
^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)  * Handle commands from user-space.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) static long pikawdt_ioctl(struct file *file,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 		unsigned int cmd, unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 	void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	int __user *p = argp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	int new_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 	switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 	case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 	case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 		return put_user(0, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 	case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 		return put_user(pikawdt_private.bootstatus, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 		pikawdt_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 		if (get_user(new_value, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 		heartbeat = new_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 		pikawdt_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 		return put_user(new_value, p);  /* return current value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 	case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 		return put_user(heartbeat, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	return -ENOTTY;
^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) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) static const struct file_operations pikawdt_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 	.owner		= THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) 	.llseek		= no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 	.open		= pikawdt_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 	.release	= pikawdt_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	.write		= pikawdt_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 	.unlocked_ioctl	= pikawdt_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 	.compat_ioctl	= compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) static struct miscdevice pikawdt_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	.minor	= WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 	.name	= "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	.fops	= &pikawdt_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) static int __init pikawdt_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 	struct device_node *np;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 	void __iomem *fpga;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 	u32 post1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 	np = of_find_compatible_node(NULL, NULL, "pika,fpga");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 	if (np == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 		pr_err("Unable to find fpga\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 		return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	pikawdt_private.fpga = of_iomap(np, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 	of_node_put(np);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 	if (pikawdt_private.fpga == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 		pr_err("Unable to map fpga\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 		return -ENOMEM;
^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) 	ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 	/* POST information is in the sd area. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 	np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 	if (np == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 		pr_err("Unable to find fpga-sd\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 		ret = -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 		goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 	fpga = of_iomap(np, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 	of_node_put(np);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 	if (fpga == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 		pr_err("Unable to map fpga-sd\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 		ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 		goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 	/* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 	 * Bit 31,   WDOG: Set to 1 when the last reset was caused by a watchdog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 	 *           timeout.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 	post1 = in_be32(fpga + 0x40);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) 	if (post1 & 0x80000000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 		pikawdt_private.bootstatus = WDIOF_CARDRESET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 	iounmap(fpga);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) 	timer_setup(&pikawdt_private.timer, pikawdt_ping, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 	ret = misc_register(&pikawdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 		pr_err("Unable to register miscdev\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 		goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) 	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) 		heartbeat, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 	iounmap(pikawdt_private.fpga);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) static void __exit pikawdt_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 	misc_deregister(&pikawdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 	iounmap(pikawdt_private.fpga);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) module_init(pikawdt_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) module_exit(pikawdt_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) MODULE_LICENSE("GPL");