diff options
author | Robin Gong <B38343@freescale.com> | 2012-07-02 10:41:57 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2015-01-15 21:18:54 -0600 |
commit | 6c84ab18126cf02e5ac946d4334d2fbcadc29468 (patch) | |
tree | efd7e12910753d5a077f93b0546c0eccbb607aa8 | |
parent | c56fd1f22520d63a12dbc523c79bc784f80c81be (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.c | 70 |
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! " |