[PATCH] watchdog: Add support for the Avionic Design Xanthos watchdog timer.
Wim Van Sebroeck
wim at iguana.be
Thu Sep 24 07:05:42 EDT 2009
Hi Thierry,
it's in the linux-watchdog-next tree now.
Kind regards,
Wim.
> This patch adds support for the watchdog timer on Avionic Design Xanthos
> boards.
>
> Signed-off-by: Thierry Reding <thierry.reding at avionic-design.de>
> Acked-by: Russell King <rmk+kernel at arm.linux.org.uk>
>
> ---
> drivers/watchdog/Kconfig | 7 +
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/adx_wdt.c | 354 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 362 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index b1ccc04..378d518 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -266,6 +266,13 @@ config STMP3XXX_WATCHDOG
> To compile this driver as a module, choose M here: the
> module will be called stmp3xxx_wdt.
>
> +config ADX_WATCHDOG
> + tristate "Avionic Design Xanthos watchdog"
> + depends on ARCH_PXA_ADX
> + help
> + Say Y here if you want support for the watchdog timer on Avionic
> + Design Xanthos boards.
> +
> # AVR32 Architecture
>
> config AT32AP700X_WDT
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 3d77429..f91719f 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
> obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
> obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
> obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
> +obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
>
> # AVR32 Architecture
> obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
> diff --git a/drivers/watchdog/adx_wdt.c b/drivers/watchdog/adx_wdt.c
> new file mode 100644
> index 0000000..77afb0a
> --- /dev/null
> +++ b/drivers/watchdog/adx_wdt.c
> @@ -0,0 +1,354 @@
> +/*
> + * Copyright (C) 2008-2009 Avionic Design GmbH
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/io.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/watchdog.h>
> +
> +#define WATCHDOG_NAME "adx-wdt"
> +
> +/* register offsets */
> +#define ADX_WDT_CONTROL 0x00
> +#define ADX_WDT_CONTROL_ENABLE (1 << 0)
> +#define ADX_WDT_CONTROL_nRESET (1 << 1)
> +#define ADX_WDT_TIMEOUT 0x08
> +
> +static struct platform_device *adx_wdt_dev;
> +static unsigned long driver_open;
> +
> +#define WDT_STATE_STOP 0
> +#define WDT_STATE_START 1
> +
> +struct adx_wdt {
> + void __iomem *base;
> + unsigned long timeout;
> + unsigned int state;
> + unsigned int wake;
> + spinlock_t lock;
> +};
> +
> +static struct watchdog_info adx_wdt_info = {
> + .identity = "Avionic Design Xanthos Watchdog",
> + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
> +};
> +
> +static void adx_wdt_start_locked(struct adx_wdt *wdt)
> +{
> + u32 ctrl;
> +
> + ctrl = readl(wdt->base + ADX_WDT_CONTROL);
> + ctrl |= ADX_WDT_CONTROL_ENABLE;
> + writel(ctrl, wdt->base + ADX_WDT_CONTROL);
> + wdt->state = WDT_STATE_START;
> +}
> +
> +static void adx_wdt_start(struct adx_wdt *wdt)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&wdt->lock, flags);
> + adx_wdt_start_locked(wdt);
> + spin_unlock_irqrestore(&wdt->lock, flags);
> +}
> +
> +static void adx_wdt_stop_locked(struct adx_wdt *wdt)
> +{
> + u32 ctrl;
> +
> + ctrl = readl(wdt->base + ADX_WDT_CONTROL);
> + ctrl &= ~ADX_WDT_CONTROL_ENABLE;
> + writel(ctrl, wdt->base + ADX_WDT_CONTROL);
> + wdt->state = WDT_STATE_STOP;
> +}
> +
> +static void adx_wdt_stop(struct adx_wdt *wdt)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&wdt->lock, flags);
> + adx_wdt_stop_locked(wdt);
> + spin_unlock_irqrestore(&wdt->lock, flags);
> +}
> +
> +static void adx_wdt_set_timeout(struct adx_wdt *wdt, unsigned long seconds)
> +{
> + unsigned long timeout = seconds * 1000;
> + unsigned long flags;
> + unsigned int state;
> +
> + spin_lock_irqsave(&wdt->lock, flags);
> + state = wdt->state;
> + adx_wdt_stop_locked(wdt);
> + writel(timeout, wdt->base + ADX_WDT_TIMEOUT);
> +
> + if (state == WDT_STATE_START)
> + adx_wdt_start_locked(wdt);
> +
> + wdt->timeout = timeout;
> + spin_unlock_irqrestore(&wdt->lock, flags);
> +}
> +
> +static void adx_wdt_get_timeout(struct adx_wdt *wdt, unsigned long *seconds)
> +{
> + *seconds = wdt->timeout / 1000;
> +}
> +
> +static void adx_wdt_keepalive(struct adx_wdt *wdt)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&wdt->lock, flags);
> + writel(wdt->timeout, wdt->base + ADX_WDT_TIMEOUT);
> + spin_unlock_irqrestore(&wdt->lock, flags);
> +}
> +
> +static int adx_wdt_open(struct inode *inode, struct file *file)
> +{
> + struct adx_wdt *wdt = platform_get_drvdata(adx_wdt_dev);
> +
> + if (test_and_set_bit(0, &driver_open))
> + return -EBUSY;
> +
> + file->private_data = wdt;
> + adx_wdt_set_timeout(wdt, 30);
> + adx_wdt_start(wdt);
> +
> + return nonseekable_open(inode, file);
> +}
> +
> +static int adx_wdt_release(struct inode *inode, struct file *file)
> +{
> + struct adx_wdt *wdt = file->private_data;
> +
> + adx_wdt_stop(wdt);
> + clear_bit(0, &driver_open);
> +
> + return 0;
> +}
> +
> +static long adx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> + struct adx_wdt *wdt = file->private_data;
> + void __user *argp = (void __user *)arg;
> + unsigned long __user *p = argp;
> + unsigned long seconds = 0;
> + unsigned int options;
> + long ret = -EINVAL;
> +
> + switch (cmd) {
> + case WDIOC_GETSUPPORT:
> + if (copy_to_user(argp, &adx_wdt_info, sizeof(adx_wdt_info)))
> + return -EFAULT;
> + else
> + return 0;
> +
> + case WDIOC_GETSTATUS:
> + case WDIOC_GETBOOTSTATUS:
> + return put_user(0, p);
> +
> + case WDIOC_KEEPALIVE:
> + adx_wdt_keepalive(wdt);
> + return 0;
> +
> + case WDIOC_SETTIMEOUT:
> + if (get_user(seconds, p))
> + return -EFAULT;
> +
> + adx_wdt_set_timeout(wdt, seconds);
> +
> + /* fallthrough */
> + case WDIOC_GETTIMEOUT:
> + adx_wdt_get_timeout(wdt, &seconds);
> + return put_user(seconds, p);
> +
> + case WDIOC_SETOPTIONS:
> + if (copy_from_user(&options, argp, sizeof(options)))
> + return -EFAULT;
> +
> + if (options & WDIOS_DISABLECARD) {
> + adx_wdt_stop(wdt);
> + ret = 0;
> + }
> +
> + if (options & WDIOS_ENABLECARD) {
> + adx_wdt_start(wdt);
> + ret = 0;
> + }
> +
> + return ret;
> +
> + default:
> + break;
> + }
> +
> + return -ENOTTY;
> +}
> +
> +static ssize_t adx_wdt_write(struct file *file, const char __user *data,
> + size_t len, loff_t *ppos)
> +{
> + struct adx_wdt *wdt = file->private_data;
> +
> + if (len)
> + adx_wdt_keepalive(wdt);
> +
> + return len;
> +}
> +
> +static const struct file_operations adx_wdt_fops = {
> + .owner = THIS_MODULE,
> + .llseek = no_llseek,
> + .open = adx_wdt_open,
> + .release = adx_wdt_release,
> + .unlocked_ioctl = adx_wdt_ioctl,
> + .write = adx_wdt_write,
> +};
> +
> +static struct miscdevice adx_wdt_miscdev = {
> + .minor = WATCHDOG_MINOR,
> + .name = "watchdog",
> + .fops = &adx_wdt_fops,
> +};
> +
> +static int __devinit adx_wdt_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct adx_wdt *wdt;
> + int ret = 0;
> + u32 ctrl;
> +
> + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
> + if (!wdt) {
> + dev_err(&pdev->dev, "cannot allocate WDT structure\n");
> + return -ENOMEM;
> + }
> +
> + spin_lock_init(&wdt->lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "cannot obtain I/O memory region\n");
> + return -ENXIO;
> + }
> +
> + res = devm_request_mem_region(&pdev->dev, res->start,
> + res->end - res->start + 1, res->name);
> + if (!res) {
> + dev_err(&pdev->dev, "cannot request I/O memory region\n");
> + return -ENXIO;
> + }
> +
> + wdt->base = devm_ioremap_nocache(&pdev->dev, res->start,
> + res->end - res->start + 1);
> + if (!wdt->base) {
> + dev_err(&pdev->dev, "cannot remap I/O memory region\n");
> + return -ENXIO;
> + }
> +
> + /* disable watchdog and reboot on timeout */
> + ctrl = readl(wdt->base + ADX_WDT_CONTROL);
> + ctrl &= ~ADX_WDT_CONTROL_ENABLE;
> + ctrl &= ~ADX_WDT_CONTROL_nRESET;
> + writel(ctrl, wdt->base + ADX_WDT_CONTROL);
> +
> + platform_set_drvdata(pdev, wdt);
> + adx_wdt_dev = pdev;
> +
> + ret = misc_register(&adx_wdt_miscdev);
> + if (ret) {
> + dev_err(&pdev->dev, "cannot register miscdev on minor %d "
> + "(err=%d)\n", WATCHDOG_MINOR, ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int __devexit adx_wdt_remove(struct platform_device *pdev)
> +{
> + struct adx_wdt *wdt = platform_get_drvdata(pdev);
> +
> + misc_deregister(&adx_wdt_miscdev);
> + adx_wdt_stop(wdt);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +static void adx_wdt_shutdown(struct platform_device *pdev)
> +{
> + struct adx_wdt *wdt = platform_get_drvdata(pdev);
> + adx_wdt_stop(wdt);
> +}
> +
> +#ifdef CONFIG_PM
> +static int adx_wdt_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct adx_wdt *wdt = platform_get_drvdata(pdev);
> +
> + wdt->wake = (wdt->state == WDT_STATE_START) ? 1 : 0;
> + adx_wdt_stop(wdt);
> +
> + return 0;
> +}
> +
> +static int adx_wdt_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct adx_wdt *wdt = platform_get_drvdata(pdev);
> +
> + if (wdt->wake)
> + adx_wdt_start(wdt);
> +
> + return 0;
> +}
> +
> +static struct dev_pm_ops adx_wdt_pm_ops = {
> + .suspend = adx_wdt_suspend,
> + .resume = adx_wdt_resume,
> +};
> +
> +# define ADX_WDT_PM_OPS (&adx_wdt_pm_ops)
> +#else
> +# define ADX_WDT_PM_OPS NULL
> +#endif
> +
> +static struct platform_driver adx_wdt_driver = {
> + .probe = adx_wdt_probe,
> + .remove = __devexit_p(adx_wdt_remove),
> + .shutdown = adx_wdt_shutdown,
> + .driver = {
> + .name = WATCHDOG_NAME,
> + .owner = THIS_MODULE,
> + .pm = ADX_WDT_PM_OPS,
> + },
> +};
> +
> +static int __init adx_wdt_init(void)
> +{
> + return platform_driver_register(&adx_wdt_driver);
> +}
> +
> +static void __exit adx_wdt_exit(void)
> +{
> + platform_driver_unregister(&adx_wdt_driver);
> +}
> +
> +module_init(adx_wdt_init);
> +module_exit(adx_wdt_exit);
> +
> +MODULE_DESCRIPTION("Avionic Design Xanthos Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Thierry Reding <thierry.reding at avionic-design.de>");
> +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
> --
> tg: (74fca6a..) adx/watchdog (depends on: adx/master)
More information about the linux-arm-kernel
mailing list