cns3xxx: fix mpcore watchdog

The original implementation loaded the count register with (wrong) semi-
random values due to its implemenation nature.

If the wrongly calulated value was below the kickrate,
the WD was triggered and rebooted the system.

Rework this, partly based on upstream patches, to dynamically fetch the
current clockrate and calculate the proper offset for the WD countdown
register.

Before:

[  143.800000] count val: 27219720
[  148.820000] count val: 50623201
[  153.830000] count val: 96425250
[  158.830000] count val: 89735401
[  163.840000] count val: 4756110

After:

[    0.700000] MPCore WD init. clockrate: 299984500 prescaler: 256
countrate: 1171814 timeout: 60s
[  358.530000] count val: 35154751
[  363.540000] count val: 35154750
[  368.540000] count val: 35154751
[  373.550000] count val: 35154750

Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
This commit is contained in:
Koen Vandeputte 2017-04-12 11:10:59 +02:00 committed by Felix Fietkau
parent 6f8a552796
commit 84acff2865

View file

@ -30,13 +30,14 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
--- /dev/null --- /dev/null
+++ b/drivers/watchdog/mpcore_wdt.c +++ b/drivers/watchdog/mpcore_wdt.c
@@ -0,0 +1,117 @@ @@ -0,0 +1,118 @@
+/* +/*
+ * Watchdog driver for ARM MPcore + * Watchdog driver for ARM MPcore
+ * + *
+ * Copyright (C) 2017 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2017 Felix Fietkau <nbd@nbd.name>
+ */ + */
+ +
+#include <linux/export.h>
+#include <linux/module.h> +#include <linux/module.h>
+#include <linux/kernel.h> +#include <linux/kernel.h>
+#include <linux/watchdog.h> +#include <linux/watchdog.h>
@ -52,10 +53,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
+ static int perturb; + static int perturb;
+ u32 count; + u32 count;
+ +
+ count = ioread32(wdt_base + TWD_WDOG_COUNTER); + count = (twd_timer_get_rate() / 256) * wdt_timeout;
+ count = (~0U - count) * HZ / 5;
+ count /= 256; /* prescale */
+ count *= wdt_timeout;
+ +
+ /* Reload register needs a different value on each refresh */ + /* Reload register needs a different value on each refresh */
+ count += perturb; + count += perturb;
@ -118,6 +116,9 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
+static int mpcore_wdt_probe(struct platform_device *pdev) +static int mpcore_wdt_probe(struct platform_device *pdev)
+{ +{
+ struct resource *res; + struct resource *res;
+ unsigned long rate = twd_timer_get_rate();
+
+ pr_info("MPCore WD init. clockrate: %u prescaler: %u countrate: %u timeout: %us\n", rate, 256, rate / 256, wdt_timeout);
+ +
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) + if (!res)
@ -148,3 +149,37 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
+module_platform_driver(mpcore_wdt_driver); +module_platform_driver(mpcore_wdt_driver);
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>"); +MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL");
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -33,5 +33,6 @@ struct twd_local_timer name __initdata =
};
int twd_local_timer_register(struct twd_local_timer *);
+unsigned long twd_timer_get_rate(void);
#endif
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/smp.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
@@ -380,6 +381,14 @@ int __init twd_local_timer_register(stru
return twd_local_timer_common_register(NULL);
}
+/* Needed by mpcore_wdt */
+unsigned long twd_timer_get_rate(void)
+{
+ return twd_timer_rate;
+}
+EXPORT_SYMBOL_GPL(twd_timer_get_rate);
+
+
#ifdef CONFIG_OF
static int __init twd_local_timer_of_register(struct device_node *np)
{