2007-05-06 14:55:32 +04:00
/*
* Driver for the MTX - 1 Watchdog .
*
2008-05-19 17:07:21 +04:00
* ( C ) Copyright 2005 4 G Systems < info @ 4 g - systems . biz > ,
* All Rights Reserved .
2007-05-06 14:55:32 +04:00
* http : //www.4g-systems.biz
*
* ( C ) Copyright 2007 OpenWrt . org , Florian Fainelli < florian @ openwrt . org >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Neither Michael Stickel nor 4 G Systems admit liability nor provide
* warranty for any of this software . This material is provided
* " AS-IS " and at no charge .
*
* ( c ) Copyright 2005 4 G Systems < info @ 4 g - systems . biz >
*
* Release 0.01 .
* Author : Michael Stickel michael . stickel @ 4 g - systems . biz
*
* Release 0.02 .
* Author : Florian Fainelli florian @ openwrt . org
* use the Linux watchdog / timer APIs
*
* The Watchdog is configured to reset the MTX - 1
* if it is not triggered for 100 seconds .
* It should not be triggered more often than 1.6 seconds .
*
* A timer triggers the watchdog every 5 seconds , until
* it is opened for the first time . After the first open
* it MUST be triggered every 2. .95 seconds .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/miscdevice.h>
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/timer.h>
# include <linux/completion.h>
# include <linux/jiffies.h>
# include <linux/watchdog.h>
2008-01-07 21:08:49 +03:00
# include <linux/platform_device.h>
2008-05-19 17:07:21 +04:00
# include <linux/io.h>
# include <linux/uaccess.h>
# include <linux/gpio.h>
2007-05-06 14:55:32 +04:00
# include <asm/mach-au1x00/au1000.h>
# define MTX1_WDT_INTERVAL (5 * HZ)
static int ticks = 100 * HZ ;
static struct {
struct completion stop ;
2008-05-19 17:07:21 +04:00
spinlock_t lock ;
2008-02-25 15:39:57 +03:00
int running ;
2007-05-06 14:55:32 +04:00
struct timer_list timer ;
2008-02-25 15:39:57 +03:00
int queue ;
2007-05-06 14:55:32 +04:00
int default_ticks ;
unsigned long inuse ;
2008-01-07 21:08:49 +03:00
unsigned gpio ;
2007-05-06 14:55:32 +04:00
} mtx1_wdt_device ;
static void mtx1_wdt_trigger ( unsigned long unused )
{
u32 tmp ;
2008-05-19 17:07:21 +04:00
spin_lock ( & mtx1_wdt_device . lock ) ;
2007-05-06 14:55:32 +04:00
if ( mtx1_wdt_device . running )
ticks - - ;
/*
* toggle GPIO2_15
*/
tmp = au_readl ( GPIO2_DIR ) ;
2008-01-07 21:08:49 +03:00
tmp = ( tmp & ~ ( 1 < < mtx1_wdt_device . gpio ) ) |
( ( ~ tmp ) & ( 1 < < mtx1_wdt_device . gpio ) ) ;
2008-05-19 17:07:21 +04:00
au_writel ( tmp , GPIO2_DIR ) ;
2007-05-06 14:55:32 +04:00
if ( mtx1_wdt_device . queue & & ticks )
mod_timer ( & mtx1_wdt_device . timer , jiffies + MTX1_WDT_INTERVAL ) ;
2008-05-19 17:07:21 +04:00
else
2007-05-06 14:55:32 +04:00
complete ( & mtx1_wdt_device . stop ) ;
2008-05-19 17:07:21 +04:00
spin_unlock ( & mtx1_wdt_device . lock ) ;
2007-05-06 14:55:32 +04:00
}
static void mtx1_wdt_reset ( void )
{
ticks = mtx1_wdt_device . default_ticks ;
}
static void mtx1_wdt_start ( void )
{
2008-10-24 21:52:56 +04:00
unsigned long flags ;
2008-05-19 17:07:21 +04:00
spin_lock_irqsave ( & mtx1_wdt_device . lock , flags ) ;
2007-05-06 14:55:32 +04:00
if ( ! mtx1_wdt_device . queue ) {
mtx1_wdt_device . queue = 1 ;
2008-01-07 21:08:49 +03:00
gpio_set_value ( mtx1_wdt_device . gpio , 1 ) ;
2007-05-06 14:55:32 +04:00
mod_timer ( & mtx1_wdt_device . timer , jiffies + MTX1_WDT_INTERVAL ) ;
}
mtx1_wdt_device . running + + ;
2008-05-19 17:07:21 +04:00
spin_unlock_irqrestore ( & mtx1_wdt_device . lock , flags ) ;
2007-05-06 14:55:32 +04:00
}
static int mtx1_wdt_stop ( void )
{
2008-10-24 21:52:56 +04:00
unsigned long flags ;
2008-05-19 17:07:21 +04:00
spin_lock_irqsave ( & mtx1_wdt_device . lock , flags ) ;
2007-05-06 14:55:32 +04:00
if ( mtx1_wdt_device . queue ) {
mtx1_wdt_device . queue = 0 ;
2008-01-07 21:08:49 +03:00
gpio_set_value ( mtx1_wdt_device . gpio , 0 ) ;
2007-05-06 14:55:32 +04:00
}
ticks = mtx1_wdt_device . default_ticks ;
2008-05-19 17:07:21 +04:00
spin_unlock_irqrestore ( & mtx1_wdt_device . lock , flags ) ;
2007-05-06 14:55:32 +04:00
return 0 ;
}
/* Filesystem functions */
static int mtx1_wdt_open ( struct inode * inode , struct file * file )
{
if ( test_and_set_bit ( 0 , & mtx1_wdt_device . inuse ) )
return - EBUSY ;
return nonseekable_open ( inode , file ) ;
}
static int mtx1_wdt_release ( struct inode * inode , struct file * file )
{
clear_bit ( 0 , & mtx1_wdt_device . inuse ) ;
return 0 ;
}
2008-05-19 17:07:21 +04:00
static long mtx1_wdt_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2007-05-06 14:55:32 +04:00
{
void __user * argp = ( void __user * ) arg ;
2008-05-19 17:07:21 +04:00
int __user * p = ( int __user * ) argp ;
2007-05-06 14:55:32 +04:00
unsigned int value ;
2008-05-19 17:07:21 +04:00
static const struct watchdog_info ident = {
2007-05-06 14:55:32 +04:00
. options = WDIOF_CARDRESET ,
. identity = " MTX-1 WDT " ,
} ;
2008-05-19 17:07:21 +04:00
switch ( cmd ) {
2008-07-18 15:41:17 +04:00
case WDIOC_GETSUPPORT :
if ( copy_to_user ( argp , & ident , sizeof ( ident ) ) )
return - EFAULT ;
2008-05-19 17:07:21 +04:00
break ;
case WDIOC_GETSTATUS :
case WDIOC_GETBOOTSTATUS :
put_user ( 0 , p ) ;
break ;
case WDIOC_SETOPTIONS :
if ( get_user ( value , p ) )
return - EFAULT ;
if ( value & WDIOS_ENABLECARD )
mtx1_wdt_start ( ) ;
else if ( value & WDIOS_DISABLECARD )
mtx1_wdt_stop ( ) ;
else
return - EINVAL ;
return 0 ;
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
mtx1_wdt_reset ( ) ;
break ;
2008-05-19 17:07:21 +04:00
default :
return - ENOTTY ;
2007-05-06 14:55:32 +04:00
}
return 0 ;
}
2008-05-19 17:07:21 +04:00
static ssize_t mtx1_wdt_write ( struct file * file , const char * buf ,
size_t count , loff_t * ppos )
2007-05-06 14:55:32 +04:00
{
if ( ! count )
return - EIO ;
mtx1_wdt_reset ( ) ;
return count ;
}
2008-01-22 22:48:10 +03:00
static const struct file_operations mtx1_wdt_fops = {
2007-05-06 14:55:32 +04:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
2008-05-19 17:07:21 +04:00
. unlocked_ioctl = mtx1_wdt_ioctl ,
2007-05-06 14:55:32 +04:00
. open = mtx1_wdt_open ,
. write = mtx1_wdt_write ,
2008-08-07 00:19:41 +04:00
. release = mtx1_wdt_release ,
2007-05-06 14:55:32 +04:00
} ;
static struct miscdevice mtx1_wdt_misc = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
2008-08-07 00:19:41 +04:00
. fops = & mtx1_wdt_fops ,
2007-05-06 14:55:32 +04:00
} ;
2008-01-07 21:08:49 +03:00
static int mtx1_wdt_probe ( struct platform_device * pdev )
2007-05-06 14:55:32 +04:00
{
int ret ;
2008-01-07 21:08:49 +03:00
mtx1_wdt_device . gpio = pdev - > resource [ 0 ] . start ;
2008-05-19 17:07:21 +04:00
spin_lock_init ( & mtx1_wdt_device . lock ) ;
2007-05-06 14:55:32 +04:00
init_completion ( & mtx1_wdt_device . stop ) ;
mtx1_wdt_device . queue = 0 ;
clear_bit ( 0 , & mtx1_wdt_device . inuse ) ;
setup_timer ( & mtx1_wdt_device . timer , mtx1_wdt_trigger , 0L ) ;
mtx1_wdt_device . default_ticks = ticks ;
2008-05-19 17:07:21 +04:00
ret = misc_register ( & mtx1_wdt_misc ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " mtx-1_wdt : failed to register \n " ) ;
return ret ;
}
2007-05-06 14:55:32 +04:00
mtx1_wdt_start ( ) ;
printk ( KERN_INFO " MTX-1 Watchdog driver \n " ) ;
return 0 ;
}
2008-01-07 21:08:49 +03:00
static int mtx1_wdt_remove ( struct platform_device * pdev )
2007-05-06 14:55:32 +04:00
{
2008-05-19 17:07:21 +04:00
/* FIXME: do we need to lock this test ? */
2007-05-06 14:55:32 +04:00
if ( mtx1_wdt_device . queue ) {
mtx1_wdt_device . queue = 0 ;
wait_for_completion ( & mtx1_wdt_device . stop ) ;
}
misc_deregister ( & mtx1_wdt_misc ) ;
2008-01-07 21:08:49 +03:00
return 0 ;
}
static struct platform_driver mtx1_wdt = {
. probe = mtx1_wdt_probe ,
. remove = mtx1_wdt_remove ,
. driver . name = " mtx1-wdt " ,
2008-04-11 08:29:23 +04:00
. driver . owner = THIS_MODULE ,
2008-01-07 21:08:49 +03:00
} ;
static int __init mtx1_wdt_init ( void )
{
return platform_driver_register ( & mtx1_wdt ) ;
}
static void __exit mtx1_wdt_exit ( void )
{
platform_driver_unregister ( & mtx1_wdt ) ;
2007-05-06 14:55:32 +04:00
}
module_init ( mtx1_wdt_init ) ;
module_exit ( mtx1_wdt_exit ) ;
MODULE_AUTHOR ( " Michael Stickel, Florian Fainelli " ) ;
MODULE_DESCRIPTION ( " Driver for the MTX-1 watchdog " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-01-07 21:08:49 +03:00
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;
2008-04-11 08:29:23 +04:00
MODULE_ALIAS ( " platform:mtx1-wdt " ) ;