linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] watchdog/imx2+: add support for pretimeout interrupt functionality
@ 2012-05-31 10:37 Oskar Schirmer
  2012-07-03  9:10 ` [PATCH] watchdog/imx2+: add support for pretimeout interrupt Oskar Schirmer
  2012-09-11 10:00 ` [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
  0 siblings, 2 replies; 9+ messages in thread
From: Oskar Schirmer @ 2012-05-31 10:37 UTC (permalink / raw)
  To: linux-kernel; +Cc: Oskar Schirmer, Wim Van Sebroeck, Wolfram Sang

This watchdog device provides pretimeout facilities:
Set some timeout value and get informed about imminent
watchdog activity thru interrupt.

Allow user to wait for this interrupt thru poll(2),
and to clear it thru read(2).

Signed-off-by: Oskar Schirmer <oskar@scara.com>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: Wolfram Sang <w.sang@pengutronix.de>
---
 drivers/watchdog/imx2_wdt.c |  129 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 128 insertions(+), 1 deletions(-)

diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index bcfab2b..09172c8 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -21,11 +21,16 @@
  */
 
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
 #include <linux/watchdog.h>
 #include <linux/clk.h>
 #include <linux/fs.h>
@@ -49,6 +54,11 @@
 #define IMX2_WDT_WRSR		0x04		/* Reset Status Register */
 #define IMX2_WDT_WRSR_TOUT	(1 << 1)	/* -> Reset due to Timeout */
 
+#define IMX2_WDT_WICT		0x06		/* Interrupt Control Reg */
+#define IMX2_WDT_WICT_WIE	(1 << 15)	/* -> Interrupt Enable */
+#define IMX2_WDT_WICT_WTIS	(1 << 14)	/* -> Timer Interrupt Status */
+#define IMX2_WDT_WICT_WICT	(0xFF << 0)	/* -> Interrupt Count Timeout */
+
 #define IMX2_WDT_MAX_TIME	128
 #define IMX2_WDT_DEFAULT_TIME	60		/* in seconds */
 
@@ -64,6 +74,11 @@ static struct {
 	unsigned timeout;
 	unsigned long status;
 	struct timer_list timer;	/* Pings the watchdog when closed */
+	int irq;
+	spinlock_t read_lock;
+	wait_queue_head_t read_q;
+	unsigned char pretimer_once;
+	unsigned char pretimer_data;
 } imx2_wdt;
 
 static struct miscdevice imx2_wdt_miscdev;
@@ -81,7 +96,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
 
 static const struct watchdog_info imx2_wdt_info = {
 	.identity = "imx2+ watchdog",
-	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+		WDIOF_PRETIMEOUT,
 };
 
 static inline void imx2_wdt_setup(void)
@@ -148,6 +164,28 @@ static void imx2_wdt_set_timeout(int new_timeout)
 	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
 }
 
+static void imx2_wdt_set_pretimeout(int new_pretimeout)
+{
+	u16 val = new_pretimeout;
+
+	if (val > 0)
+		val = (val << 1) | IMX2_WDT_WICT_WIE;
+	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+}
+
+static void imx2_wdt_interrupt(int irq, void *dev_id)
+{
+	u16 val;
+	spin_lock(&imx2_wdt.read_lock);
+	val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+	if (val & IMX2_WDT_WICT_WTIS) {
+		__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+		imx2_wdt.pretimer_data = 1;
+	}
+	wake_up_interruptible(&imx2_wdt.read_q);
+	spin_unlock(&imx2_wdt.read_lock);
+}
+
 static int imx2_wdt_open(struct inode *inode, struct file *file)
 {
 	if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
@@ -210,6 +248,26 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
 	case WDIOC_GETTIMEOUT:
 		return put_user(imx2_wdt.timeout, p);
 
+	case WDIOC_SETPRETIMEOUT:
+		if (get_user(new_value, p))
+			return -EFAULT;
+		if ((new_value < 0) || (new_value >= IMX2_WDT_MAX_TIME))
+			return -EINVAL;
+		if (imx2_wdt.irq < 0)
+			return -EINVAL;
+		if (imx2_wdt.pretimer_once)
+			return -EPERM;
+		imx2_wdt.pretimer_once = 1;
+		imx2_wdt_set_pretimeout(new_value);
+
+	case WDIOC_GETPRETIMEOUT:
+		val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+		if (val & IMX2_WDT_WICT_WIE)
+			val = (val & IMX2_WDT_WICT_WICT) >> 1;
+		else
+			val = 0;
+		return put_user(val, p);
+
 	default:
 		return -ENOTTY;
 	}
@@ -237,6 +295,59 @@ static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
 	return len;
 }
 
