^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");