X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fwatchdog%2Fs3c2410_wdt.c;h=8760a26ab2a3996fd7b48b4615d7d68f85055b1d;hb=715848ca6fffeb6362a50887d9c26245bd5dfba9;hp=97b4a2e8eb09a62db3e5fa0eae184952be4e76b3;hpb=edef7a93f9414e1d4864150eabb49a618222c2bd;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 97b4a2e..8760a26 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -6,7 +6,7 @@ * S3C2410 Watchdog Timer Support * * Based on, softdog.c by Alan Cox, - * (c) Copyright 1996 Alan Cox + * (c) Copyright 1996 Alan Cox * * 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 @@ -21,18 +21,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Changelog: - * 05-Oct-2004 BJD Added semaphore init to stop crashes on open - * Fixed tmr_count / wdt_count confusion - * Added configurable debug - * - * 11-Jan-2005 BJD Fixed divide-by-2 in timeout code - * - * 25-Jan-2005 DA Added suspend/resume support - * Replaced reboot notifier with .shutdown method - * - * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA */ #include @@ -48,13 +36,14 @@ #include #include #include +#include -#include +#include #undef S3C_VA_WATCHDOG #define S3C_VA_WATCHDOG (0) -#include +#include #define PFX "s3c2410-wdt: " @@ -80,15 +69,10 @@ MODULE_PARM_DESC(tmr_atboot, __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT)); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default depends on ONLY_TESTING)"); +MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " + "0 to reboot (default depends on ONLY_TESTING)"); MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)"); - -typedef enum close_state { - CLOSE_STATE_NOT, - CLOSE_STATE_ALLOW = 0x4021 -} close_state_t; - static unsigned long open_lock; static struct device *wdt_dev; /* platform device attached to */ static struct resource *wdt_mem; @@ -96,7 +80,7 @@ static struct resource *wdt_irq; static struct clk *wdt_clock; static void __iomem *wdt_base; static unsigned int wdt_count; -static close_state_t allow_close; +static char expect_close; static DEFINE_SPINLOCK(wdt_lock); /* watchdog control routines */ @@ -119,17 +103,6 @@ static void __s3c2410wdt_stop(void) { unsigned long wtcon; - spin_lock(&wdt_lock); - wtcon = readl(wdt_base + S3C2410_WTCON); - wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); - writel(wtcon, wdt_base + S3C2410_WTCON); - spin_unlock(&wdt_lock); -} - -static void __s3c2410wdt_stop(void) -{ - unsigned long wtcon; - wtcon = readl(wdt_base + S3C2410_WTCON); wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); writel(wtcon, wdt_base + S3C2410_WTCON); @@ -168,13 +141,16 @@ static void s3c2410wdt_start(void) writel(wdt_count, wdt_base + S3C2410_WTCNT); writel(wtcon, wdt_base + S3C2410_WTCON); spin_unlock(&wdt_lock); +} - return 0; +static inline int s3c2410wdt_is_running(void) +{ + return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; } static int s3c2410wdt_set_heartbeat(int timeout) { - unsigned int freq = clk_get_rate(wdt_clock); + unsigned long freq = clk_get_rate(wdt_clock); unsigned int count; unsigned int divisor = 1; unsigned long wtcon; @@ -185,7 +161,7 @@ static int s3c2410wdt_set_heartbeat(int timeout) freq /= 128; count = timeout * freq; - DBG("%s: count=%d, timeout=%d, freq=%d\n", + DBG("%s: count=%d, timeout=%d, freq=%lu\n", __func__, count, timeout, freq); /* if the count is bigger than the watchdog register, @@ -236,7 +212,7 @@ static int s3c2410wdt_open(struct inode *inode, struct file *file) if (nowayout) __module_get(THIS_MODULE); - allow_close = CLOSE_STATE_NOT; + expect_close = 0; /* start the timer */ s3c2410wdt_start(); @@ -250,13 +226,13 @@ static int s3c2410wdt_release(struct inode *inode, struct file *file) * Lock it in if it's a module and we set nowayout */ - if (allow_close == CLOSE_STATE_ALLOW) + if (expect_close == 42) s3c2410wdt_stop(); else { dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n"); s3c2410wdt_keepalive(); } - allow_close = CLOSE_STATE_NOT; + expect_close = 0; clear_bit(0, &open_lock); return 0; } @@ -272,7 +248,7 @@ static ssize_t s3c2410wdt_write(struct file *file, const char __user *data, size_t i; /* In case it was set long ago */ - allow_close = CLOSE_STATE_NOT; + expect_close = 0; for (i = 0; i != len; i++) { char c; @@ -280,7 +256,7 @@ static ssize_t s3c2410wdt_write(struct file *file, const char __user *data, if (get_user(c, data + i)) return -EFAULT; if (c == 'V') - allow_close = CLOSE_STATE_ALLOW; + expect_close = 42; } } s3c2410wdt_keepalive(); @@ -288,7 +264,7 @@ static ssize_t s3c2410wdt_write(struct file *file, const char __user *data, return len; } -#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE +#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) static const struct watchdog_info s3c2410_wdt_ident = { .options = OPTIONS, @@ -305,8 +281,6 @@ static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd, int new_margin; switch (cmd) { - default: - return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &s3c2410_wdt_ident, sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0; @@ -325,6 +299,8 @@ static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd, return put_user(tmr_margin, p); case WDIOC_GETTIMEOUT: return put_user(tmr_margin, p); + default: + return -ENOTTY; } } @@ -354,9 +330,76 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) s3c2410wdt_keepalive(); return IRQ_HANDLED; } + + +#ifdef CONFIG_CPU_FREQ + +static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + int ret; + + if (!s3c2410wdt_is_running()) + goto done; + + if (val == CPUFREQ_PRECHANGE) { + /* To ensure that over the change we don't cause the + * watchdog to trigger, we perform an keep-alive if + * the watchdog is running. + */ + + s3c2410wdt_keepalive(); + } else if (val == CPUFREQ_POSTCHANGE) { + s3c2410wdt_stop(); + + ret = s3c2410wdt_set_heartbeat(tmr_margin); + + if (ret >= 0) + s3c2410wdt_start(); + else + goto err; + } + +done: + return 0; + + err: + dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin); + return ret; +} + +static struct notifier_block s3c2410wdt_cpufreq_transition_nb = { + .notifier_call = s3c2410wdt_cpufreq_transition, +}; + +static inline int s3c2410wdt_cpufreq_register(void) +{ + return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c2410wdt_cpufreq_deregister(void) +{ + cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c2410wdt_cpufreq_register(void) +{ + return 0; +} + +static inline void s3c2410wdt_cpufreq_deregister(void) +{ +} +#endif + + + /* device interface */ -static int s3c2410wdt_probe(struct platform_device *pdev) +static int __devinit s3c2410wdt_probe(struct platform_device *pdev) { struct resource *res; struct device *dev; @@ -378,7 +421,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) return -ENOENT; } - size = (res->end-res->start)+1; + size = resource_size(res); wdt_mem = request_mem_region(res->start, size, pdev->name); if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region\n"); @@ -387,7 +430,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) } wdt_base = ioremap(res->start, size); - if (wdt_base == 0) { + if (wdt_base == NULL) { dev_err(dev, "failed to ioremap() region\n"); ret = -EINVAL; goto err_req; @@ -417,6 +460,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) clk_enable(wdt_clock); + if (s3c2410wdt_cpufreq_register() < 0) { + printk(KERN_ERR PFX "failed to register cpufreq\n"); + goto err_clk; + } + /* see if we can actually set the requested timer margin, and if * not, try the default value */ @@ -429,14 +477,15 @@ static int s3c2410wdt_probe(struct platform_device *pdev) "tmr_margin value out of range, default %d used\n", CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); else - dev_info(dev, "default timer value is out of range, cannot start\n"); + dev_info(dev, "default timer value is out of range, " + "cannot start\n"); } ret = misc_register(&s3c2410wdt_miscdev); if (ret) { dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", WATCHDOG_MINOR, ret); - goto err_clk; + goto err_cpufreq; } if (tmr_atboot && started == 0) { @@ -461,6 +510,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev) return 0; + err_cpufreq: + s3c2410wdt_cpufreq_deregister(); + err_clk: clk_disable(wdt_clock); clk_put(wdt_clock); @@ -478,8 +530,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev) return ret; } -static int s3c2410wdt_remove(struct platform_device *dev) +static int __devexit s3c2410wdt_remove(struct platform_device *dev) { + s3c2410wdt_cpufreq_deregister(); + release_resource(wdt_mem); kfree(wdt_mem); wdt_mem = NULL; @@ -540,7 +594,7 @@ static int s3c2410wdt_resume(struct platform_device *dev) static struct platform_driver s3c2410wdt_driver = { .probe = s3c2410wdt_probe, - .remove = s3c2410wdt_remove, + .remove = __devexit_p(s3c2410wdt_remove), .shutdown = s3c2410wdt_shutdown, .suspend = s3c2410wdt_suspend, .resume = s3c2410wdt_resume,