+static ssize_t imx2_wdt_read(struct file *file, char __user *data,
+				size_t count, loff_t *ppos)
+{
+	wait_queue_t wait;
+	unsigned long flags;
+
+	if (count <= 0)
+		return 0;
+
+	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+	while (!imx2_wdt.pretimer_data) {
+		if (file->f_flags & O_NONBLOCK) {
+			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+			return -EAGAIN;
+		}
+
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&imx2_wdt.read_q, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+		schedule();
+		spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+		remove_wait_queue(&imx2_wdt.read_q, &wait);
+
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
+
+	imx2_wdt.pretimer_data = 0;
+	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+	if (copy_to_user(data, &imx2_wdt.pretimer_data, 1))
+		return -EFAULT;
+
+	return 1;
+}
+
+static unsigned int imx2_wdt_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	unsigned long flags;
+
+	poll_wait(file, &imx2_wdt.read_q, wait);
+
+	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+	if (imx2_wdt.pretimer_data)
+		mask |= POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+
+	return mask;
+}
+
 static const struct file_operations imx2_wdt_fops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -244,6 +355,8 @@ static const struct file_operations imx2_wdt_fops = {
 	.open = imx2_wdt_open,
 	.release = imx2_wdt_close,
 	.write = imx2_wdt_write,
+	.read = imx2_wdt_read,
+	.poll = imx2_wdt_poll,
 };
 
 static struct miscdevice imx2_wdt_miscdev = {
@@ -282,6 +395,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 
 	setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
 
+	init_waitqueue_head(&imx2_wdt.read_q);
+	spin_lock_init(&imx2_wdt.read_lock);
+	imx2_wdt.irq = platform_get_irq(pdev, 0);
+	if (imx2_wdt.irq >= 0) {
+		ret = request_irq(imx2_wdt.irq, imx2_wdt_interrupt,
+					0, "watchdog", pdev);
+		if (ret)
+			goto fail;
+	}
+
 	imx2_wdt_miscdev.parent = &pdev->dev;
 	ret = misc_register(&imx2_wdt_miscdev);
 	if (ret)
@@ -294,6 +417,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 
 fail:
 	imx2_wdt_miscdev.parent = NULL;
+	if (imx2_wdt.irq >= 0)
+		free_irq(imx2_wdt.irq, pdev);
 	clk_put(imx2_wdt.clk);
 	return ret;
 }
@@ -301,6 +426,8 @@ fail:
 static int __exit imx2_wdt_remove(struct platform_device *pdev)
 {
 	misc_deregister(&imx2_wdt_miscdev);
+	if (imx2_wdt.irq >= 0)
+		free_irq(imx2_wdt.irq, pdev);
 
 	if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
 		del_timer_sync(&imx2_wdt.timer);
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH] watchdog/imx2+: add support for pretimeout interrupt
  2012-05-31 10:37 [PATCH] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
@ 2012-07-03  9:10 ` Oskar Schirmer
  2012-09-05  7:23   ` Oskar Schirmer
  2012-09-11 10:00 ` [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
  1 sibling, 1 reply; 9+ messages in thread
From: Oskar Schirmer @ 2012-07-03  9:10 UTC (permalink / raw)
  To: Wim Van Sebroeck
  Cc: linux-kernel, Oskar Schirmer, Wim Van Sebroeck, Wolfram Sang,
	Andrew Morton

This watchdog device provides pretimeout facilities:
Set some timeout value and get informed about imminent
watchdog activity thru interrupt.

Allow user to wait for this interrupt thru poll(2),
and to clear it thru read(2).

Signed-off-by: Oskar Schirmer <oskar@scara.com>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: Wolfram Sang <w.sang@pengutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
---
Resubmitted as it probably got lost on first attempt (2012/05/31)

 drivers/watchdog/imx2_wdt.c |  129 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 128 insertions(+), 1 deletions(-)

diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index bcfab2b..09172c8 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -21,11 +21,16 @@
  */
 
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
 #include <linux/watchdog.h>
 #include <linux/clk.h>
 #include <linux/fs.h>
@@ -49,6 +54,11 @@
 #define IMX2_WDT_WRSR		0x04		/* Reset Status Register */
 #define IMX2_WDT_WRSR_TOUT	(1 << 1)	/* -> Reset due to Timeout */
 
+#define IMX2_WDT_WICT		0x06		/* Interrupt Control Reg */
+#define IMX2_WDT_WICT_WIE	(1 << 15)	/* -> Interrupt Enable */
+#define IMX2_WDT_WICT_WTIS	(1 << 14)	/* -> Timer Interrupt Status */
+#define IMX2_WDT_WICT_WICT	(0xFF << 0)	/* -> Interrupt Count Timeout */
+
 #define IMX2_WDT_MAX_TIME	128
 #define IMX2_WDT_DEFAULT_TIME	60		/* in seconds */
 
@@ -64,6 +74,11 @@ static struct {
 	unsigned timeout;
 	unsigned long status;
 	struct timer_list timer;	/* Pings the watchdog when closed */
+	int irq;
+	spinlock_t read_lock;
+	wait_queue_head_t read_q;
+	unsigned char pretimer_once;
+	unsigned char pretimer_data;
 } imx2_wdt;
 
 static struct miscdevice imx2_wdt_miscdev;
@@ -81,7 +96,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
 
 static const struct watchdog_info imx2_wdt_info = {
 	.identity = "imx2+ watchdog",
-	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+		WDIOF_PRETIMEOUT,
 };
 
 static inline void imx2_wdt_setup(void)
@@ -148,6 +164,28 @@ static void imx2_wdt_set_timeout(int new_timeout)
 	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
 }
 
+static void imx2_wdt_set_pretimeout(int new_pretimeout)
+{
+	u16 val = new_pretimeout;
+
+	if (val > 0)
+		val = (val << 1) | IMX2_WDT_WICT_WIE;
+	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+}
+
+static void imx2_wdt_interrupt(int irq, void *dev_id)
+{
+	u16 val;
+	spin_lock(&imx2_wdt.read_lock);
+	val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+	if (val & IMX2_WDT_WICT_WTIS) {
+		__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+		imx2_wdt.pretimer_data = 1;
+	}
+	wake_up_interruptible(&imx2_wdt.read_q);
+	spin_unlock(&imx2_wdt.read_lock);
+}
+
 static int imx2_wdt_open(struct inode *inode, struct file *file)
 {
 	if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
@@ -210,6 +248,26 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
 	case WDIOC_GETTIMEOUT:
 		return put_user(imx2_wdt.timeout, p);
 
+	case WDIOC_SETPRETIMEOUT:
+		if (get_user(new_value, p))
+			return -EFAULT;
+		if ((new_value < 0) || (new_value >= IMX2_WDT_MAX_TIME))
+			return -EINVAL;
+		if (imx2_wdt.irq < 0)
+			return -EINVAL;
+		if (imx2_wdt.pretimer_once)
+			return -EPERM;
+		imx2_wdt.pretimer_once = 1;
+		imx2_wdt_set_pretimeout(new_value);
+
+	case WDIOC_GETPRETIMEOUT:
+		val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+		if (val & IMX2_WDT_WICT_WIE)
+			val = (val & IMX2_WDT_WICT_WICT) >> 1;
+		else
+			val = 0;
+		return put_user(val, p);
+
 	default:
 		return -ENOTTY;
 	}
@@ -237,6 +295,59 @@ static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
 	return len;
 }
 
+static ssize_t imx2_wdt_read(struct file *file, char __user *data,
+				size_t count, loff_t *ppos)
+{
+	wait_queue_t wait;
+	unsigned long flags;
+
+	if (count <= 0)
+		return 0;
+
+	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+	while (!imx2_wdt.pretimer_data) {
+		if (file->f_flags & O_NONBLOCK) {
+			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+			return -EAGAIN;
+		}
+
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&imx2_wdt.read_q, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+		schedule();
+		spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+		remove_wait_queue(&imx2_wdt.read_q, &wait);
+
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
+
+	imx2_wdt.pretimer_data = 0;
+	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+	if (copy_to_user(data, &imx2_wdt.pretimer_data, 1))
+		return -EFAULT;
+
+	return 1;
+}
+
+static unsigned int imx2_wdt_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	unsigned long flags;
+
+	poll_wait(file, &imx2_wdt.read_q, wait);
+
+	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+	if (imx2_wdt.pretimer_data)
+		mask |= POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+
+	return mask;
+}
+
 static const struct file_operations imx2_wdt_fops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -244,6 +355,8 @@ static const struct file_operations imx2_wdt_fops = {
 	.open = imx2_wdt_open,
 	.release = imx2_wdt_close,
 	.write = imx2_wdt_write,
+	.read = imx2_wdt_read,
+	.poll = imx2_wdt_poll,
 };
 
 static struct miscdevice imx2_wdt_miscdev = {
@@ -282,6 +395,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 
 	setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
 
+	init_waitqueue_head(&imx2_wdt.read_q);
+	spin_lock_init(&imx2_wdt.read_lock);
+	imx2_wdt.irq = platform_get_irq(pdev, 0);
+	if (imx2_wdt.irq >= 0) {
+		ret = request_irq(imx2_wdt.irq, imx2_wdt_interrupt,
+					0, "watchdog", pdev);
+		if (ret)
+			goto fail;
+	}
+
 	imx2_wdt_miscdev.parent = &pdev->dev;
 	ret = misc_register(&imx2_wdt_miscdev);
 	if (ret)
@@ -294,6 +417,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 
 fail:
 	imx2_wdt_miscdev.parent = NULL;
+	if (imx2_wdt.irq >= 0)
+		free_irq(imx2_wdt.irq, pdev);
 	clk_put(imx2_wdt.clk);
 	return ret;
 }
@@ -301,6 +426,8 @@ fail:
 static int __exit imx2_wdt_remove(struct platform_device *pdev)
 {
 	misc_deregister(&imx2_wdt_miscdev);
+	if (imx2_wdt.irq >= 0)
+		free_irq(imx2_wdt.irq, pdev);
 
 	if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
 		del_timer_sync(&imx2_wdt.timer);
-- 
1.7.5.4



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] watchdog/imx2+: add support for pretimeout interrupt
  2012-07-03  9:10 ` [PATCH] watchdog/imx2+: add support for pretimeout interrupt Oskar Schirmer
