RTI watchdog can support different open window sizes. Add support for these and add a new module parameter to configure it. The default open window size for the driver still remains at 50%. Also, modify the margin calculation logic a bit for 32k source clock, instead of adding a margin to every window config, assume the 32k source clock is running slower. Signed-off-by: Tero Kristo <t-kristo@xxxxxx> --- drivers/watchdog/rti_wdt.c | 112 +++++++++++++++++++++++++++++++------ 1 file changed, 95 insertions(+), 17 deletions(-) diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index d456dd72d99a..110bfc8d0bb3 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -19,7 +19,8 @@ #include <linux/types.h> #include <linux/watchdog.h> -#define DEFAULT_HEARTBEAT 60 +#define DEFAULT_HEARTBEAT 60 +#define DEFAULT_WINDOWSIZE 50 /* Max heartbeat is calculated at 32kHz source clock */ #define MAX_HEARTBEAT 1000 @@ -35,9 +36,13 @@ #define RTIWWDRX_NMI 0xa -#define RTIWWDSIZE_50P 0x50 +#define RTIWWDSIZE_50P 0x50 +#define RTIWWDSIZE_25P 0x500 +#define RTIWWDSIZE_12P5 0x5000 +#define RTIWWDSIZE_6P25 0x50000 +#define RTIWWDSIZE_3P125 0x500000 -#define WDENABLE_KEY 0xa98559da +#define WDENABLE_KEY 0xa98559da #define WDKEY_SEQ0 0xe51a #define WDKEY_SEQ1 0xa35c @@ -48,7 +53,8 @@ #define DWDST BIT(1) -static int heartbeat; +static int heartbeat = DEFAULT_HEARTBEAT; +static u32 wsize = DEFAULT_WINDOWSIZE; /* * struct to hold data for each WDT device @@ -62,34 +68,93 @@ struct rti_wdt_device { struct watchdog_device wdd; }; +static int rti_wdt_convert_wsize(void) +{ + if (wsize >= 50) { + wsize = RTIWWDSIZE_50P; + } else if (wsize >= 25) { + wsize = RTIWWDSIZE_25P; + } else if (wsize > 12) { + wsize = RTIWWDSIZE_12P5; + } else if (wsize > 6) { + wsize = RTIWWDSIZE_6P25; + } else if (wsize > 3) { + wsize = RTIWWDSIZE_3P125; + } else { + pr_err("%s: bad windowsize: %d\n", __func__, wsize); + return -EINVAL; + } + + return 0; +} + +static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd) +{ + /* + * RTI only supports a windowed mode, where the watchdog can only + * be petted during the open window; not too early or not too late. + * The HW configuration options only allow for the open window size + * to be 50% or less than that. + */ + switch (wsize) { + case RTIWWDSIZE_50P: + /* 50% open window => 50% min heartbeat */ + wdd->min_hw_heartbeat_ms = 500 * heartbeat; + break; + + case RTIWWDSIZE_25P: + /* 25% open window => 75% min heartbeat */ + wdd->min_hw_heartbeat_ms = 750 * heartbeat; + break; + + case RTIWWDSIZE_12P5: + /* 12.5% open window => 87.5% min heartbeat */ + wdd->min_hw_heartbeat_ms = 875 * heartbeat; + break; + + case RTIWWDSIZE_6P25: + /* 6.5% open window => 93.5% min heartbeat */ + wdd->min_hw_heartbeat_ms = 935 * heartbeat; + break; + + case RTIWWDSIZE_3P125: + /* 3.125% open window => 96.9% min heartbeat */ + wdd->min_hw_heartbeat_ms = 969 * heartbeat; + break; + + default: + pr_err("%s: Bad watchdog window size!\n", __func__); + return -EINVAL; + } + + return 0; +} + static int rti_wdt_start(struct watchdog_device *wdd) { u32 timer_margin; struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd); + int ret; /* set timeout period */ - timer_margin = (u64)wdd->timeout * wdt->freq; + timer_margin = (u64)heartbeat * wdt->freq; timer_margin >>= WDT_PRELOAD_SHIFT; if (timer_margin > WDT_PRELOAD_MAX) timer_margin = WDT_PRELOAD_MAX; writel_relaxed(timer_margin, wdt->base + RTIDWDPRLD); - /* - * RTI only supports a windowed mode, where the watchdog can only - * be petted during the open window; not too early or not too late. - * The HW configuration options only allow for the open window size - * to be 50% or less than that; we obviouly want to configure the open - * window as large as possible so we select the 50% option. To avoid - * any glitches, we accommodate 5% safety margin also, so we setup - * the min_hw_hearbeat at 55% of the timeout period. - */ - wdd->min_hw_heartbeat_ms = 11 * wdd->timeout * 1000 / 20; + ret = rti_wdt_convert_wsize(); + if (ret) + return ret; + + ret = rti_wdt_setup_hw_hb(wdd); + if (ret) + return ret; /* Generate NMI when wdt expires */ writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); - /* Open window size 50%; this is the largest window size available */ - writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL); + writel_relaxed(wsize, wdt->base + RTIWWDSIZECTRL); readl_relaxed(wdt->base + RTIWWDSIZECTRL); @@ -169,6 +234,14 @@ static int rti_wdt_probe(struct platform_device *pdev) return -EINVAL; } + /* + * If watchdog is running at 32k clock, it is not accurate. + * Adjust frequency down in this case so that we don't pet + * the watchdog too often. + */ + if (wdt->freq > 30000 && wdt->freq < 32768) + wdt->freq = 30000; + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret) { @@ -251,5 +324,10 @@ MODULE_PARM_DESC(heartbeat, __MODULE_STRING(MAX_HEARTBEAT) ", default " __MODULE_STRING(DEFAULT_HEARTBEAT)); +module_param(wsize, uint, 0); +MODULE_PARM_DESC(wsize, + "Watchdog open window size in percentage from 3 to 50, " + "default " __MODULE_STRING(DEFAULT_WINDOW_SIZE)); + MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:rti-wdt"); -- 2.17.1 -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki