All of lore.kernel.org
 help / color / mirror / Atom feed
From: viresh.kumar@st.com (Viresh KUMAR)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] Watchdog: Adding support for Watchdog found with ARM CortexA9
Date: Mon, 17 May 2010 15:26:03 +0530	[thread overview]
Message-ID: <4BF112B3.5000001@st.com> (raw)
In-Reply-To: <1274090044-10236-1-git-send-email-viresh.kumar@st.com>

On 5/17/2010 3:24 PM, Viresh KUMAR wrote:
> Watchdog details can be found in ARM Cortex A9 reference manual at:
> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407e/indix.html
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
> ---
>  drivers/watchdog/Kconfig        |    7 +
>  drivers/watchdog/Makefile       |    1 +
>  drivers/watchdog/cortexa9_wdt.c |  414 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 422 insertions(+), 0 deletions(-)
>  create mode 100755 drivers/watchdog/cortexa9_wdt.c

Please ignore this.
mode is incorrect, i will resend it.

> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 0bf5020..b5c0f6f 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -73,6 +73,13 @@ config WM8350_WATCHDOG
>  
>  # ARM Architecture
>  
> +config ARM_CORTEXA9_WATCHDOG
> +	tristate "ARM CortexA9 Watchdog"
> +	depends on ARM_AMBA
> +	help
> +	  Watchdog timer present with ARM Cortex A9 processor. This will reboot
> +	  your system when the timeout is reached.
> +
>  config AT91RM9200_WATCHDOG
>  	tristate "AT91RM9200 watchdog"
>  	depends on ARCH_AT91RM9200
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 5e3cb95..7704efc 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>  # ALPHA Architecture
>  
>  # ARM Architecture
> +obj-$(CONFIG_ARM_CORTEXA9_WATCHDOG) += cortexa9_wdt.o
>  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
>  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
>  obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
> diff --git a/drivers/watchdog/cortexa9_wdt.c b/drivers/watchdog/cortexa9_wdt.c
> new file mode 100755
> index 0000000..f50ec64
> --- /dev/null
> +++ b/drivers/watchdog/cortexa9_wdt.c
> @@ -0,0 +1,414 @@
> +/*
> + * drivers/char/watchdog/cortexa9-wdt.c
> + *
> + * Watchdog driver for watchdog module present with ARM CORTEX A9
> + *
> + * Copyright (C) 2010 ST Microelectronics
> + * Viresh Kumar<viresh.kumar@st.com>
> + *
> + * 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/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/watchdog.h>
> +
> +/* default timeout in seconds */
> +#define DEFAULT_TIMEOUT		60
> +
> +#define MODULE_NAME		"cortexa9-wdt"
> +
> +/* watchdog register offsets and masks */
> +#define WDTLOAD			0x000
> +	#define LOAD_MIN	0x00000000
> +	#define LOAD_MAX	0xFFFFFFFF
> +#define WDTCOUNTER		0x004
> +#define WDTCONTROL		0x008
> +	/* control register masks */
> +	#define WDT_ENB		(1 << 0)
> +	#define WDT_ARLD_ENB	(1 << 1)	/* Autoreload enable */
> +	#define WDT_IRQ_ENB	(1 << 2)
> +	#define WDT_MODE	(1 << 3)
> +	#define WDT_PRESCALE(x)	((x) << 8)
> +	#define PRESCALE_MIN	0x00
> +	#define PRESCALE_MAX	0xFF
> +#define WDTINTSTS		0x00C
> +	#define INT_MASK	0x1
> +#define WDTRSTSTS		0x010
> +	#define RST_MASK	0x1
> +#define WDTDISABLE		0x014
> +	#define DISABLE_VAL1	0x12345678
> +	#define DISABLE_VAL2	0x87654321
> +
> +/**
> + * struct cortexa9_wdt: cortex a9 wdt device structure
> + *
> + * lock: spin lock protecting dev structure and io access
> + * base: base address of wdt
> + * clk: clock structure of wdt
> + * pdev: platform device structure of wdt
> + * boot_status: boot time status of wdt
> + * status: current status of wdt
> + * prescale: prescale value to be set for current timeout
> + * load_val: load value to be set for current timeout
> + * timeout: current programmed timeout
> + */
> +struct cortexa9_wdt {
> +	spinlock_t			lock;
> +	void __iomem			*base;
> +	struct clk			*clk;
> +	struct platform_device		*pdev;
> +	unsigned long			boot_status;
> +	unsigned long			status;
> +	#define WDT_BUSY		0
> +	#define WDT_CAN_BE_CLOSED	1
> +	unsigned int			prescale;
> +	unsigned int			load_val;
> +	unsigned int			timeout;
> +};
> +
> +/* local variables */
> +static struct cortexa9_wdt *wdt;
> +static int nowayout = WATCHDOG_NOWAYOUT;
> +
> +/* binary search */
> +static inline void bsearch(u32 *var, u32 var_start, u32 var_end,
> +		const u64 param, const u32 timeout, u32 rate)
> +{
> +	u64 tmp = 0;
> +
> +	/* get the lowest var value that can satisfy our requirement */
> +	while (var_start < var_end) {
> +		tmp = var_start;
> +		tmp += var_end;
> +		tmp = div_u64(tmp, 2);
> +		if (timeout > div_u64((param + 1) * (tmp + 1), rate)) {
> +			if (var_start == tmp)
> +				break;
> +			else
> +				var_start = tmp;
> +		} else {
> +			if (var_end == tmp)
> +				break;
> +			else
> +				var_end = tmp;
> +		}
> +	}
> +	*var = tmp;
> +}
> +
> +/*
> + * This routine finds the most appropriate prescale and load value for input
> + * timout value
> + */
> +static void wdt_config(unsigned int timeout)
> +{
> +	unsigned int psc, rate;
> +	u64 load = 0;
> +
> +	rate = clk_get_rate(wdt->clk);
> +
> +	/* get appropriate value of psc and load */
> +	bsearch(&psc, PRESCALE_MIN, PRESCALE_MAX, LOAD_MAX, timeout, rate);
> +	bsearch((u32 *)&load, LOAD_MIN, LOAD_MAX, psc, timeout, rate);
> +
> +	spin_lock(&wdt->lock);
> +	wdt->prescale = psc;
> +	wdt->load_val = load;
> +
> +	/* roundup timeout to closest positive integer value */
> +	wdt->timeout = div_u64((psc + 1) * (load + 1) + (rate / 2), rate);
> +	spin_unlock(&wdt->lock);
> +}
> +
> +static void wdt_enable(void)
> +{
> +	u32 val;
> +
> +	spin_lock(&wdt->lock);
> +
> +	writel(wdt->load_val, wdt->base + WDTLOAD);
> +	val = WDT_MODE | WDT_PRESCALE(wdt->prescale) | WDT_ENB;
> +	writel(val, wdt->base + WDTCONTROL);
> +
> +	spin_unlock(&wdt->lock);
> +}
> +
> +static void wdt_disable(void)
> +{
> +	spin_lock(&wdt->lock);
> +
> +	/* firstly we need to put watchdog in timer mode */
> +	writel(DISABLE_VAL1, wdt->base + WDTDISABLE);
> +	writel(DISABLE_VAL2, wdt->base + WDTDISABLE);
> +
> +	writel(0, wdt->base + WDTCONTROL);
> +	writel(INT_MASK, wdt->base + WDTINTSTS);
> +	writel(RST_MASK, wdt->base + WDTRSTSTS);
> +
> +	spin_unlock(&wdt->lock);
> +}
> +
> +static ssize_t cortexa9_wdt_write(struct file *file, const char *data,
> +		size_t len, loff_t *ppos)
> +{
> +	if (len) {
> +		if (!nowayout) {
> +			size_t i;
> +
> +			clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
> +
> +			for (i = 0; i != len; i++) {
> +				char c;
> +
> +				if (get_user(c, data + i))
> +					return -EFAULT;
> +				if (c == 'V') {
> +					set_bit(WDT_CAN_BE_CLOSED,
> +							&wdt->status);
> +					break;
> +				}
> +			}
> +		}
> +		wdt_enable();
> +	}
> +	return len;
> +}
> +
> +static const struct watchdog_info ident = {
> +	.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
> +		WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
> +	.identity = MODULE_NAME,
> +};
> +
> +static long cortexa9_wdt_ioctl(struct file *file, unsigned int cmd,
> +		unsigned long arg)
> +{
> +	int ret = -ENOTTY;
> +	int timeout;
> +
> +	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(wdt->boot_status, (int *)arg);
> +		break;
> +
> +	case WDIOC_KEEPALIVE:
> +		wdt_enable();
> +		ret = 0;
> +		break;
> +
> +	case WDIOC_SETTIMEOUT:
> +		ret = get_user(timeout, (int *)arg);
> +		if (ret)
> +			break;
> +
> +		wdt_config(timeout);
> +
> +		wdt_enable();
> +		/* Fall through */
> +
> +	case WDIOC_GETTIMEOUT:
> +		ret = put_user(wdt->timeout, (int *)arg);
> +		break;
> +	}
> +	return ret;
> +}
> +
> +static int cortexa9_wdt_open(struct inode *inode, struct file *file)
> +{
> +	int ret = 0;
> +
> +	if (test_and_set_bit(WDT_BUSY, &wdt->status))
> +		return -EBUSY;
> +
> +	ret = clk_enable(wdt->clk);
> +	if (ret) {
> +		dev_err(&wdt->pdev->dev, "clock enable fail");
> +		goto err;
> +	}
> +
> +	wdt_enable();
> +
> +	/* can not be closed, once enabled */
> +	clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
> +	return nonseekable_open(inode, file);
> +
> +err:
> +	clear_bit(WDT_BUSY, &wdt->status);
> +	return ret;
> +}
> +
> +static int cortexa9_wdt_release(struct inode *inode, struct file *file)
> +{
> +	if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
> +		clear_bit(WDT_BUSY, &wdt->status);
> +		dev_warn(&wdt->pdev->dev, "Device closed unexpectedly\n");
> +		return 0;
> +	}
> +
> +	wdt_disable();
> +	clk_disable(wdt->clk);
> +	clear_bit(WDT_BUSY, &wdt->status);
> +
> +	return 0;
> +}
> +
> +static const struct file_operations cortexa9_wdt_fops = {
> +	.owner = THIS_MODULE,
> +	.llseek = no_llseek,
> +	.write = cortexa9_wdt_write,
> +	.unlocked_ioctl = cortexa9_wdt_ioctl,
> +	.open = cortexa9_wdt_open,
> +	.release = cortexa9_wdt_release,
> +};
> +
> +static struct miscdevice cortexa9_wdt_miscdev = {
> +	.minor = WATCHDOG_MINOR,
> +	.name = "watchdog",
> +	.fops = &cortexa9_wdt_fops,
> +};
> +
> +static int __devinit cortexa9_wdt_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct resource *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -ENOENT;
> +		dev_warn(&pdev->dev, "Memory resource not defined\n");
> +		goto err;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
> +		dev_warn(&pdev->dev, "Failed to get memory region resource\n");
> +		ret = -ENOENT;
> +		goto err;
> +	}
> +
> +	wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
> +	if (!wdt) {
> +		dev_warn(&pdev->dev, "Kzalloc failed\n");
> +		ret = -ENOMEM;
> +		goto err_kzalloc;
> +	}
> +
> +	wdt->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(wdt->clk)) {
> +		dev_warn(&pdev->dev, "Clock not found\n");
> +		ret = PTR_ERR(wdt->clk);
> +		goto err_clk_get;
> +	}
> +
> +	wdt->base = ioremap(res->start, resource_size(res));
> +	if (!wdt->base) {
> +		ret = -ENOMEM;
> +		dev_warn(&pdev->dev, "ioremap fail\n");
> +		goto err_ioremap;
> +	}
> +
> +	spin_lock_init(&wdt->lock);
> +	/* This checks if system booted after watchdog reset or not */
> +	ret = clk_enable(wdt->clk);
> +	if (!ret) {
> +		wdt->boot_status = (readl(wdt->base + WDTRSTSTS) & RST_MASK) ?
> +			WDIOF_CARDRESET : 0;
> +		wdt_disable();
> +		clk_disable(wdt->clk);
> +	}
> +
> +	wdt->pdev = pdev;
> +	wdt_config(DEFAULT_TIMEOUT);
> +
> +	ret = misc_register(&cortexa9_wdt_miscdev);
> +	if (ret < 0) {
> +		dev_warn(&pdev->dev, "cannot register misc device\n");
> +		goto err_misc_register;
> +	}
> +
> +	return 0;
> +
> +err_misc_register:
> +	iounmap(wdt->base);
> +err_ioremap:
> +	clk_put(wdt->clk);
> +err_clk_get:
> +	kfree(wdt);
> +	wdt = NULL;
> +err_kzalloc:
> +	release_mem_region(res->start, resource_size(res));
> +err:
> +	dev_err(&pdev->dev, "Probe Failed!!!\n");
> +	return ret;
> +}
> +
> +static int __devexit cortexa9_wdt_remove(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +
> +	misc_deregister(&cortexa9_wdt_miscdev);
> +	iounmap(wdt->base);
> +	clk_put(wdt->clk);
> +	kfree(wdt);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res)
> +		release_mem_region(res->start, resource_size(res));
> +
> +	return 0;
> +}
> +
> +static struct platform_driver cortexa9_wdt_driver = {
> +	.driver = {
> +		.name = MODULE_NAME,
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe = cortexa9_wdt_probe,
> +	.remove = __devexit_p(cortexa9_wdt_remove),
> +};
> +
> +static int __init cortexa9_wdt_init(void)
> +{
> +	return platform_driver_register(&cortexa9_wdt_driver);
> +}
> +module_init(cortexa9_wdt_init);
> +
> +static void __exit cortexa9_wdt_exit(void)
> +{
> +	platform_driver_unregister(&cortexa9_wdt_driver);
> +}
> +module_exit(cortexa9_wdt_exit);
> +
> +module_param(nowayout, int, 0);
> +MODULE_PARM_DESC(nowayout,
> +		"Set to 1 to keep watchdog running after device release");
> +
> +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
> +MODULE_DESCRIPTION("ARM CORTEX A9 Watchdog Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

  reply	other threads:[~2010-05-17  9:56 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-05-17  9:54 [PATCH] Watchdog: Adding support for Watchdog found with ARM CortexA9 Viresh KUMAR
2010-05-17  9:56 ` Viresh KUMAR [this message]
2010-05-17 10:44 ` Jassi Brar
2010-05-17 11:24   ` Viresh KUMAR
2010-05-18  9:25     ` srinidhi kasagar
2010-05-18 13:20       ` George G. Davis
2010-05-19  4:02         ` Viresh KUMAR
2010-05-19  5:17           ` srinidhi kasagar
2010-05-19  6:44             ` Viresh KUMAR
2010-09-22 15:36             ` mpcore_wdt A9 questions John Linn
2010-09-22 15:56               ` George G. Davis
2010-05-19  5:32           ` [PATCH] Watchdog: Adding support for Watchdog found with ARM CortexA9 Shilimkar, Santosh
2010-05-19  6:45             ` Viresh KUMAR

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4BF112B3.5000001@st.com \
    --to=viresh.kumar@st.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.