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: 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>
This commit is contained in:
parent
d5226fa6db
commit
bb44aa09e5
|
@ -24,7 +24,10 @@
|
|||
#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 */
|
||||
|
@ -37,4 +40,22 @@
|
|||
#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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user