summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gong <B38343@freescale.com>2012-07-02 10:41:57 +0800
committerNitin Garg <nitin.garg@freescale.com>2015-01-15 21:18:54 -0600
commit6c84ab18126cf02e5ac946d4334d2fbcadc29468 (patch)
treeefd7e12910753d5a077f93b0546c0eccbb607aa8
parentc56fd1f22520d63a12dbc523c79bc784f80c81be (diff)
ENGR00215489-1 WDOG:add WDIOC_SETPRETIMEOUT and WDIOC_GETPRETIMEOUT interface
Add these two interface, so than user can set and get pre-timeout value to save some important data before watchdog reboot. Signed-off-by: Robin Gong <B38343@freescale.com> (cherry picked from commit 36d2c03e30f18a34439fdb928ad5bad8d66a1b64) (cherry picked from commit 19b8c1ed3997e01c108c9b22f5541a28f2b23923)
-rw-r--r--drivers/watchdog/imx2_wdt.c70
1 files changed, 68 insertions, 2 deletions
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index dd51d9539b33..11bda8079b12 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -2,7 +2,7 @@
* Watchdog driver for IMX2 and later processors
*
* Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de>
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
*
* some parts adapted by similar drivers from Darius Augulis and Vladimir
* Zapolskiy, additional improvements by Wim Van Sebroeck.
@@ -22,6 +22,8 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -43,17 +45,23 @@
#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */
+
#define IMX2_WDT_WSR 0x02 /* Service Register */
#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
#define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
#define IMX2_WDT_WRSR 0x04 /* Reset Status Register */
#define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */
+#define IMX2_WDT_WICR 0x06 /*Interrupt Control Register*/
+#define IMX2_WDT_WICR_WIE (1 << 15) /* -> Interrupt Enable */
+#define IMX2_WDT_WICR_WTIS (1 << 14) /* -> Interrupt Status */
+#define IMX2_WDT_WICR_WICT (0xFF << 0) /* -> Watchdog Interrupt Timeout Field */
#define IMX2_WDT_MAX_TIME 128
#define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
+#define WDOG_SEC_TO_PRECOUNT(s) (s * 2) /* set WDOG pre timeout count*/
#define IMX2_WDT_STATUS_OPEN 0
#define IMX2_WDT_STATUS_STARTED 1
@@ -63,6 +71,7 @@ static struct {
struct clk *clk;
void __iomem *base;
unsigned timeout;
+ unsigned pretimeout;
unsigned long status;
struct timer_list timer; /* Pings the watchdog when closed */
} imx2_wdt;
@@ -82,7 +91,7 @@ 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)
@@ -151,6 +160,38 @@ static void imx2_wdt_set_timeout(int new_timeout)
__raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
}
+
+static int imx2_wdt_check_pretimeout_set(void)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICR);
+ return (val & IMX2_WDT_WICR_WIE) ? 1 : 0;
+}
+
+static void imx2_wdt_set_pretimeout(int new_timeout)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICR);
+
+ /* set the new pre-timeout value in the WSR */
+ val &= ~IMX2_WDT_WICR_WICT;
+ val |= WDOG_SEC_TO_PRECOUNT(new_timeout);
+
+ if (!imx2_wdt_check_pretimeout_set())
+ val |= IMX2_WDT_WICR_WIE; /*enable*/
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WICR);
+}
+
+static irqreturn_t imx2_wdt_isr(int irq, void *dev_id)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICR);
+ if (val & IMX2_WDT_WICR_WTIS) {
+ /*clear interrupt status bit*/
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WICR);
+ printk(KERN_INFO "watchdog pre-timeout:%d, %d Seconds remained\n", \
+ imx2_wdt.pretimeout, imx2_wdt.timeout-imx2_wdt.pretimeout);
+ }
+ return IRQ_HANDLED;
+}
+
static int imx2_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
@@ -213,6 +254,17 @@ 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.timeout))
+ return -EINVAL;
+ imx2_wdt_set_pretimeout(new_value);
+ imx2_wdt.pretimeout = new_value;
+
+ case WDIOC_GETPRETIMEOUT:
+ return put_user(imx2_wdt.pretimeout, p);
+
default:
return -ENOTTY;
}
@@ -258,6 +310,7 @@ static struct miscdevice imx2_wdt_miscdev = {
static int __init imx2_wdt_probe(struct platform_device *pdev)
{
int ret;
+ int irq;
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -271,6 +324,19 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
return PTR_ERR(imx2_wdt.clk);
}
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto fail;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, imx2_wdt_isr, 0,
+ dev_name(&pdev->dev), NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get irq %d\n", irq);
+ goto fail;
+ }
+
imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
if (imx2_wdt.timeout != timeout)
dev_warn(&pdev->dev, "Initial timeout out of range! "