Commit bb44aa09 authored by Eugen Hristev's avatar Eugen Hristev Committed by Wim Van Sebroeck

watchdog: sama5d4_wdt: addition of sam9x60 compatible watchdog

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: default avatarEugen Hristev <eugen.hristev@microchip.com>
Reviewed-by-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/1574067012-18559-3-git-send-email-eugen.hristev@microchip.comSigned-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarWim Van Sebroeck <wim@linux-watchdog.org>
parent d5226fa6
...@@ -24,7 +24,10 @@ ...@@ -24,7 +24,10 @@
#define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */
#define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */ #define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */
#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) #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_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_WDRSTEN BIT(13) /* Reset Processor */
#define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ #define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */
#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ #define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */
...@@ -37,4 +40,22 @@ ...@@ -37,4 +40,22 @@
#define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */ #define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */
#define AT91_WDT_WDERR BIT(1) /* Watchdog Error */ #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 #endif
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* Driver for Atmel SAMA5D4 Watchdog Timer * 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> #include <linux/delay.h>
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reboot.h> #include <linux/reboot.h>
...@@ -29,7 +30,10 @@ struct sama5d4_wdt { ...@@ -29,7 +30,10 @@ struct sama5d4_wdt {
struct watchdog_device wdd; struct watchdog_device wdd;
void __iomem *reg_base; void __iomem *reg_base;
u32 mr; u32 mr;
u32 ir;
unsigned long last_ping; unsigned long last_ping;
bool need_irq;
bool sam9x60_support;
}; };
static int wdt_timeout; static int wdt_timeout;
...@@ -78,7 +82,12 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) ...@@ -78,7 +82,12 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd)
{ {
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
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->mr &= ~AT91_WDT_WDDIS;
}
wdt_write(wdt, AT91_WDT_MR, wdt->mr); wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0; return 0;
...@@ -88,7 +97,12 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) ...@@ -88,7 +97,12 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd)
{ {
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
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->mr |= AT91_WDT_WDDIS;
}
wdt_write(wdt, AT91_WDT_MR, wdt->mr); wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0; return 0;
...@@ -109,6 +123,14 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -109,6 +123,14 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
u32 value = WDT_SEC2TICKS(timeout); 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_WDV;
wdt->mr |= AT91_WDT_SET_WDV(value); wdt->mr |= AT91_WDT_SET_WDV(value);
...@@ -143,8 +165,14 @@ static const struct watchdog_ops sama5d4_wdt_ops = { ...@@ -143,8 +165,14 @@ static const struct watchdog_ops sama5d4_wdt_ops = {
static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
{ {
struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
u32 reg;
if (wdt->sam9x60_support)
reg = wdt_read(wdt, AT91_SAM9X60_ISR);
else
reg = wdt_read(wdt, AT91_WDT_SR);
if (wdt_read(wdt, AT91_WDT_SR)) { if (reg) {
pr_crit("Atmel Watchdog Software Reset\n"); pr_crit("Atmel Watchdog Software Reset\n");
emergency_restart(); emergency_restart();
pr_crit("Reboot didn't succeed\n"); 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) ...@@ -157,13 +185,14 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
{ {
const char *tmp; const char *tmp;
if (wdt->sam9x60_support)
wdt->mr = AT91_SAM9X60_WDDIS;
else
wdt->mr = AT91_WDT_WDDIS; wdt->mr = AT91_WDT_WDDIS;
if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
!strcmp(tmp, "software")) !strcmp(tmp, "software"))
wdt->mr |= AT91_WDT_WDFIEN; wdt->need_irq = true;
else
wdt->mr |= AT91_WDT_WDRSTEN;
if (of_property_read_bool(np, "atmel,idle-halt")) if (of_property_read_bool(np, "atmel,idle-halt"))
wdt->mr |= AT91_WDT_WDIDLEHLT; wdt->mr |= AT91_WDT_WDIDLEHLT;
...@@ -176,21 +205,46 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) ...@@ -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) 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 * When booting and resuming, the bootloader may have changed the
* watchdog configuration. * watchdog configuration.
* If the watchdog is already running, we can safely update it. * If the watchdog is already running, we can safely update it.
* Else, we have to disable it properly. * Else, we have to disable it properly.
*/ */
if (wdt_enabled) { if (!wdt_enabled) {
wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
} else {
reg = wdt_read(wdt, AT91_WDT_MR); 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, wdt_write_nosleep(wdt, AT91_WDT_MR,
reg | AT91_WDT_WDDIS); 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; return 0;
} }
...@@ -201,7 +255,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -201,7 +255,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
struct sama5d4_wdt *wdt; struct sama5d4_wdt *wdt;
void __iomem *regs; void __iomem *regs;
u32 irq = 0; u32 irq = 0;
u32 timeout;
int ret; int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
...@@ -215,6 +268,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -215,6 +268,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->min_timeout = MIN_WDT_TIMEOUT;
wdd->max_timeout = MAX_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT;
wdt->last_ping = jiffies; wdt->last_ping = jiffies;
wdt->sam9x60_support = of_device_is_compatible(dev->of_node,
"microchip,sam9x60-wdt");
watchdog_set_drvdata(wdd, wdt); watchdog_set_drvdata(wdd, wdt);
...@@ -224,15 +279,19 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -224,15 +279,19 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
wdt->reg_base = regs; 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); ret = of_sama5d4_wdt_init(dev->of_node, wdt);
if (ret) if (ret)
return 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, ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
IRQF_SHARED | IRQF_IRQPOLL | IRQF_SHARED | IRQF_IRQPOLL |
IRQF_NO_SUSPEND, pdev->name, pdev); IRQF_NO_SUSPEND, pdev->name, pdev);
...@@ -244,11 +303,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -244,11 +303,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(wdd, wdt_timeout, dev); 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); ret = sama5d4_wdt_init(wdt);
if (ret) if (ret)
return ret; return ret;
...@@ -269,7 +323,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) ...@@ -269,7 +323,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
} }
static const struct of_device_id sama5d4_wdt_of_match[] = { 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); MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment