of_xilinx_wdt.c revision 27c766aaacb265d625dc634bf7903f7f9fd0c697
1e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* 2e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt 3e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* 4e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) 5e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* 6e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* ----------------------- 7e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* 8e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* This program is free software; you can redistribute it and/or 9e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* modify it under the terms of the GNU General Public License 10e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* as published by the Free Software Foundation; either version 11e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* 2 of the License, or (at your option) any later version. 12e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* 13e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* ----------------------- 14e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* 30-May-2011 Alejandro Cabrera <aldaya@gmail.com> 15e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* - If "xlnx,wdt-enable-once" wasn't found on device tree the 16e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* module will use CONFIG_WATCHDOG_NOWAYOUT 17e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* - If the device tree parameters ("clock-frequency" and 18e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* "xlnx,wdt-interval") wasn't found the driver won't 19e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera* know the wdt reset interval 20e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera*/ 21e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 2227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches 24e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/module.h> 25e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/types.h> 26e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/kernel.h> 27e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/fs.h> 28e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/miscdevice.h> 29e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/init.h> 30e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/ioport.h> 31e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/watchdog.h> 32e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/io.h> 33e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/uaccess.h> 34e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/of.h> 35e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/of_device.h> 36e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/of_address.h> 37e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 38e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Register offsets for the Wdt device */ 39e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */ 40e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */ 41e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */ 42e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 43e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Control/Status Register Masks */ 44e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */ 45e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */ 46e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */ 47e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 48e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Control/Status Register 0/1 bits */ 49e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */ 50e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 51e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* SelfTest constants */ 52e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000 53e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TIMER_FAILED 0xFFFFFFFF 54e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 55e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define WATCHDOG_NAME "Xilinx Watchdog" 56e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define PFX WATCHDOG_NAME ": " 57e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 58e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastruct xwdt_device { 59e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera struct resource res; 60e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera void __iomem *base; 61e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 nowayout; 62e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 wdt_interval; 63e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 boot_status; 64e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}; 65e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 66e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic struct xwdt_device xdev; 67e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 68e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic u32 timeout; 69e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic u32 control_status_reg; 70e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic u8 expect_close; 71e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic u8 no_timeout; 72e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic unsigned long driver_open; 73e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 74e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic DEFINE_SPINLOCK(spinlock); 75e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 76e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic void xwdt_start(void) 77e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 78e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_lock(&spinlock); 79e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 80e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera /* Clean previous status and enable the watchdog timer */ 81e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 82e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); 83e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 84e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), 85e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xdev.base + XWT_TWCSR0_OFFSET); 86e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 87e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); 88e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 89e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_unlock(&spinlock); 90e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 91e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 92e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic void xwdt_stop(void) 93e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 94e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_lock(&spinlock); 95e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 96e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 97e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 98e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), 99e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xdev.base + XWT_TWCSR0_OFFSET); 100e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 101e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); 102e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 103e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_unlock(&spinlock); 10427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("Stopped!\n"); 105e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 106e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 107e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic void xwdt_keepalive(void) 108e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 109e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_lock(&spinlock); 110e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 111e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 112e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); 113e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); 114e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 115e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_unlock(&spinlock); 116e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 117e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 118e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic void xwdt_get_status(int *status) 119e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 120e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera int new_status; 121e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 122e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_lock(&spinlock); 123e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 124e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); 125e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera new_status = ((control_status_reg & 126e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); 127e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_unlock(&spinlock); 128e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 129e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera *status = 0; 130e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (new_status & 1) 131e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera *status |= WDIOF_CARDRESET; 132e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 133e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 134e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic u32 xwdt_selftest(void) 135e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 136e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera int i; 137e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 timer_value1; 138e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 timer_value2; 139e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 140e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_lock(&spinlock); 141e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 142e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); 143e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); 144e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 145e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera for (i = 0; 146e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && 147e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera (timer_value2 == timer_value1)); i++) { 148e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); 149e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 150e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 151e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera spin_unlock(&spinlock); 152e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 153e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (timer_value2 != timer_value1) 154e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return ~XWT_TIMER_FAILED; 155e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera else 156e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return XWT_TIMER_FAILED; 157e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 158e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 159e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic int xwdt_open(struct inode *inode, struct file *file) 160e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 161e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera /* Only one process can handle the wdt at a time */ 162e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (test_and_set_bit(0, &driver_open)) 163e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return -EBUSY; 164e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 165e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera /* Make sure that the module are always loaded...*/ 166e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (xdev.nowayout) 167e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera __module_get(THIS_MODULE); 168e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 169e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_start(); 17027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("Started...\n"); 171e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 172e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return nonseekable_open(inode, file); 173e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 174e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 175e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic int xwdt_release(struct inode *inode, struct file *file) 176e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 177e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (expect_close == 42) { 178e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_stop(); 179e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } else { 18027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_crit("Unexpected close, not stopping watchdog!\n"); 181e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_keepalive(); 182e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 183e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 184e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera clear_bit(0, &driver_open); 185e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera expect_close = 0; 186e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return 0; 187e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 188e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 189e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* 190e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * xwdt_write: 191e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @file: file handle to the watchdog 192e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @buf: buffer to write (unused as data does not matter here 193e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @count: count of bytes 194e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @ppos: pointer to the position to write. No seeks allowed 195e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * 196e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * A write to a watchdog device is defined as a keepalive signal. Any 197e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * write of data will do, as we don't define content meaning. 198e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera */ 199e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic ssize_t xwdt_write(struct file *file, const char __user *buf, 200e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera size_t len, loff_t *ppos) 201e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 202e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (len) { 203e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (!xdev.nowayout) { 204e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera size_t i; 205e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 206e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera /* In case it was set long ago */ 207e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera expect_close = 0; 208e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 209e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera for (i = 0; i != len; i++) { 210e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera char c; 211e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 212e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (get_user(c, buf + i)) 213e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return -EFAULT; 214e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (c == 'V') 215e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera expect_close = 42; 216e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 217e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 218e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_keepalive(); 219e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 220e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return len; 221e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 222e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 223e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic const struct watchdog_info ident = { 224e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .options = WDIOF_MAGICCLOSE | 225e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera WDIOF_KEEPALIVEPING, 226e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .firmware_version = 1, 227e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .identity = WATCHDOG_NAME, 228e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}; 229e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 230e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* 231e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * xwdt_ioctl: 232e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @file: file handle to the device 233e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @cmd: watchdog command 234e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * @arg: argument pointer 235e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * 236e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * The watchdog API defines a common set of functions for all watchdogs 237e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * according to their available features. 238e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera */ 239e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 240e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 241e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera int status; 242e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 243e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera union { 244e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera struct watchdog_info __user *ident; 245e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera int __user *i; 246e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } uarg; 247e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 248e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera uarg.i = (int __user *)arg; 249e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 250e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera switch (cmd) { 251e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera case WDIOC_GETSUPPORT: 252e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return copy_to_user(uarg.ident, &ident, 253e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera sizeof(ident)) ? -EFAULT : 0; 254e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 255e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera case WDIOC_GETBOOTSTATUS: 256e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return put_user(xdev.boot_status, uarg.i); 257e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 258e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera case WDIOC_GETSTATUS: 259e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_get_status(&status); 260e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return put_user(status, uarg.i); 261e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 262e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera case WDIOC_KEEPALIVE: 263e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_keepalive(); 264e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return 0; 265e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 266e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera case WDIOC_GETTIMEOUT: 267e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (no_timeout) 268e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return -ENOTTY; 269e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera else 270e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return put_user(timeout, uarg.i); 271e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 272e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera default: 273e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return -ENOTTY; 274e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 275e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 276e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 277e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic const struct file_operations xwdt_fops = { 278e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .owner = THIS_MODULE, 279e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .llseek = no_llseek, 280e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .write = xwdt_write, 281e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .open = xwdt_open, 282e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .release = xwdt_release, 283e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .unlocked_ioctl = xwdt_ioctl, 284e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}; 285e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 286e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic struct miscdevice xwdt_miscdev = { 287e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .minor = WATCHDOG_MINOR, 288e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .name = "watchdog", 289e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .fops = &xwdt_fops, 290e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}; 291e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 292e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic int __devinit xwdt_probe(struct platform_device *pdev) 293e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 294e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera int rc; 295e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 *tmptr; 296e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera u32 *pfreq; 297e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 298e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera no_timeout = 0; 299e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 300e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent, 301e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera "clock-frequency", NULL); 302e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 303e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (pfreq == NULL) { 30427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_warn("The watchdog clock frequency cannot be obtained!\n"); 305e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera no_timeout = 1; 306e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 307e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 308e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); 309e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (rc) { 31027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_warn("invalid address!\n"); 311e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return rc; 312e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 313e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 314e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera tmptr = (u32 *)of_get_property(pdev->dev.of_node, 315e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera "xlnx,wdt-interval", NULL); 316e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (tmptr == NULL) { 31727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_warn("Parameter \"xlnx,wdt-interval\" not found in device tree!\n"); 318e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera no_timeout = 1; 319e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } else { 320e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xdev.wdt_interval = *tmptr; 321e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 322e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 323e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera tmptr = (u32 *)of_get_property(pdev->dev.of_node, 324e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera "xlnx,wdt-enable-once", NULL); 325e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (tmptr == NULL) { 32627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_warn("Parameter \"xlnx,wdt-enable-once\" not found in device tree!\n"); 327e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xdev.nowayout = WATCHDOG_NOWAYOUT; 328e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 329e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 330e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* 331e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * Twice of the 2^wdt_interval / freq because the first wdt overflow is 332e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera * ignored (interrupt), reset is only generated at second wdt overflow 333e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera */ 334e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (!no_timeout) 335e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); 336e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 337e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (!request_mem_region(xdev.res.start, 338e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { 339e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera rc = -ENXIO; 34027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("memory request failure!\n"); 341e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera goto err_out; 342e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 343e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 344e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); 345e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (xdev.base == NULL) { 346e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera rc = -ENOMEM; 34727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("ioremap failure!\n"); 348e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera goto release_mem; 349e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 350e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 351e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera rc = xwdt_selftest(); 352e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (rc == XWT_TIMER_FAILED) { 35327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("SelfTest routine error!\n"); 354e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera goto unmap_io; 355e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 356e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 357e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera xwdt_get_status(&xdev.boot_status); 358e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 359e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera rc = misc_register(&xwdt_miscdev); 360e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (rc) { 36127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("cannot register miscdev on minor=%d (err=%d)\n", 36227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches xwdt_miscdev.minor, rc); 363e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera goto unmap_io; 364e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera } 365e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 366e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera if (no_timeout) 36727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("driver loaded (timeout=? sec, nowayout=%d)\n", 36827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches xdev.nowayout); 369e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera else 37027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("driver loaded (timeout=%d sec, nowayout=%d)\n", 37127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches timeout, xdev.nowayout); 372e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 373e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera expect_close = 0; 374e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera clear_bit(0, &driver_open); 375e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 376e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return 0; 377e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 378e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabreraunmap_io: 379e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iounmap(xdev.base); 380e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerarelease_mem: 381e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera release_mem_region(xdev.res.start, resource_size(&xdev.res)); 382e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabreraerr_out: 383e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return rc; 384e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 385e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 386e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic int __devexit xwdt_remove(struct platform_device *dev) 387e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{ 388e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera misc_deregister(&xwdt_miscdev); 389e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera iounmap(xdev.base); 390e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera release_mem_region(xdev.res.start, resource_size(&xdev.res)); 391e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 392e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera return 0; 393e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera} 394e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 395e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Match table for of_platform binding */ 396e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic struct of_device_id __devinitdata xwdt_of_match[] = { 397e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera { .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, 398e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera {}, 399e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}; 400e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_DEVICE_TABLE(of, xwdt_of_match); 401e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 402e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic struct platform_driver xwdt_driver = { 403e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .probe = xwdt_probe, 404e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .remove = __devexit_p(xwdt_remove), 405e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .driver = { 406e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .owner = THIS_MODULE, 407e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .name = WATCHDOG_NAME, 408e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera .of_match_table = xwdt_of_match, 409e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera }, 410e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}; 411e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 412b8ec61189f3b4cd9d1b2856342f5d7676151d01cAxel Linmodule_platform_driver(xwdt_driver); 413e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera 414e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); 415e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_DESCRIPTION("Xilinx Watchdog driver"); 416e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_LICENSE("GPL"); 417e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 418