diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2009-10-17 01:51:11 +0200 |
---|---|---|
committer | Paul Fertser <fercerpav@gmail.com> | 2009-10-17 04:23:23 +0400 |
commit | ee73a861f3273b9603577468a06b433d5f5be129 (patch) | |
tree | f0647be1ca67e7842e787223712d169f03f5eed9 /drivers/watchdog | |
parent | 74fca6a42863ffacaf7ba6f1936a9f228950f657 (diff) |
Inital pcf50606 support
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 7 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/pcf50606_wdt.c | 223 |
3 files changed, 231 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b1ccc04f3c9..618cc10acc6 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -154,6 +154,13 @@ config S3C2410_WATCHDOG The driver can be built as a module by choosing M, and will be called s3c2410_wdt +config PCF50606_WATCHDOG + depends on MFD_PCF50606 + tristate "Philips PCF50606 watchdog" + help + If you say yes here you get support for the Philips PCF50606 + PMU's watchdog. + config SA1100_WATCHDOG tristate "SA1100/PXA2xx watchdog" depends on ARCH_SA1100 || ARCH_PXA diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 3d774294a2b..638e1c1d743 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o +obj-$(CONFIG_PCF50606_WATCHDOG) += pcf50606_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o diff --git a/drivers/watchdog/pcf50606_wdt.c b/drivers/watchdog/pcf50606_wdt.c new file mode 100644 index 00000000000..f0858747bf9 --- /dev/null +++ b/drivers/watchdog/pcf50606_wdt.c @@ -0,0 +1,223 @@ +/* Philips PCF50606 Watchdog Timer Driver + * + * (C) 2006-2008 by Openmoko, Inc. + * Author: Balaji Rao <balajirrao@openmoko.org> + * All rights reserved. + * + * Broken down from monstrous PCF50606 driver mainly by + * Harald Welte, Matt Hsu, Andy Green and Werner Almesberger + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/err.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> + +#include <linux/mfd/pcf50606/core.h> + +static struct pcf50606 *pcf = NULL; +static unsigned long wdt_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 +#define WDT_REGION_INITED 2 +#define WDT_DEVICE_INITED 3 + +static int allow_close; +#define CLOSE_STATE_NOT 0x0000 +#define CLOSE_STATE_ALLOW 0x2342 + +#define PCF50606_REG_OOCC1 0x08 +#define PCF50606_REG_OOCS 0x01 + +#define PCF50606_OOCS_WDTEXP 0x80 +#define PCF50606_OOCC1_WDTRST 0x08 + +static void pcf50606_wdt_start(void) +{ + pcf50606_reg_set_bit_mask(pcf, PCF50606_REG_OOCC1, PCF50606_OOCC1_WDTRST, + PCF50606_OOCC1_WDTRST); +} + +static void pcf50606_wdt_stop(void) +{ + pcf50606_reg_clear_bits(pcf, PCF50606_REG_OOCS, PCF50606_OOCS_WDTEXP); +} + +static void pcf50606_wdt_keepalive(void) +{ + pcf50606_wdt_start(); +} + +static int pcf50606_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + pcf50606_wdt_start(); + + return nonseekable_open(inode, file); +} + +static int pcf50606_wdt_release(struct inode *inode, struct file *file) +{ + if (allow_close == CLOSE_STATE_ALLOW) + pcf50606_wdt_stop(); + else { + printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); + pcf50606_wdt_keepalive(); + } + + allow_close = CLOSE_STATE_NOT; + clear_bit(WDT_IN_USE, &wdt_status); + + return 0; +} + +static ssize_t pcf50606_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) { + size_t i; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + allow_close = CLOSE_STATE_ALLOW; + } + pcf50606_wdt_keepalive(); + } + + return len; +} + +static struct watchdog_info pcf50606_wdt_ident = { + .options = WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "PCF50606 Watchdog", +}; + +static int pcf50606_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &pcf50606_wdt_ident, + sizeof(pcf50606_wdt_ident)) ? -EFAULT : 0; + break; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + case WDIOC_KEEPALIVE: + pcf50606_wdt_keepalive(); + return 0; + case WDIOC_GETTIMEOUT: + return put_user(8, p); + default: + return -ENOIOCTLCMD; + } +} + +static struct file_operations pcf50606_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = &pcf50606_wdt_write, + .ioctl = &pcf50606_wdt_ioctl, + .open = &pcf50606_wdt_open, + .release = &pcf50606_wdt_release, +}; + +static struct miscdevice pcf50606_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pcf50606_wdt_fops, +}; + +static void pcf50606_wdt_irq(int irq, void *unused) +{ + pcf50606_reg_set_bit_mask(pcf, PCF50606_REG_OOCC1, + PCF50606_OOCC1_WDTRST, + PCF50606_OOCC1_WDTRST); +} + +int __init pcf50606_wdt_probe(struct platform_device *pdev) +{ + struct pcf50606_subdev_pdata *pdata; + int err; + + if (pcf) { + dev_err(pcf->dev, "Only one instance of WDT supported\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data available\n"); + return -EINVAL; + } + + pcf = pdata->pcf; + + err = misc_register(&pcf50606_wdt_miscdev); + if (err) { + dev_err(&pdev->dev, "cannot register miscdev on " + "minor=%d (%d)\n", WATCHDOG_MINOR, err); + return err; + } + set_bit(WDT_DEVICE_INITED, &wdt_status); + + pcf50606_register_irq(pcf, PCF50606_IRQ_CHGWD10S, pcf50606_wdt_irq, NULL); + + return 0; +} + +static int __devexit pcf50606_wdt_remove(struct platform_device *pdev) +{ + pcf50606_free_irq(pcf, PCF50606_IRQ_CHGWD10S); + misc_deregister(&pcf50606_wdt_miscdev); + pcf = NULL; + + return 0; +} + +struct platform_driver pcf50606_wdt_driver = { + .driver = { + .name = "pcf50606-wdt", + }, + .probe = pcf50606_wdt_probe, + .remove = __devexit_p(pcf50606_wdt_remove), +}; + +static int __init pcf50606_wdt_init(void) +{ + return platform_driver_register(&pcf50606_wdt_driver); +} +module_init(pcf50606_wdt_init); + +static void __exit pcf50606_wdt_exit(void) +{ + platform_driver_unregister(&pcf50606_wdt_driver); +} +module_exit(pcf50606_wdt_exit); + +MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); +MODULE_DESCRIPTION("PCF50606 wdt driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcf50606-wdt"); + |