forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
watchdog: Add MCF548x watchdog driver.
Add watchdog driver for MCF548x. Signed-off-by: Philippe De Muyter <phdm@macqel.be> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
- Loading branch information
Showing
4 changed files
with
236 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* drivers/watchdog/m548x_wdt.c | ||
* | ||
* Watchdog driver for ColdFire MCF548x processors | ||
* Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be> | ||
* | ||
* Adapted from the IXP4xx watchdog driver, which carries these notices: | ||
* | ||
* Author: Deepak Saxena <dsaxena@plexity.net> | ||
* | ||
* Copyright 2004 (c) MontaVista, Software, Inc. | ||
* Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> | ||
* | ||
* This file is licensed under the terms of the GNU General Public | ||
* License version 2. This program is licensed "as is" without any | ||
* warranty of any kind, whether express or implied. | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/moduleparam.h> | ||
#include <linux/types.h> | ||
#include <linux/kernel.h> | ||
#include <linux/fs.h> | ||
#include <linux/miscdevice.h> | ||
#include <linux/watchdog.h> | ||
#include <linux/init.h> | ||
#include <linux/bitops.h> | ||
#include <linux/ioport.h> | ||
#include <linux/uaccess.h> | ||
|
||
#include <asm/coldfire.h> | ||
#include <asm/m548xsim.h> | ||
#include <asm/m548xgpt.h> | ||
|
||
static int nowayout = WATCHDOG_NOWAYOUT; | ||
static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */ | ||
static unsigned long wdt_status; | ||
|
||
#define WDT_IN_USE 0 | ||
#define WDT_OK_TO_CLOSE 1 | ||
|
||
static void wdt_enable(void) | ||
{ | ||
unsigned int gms0; | ||
|
||
/* preserve GPIO usage, if any */ | ||
gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); | ||
if (gms0 & MCF_GPT_GMS_TMS_GPIO) | ||
gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK | ||
| MCF_GPT_GMS_OD); | ||
else | ||
gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD; | ||
__raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
__raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) | | ||
MCF_GPT_GCIR_CNT(0xffff), MCF_MBAR + MCF_GPT_GCIR0); | ||
gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE; | ||
__raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
} | ||
|
||
static void wdt_disable(void) | ||
{ | ||
unsigned int gms0; | ||
|
||
/* disable watchdog */ | ||
gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); | ||
gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE); | ||
__raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
} | ||
|
||
static void wdt_keepalive(void) | ||
{ | ||
unsigned int gms0; | ||
|
||
gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); | ||
gms0 |= MCF_GPT_GMS_OCPW(0xA5); | ||
__raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
} | ||
|
||
static int m548x_wdt_open(struct inode *inode, struct file *file) | ||
{ | ||
if (test_and_set_bit(WDT_IN_USE, &wdt_status)) | ||
return -EBUSY; | ||
|
||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
wdt_enable(); | ||
return nonseekable_open(inode, file); | ||
} | ||
|
||
static ssize_t m548x_wdt_write(struct file *file, const char *data, | ||
size_t len, loff_t *ppos) | ||
{ | ||
if (len) { | ||
if (!nowayout) { | ||
size_t i; | ||
|
||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
|
||
for (i = 0; i != len; i++) { | ||
char c; | ||
|
||
if (get_user(c, data + i)) | ||
return -EFAULT; | ||
if (c == 'V') | ||
set_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
} | ||
} | ||
wdt_keepalive(); | ||
} | ||
return len; | ||
} | ||
|
||
static const struct watchdog_info ident = { | ||
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | | ||
WDIOF_KEEPALIVEPING, | ||
.identity = "Coldfire M548x Watchdog", | ||
}; | ||
|
||
static long m548x_wdt_ioctl(struct file *file, unsigned int cmd, | ||
unsigned long arg) | ||
{ | ||
int ret = -ENOTTY; | ||
int time; | ||
|
||
switch (cmd) { | ||
case WDIOC_GETSUPPORT: | ||
ret = copy_to_user((struct watchdog_info *)arg, &ident, | ||
sizeof(ident)) ? -EFAULT : 0; | ||
break; | ||
|
||
case WDIOC_GETSTATUS: | ||
ret = put_user(0, (int *)arg); | ||
break; | ||
|
||
case WDIOC_GETBOOTSTATUS: | ||
ret = put_user(0, (int *)arg); | ||
break; | ||
|
||
case WDIOC_KEEPALIVE: | ||
wdt_keepalive(); | ||
ret = 0; | ||
break; | ||
|
||
case WDIOC_SETTIMEOUT: | ||
ret = get_user(time, (int *)arg); | ||
if (ret) | ||
break; | ||
|
||
if (time <= 0 || time > 30) { | ||
ret = -EINVAL; | ||
break; | ||
} | ||
|
||
heartbeat = time; | ||
wdt_enable(); | ||
/* Fall through */ | ||
|
||
case WDIOC_GETTIMEOUT: | ||
ret = put_user(heartbeat, (int *)arg); | ||
break; | ||
} | ||
return ret; | ||
} | ||
|
||
static int m548x_wdt_release(struct inode *inode, struct file *file) | ||
{ | ||
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) | ||
wdt_disable(); | ||
else { | ||
printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " | ||
"timer will not stop\n"); | ||
wdt_keepalive(); | ||
} | ||
clear_bit(WDT_IN_USE, &wdt_status); | ||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
|
||
return 0; | ||
} | ||
|
||
|
||
static const struct file_operations m548x_wdt_fops = { | ||
.owner = THIS_MODULE, | ||
.llseek = no_llseek, | ||
.write = m548x_wdt_write, | ||
.unlocked_ioctl = m548x_wdt_ioctl, | ||
.open = m548x_wdt_open, | ||
.release = m548x_wdt_release, | ||
}; | ||
|
||
static struct miscdevice m548x_wdt_miscdev = { | ||
.minor = WATCHDOG_MINOR, | ||
.name = "watchdog", | ||
.fops = &m548x_wdt_fops, | ||
}; | ||
|
||
static int __init m548x_wdt_init(void) | ||
{ | ||
if (!request_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4, | ||
"Coldfire M548x Watchdog")) { | ||
printk(KERN_WARNING | ||
"Coldfire M548x Watchdog : I/O region busy\n"); | ||
return -EBUSY; | ||
} | ||
printk(KERN_INFO "ColdFire watchdog driver is loaded.\n"); | ||
|
||
return misc_register(&m548x_wdt_miscdev); | ||
} | ||
|
||
static void __exit m548x_wdt_exit(void) | ||
{ | ||
misc_deregister(&m548x_wdt_miscdev); | ||
release_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4); | ||
} | ||
|
||
module_init(m548x_wdt_init); | ||
module_exit(m548x_wdt_exit); | ||
|
||
MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>"); | ||
MODULE_DESCRIPTION("Coldfire M548x Watchdog"); | ||
|
||
module_param(heartbeat, int, 0); | ||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)"); | ||
|
||
module_param(nowayout, int, 0); | ||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |