watchdog: sama5d4_wdt: addition of sam9x60 compatible watchdog
authorEugen Hristev <eugen.hristev@microchip.com>
Mon, 18 Nov 2019 08:50:36 +0000 (08:50 +0000)
committerWim Van Sebroeck <wim@linux-watchdog.org>
Mon, 27 Jan 2020 14:55:44 +0000 (15:55 +0100)
Add support for SAM9X60 WDT into sama5d4_wdt.
This means that this driver gets a flag inside the data struct
that represents the sam9x60 support.
This flag differentiates between the two hardware blocks, and is set
according to the compatible of the driver instantiation.

Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Reviewed-by-off-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/1574067012-18559-3-git-send-email-eugen.hristev@microchip.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
drivers/watchdog/at91sam9_wdt.h
drivers/watchdog/sama5d4_wdt.c

index abfe34d..298d545 100644 (file)
 #define AT91_WDT_MR            0x04                    /* Watchdog Mode Register */
 #define  AT91_WDT_WDV          (0xfffUL << 0)          /* Counter Value */
 #define  AT91_WDT_SET_WDV(x)   ((x) & AT91_WDT_WDV)
+#define  AT91_SAM9X60_PERIODRST        BIT(4)          /* Period Reset */
+#define  AT91_SAM9X60_RPTHRST  BIT(5)          /* Minimum Restart Period */
 #define  AT91_WDT_WDFIEN       BIT(12)         /* Fault Interrupt Enable */
+#define  AT91_SAM9X60_WDDIS    BIT(12)         /* Watchdog Disable */
 #define  AT91_WDT_WDRSTEN      BIT(13)         /* Reset Processor */
 #define  AT91_WDT_WDRPROC      BIT(14)         /* Timer Restart */
 #define  AT91_WDT_WDDIS                BIT(15)         /* Watchdog Disable */
 #define  AT91_WDT_WDUNF                BIT(0)          /* Watchdog Underflow */
 #define  AT91_WDT_WDERR                BIT(1)          /* Watchdog Error */
 
+/* Watchdog Timer Value Register */
+#define AT91_SAM9X60_VR                0x08
+
+/* Watchdog Window Level Register */
+#define AT91_SAM9X60_WLR       0x0c
+/* Watchdog Period Value */
+#define  AT91_SAM9X60_COUNTER  (0xfffUL << 0)
+#define  AT91_SAM9X60_SET_COUNTER(x)   ((x) & AT91_SAM9X60_COUNTER)
+
+/* Interrupt Enable Register */
+#define AT91_SAM9X60_IER       0x14
+/* Period Interrupt Enable */
+#define  AT91_SAM9X60_PERINT   BIT(0)
+/* Interrupt Disable Register */
+#define AT91_SAM9X60_IDR       0x18
+/* Interrupt Status Register */
+#define AT91_SAM9X60_ISR       0x1c
+
 #endif
index d193a60..e5d11d6 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Driver for Atmel SAMA5D4 Watchdog Timer
  *
- * Copyright (C) 2015 Atmel Corporation
+ * Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries
  */
 
 #include <linux/delay.h>
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/reboot.h>
@@ -29,7 +30,10 @@ struct sama5d4_wdt {
        struct watchdog_device  wdd;
        void __iomem            *reg_base;
        u32                     mr;
+       u32                     ir;
        unsigned long           last_ping;
+       bool                    need_irq;
+       bool                    sam9x60_support;
 };
 
 static int wdt_timeout;
@@ -78,7 +82,12 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd)
 {
        struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
 
-       wdt->mr &= ~AT91_WDT_WDDIS;
+       if (wdt->sam9x60_support) {
+               writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER);
+               wdt->mr &= ~AT91_SAM9X60_WDDIS;
+       } else {
+               wdt->mr &= ~AT91_WDT_WDDIS;
+       }
        wdt_write(wdt, AT91_WDT_MR, wdt->mr);
 
        return 0;
@@ -88,7 +97,12 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd)
 {
        struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
 
-       wdt->mr |= AT91_WDT_WDDIS;
+       if (wdt->sam9x60_support) {
+               writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR);
+               wdt->mr |= AT91_SAM9X60_WDDIS;
+       } else {
+               wdt->mr |= AT91_WDT_WDDIS;
+       }
        wdt_write(wdt, AT91_WDT_MR, wdt->mr);
 
        return 0;
@@ -109,6 +123,14 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
        struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
        u32 value = WDT_SEC2TICKS(timeout);
 
+       if (wdt->sam9x60_support) {
+               wdt_write(wdt, AT91_SAM9X60_WLR,
+                         AT91_SAM9X60_SET_COUNTER(value));
+
+               wdd->timeout = timeout;
+               return 0;
+       }
+
        wdt->mr &= ~AT91_WDT_WDV;
        wdt->mr |= AT91_WDT_SET_WDV(value);
 
@@ -143,8 +165,14 @@ static const struct watchdog_ops sama5d4_wdt_ops = {
 static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
 {
        struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
+       u32 reg;
 
-       if (wdt_read(wdt, AT91_WDT_SR)) {
+       if (wdt->sam9x60_support)
+               reg = wdt_read(wdt, AT91_SAM9X60_ISR);
+       else
+               reg = wdt_read(wdt, AT91_WDT_SR);
+
+       if (reg) {
                pr_crit("Atmel Watchdog Software Reset\n");
                emergency_restart();
                pr_crit("Reboot didn't succeed\n");
@@ -157,13 +185,14 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
 {
        const char *tmp;
 
-       wdt->mr = AT91_WDT_WDDIS;
+       if (wdt->sam9x60_support)
+               wdt->mr = AT91_SAM9X60_WDDIS;
+       else
+               wdt->mr = AT91_WDT_WDDIS;
 
        if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
            !strcmp(tmp, "software"))
-               wdt->mr |= AT91_WDT_WDFIEN;
-       else
-               wdt->mr |= AT91_WDT_WDRSTEN;
+               wdt->need_irq = true;
 
        if (of_property_read_bool(np, "atmel,idle-halt"))
                wdt->mr |= AT91_WDT_WDIDLEHLT;
@@ -176,21 +205,46 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
 
 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
 {
-       u32 reg;
+       u32 reg, val;
+
+       val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT);
        /*
         * When booting and resuming, the bootloader may have changed the
         * watchdog configuration.
         * If the watchdog is already running, we can safely update it.
         * Else, we have to disable it properly.
         */
-       if (wdt_enabled) {
-               wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
-       } else {
+       if (!wdt_enabled) {
                reg = wdt_read(wdt, AT91_WDT_MR);
-               if (!(reg & AT91_WDT_WDDIS))
+               if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS)))
+                       wdt_write_nosleep(wdt, AT91_WDT_MR,
+                                         reg | AT91_SAM9X60_WDDIS);
+               else if (!wdt->sam9x60_support &&
+                        (!(reg & AT91_WDT_WDDIS)))
                        wdt_write_nosleep(wdt, AT91_WDT_MR,
                                          reg | AT91_WDT_WDDIS);
        }
+
+       if (wdt->sam9x60_support) {
+               if (wdt->need_irq)
+                       wdt->ir = AT91_SAM9X60_PERINT;
+               else
+                       wdt->mr |= AT91_SAM9X60_PERIODRST;
+
+               wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir);
+               wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val));
+       } else {
+               wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
+               wdt->mr |= AT91_WDT_SET_WDV(val);
+
+               if (wdt->need_irq)
+                       wdt->mr |= AT91_WDT_WDFIEN;
+               else
+                       wdt->mr |= AT91_WDT_WDRSTEN;
+       }
+
+       wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
+
        return 0;
 }
 
@@ -201,7 +255,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
        struct sama5d4_wdt *wdt;
        void __iomem *regs;
        u32 irq = 0;
-       u32 timeout;
        int ret;
 
        wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
@@ -215,6 +268,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
        wdd->min_timeout = MIN_WDT_TIMEOUT;
        wdd->max_timeout = MAX_WDT_TIMEOUT;
        wdt->last_ping = jiffies;
+       wdt->sam9x60_support = of_device_is_compatible(dev->of_node,
+                                                      "microchip,sam9x60-wdt");
 
        watchdog_set_drvdata(wdd, wdt);
 
@@ -224,15 +279,19 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 
        wdt->reg_base = regs;
 
-       irq = irq_of_parse_and_map(dev->of_node, 0);
-       if (!irq)
-               dev_warn(dev, "failed to get IRQ from DT\n");
-
        ret = of_sama5d4_wdt_init(dev->of_node, wdt);
        if (ret)
                return ret;
 
-       if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
+       if (wdt->need_irq) {
+               irq = irq_of_parse_and_map(dev->of_node, 0);
+               if (!irq) {
+                       dev_warn(dev, "failed to get IRQ from DT\n");
+                       wdt->need_irq = false;
+               }
+       }
+
+       if (wdt->need_irq) {
                ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
                                       IRQF_SHARED | IRQF_IRQPOLL |
                                       IRQF_NO_SUSPEND, pdev->name, pdev);
@@ -244,11 +303,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 
        watchdog_init_timeout(wdd, wdt_timeout, dev);
 
-       timeout = WDT_SEC2TICKS(wdd->timeout);
-
-       wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
-       wdt->mr |= AT91_WDT_SET_WDV(timeout);
-
        ret = sama5d4_wdt_init(wdt);
        if (ret)
                return ret;
@@ -269,7 +323,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 }
 
 static const struct of_device_id sama5d4_wdt_of_match[] = {
-       { .compatible = "atmel,sama5d4-wdt", },
+       {
+               .compatible = "atmel,sama5d4-wdt",
+       },
+       {
+               .compatible = "microchip,sam9x60-wdt",
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);