Merge git://www.linux-watchdog.org/linux-watchdog
[cascardo/linux.git] / drivers / watchdog / imx2_wdt.c
index 62f346b..4874b0f 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 
 #define IMX2_WDT_WCR           0x00            /* Control Register */
 #define IMX2_WDT_WCR_WT                (0xFF << 8)     /* -> Watchdog Timeout Field */
-#define IMX2_WDT_WCR_WDA       (1 << 5)        /* -> External Reset WDOG_B */
-#define IMX2_WDT_WCR_SRS       (1 << 4)        /* -> Software Reset Signal */
-#define IMX2_WDT_WCR_WRE       (1 << 3)        /* -> WDOG Reset Enable */
-#define IMX2_WDT_WCR_WDE       (1 << 2)        /* -> Watchdog Enable */
-#define IMX2_WDT_WCR_WDZST     (1 << 0)        /* -> Watchdog timer Suspend */
+#define IMX2_WDT_WCR_WDA       BIT(5)          /* -> External Reset WDOG_B */
+#define IMX2_WDT_WCR_SRS       BIT(4)          /* -> Software Reset Signal */
+#define IMX2_WDT_WCR_WRE       BIT(3)          /* -> WDOG Reset Enable */
+#define IMX2_WDT_WCR_WDE       BIT(2)          /* -> Watchdog Enable */
+#define IMX2_WDT_WCR_WDZST     BIT(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_WRSR_TOUT     BIT(1)          /* -> Reset due to Timeout */
+
+#define IMX2_WDT_WICR          0x06            /* Interrupt Control Register */
+#define IMX2_WDT_WICR_WIE      BIT(15)         /* -> Interrupt Enable */
+#define IMX2_WDT_WICR_WTIS     BIT(14)         /* -> Interrupt Status */
+#define IMX2_WDT_WICR_WICT     0xFF            /* -> Interrupt Count Timeout */
 
 #define IMX2_WDT_WMCR          0x08            /* Misc Register */
 
@@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = {
        .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
 };
 
+static const struct watchdog_info imx2_wdt_pretimeout_info = {
+       .identity = "imx2+ watchdog",
+       .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+                  WDIOF_PRETIMEOUT,
+};
+
 static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action,
                            void *data)
 {
@@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
        return 0;
 }
 
+static int imx2_wdt_set_pretimeout(struct watchdog_device *wdog,
+                                  unsigned int new_pretimeout)
+{
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       if (new_pretimeout >= IMX2_WDT_MAX_TIME)
+               return -EINVAL;
+
+       wdog->pretimeout = new_pretimeout;
+
+       regmap_update_bits(wdev->regmap, IMX2_WDT_WICR,
+                          IMX2_WDT_WICR_WIE | IMX2_WDT_WICR_WICT,
+                          IMX2_WDT_WICR_WIE | (new_pretimeout << 1));
+       return 0;
+}
+
+static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg)
+{
+       struct watchdog_device *wdog = wdog_arg;
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       regmap_write_bits(wdev->regmap, IMX2_WDT_WICR,
+                         IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS);
+
+       watchdog_notify_pretimeout(wdog);
+
+       return IRQ_HANDLED;
+}
+
 static int imx2_wdt_start(struct watchdog_device *wdog)
 {
        struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
@@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = {
        .start = imx2_wdt_start,
        .ping = imx2_wdt_ping,
        .set_timeout = imx2_wdt_set_timeout,
+       .set_pretimeout = imx2_wdt_set_pretimeout,
        .restart = imx2_wdt_restart,
 };
 
@@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
        wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
        wdog->parent            = &pdev->dev;
 
+       ret = platform_get_irq(pdev, 0);
+       if (ret > 0)
+               if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0,
+                                     dev_name(&pdev->dev), wdog))
+                       wdog->info = &imx2_wdt_pretimeout_info;
+
        ret = clk_prepare_enable(wdev->clk);
        if (ret)
                return ret;