@ 2012-09-05  7:23   ` Oskar Schirmer
  0 siblings, 0 replies; 9+ messages in thread
From: Oskar Schirmer @ 2012-09-05  7:23 UTC (permalink / raw)
  To: Wim Van Sebroeck; +Cc: linux-kernel, Wolfram Sang,  Andrew Morton

Hi Wim,

On Tue, Jul 03, 2012 at 09:10:08 +0000, Oskar Schirmer wrote:
> This watchdog device provides pretimeout facilities:
> Set some timeout value and get informed about imminent
> watchdog activity thru interrupt.

sent this patch a while ago, as yet unprocessed.
Could You give it an ack?
If not so, please let me know reasons for it, so I can rework it.

thanks a lot,
  Oskar

> Allow user to wait for this interrupt thru poll(2),
> and to clear it thru read(2).
> 
> Signed-off-by: Oskar Schirmer <oskar@scara.com>
> Cc: Wim Van Sebroeck <wim@iguana.be>
> Cc: Wolfram Sang <w.sang@pengutronix.de>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> ---
> Resubmitted as it probably got lost on first attempt (2012/05/31)
> 
>  drivers/watchdog/imx2_wdt.c |  129 ++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 128 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
> index bcfab2b..09172c8 100644
> --- a/drivers/watchdog/imx2_wdt.c
> +++ b/drivers/watchdog/imx2_wdt.c
> @@ -21,11 +21,16 @@
>   */
>  
>  #include <linux/init.h>
> +#include <linux/interrupt.h>
>  #include <linux/kernel.h>
>  #include <linux/miscdevice.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
>  #include <linux/platform_device.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/wait.h>
>  #include <linux/watchdog.h>
>  #include <linux/clk.h>
>  #include <linux/fs.h>
> @@ -49,6 +54,11 @@
>  #define IMX2_WDT_WRSR		0x04		/* Reset Status Register */
>  #define IMX2_WDT_WRSR_TOUT	(1 << 1)	/* -> Reset due to Timeout */
>  
> +#define IMX2_WDT_WICT		0x06		/* Interrupt Control Reg */
> +#define IMX2_WDT_WICT_WIE	(1 << 15)	/* -> Interrupt Enable */
> +#define IMX2_WDT_WICT_WTIS	(1 << 14)	/* -> Timer Interrupt Status */
> +#define IMX2_WDT_WICT_WICT	(0xFF << 0)	/* -> Interrupt Count Timeout */
> +
>  #define IMX2_WDT_MAX_TIME	128
>  #define IMX2_WDT_DEFAULT_TIME	60		/* in seconds */
>  
> @@ -64,6 +74,11 @@ static struct {
>  	unsigned timeout;
>  	unsigned long status;
>  	struct timer_list timer;	/* Pings the watchdog when closed */
> +	int irq;
> +	spinlock_t read_lock;
> +	wait_queue_head_t read_q;
> +	unsigned char pretimer_once;
> +	unsigned char pretimer_data;
>  } imx2_wdt;
>  
>  static struct miscdevice imx2_wdt_miscdev;
> @@ -81,7 +96,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
>  
>  static const struct watchdog_info imx2_wdt_info = {
>  	.identity = "imx2+ watchdog",
> -	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
> +	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
> +		WDIOF_PRETIMEOUT,
>  };
>  
>  static inline void imx2_wdt_setup(void)
> @@ -148,6 +164,28 @@ static void imx2_wdt_set_timeout(int new_timeout)
>  	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
>  }
>  
> +static void imx2_wdt_set_pretimeout(int new_pretimeout)
> +{
> +	u16 val = new_pretimeout;
> +
> +	if (val > 0)
> +		val = (val << 1) | IMX2_WDT_WICT_WIE;
> +	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
> +}
> +
> +static void imx2_wdt_interrupt(int irq, void *dev_id)
> +{
> +	u16 val;
> +	spin_lock(&imx2_wdt.read_lock);
> +	val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
> +	if (val & IMX2_WDT_WICT_WTIS) {
> +		__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
> +		imx2_wdt.pretimer_data = 1;
> +	}
> +	wake_up_interruptible(&imx2_wdt.read_q);
> +	spin_unlock(&imx2_wdt.read_lock);
> +}
> +
>  static int imx2_wdt_open(struct inode *inode, struct file *file)
>  {
>  	if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
> @@ -210,6 +248,26 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
>  	case WDIOC_GETTIMEOUT:
>  		return put_user(imx2_wdt.timeout, p);
>  
> +	case WDIOC_SETPRETIMEOUT:
> +		if (get_user(new_value, p))
> +			return -EFAULT;
> +		if ((new_value < 0) || (new_value >= IMX2_WDT_MAX_TIME))
> +			return -EINVAL;
> +		if (imx2_wdt.irq < 0)
> +			return -EINVAL;
> +		if (imx2_wdt.pretimer_once)
> +			return -EPERM;
> +		imx2_wdt.pretimer_once = 1;
> +		imx2_wdt_set_pretimeout(new_value);
> +
> +	case WDIOC_GETPRETIMEOUT:
> +		val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
> +		if (val & IMX2_WDT_WICT_WIE)
> +			val = (val & IMX2_WDT_WICT_WICT) >> 1;
> +		else
> +			val = 0;
> +		return put_user(val, p);
> +
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -237,6 +295,59 @@ static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
>  	return len;
>  }
>  
> +static ssize_t imx2_wdt_read(struct file *file, char __user *data,
> +				size_t count, loff_t *ppos)
> +{
> +	wait_queue_t wait;
> +	unsigned long flags;
> +
> +	if (count <= 0)
> +		return 0;
> +
> +	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
> +	while (!imx2_wdt.pretimer_data) {
> +		if (file->f_flags & O_NONBLOCK) {
> +			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
> +			return -EAGAIN;
> +		}
> +
> +		init_waitqueue_entry(&wait, current);
> +		add_wait_queue(&imx2_wdt.read_q, &wait);
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
> +		schedule();
> +		spin_lock_irqsave(&imx2_wdt.read_lock, flags);
> +		remove_wait_queue(&imx2_wdt.read_q, &wait);
> +
> +		if (signal_pending(current)) {
> +			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
> +			return -ERESTARTSYS;
> +		}
> +	}
> +
> +	imx2_wdt.pretimer_data = 0;
> +	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
> +	if (copy_to_user(data, &imx2_wdt.pretimer_data, 1))
> +		return -EFAULT;
> +
> +	return 1;
> +}
> +
> +static unsigned int imx2_wdt_poll(struct file *file, poll_table *wait)
> +{
> +	unsigned int mask = 0;
> +	unsigned long flags;
> +
> +	poll_wait(file, &imx2_wdt.read_q, wait);
> +
> +	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
> +	if (imx2_wdt.pretimer_data)
> +		mask |= POLLIN | POLLRDNORM;
> +	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
> +
> +	return mask;
> +}
> +
>  static const struct file_operations imx2_wdt_fops = {
>  	.owner = THIS_MODULE,
>  	.llseek = no_llseek,
> @@ -244,6 +355,8 @@ static const struct file_operations imx2_wdt_fops = {
>  	.open = imx2_wdt_open,
>  	.release = imx2_wdt_close,
>  	.write = imx2_wdt_write,
> +	.read = imx2_wdt_read,
> +	.poll = imx2_wdt_poll,
>  };
>  
>  static struct miscdevice imx2_wdt_miscdev = {
> @@ -282,6 +395,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
>  
>  	setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
>  
> +	init_waitqueue_head(&imx2_wdt.read_q);
> +	spin_lock_init(&imx2_wdt.read_lock);
> +	imx2_wdt.irq = platform_get_irq(pdev, 0);
> +	if (imx2_wdt.irq >= 0) {
> +		ret = request_irq(imx2_wdt.irq, imx2_wdt_interrupt,
> +					0, "watchdog", pdev);
> +		if (ret)
> +			goto fail;
> +	}
> +
>  	imx2_wdt_miscdev.parent = &pdev->dev;
>  	ret = misc_register(&imx2_wdt_miscdev);
>  	if (ret)
> @@ -294,6 +417,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
>  
>  fail:
>  	imx2_wdt_miscdev.parent = NULL;
> +	if (imx2_wdt.irq >= 0)
> +		free_irq(imx2_wdt.irq, pdev);
>  	clk_put(imx2_wdt.clk);
>  	return ret;
>  }
> @@ -301,6 +426,8 @@ fail:
>  static int __exit imx2_wdt_remove(struct platform_device *pdev)
>  {
>  	misc_deregister(&imx2_wdt_miscdev);
> +	if (imx2_wdt.irq >= 0)
> +		free_irq(imx2_wdt.irq, pdev);
>  
>  	if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
>  		del_timer_sync(&imx2_wdt.timer);
> -- 
> 1.7.5.4
> 
> 
> 

-- 
oskar schirmer
calsowstr 22 / 37085 göttingen / germany
tel +49 551 5315924
fax +49 551 5315925
mailto:oskar@scara.com, http://scara.com/~schirmer/o

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality
  2012-05-31 10:37 [PATCH] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
  2012-07-03  9:10 ` [PATCH] watchdog/imx2+: add support for pretimeout interrupt Oskar Schirmer
