1618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 2618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * PIKA FPGA based Watchdog Timer 3618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * 4618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Copyright (c) 2008 PIKA Technologies 5618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Sean MacLennan <smaclennan@pikatech.com> 6618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 7618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches 10618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/init.h> 11618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/errno.h> 12618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/module.h> 13618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/moduleparam.h> 14618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/types.h> 15618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/kernel.h> 16618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/fs.h> 17618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/miscdevice.h> 18618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/watchdog.h> 19618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/reboot.h> 20618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/jiffies.h> 21618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/timer.h> 22618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/bitops.h> 23618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/uaccess.h> 24618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/io.h> 25c11eede69b6ad0ac44ebc1e021a8d2699c5f1f8fRob Herring#include <linux/of_address.h> 26618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#include <linux/of_platform.h> 27618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 28618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#define DRV_NAME "PIKA-WDT" 29618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 30618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* Hardware timeout in seconds */ 31618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#define WDT_HW_TIMEOUT 2 32618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 33618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* Timer heartbeat (500ms) */ 34618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#define WDT_TIMEOUT (HZ/2) 35618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 36618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* User land timeout */ 37618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan#define WDT_HEARTBEAT 15 38618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic int heartbeat = WDT_HEARTBEAT; 39618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanmodule_param(heartbeat, int, 0); 40618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanMODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " 41618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); 42618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 4386a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT; 4486a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0); 45618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 46618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 47618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 48618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic struct { 49618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan void __iomem *fpga; 50618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan unsigned long next_heartbeat; /* the next_heartbeat for the timer */ 51618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan unsigned long open; 52618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan char expect_close; 53618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan int bootstatus; 54618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan struct timer_list timer; /* The timer that pings the watchdog */ 55618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} pikawdt_private; 56618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 5735c79780064976cf9d7537a00e59f97c2061fa7dSean MacLennanstatic struct watchdog_info ident = { 58618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .identity = DRV_NAME, 59618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .options = WDIOF_CARDRESET | 60618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan WDIOF_SETTIMEOUT | 61618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan WDIOF_KEEPALIVEPING | 62618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan WDIOF_MAGICCLOSE, 63618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan}; 64618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 65618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 66618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Reload the watchdog timer. (ie, pat the watchdog) 67618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 68618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic inline void pikawdt_reset(void) 69618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 70618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) -- 71618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Bit 7, WTCHDG_EN: When set to 1, the watchdog timer is enabled. 72618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Once enabled, it cannot be disabled. The watchdog can be 73618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * kicked by performing any write access to the reset 74618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * control register (this register). 75618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in 76618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * seconds. Valid ranges are 1 to 15 seconds. The value can 77618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * be modified dynamically. 78618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 79618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan unsigned reset = in_be32(pikawdt_private.fpga + 0x14); 80618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* enable with max timeout - 15 seconds */ 81618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8); 82618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan out_be32(pikawdt_private.fpga + 0x14, reset); 83618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 84618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 85618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 86618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Timer tick 87618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 88618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic void pikawdt_ping(unsigned long data) 89618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 90618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (time_before(jiffies, pikawdt_private.next_heartbeat) || 91618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan (!nowayout && !pikawdt_private.open)) { 92618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_reset(); 93618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); 94618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } else 9527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_crit("I will reset your machine !\n"); 96618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 97618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 98618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 99618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic void pikawdt_keepalive(void) 100618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 101618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ; 102618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 103618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 104618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic void pikawdt_start(void) 105618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 106618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_keepalive(); 107618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); 108618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 109618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 110618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 111618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Watchdog device is opened, and watchdog starts running. 112618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 113618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic int pikawdt_open(struct inode *inode, struct file *file) 114618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 115618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* /dev/watchdog can only be opened once */ 116618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (test_and_set_bit(0, &pikawdt_private.open)) 117618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return -EBUSY; 118618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 119618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_start(); 120618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 121618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return nonseekable_open(inode, file); 122618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 123618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 124618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 125618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Close the watchdog device. 126618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 127618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic int pikawdt_release(struct inode *inode, struct file *file) 128618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 129618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* stop internal ping */ 130618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (!pikawdt_private.expect_close) 131618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan del_timer(&pikawdt_private.timer); 132618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 133618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan clear_bit(0, &pikawdt_private.open); 134618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_private.expect_close = 0; 135618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return 0; 136618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 137618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 138618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 139618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Pat the watchdog whenever device is written to. 140618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 141618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic ssize_t pikawdt_write(struct file *file, const char __user *data, 142618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan size_t len, loff_t *ppos) 143618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 144618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (!len) 145618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return 0; 146618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 147618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* Scan for magic character */ 148618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (!nowayout) { 149618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan size_t i; 150618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 151618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_private.expect_close = 0; 152618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 153618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan for (i = 0; i < len; i++) { 154618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan char c; 155618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (get_user(c, data + i)) 156618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return -EFAULT; 157618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (c == 'V') { 158618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_private.expect_close = 42; 159618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan break; 160618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 161618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 162618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 163618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 164618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_keepalive(); 165618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 166618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return len; 167618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 168618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 169618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan/* 170618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Handle commands from user-space. 171618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 172618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic long pikawdt_ioctl(struct file *file, 173618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan unsigned int cmd, unsigned long arg) 174618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 175618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan void __user *argp = (void __user *)arg; 176618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan int __user *p = argp; 177618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan int new_value; 178618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 179618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan switch (cmd) { 180618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan case WDIOC_GETSUPPORT: 181618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 182618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 183618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan case WDIOC_GETSTATUS: 184618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return put_user(0, p); 185618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 186618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan case WDIOC_GETBOOTSTATUS: 187618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return put_user(pikawdt_private.bootstatus, p); 188618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 189618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan case WDIOC_KEEPALIVE: 190618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_keepalive(); 191618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return 0; 192618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 193618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan case WDIOC_SETTIMEOUT: 194618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (get_user(new_value, p)) 195618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return -EFAULT; 196618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 197618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan heartbeat = new_value; 198618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_keepalive(); 199618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 200618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return put_user(new_value, p); /* return current value */ 201618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 202618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan case WDIOC_GETTIMEOUT: 203618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return put_user(heartbeat, p); 204618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 205618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return -ENOTTY; 206618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 207618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 208618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 209618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic const struct file_operations pikawdt_fops = { 210618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .owner = THIS_MODULE, 211618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .llseek = no_llseek, 212618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .open = pikawdt_open, 213618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .release = pikawdt_release, 214618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .write = pikawdt_write, 215618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .unlocked_ioctl = pikawdt_ioctl, 216618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan}; 217618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 218618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic struct miscdevice pikawdt_miscdev = { 219618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .minor = WATCHDOG_MINOR, 220618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .name = "watchdog", 221618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan .fops = &pikawdt_fops, 222618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan}; 223618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 224618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic int __init pikawdt_init(void) 225618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 226618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan struct device_node *np; 227618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan void __iomem *fpga; 228618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan static u32 post1; 229618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan int ret; 230618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 231618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan np = of_find_compatible_node(NULL, NULL, "pika,fpga"); 232618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (np == NULL) { 23327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("Unable to find fpga\n"); 234618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return -ENOENT; 235618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 236618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 237618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_private.fpga = of_iomap(np, 0); 238618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan of_node_put(np); 239618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (pikawdt_private.fpga == NULL) { 24027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("Unable to map fpga\n"); 241618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return -ENOMEM; 242618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 243618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 244618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff; 245618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 246618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* POST information is in the sd area. */ 247618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); 248618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (np == NULL) { 24927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("Unable to find fpga-sd\n"); 250618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan ret = -ENOENT; 251618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan goto out; 252618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 253618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 254618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan fpga = of_iomap(np, 0); 255618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan of_node_put(np); 256618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (fpga == NULL) { 25727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("Unable to map fpga-sd\n"); 258618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan ret = -ENOMEM; 259618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan goto out; 260618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 261618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 262618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) -- 263618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * Bit 31, WDOG: Set to 1 when the last reset was caused by a watchdog 264618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan * timeout. 265618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan */ 266618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan post1 = in_be32(fpga + 0x40); 267618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (post1 & 0x80000000) 268618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan pikawdt_private.bootstatus = WDIOF_CARDRESET; 269618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 270618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan iounmap(fpga); 271618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 272618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan setup_timer(&pikawdt_private.timer, pikawdt_ping, 0); 273618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 274618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan ret = misc_register(&pikawdt_miscdev); 275618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan if (ret) { 27627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("Unable to register miscdev\n"); 277618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan goto out; 278618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan } 279618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 28027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", 28127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches heartbeat, nowayout); 282618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return 0; 283618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 284618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanout: 285618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan iounmap(pikawdt_private.fpga); 286618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan return ret; 287618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 288618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 289618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanstatic void __exit pikawdt_exit(void) 290618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan{ 291618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan misc_deregister(&pikawdt_miscdev); 292618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 293618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan iounmap(pikawdt_private.fpga); 294618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan} 295618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 296618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanmodule_init(pikawdt_init); 297618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanmodule_exit(pikawdt_exit); 298618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennan 299618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanMODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>"); 300618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanMODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer"); 301618efba999d0e7f4bcde93231dcb9a748223c6e3Sean MacLennanMODULE_LICENSE("GPL"); 302