From: Komal Shah Date: Tue, 11 Oct 2005 16:04:05 +0000 (+0300) Subject: [PATCH] ARM: OMAP: omap24xx watchdog support X-Git-Tag: v2.6.15-omap2~160 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=d63c8843778540c037f1b2de3a9de58ebd5889c3;p=linux-2.6-omap-h63xx.git [PATCH] ARM: OMAP: omap24xx watchdog support Added 24xx watchdog support Signed-off-by: Komal Shah Signed-off-by: Tony Lindgren --- diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index 62a9668a5da..4ce3644929b 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -99,38 +99,6 @@ static void omap_init_rtc(void) static inline void omap_init_rtc(void) {} #endif -/*-------------------------------------------------------------------------*/ - -#if defined(CONFIG_OMAP16XX_WATCHDOG) || defined(CONFIG_OMAP16XX_WATCHDOG_MODULE) - -#define OMAP_WDT_BASE 0xfffeb000 - -static struct resource wdt_resources[] = { - { - .start = OMAP_WDT_BASE, - .end = OMAP_WDT_BASE + 0x4f, - .flags = IORESOURCE_MEM, - }, -}; - -static struct platform_device omap_wdt_device = { - .name = "omap1610_wdt", - .id = -1, - .dev = { - .release = omap_nop_release, - }, - .num_resources = ARRAY_SIZE(wdt_resources), - .resource = wdt_resources, -}; - -static void omap_init_wdt(void) -{ - (void) platform_device_register(&omap_wdt_device); -} -#else -static inline void omap_init_wdt(void) {} -#endif - /*-------------------------------------------------------------------------*/ @@ -161,7 +129,6 @@ static int __init omap1_init_devices(void) */ omap_init_irda(); omap_init_rtc(); - omap_init_wdt(); return 0; } diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index a80c415e709..9904158b141 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -271,6 +271,41 @@ static void __init omap_init_mmc(void) static inline void omap_init_mmc(void) {} #endif +#if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) + +#ifdef CONFIG_ARCH_OMAP24XX +#define OMAP_WDT_BASE 0x48022000 +#else +#define OMAP_WDT_BASE 0xfffeb000 +#endif + +static struct resource wdt_resources[] = { + { + .start = OMAP_WDT_BASE, + .end = OMAP_WDT_BASE + 0x4f, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap_wdt_device = { + .name = "omap_wdt", + .id = -1, + .dev = { + .release = omap_nop_release, + }, + .num_resources = ARRAY_SIZE(wdt_resources), + .resource = wdt_resources, +}; + +static void omap_init_wdt(void) +{ + (void) platform_device_register(&omap_wdt_device); +} +#else +static inline void omap_init_wdt(void) {} +#endif + + /* * This gets called after board-specific INIT_MACHINE, and initializes most * on-chip peripherals accessible on this board (except for few like USB): @@ -298,6 +333,7 @@ static int __init omap_init_devices(void) */ omap_init_i2c(); omap_init_mmc(); + omap_init_wdt(); return 0; } diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index f3d8d275e95..974ec86068c 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -572,11 +572,11 @@ config USBPCWATCHDOG Most people will say N. -config OMAP16XX_WATCHDOG - tristate "OMAP1610/OMAP1710 Watchdog" - depends on WATCHDOG && ARCH_OMAP16XX +config OMAP_WATCHDOG + tristate "OMAP Watchdog" + depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX) help - Support for TI OMAP1610/OMAP1710 watchdog. Say 'Y' here to enable the + Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to enable the OMAP1610/OMAP1710 watchdog timer. endmenu diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 1e430edc303..c450e737a07 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o -obj-$(CONFIG_OMAP16XX_WATCHDOG) += omap1610_wdt.o +obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o diff --git a/drivers/char/watchdog/omap1610_wdt.c b/drivers/char/watchdog/omap1610_wdt.c index 1a0a33ae5a8..e69de29bb2d 100644 --- a/drivers/char/watchdog/omap1610_wdt.c +++ b/drivers/char/watchdog/omap1610_wdt.c @@ -1,334 +0,0 @@ -/* - * linux/drivers/char/omap1610_wdt.c - * - * Watchdog driver for the TI OMAP 16xx 32KHz (non-secure) watchdog - * - * Author: MontaVista Software, Inc. - * or - * - * 2003 (c) MontaVista Software, Inc. 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. - * - * History: - * - * 20030527: George G. Davis - * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c - * (c) Copyright 2000 Oleg Drokin - * Based on SoftDog driver by Alan Cox - * - * Copyright (c) 2004 Texas Instruments. - * 1. Modified to support OMAP1610 32-KHz watchdog timer - * 2. Ported to 2.6 kernel - * - * Copyright (c) 2005 David Brownell - * Use the driver model and standard identifiers; handle bigger timeouts. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "omap1610_wdt.h" - - -static unsigned timer_margin; -module_param(timer_margin, uint, 0); -MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); - -static int omap_wdt_users; -static struct clk *armwdt_ck = NULL; - -static unsigned int wdt_trgr_pattern = 0x1234; - -static void -omap_wdt_ping(void) -{ - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) ; /* wait for posted write to complete */ - wdt_trgr_pattern = ~wdt_trgr_pattern; - omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) ; /* wait for posted write to complete */ - /* reloaded WCRR from WLDR */ -} - -static void -omap_wdt_enable(void) -{ - /* Sequence to enable the watchdog */ - omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) ; - omap_writel(0x4444, OMAP_WATCHDOG_SPR); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) ; -} - -static void -omap_wdt_disable(void) -{ - /* sequence required to disable watchdog */ - omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) ; - omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) ; -} - -static void -omap_wdt_adjust_timeout(unsigned new_timeout) -{ - if (new_timeout < TIMER_MARGIN_MIN) - new_timeout = TIMER_MARGIN_DEFAULT; - if (new_timeout > TIMER_MARGIN_MAX) - new_timeout = TIMER_MARGIN_MAX; - timer_margin = new_timeout; -} - -static void -omap_wdt_set_timeout(void) -{ - u32 pre_margin = GET_WLDR_VAL(timer_margin); - - /* just count up at 32 KHz */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) continue; - omap_writel(pre_margin, OMAP_WATCHDOG_LDR); - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) continue; -} - - -/* - * Allow only one task to hold it open - */ - -static int -omap_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(1, (unsigned long *) &omap_wdt_users)) - return -EBUSY; - - clk_use(armwdt_ck); /* Enable the clock */ - - /* initialize prescaler */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) continue; - omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) continue; - - omap_wdt_set_timeout(); - omap_wdt_enable(); - return 0; -} - -static int -omap_wdt_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer unless NOWAYOUT is defined. - */ -#ifndef CONFIG_WATCHDOG_NOWAYOUT - omap_wdt_disable(); - clk_unuse(armwdt_ck); /* Disable the clock */ - clk_put(armwdt_ck); - armwdt_ck = NULL; -#else - printk(KERN_CRIT "omap1610_wdt: Unexpected close, not stopping!\n"); -#endif - omap_wdt_users = 0; - return 0; -} - -static ssize_t -omap_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t * ppos) -{ - /* Refresh LOAD_TIME. */ - if (len) - omap_wdt_ping(); - return len; -} - -static int -omap_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int new_margin; - static struct watchdog_info ident = { - .identity = "OMAP Watchdog", - .options = WDIOF_SETTIMEOUT, - .firmware_version = 0, - }; - - switch (cmd) { - default: - return -ENOIOCTLCMD; - case WDIOC_GETSUPPORT: - return copy_to_user((struct watchdog_info __user *) arg, &ident, - sizeof (ident)); - case WDIOC_GETSTATUS: - return put_user(0, (int __user *) arg); - case WDIOC_GETBOOTSTATUS: - return put_user(omap_readw(ARM_SYSST), (int __user *) arg); - case WDIOC_KEEPALIVE: - omap_wdt_ping(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, (int __user *) arg)) - return -EFAULT; - omap_wdt_adjust_timeout(new_margin); - - omap_wdt_disable(); - omap_wdt_set_timeout(); - omap_wdt_enable(); - - omap_wdt_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(timer_margin, (int __user *) arg); - } -} - -static struct file_operations omap_wdt_fops = { - .owner = THIS_MODULE, - .write = omap_wdt_write, - .ioctl = omap_wdt_ioctl, - .open = omap_wdt_open, - .release = omap_wdt_release, -}; - -static struct miscdevice omap_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &omap_wdt_fops -}; - -static int __init -omap1610_wdt_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct resource *res, *mem; - int ret; - - /* reserve static register mappings */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - mem = request_mem_region(res->start, res->end - res->start + 1, - pdev->name); - if (mem == NULL) - return -EBUSY; - dev_set_drvdata(dev, mem); - - omap_wdt_users = 0; - armwdt_ck = clk_get(dev, "armwdt_ck"); - if (IS_ERR(armwdt_ck)) { - ret = PTR_ERR(armwdt_ck); - armwdt_ck = NULL; - goto fail; - } - - omap_wdt_disable(); - omap_wdt_adjust_timeout(timer_margin); - - omap_wdt_miscdev.dev = dev; - ret = misc_register(&omap_wdt_miscdev); - if (ret) - goto fail; - - pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); - - /* autogate OCP interface clock */ - omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); - return 0; - -fail: - if (armwdt_ck) - clk_put(armwdt_ck); - release_resource(mem); - return ret; -} - -static void -omap1610_wdt_shutdown(struct device *dev) -{ - omap_wdt_disable(); -} - -static int __exit -omap1610_wdt_remove(struct device *dev) -{ - struct resource *mem = dev_get_drvdata(dev); - misc_deregister(&omap_wdt_miscdev); - release_resource(mem); - clk_put(armwdt_ck); - return 0; -} - -#ifdef CONFIG_PM - -/* REVISIT ... not clear this is the best way to handle system suspend; and - * it's very inappropriate for selective device suspend (e.g. suspending this - * through sysfs rather than by stopping the watchdog daemon). Also, this - * may not play well enough with NOWAYOUT... - */ - -static int omap1610_wdt_suspend(struct device *dev, pm_message_t mesg, u32 level) -{ - if (level == SUSPEND_POWER_DOWN && omap_wdt_users) - omap_wdt_disable(); - return 0; -} - -static int omap1610_wdt_resume(struct device *dev, u32 level) -{ - if (level == RESUME_POWER_ON && omap_wdt_users) { - omap_wdt_enable(); - omap_wdt_ping(); - } - return 0; -} - -#else -#define omap1610_wdt_suspend NULL -#define omap1610_wdt_resume NULL -#endif - -static struct device_driver omap1610_wdt_driver = { - .name = "omap1610_wdt", - .bus = &platform_bus_type, - .probe = omap1610_wdt_probe, - .shutdown = omap1610_wdt_shutdown, - .remove = __exit_p(omap1610_wdt_remove), - .suspend = omap1610_wdt_suspend, - .resume = omap1610_wdt_resume, -}; - -static int __init omap_wdt_init(void) -{ - return driver_register(&omap1610_wdt_driver); -} - -static void __exit omap_wdt_exit(void) -{ - driver_unregister(&omap1610_wdt_driver); -} - -module_init(omap_wdt_init); -module_exit(omap_wdt_exit); - -MODULE_AUTHOR("George G. Davis"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap1610_wdt.h b/drivers/char/watchdog/omap1610_wdt.h index 460d6569820..e69de29bb2d 100644 --- a/drivers/char/watchdog/omap1610_wdt.h +++ b/drivers/char/watchdog/omap1610_wdt.h @@ -1,58 +0,0 @@ -/* - * linux/drivers/char/watchdog/omap1610_wdt.h - * - * BRIEF MODULE DESCRIPTION - * OMAP Watchdog timer register definitions - * - * Copyright (C) 2004 Texas Instruments. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _OMAP_WATCHDOG_H -#define _OMAP_WATCHDOG_H - -#define OMAP1610_WATCHDOG_BASE 0xfffeb000 - -#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE - -#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) -#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) -#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) -#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) -#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) -#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) -#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) -#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) -#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) - -/* Using the prescaler, the OMAP watchdog could go for many - * months before firing. These limits work without scaling, - * with the 60 second default assumed by most tools and docs. - */ -#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ -#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ -#define TIMER_MARGIN_MIN 1 - -#define PTV 0 /* prescale */ -#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1< or + * + * 2003 (c) MontaVista Software, Inc. 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. + * + * History: + * + * 20030527: George G. Davis + * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * Copyright (c) 2004 Texas Instruments. + * 1. Modified to support OMAP1610 32-KHz watchdog timer + * 2. Ported to 2.6 kernel + * + * Copyright (c) 2005 David Brownell + * Use the driver model and standard identifiers; handle bigger timeouts. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_OMAP24XX +#include +#endif + +#include "omap_wdt.h" + +static unsigned timer_margin; +module_param(timer_margin, uint, 0); +MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); + +static int omap_wdt_users; +static struct clk *armwdt_ck = NULL; +static struct clk *mpu_wdt_ick = NULL; +static struct clk *mpu_wdt_fck = NULL; + +static unsigned int wdt_trgr_pattern = 0x1234; + +static void omap_wdt_ping(void) +{ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) ; /* wait for posted write to complete */ + wdt_trgr_pattern = ~wdt_trgr_pattern; + omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) ; /* wait for posted write to complete */ + /* reloaded WCRR from WLDR */ +} + +static void omap_wdt_enable(void) +{ + /* Sequence to enable the watchdog */ + omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) ; + omap_writel(0x4444, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) ; +} + +static void omap_wdt_disable(void) +{ + /* sequence required to disable watchdog */ + omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) ; + omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) ; +} + +static void omap_wdt_adjust_timeout(unsigned new_timeout) +{ + if (new_timeout < TIMER_MARGIN_MIN) + new_timeout = TIMER_MARGIN_DEFAULT; + if (new_timeout > TIMER_MARGIN_MAX) + new_timeout = TIMER_MARGIN_MAX; + timer_margin = new_timeout; +} + +static void omap_wdt_set_timeout(void) +{ + u32 pre_margin = GET_WLDR_VAL(timer_margin); + + /* just count up at 32 KHz */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + continue; + omap_writel(pre_margin, OMAP_WATCHDOG_LDR); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + continue; +} + +/* + * Allow only one task to hold it open + */ + +static int omap_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) + return -EBUSY; + + if (cpu_is_omap16xx()) { + clk_use(armwdt_ck); /* Enable the clock */ + } + + if (cpu_is_omap24xx()) { + clk_use(mpu_wdt_ick); /* Enable the interface clock */ + clk_use(mpu_wdt_fck); /* Enable the functional clock */ + } + + /* initialize prescaler */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + continue; + omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + continue; + + omap_wdt_set_timeout(); + omap_wdt_enable(); + return 0; +} + +static int omap_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer unless NOWAYOUT is defined. + */ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + omap_wdt_disable(); + + if (cpu_is_omap16xx()) { + clk_unuse(armwdt_ck); /* Disable the clock */ + clk_put(armwdt_ck); + armwdt_ck = NULL; + } + + if (cpu_is_omap24xx()) { + clk_unuse(mpu_wdt_ick); /* Disable the clock */ + clk_unuse(mpu_wdt_fck); /* Disable the clock */ + clk_put(mpu_wdt_ick); + clk_put(mpu_wdt_fck); + mpu_wdt_ick = NULL; + mpu_wdt_fck = NULL; + } +#else + printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); +#endif + omap_wdt_users = 0; + return 0; +} + +static ssize_t +omap_wdt_write(struct file *file, const char __user * data, + size_t len, loff_t * ppos) +{ + /* Refresh LOAD_TIME. */ + if (len) + omap_wdt_ping(); + return len; +} + +static int +omap_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "OMAP Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int __user *)arg); + case WDIOC_GETBOOTSTATUS: + if (cpu_is_omap16xx()) + return put_user(omap_readw(ARM_SYSST), + (int __user *)arg); + if (cpu_is_omap24xx()) + return put_user(RM_RSTST_WKUP, (int __user *)arg); + case WDIOC_KEEPALIVE: + omap_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + omap_wdt_adjust_timeout(new_margin); + + omap_wdt_disable(); + omap_wdt_set_timeout(); + omap_wdt_enable(); + + omap_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int __user *)arg); + } +} + +static struct file_operations omap_wdt_fops = { + .owner = THIS_MODULE, + .write = omap_wdt_write, + .ioctl = omap_wdt_ioctl, + .open = omap_wdt_open, + .release = omap_wdt_release, +}; + +static struct miscdevice omap_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &omap_wdt_fops +}; + +static int __init omap_wdt_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *res, *mem; + int ret; + + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + mem = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (mem == NULL) + return -EBUSY; + + dev_set_drvdata(dev, mem); + + omap_wdt_users = 0; + + if (cpu_is_omap16xx()) { + armwdt_ck = clk_get(dev, "armwdt_ck"); + if (IS_ERR(armwdt_ck)) { + ret = PTR_ERR(armwdt_ck); + armwdt_ck = NULL; + goto fail; + } + } + + if (cpu_is_omap24xx()) { + mpu_wdt_ick = clk_get(dev, "mpu_wdt_ick"); + if (IS_ERR(mpu_wdt_ick)) { + ret = PTR_ERR(mpu_wdt_ick); + mpu_wdt_ick = NULL; + goto fail; + } + mpu_wdt_fck = clk_get(dev, "mpu_wdt_fck"); + if (IS_ERR(mpu_wdt_fck)) { + ret = PTR_ERR(mpu_wdt_fck); + mpu_wdt_fck = NULL; + goto fail; + } + } + + omap_wdt_disable(); + omap_wdt_adjust_timeout(timer_margin); + + omap_wdt_miscdev.dev = dev; + ret = misc_register(&omap_wdt_miscdev); + if (ret) + goto fail; + + pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); + + /* autogate OCP interface clock */ + omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); + return 0; + +fail: + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + release_resource(mem); + return ret; +} + +static void omap_wdt_shutdown(struct device *dev) +{ + omap_wdt_disable(); +} + +static int __exit omap_wdt_remove(struct device *dev) +{ + struct resource *mem = dev_get_drvdata(dev); + misc_deregister(&omap_wdt_miscdev); + release_resource(mem); + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + return 0; +} + +#ifdef CONFIG_PM + +/* REVISIT ... not clear this is the best way to handle system suspend; and + * it's very inappropriate for selective device suspend (e.g. suspending this + * through sysfs rather than by stopping the watchdog daemon). Also, this + * may not play well enough with NOWAYOUT... + */ + +static int omap_wdt_suspend(struct device *dev, pm_message_t mesg, u32 level) +{ + if (level == SUSPEND_POWER_DOWN && omap_wdt_users) + omap_wdt_disable(); + return 0; +} + +static int omap_wdt_resume(struct device *dev, u32 level) +{ + if (level == RESUME_POWER_ON && omap_wdt_users) { + omap_wdt_enable(); + omap_wdt_ping(); + } + return 0; +} + +#else +#define omap_wdt_suspend NULL +#define omap_wdt_resume NULL +#endif + +static struct device_driver omap_wdt_driver = { + .name = "omap_wdt", + .bus = &platform_bus_type, + .probe = omap_wdt_probe, + .shutdown = omap_wdt_shutdown, + .remove = __exit_p(omap_wdt_remove), + .suspend = omap_wdt_suspend, + .resume = omap_wdt_resume, +}; + +static int __init omap_wdt_init(void) +{ + return driver_register(&omap_wdt_driver); +} + +static void __exit omap_wdt_exit(void) +{ + driver_unregister(&omap_wdt_driver); +} + +module_init(omap_wdt_init); +module_exit(omap_wdt_exit); + +MODULE_AUTHOR("George G. Davis"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h new file mode 100644 index 00000000000..f0dcfc9eac8 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.h @@ -0,0 +1,63 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.h + * + * BRIEF MODULE DESCRIPTION + * OMAP Watchdog timer register definitions + * + * Copyright (C) 2004 Texas Instruments. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _OMAP_WATCHDOG_H +#define _OMAP_WATCHDOG_H + +#define OMAP1610_WATCHDOG_BASE 0xfffeb000 +#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ + +#ifdef CONFIG_ARCH_OMAP24XX +#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE +#else +#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE +#endif + +#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) +#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) +#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) +#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) +#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) +#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) +#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) +#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) +#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) + +/* Using the prescaler, the OMAP watchdog could go for many + * months before firing. These limits work without scaling, + * with the 60 second default assumed by most tools and docs. + */ +#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ +#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ +#define TIMER_MARGIN_MIN 1 + +#define PTV 0 /* prescale */ +#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<