@ 2012-09-11 10:00 ` Oskar Schirmer
  2012-09-11 11:00   ` Wim Van Sebroeck
  2012-09-20 15:37   ` [PATCH v3] " Oskar Schirmer
  1 sibling, 2 replies; 9+ messages in thread
From: Oskar Schirmer @ 2012-09-11 10:00 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, Oskar Schirmer, Wim Van Sebroeck, Wolfram Sang,
	Sascha Hauer, Andrew Morton

This watchdog device provides pretimeout facilities:
Set some timeout value and get informed about imminent
watchdog activity thru interrupt.

Allow user to wait for this interrupt thru poll(2),
and to clear it thru read(2).

Signed-off-by: Oskar Schirmer <oskar@scara.com>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: Wolfram Sang <w.sang@pengutronix.de>
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
---
Switched to using devm_request_irq in favour of request_irq

 drivers/watchdog/imx2_wdt.c |  128 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 127 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index bcfab2b..2a39d7b 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -21,11 +21,16 @@
  */
 
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
 #include <linux/watchdog.h>
 #include <linux/clk.h>
 #include <linux/fs.h>
@@ -49,6 +54,11 @@
 #define IMX2_WDT_WRSR		0x04		/* Reset Status Register */
 #define IMX2_WDT_WRSR_TOUT	(1 << 1)	/* -> Reset due to Timeout */
 
+#define IMX2_WDT_WICT		0x06		/* Interrupt Control Reg */
+#define IMX2_WDT_WICT_WIE	(1 << 15)	/* -> Interrupt Enable */
+#define IMX2_WDT_WICT_WTIS	(1 << 14)	/* -> Timer Interrupt Status */
+#define IMX2_WDT_WICT_WICT	(0xFF << 0)	/* -> Interrupt Count Timeout */
+
 #define IMX2_WDT_MAX_TIME	128
 #define IMX2_WDT_DEFAULT_TIME	60		/* in seconds */
 
@@ -64,6 +74,11 @@ static struct {
 	unsigned timeout;
 	unsigned long status;
 	struct timer_list timer;	/* Pings the watchdog when closed */
+	int irq;
+	spinlock_t read_lock;
+	wait_queue_head_t read_q;
+	unsigned char pretimer_once;
+	unsigned char pretimer_data;
 } imx2_wdt;
 
 static struct miscdevice imx2_wdt_miscdev;
@@ -81,7 +96,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
 
 static const struct watchdog_info imx2_wdt_info = {
 	.identity = "imx2+ watchdog",
-	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+		WDIOF_PRETIMEOUT,
 };
 
 static inline void imx2_wdt_setup(void)
@@ -148,6 +164,31 @@ static void imx2_wdt_set_timeout(int new_timeout)
 	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
 }
 
+static void imx2_wdt_set_pretimeout(int new_pretimeout)
+{
+	u16 val = new_pretimeout;
+
+	if (val > 0)
+		val = (val << 1) | IMX2_WDT_WICT_WIE;
+	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+}
+
+static irqreturn_t imx2_wdt_interrupt(int irq, void *arg)
+{
+	u16 val;
+	spin_lock(&imx2_wdt.read_lock);
+	val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+	if (val & IMX2_WDT_WICT_WTIS) {
+		__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+		imx2_wdt.pretimer_data = 1;
+		wake_up_interruptible(&imx2_wdt.read_q);
+		spin_unlock(&imx2_wdt.read_lock);
+		return IRQ_HANDLED;
+	}
+	spin_unlock(&imx2_wdt.read_lock);
+	return IRQ_NONE;
+}
+
 static int imx2_wdt_open(struct inode *inode, struct file *file)
 {
 	if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
@@ -210,6 +251,26 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
 	case WDIOC_GETTIMEOUT:
 		return put_user(imx2_wdt.timeout, p);
 
+	case WDIOC_SETPRETIMEOUT:
+		if (get_user(new_value, p))
+			return -EFAULT;
+		if ((new_value < 0) || (new_value >= IMX2_WDT_MAX_TIME))
+			return -EINVAL;
+		if (imx2_wdt.irq < 0)
+			return -EINVAL;
+		if (imx2_wdt.pretimer_once)
+			return -EPERM;
+		imx2_wdt.pretimer_once = 1;
+		imx2_wdt_set_pretimeout(new_value);
+
+	case WDIOC_GETPRETIMEOUT:
+		val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+		if (val & IMX2_WDT_WICT_WIE)
+			val = (val & IMX2_WDT_WICT_WICT) >> 1;
+		else
+			val = 0;
+		return put_user(val, p);
+
 	default:
 		return -ENOTTY;
 	}
@@ -237,6 +298,59 @@ static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
 	return len;
 }
 
+static ssize_t imx2_wdt_read(struct file *file, char __user *data,
+				size_t count, loff_t *ppos)
+{
+	wait_queue_t wait;
+	unsigned long flags;
+
+	if (count <= 0)
+		return 0;
+
+	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+	while (!imx2_wdt.pretimer_data) {
+		if (file->f_flags & O_NONBLOCK) {
+			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+			return -EAGAIN;
+		}
+
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&imx2_wdt.read_q, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+		schedule();
+		spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+		remove_wait_queue(&imx2_wdt.read_q, &wait);
+
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
+
+	imx2_wdt.pretimer_data = 0;
+	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+	if (copy_to_user(data, &imx2_wdt.pretimer_data, 1))
+		return -EFAULT;
+
+	return 1;
+}
+
+static unsigned int imx2_wdt_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	unsigned long flags;
+
+	poll_wait(file, &imx2_wdt.read_q, wait);
+
+	spin_lock_irqsave(&imx2_wdt.read_lock, flags);
+	if (imx2_wdt.pretimer_data)
+		mask |= POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&imx2_wdt.read_lock, flags);
+
+	return mask;
+}
+
 static const struct file_operations imx2_wdt_fops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -244,6 +358,8 @@ static const struct file_operations imx2_wdt_fops = {
 	.open = imx2_wdt_open,
 	.release = imx2_wdt_close,
 	.write = imx2_wdt_write,
+	.read = imx2_wdt_read,
+	.poll = imx2_wdt_poll,
 };
 
 static struct miscdevice imx2_wdt_miscdev = {
@@ -282,6 +398,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 
 	setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
 
+	init_waitqueue_head(&imx2_wdt.read_q);
+	spin_lock_init(&imx2_wdt.read_lock);
+	imx2_wdt.irq = platform_get_irq(pdev, 0);
+	if (imx2_wdt.irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, imx2_wdt.irq,
+			imx2_wdt_interrupt, 0, "watchdog", pdev);
+		if (ret)
+			goto fail;
+	}
+
 	imx2_wdt_miscdev.parent = &pdev->dev;
 	ret = misc_register(&imx2_wdt_miscdev);
 	if (ret)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality
  2012-09-11 10:00 ` [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
@ 2012-09-11 11:00   ` Wim Van Sebroeck
  2012-09-11 13:39     ` Oskar Schirmer
  2012-09-20 15:37   ` [PATCH v3] " Oskar Schirmer
  1 sibling, 1 reply; 9+ messages in thread
From: Wim Van Sebroeck @ 2012-09-11 11:00 UTC (permalink / raw)
  To: Oskar Schirmer; +Cc: Sascha Hauer, linux-kernel, Wolfram Sang, Andrew Morton

Hi Oskar,

> This watchdog device provides pretimeout facilities:
> Set some timeout value and get informed about imminent
> watchdog activity thru interrupt.
> 
> Allow user to wait for this interrupt thru poll(2),
> and to clear it thru read(2).
> 
> Signed-off-by: Oskar Schirmer <oskar@scara.com>
> Cc: Wim Van Sebroeck <wim@iguana.be>
> Cc: Wolfram Sang <w.sang@pengutronix.de>
> Cc: Sascha Hauer <kernel@pengutronix.de>
> Cc: Andrew Morton <akpm@linux-foundation.org>

I have a problem with the read and poll.
We had "read()" in the past so we have a possible issue here (different function but userspace apps could still use the old read)...
Can you explain what the read and poll does and where you want to use it.

Kind regards,
Wim.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality
  2012-09-11 11:00   ` Wim Van Sebroeck
@ 2012-09-11 13:39     ` Oskar Schirmer
  0 siblings, 0 replies; 9+ messages in thread
From: Oskar Schirmer @ 2012-09-11 13:39 UTC (permalink / raw)
  To: Wim Van Sebroeck
  Cc: Oskar Schirmer, Sascha Hauer, linux-kernel, Wolfram Sang,  Andrew Morton

Hi Wim,

On Tue, Sep 11, 2012 at 13:00:21 +0200, Wim Van Sebroeck wrote:
> Hi Oskar,
> 
> > This watchdog device provides pretimeout facilities:
> > Set some timeout value and get informed about imminent
> > watchdog activity thru interrupt.
> > 
> > Allow user to wait for this interrupt thru poll(2),
> > and to clear it thru read(2).
> > 
> > Signed-off-by: Oskar Schirmer <oskar@scara.com>
> > Cc: Wim Van Sebroeck <wim@iguana.be>
> > Cc: Wolfram Sang <w.sang@pengutronix.de>
> > Cc: Sascha Hauer <kernel@pengutronix.de>
> > Cc: Andrew Morton <akpm@linux-foundation.org>
> 
> I have a problem with the read and poll.
> We had "read()" in the past so we have a possible issue here (different function but userspace apps could still use the old read)...
> Can you explain what the read and poll does and where you want to use it.

To wait for some hardware event without blocking,
poll is used, primarily in places where it's about
data transmission. Ioctl would be an alternative
for cases without data transmission, but poll is
more generic.

Events causing pollin to return usually provide
data for read access, or, where no data is involved,
at least allow to clear the event thru read.

Now, checking current kernel for precedent, there
is only one driver that comes with pretimeouts: ipmi_watchdog.
It allows to set/get pretimeout, wait for it thru poll,
and clear it's occurence thru read.

So the obvious approach is to have a similar api for
the imx watchdog, as well, to avoid conflicting designs
right away.

Documentation does introduce set/get pretimeout, but
does not define how to detect and clear events.
Actually it does not define any meaning of poll or read.
Maybe this should be added.

For the past use of read, could You point me to where
imx2_wdt.c did have it?

There are watchdog drivers around that do implement a
read function, most of them to read temperature values.
Temperature sensor and watchdog are quite
different functionalities, so even where provided by
a single piece of hardware they probably should go
along two distinct drivers to make it a clean design.

Grüße,
  Oskar

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3] watchdog/imx2+: add support for pretimeout interrupt functionality
  2012-09-11 10:00 ` [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
  2012-09-11 11:00   ` Wim Van Sebroeck
@ 2012-09-20 15:37   ` Oskar Schirmer
  2012-09-27 13:08     ` Oskar Schirmer
  1 sibling, 1 reply; 9+ messages in thread
From: Oskar Schirmer @ 2012-09-20 15:37 UTC (permalink / raw)
  To: linux-watchdog
  Cc: linux-kernel, Oskar Schirmer, Wim Van Sebroeck, Wolfram Sang,
	Sascha Hauer, Andrew Morton, Johannes Weiner

This watchdog device provides pretimeout facilities:
Set some timeout value and get informed about imminent
watchdog activity thru interrupt.

Allow user to wait for this asynchronous event thru poll(2),
and to clear it implicitely upon dog appeasement.

There is only one precedent in current kernel that implements
watchdog pretimeout, ipmi_watchdog. It provides pretimeout
event thru poll, and requires a read(2) call to clear it.

However, as write(2) does calm the dog and so wind up the
timer anyway, it is obvious to let poll(2) state writability
where pretimeout has passed.

Signed-off-by: Oskar Schirmer <oskar@scara.com>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: Wolfram Sang <w.sang@pengutronix.de>
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
---
v2: switched to using devm_request_irq in favour of request_irq
v3: connect poll to write, to save extra read interface

 drivers/watchdog/imx2_wdt.c |   78 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index bcfab2b..4ecb80b 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -21,11 +21,15 @@
  */
 
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
 #include <linux/watchdog.h>
 #include <linux/clk.h>
 #include <linux/fs.h>
@@ -49,6 +53,11 @@
 #define IMX2_WDT_WRSR		0x04		/* Reset Status Register */
 #define IMX2_WDT_WRSR_TOUT	(1 << 1)	/* -> Reset due to Timeout */
 
+#define IMX2_WDT_WICT		0x06		/* Interrupt Control Reg */
+#define IMX2_WDT_WICT_WIE	(1 << 15)	/* -> Interrupt Enable */
+#define IMX2_WDT_WICT_WTIS	(1 << 14)	/* -> Timer Interrupt Status */
+#define IMX2_WDT_WICT_WICT	(0xFF << 0)	/* -> Interrupt Count Timeout */
+
 #define IMX2_WDT_MAX_TIME	128
 #define IMX2_WDT_DEFAULT_TIME	60		/* in seconds */
 
@@ -64,6 +73,10 @@ static struct {
 	unsigned timeout;
 	unsigned long status;
 	struct timer_list timer;	/* Pings the watchdog when closed */
+	int irq;
+	wait_queue_head_t write_q;
+	unsigned char pretimer_once;
+	unsigned char pretimer_gone;
 } imx2_wdt;
 
 static struct miscdevice imx2_wdt_miscdev;
@@ -81,7 +94,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
 
 static const struct watchdog_info imx2_wdt_info = {
 	.identity = "imx2+ watchdog",
-	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+		WDIOF_PRETIMEOUT,
 };
 
 static inline void imx2_wdt_setup(void)
@@ -108,6 +122,7 @@ static inline void imx2_wdt_ping(void)
 {
 	__raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR);
 	__raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR);
+	imx2_wdt.pretimer_gone = 0;
 }
 
 static void imx2_wdt_timer_ping(unsigned long arg)
@@ -148,6 +163,28 @@ static void imx2_wdt_set_timeout(int new_timeout)
 	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
 }
 
+static void imx2_wdt_set_pretimeout(int new_pretimeout)
+{
+	u16 val = new_pretimeout;
+
+	if (val > 0)
+		val = (val << 1) | IMX2_WDT_WICT_WIE;
+	__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+}
+
+static irqreturn_t imx2_wdt_interrupt(int irq, void *arg)
+{
+	u16 val;
+	val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+	if (val & IMX2_WDT_WICT_WTIS) {
+		__raw_writew(val, imx2_wdt.base + IMX2_WDT_WICT);
+		imx2_wdt.pretimer_gone = 1;
+		wake_up_interruptible(&imx2_wdt.write_q);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
 static int imx2_wdt_open(struct inode *inode, struct file *file)
 {
 	if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
@@ -210,6 +247,26 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
 	case WDIOC_GETTIMEOUT:
 		return put_user(imx2_wdt.timeout, p);
 
+	case WDIOC_SETPRETIMEOUT:
+		if (get_user(new_value, p))
+			return -EFAULT;
+		if ((new_value < 0) || (new_value >= IMX2_WDT_MAX_TIME))
+			return -EINVAL;
+		if (imx2_wdt.irq < 0)
+			return -EINVAL;
+		if (imx2_wdt.pretimer_once)
+			return -EPERM;
+		imx2_wdt.pretimer_once = 1;
+		imx2_wdt_set_pretimeout(new_value);
+
+	case WDIOC_GETPRETIMEOUT:
+		val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICT);
+		if (val & IMX2_WDT_WICT_WIE)
+			val = (val & IMX2_WDT_WICT_WICT) >> 1;
+		else
+			val = 0;
+		return put_user(val, p);
+
 	default:
 		return -ENOTTY;
 	}
@@ -237,6 +294,15 @@ static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
 	return len;
 }
 
+static unsigned int imx2_wdt_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	poll_wait(file, &imx2_wdt.write_q, wait);
+	if (imx2_wdt.pretimer_gone)
+		mask |= POLLOUT | POLLWRNORM;
+	return mask;
+}
+
 static const struct file_operations imx2_wdt_fops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -244,6 +310,7 @@ static const struct file_operations imx2_wdt_fops = {
 	.open = imx2_wdt_open,
 	.release = imx2_wdt_close,
 	.write = imx2_wdt_write,
+	.poll = imx2_wdt_poll,
 };
 
 static struct miscdevice imx2_wdt_miscdev = {
@@ -282,6 +349,15 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 
 	setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
 
+	init_waitqueue_head(&imx2_wdt.write_q);
+	imx2_wdt.irq = platform_get_irq(pdev, 0);
+	if (imx2_wdt.irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, imx2_wdt.irq,
+			imx2_wdt_interrupt, 0, "watchdog", pdev);
+		if (ret)
+			goto fail;
+	}
+
 	imx2_wdt_miscdev.parent = &pdev->dev;
 	ret = misc_register(&imx2_wdt_miscdev);
 	if (ret)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v3] watchdog/imx2+: add support for pretimeout interrupt functionality
  2012-09-20 15:37   ` [PATCH v3] " Oskar Schirmer
@ 2012-09-27 13:08     ` Oskar Schirmer
  2012-09-27 21:24       ` Wim Van Sebroeck
  0 siblings, 1 reply; 9+ messages in thread
From: Oskar Schirmer @ 2012-09-27 13:08 UTC (permalink / raw)
  To: Wim Van Sebroeck
  Cc: linux-watchdog, linux-kernel,  Wolfram Sang,  Sascha Hauer,
	 Andrew Morton,  Johannes Weiner,  Oskar Schirmer

Hi Wim,

unless there is another issue with this patch,
could You give an ack now?

thanks,
  Oskar

On Thu, Sep 20, 2012 at 15:37:24 +0000, Oskar Schirmer wrote:
> This watchdog device provides pretimeout facilities:
> Set some timeout value and get informed about imminent
> watchdog activity thru interrupt.
> 
> Allow user to wait for this asynchronous event thru poll(2),
> and to clear it implicitely upon dog appeasement.
> 
> There is only one precedent in current kernel that implements
> watchdog pretimeout, ipmi_watchdog. It provides pretimeout
> event thru poll, and requires a read(2) call to clear it.
> 
> However, as write(2) does calm the dog and so wind up the
> timer anyway, it is obvious to let poll(2) state writability
> where pretimeout has passed.
[...]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3] watchdog/imx2+: add support for pretimeout interrupt functionality
  2012-09-27 13:08     ` Oskar Schirmer
@ 2012-09-27 21:24       ` Wim Van Sebroeck
  0 siblings, 0 replies; 9+ messages in thread
From: Wim Van Sebroeck @ 2012-09-27 21:24 UTC (permalink / raw)
  To: Oskar Schirmer
  Cc: linux-watchdog, linux-kernel, Wolfram Sang, Sascha Hauer,
	Andrew Morton, Johannes Weiner

Hi Oskar,

> unless there is another issue with this patch,
> could You give an ack now?
> 
> thanks,
>   Oskar
> 
> On Thu, Sep 20, 2012 at 15:37:24 +0000, Oskar Schirmer wrote:
> > This watchdog device provides pretimeout facilities:
> > Set some timeout value and get informed about imminent
> > watchdog activity thru interrupt.
> > 
> > Allow user to wait for this asynchronous event thru poll(2),
> > and to clear it implicitely upon dog appeasement.
> > 
> > There is only one precedent in current kernel that implements
> > watchdog pretimeout, ipmi_watchdog. It provides pretimeout
> > event thru poll, and requires a read(2) call to clear it.
> > 
> > However, as write(2) does calm the dog and so wind up the
> > timer anyway, it is obvious to let poll(2) state writability
> > where pretimeout has passed.
> [...]

No, I'm not givinbg this an ACK nor a NAK at this moment.
I will review best options after the next merge window.

Kind regards,
Wim.


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2012-09-27 21:24 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-31 10:37 [PATCH] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
2012-07-03  9:10 ` [PATCH] watchdog/imx2+: add support for pretimeout interrupt Oskar Schirmer
2012-09-05  7:23   ` Oskar Schirmer
2012-09-11 10:00 ` [PATCH v2] watchdog/imx2+: add support for pretimeout interrupt functionality Oskar Schirmer
2012-09-11 11:00   ` Wim Van Sebroeck
2012-09-11 13:39     ` Oskar Schirmer
2012-09-20 15:37   ` [PATCH v3] " Oskar Schirmer
2012-09-27 13:08     ` Oskar Schirmer
2012-09-27 21:24       ` Wim Van Sebroeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).