123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- From 4823856642a29f81c2310a05b4687588c62633fb Mon Sep 17 00:00:00 2001
- From: Jan Kiszka <jan.kiszka@siemens.com>
- Date: Fri, 30 Jul 2021 13:53:20 +0200
- Subject: [PATCH 25/26] watchdog: rti_wdt: Backport mainline driver
- Corresponds to 8711071e9700b67045fe5518161d63f7a03e3c9e upstream.
- This comes with a lot of improvements, specifically for picking up a
- watchdog already started by the firmware.
- Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
- ---
- drivers/watchdog/rti_wdt.c | 194 ++++++++++++++++++++++++++++---------
- 1 file changed, 146 insertions(+), 48 deletions(-)
- diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c
- index b0933b090f53..e3c90e617c7a 100644
- --- a/drivers/watchdog/rti_wdt.c
- +++ b/drivers/watchdog/rti_wdt.c
- @@ -2,26 +2,27 @@
- /*
- * Watchdog driver for the K3 RTI module
- *
- - * (c) Copyright 2019 Texas Instruments Inc.
- + * (c) Copyright 2019-2020 Texas Instruments Inc.
- * All rights reserved.
- */
-
- +#include <linux/clk.h>
- +#include <linux/device.h>
- +#include <linux/err.h>
- +#include <linux/io.h>
- +#include <linux/kernel.h>
- +#include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- -#include <linux/mod_devicetable.h>
- -#include <linux/types.h>
- -#include <linux/kernel.h>
- -#include <linux/watchdog.h>
- #include <linux/platform_device.h>
- -#include <linux/io.h>
- -#include <linux/device.h>
- -#include <linux/clk.h>
- -#include <linux/err.h>
- #include <linux/pm_runtime.h>
- +#include <linux/types.h>
- +#include <linux/watchdog.h>
-
- -#define MODULE_NAME "rti-wdt"
- #define DEFAULT_HEARTBEAT 60
- -#define MAX_HEARTBEAT 1000
- +
- +/* Max heartbeat is calculated at 32kHz source clock */
- +#define MAX_HEARTBEAT 1000
-
- /* Timer register set definition */
- #define RTIDWDCTRL 0x90
- @@ -34,7 +35,11 @@
-
- #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
-
- @@ -47,42 +52,45 @@
-
- #define DWDST BIT(1)
-
- -static int heartbeat;
- +static int heartbeat = DEFAULT_HEARTBEAT;
-
- /*
- * struct to hold data for each WDT device
- * @base - base io address of WD device
- - * @clk - source clock of WDT
- - * @wdd - hold watchdog device as is in WDT core
- + * @freq - source clock frequency of WDT
- + * @wdd - hold watchdog device as is in WDT core
- */
- struct rti_wdt_device {
- void __iomem *base;
- - struct clk *clk;
- + unsigned long freq;
- struct watchdog_device wdd;
- };
-
- static int rti_wdt_start(struct watchdog_device *wdd)
- {
- u32 timer_margin;
- - unsigned long freq;
- struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);
-
- - freq = clk_get_rate(wdt->clk);
- -
- /* set timeout period */
- - timer_margin = (u64)wdd->timeout * freq;
- + timer_margin = (u64)wdd->timeout * 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);
-
- - /* Set min heartbeat to 1.1x window size */
- - wdd->min_hw_heartbeat_ms = 11 * wdd->timeout * 1000 / 20;
- + /*
- + * 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.
- + */
- + wdd->min_hw_heartbeat_ms = 500 * wdd->timeout;
-
- /* Generate NMI when wdt expires */
- writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL);
-
- - /* Window size 50% */
- + /* Open window size 50%; this is the largest window size available */
- writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL);
-
- readl_relaxed(wdt->base + RTIWWDSIZECTRL);
- @@ -101,16 +109,53 @@ static int rti_wdt_ping(struct watchdog_device *wdd)
- /* put watchdog in active state */
- writel_relaxed(WDKEY_SEQ1, wdt->base + RTIWDKEY);
-
- - if (readl_relaxed(wdt->base + RTIWDSTATUS))
- - WARN_ON_ONCE(1);
- + return 0;
- +}
- +
- +static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd, u32 wsize)
- +{
- + /*
- + * 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:
- + return -EINVAL;
- + }
-
- return 0;
- }
-
- -static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)
- +static unsigned int rti_wdt_get_timeleft_ms(struct watchdog_device *wdd)
- {
- u64 timer_counter;
- - unsigned long freq;
- u32 val;
- struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);
-
- @@ -119,17 +164,20 @@ static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)
- if (val & DWDST)
- return 0;
-
- - freq = clk_get_rate(wdt->clk);
- - if (!freq)
- - return 0;
- -
- timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR);
-
- - do_div(timer_counter, freq);
- + timer_counter *= 1000;
- +
- + do_div(timer_counter, wdt->freq);
-
- return timer_counter;
- }
-
- +static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)
- +{
- + return rti_wdt_get_timeleft_ms(wdd) / 1000;
- +}
- +
- static const struct watchdog_info rti_wdt_info = {
- .options = WDIOF_KEEPALIVEPING,
- .identity = "K3 RTI Watchdog",
- @@ -149,23 +197,43 @@ static int rti_wdt_probe(struct platform_device *pdev)
- struct resource *wdt_mem;
- struct watchdog_device *wdd;
- struct rti_wdt_device *wdt;
- + struct clk *clk;
- + u32 last_ping = 0;
-
- wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
- if (!wdt)
- return -ENOMEM;
-
- - wdt->clk = devm_clk_get(dev, NULL);
- - if (IS_ERR(wdt->clk)) {
- - if (PTR_ERR(wdt->clk) != -EPROBE_DEFER)
- + clk = clk_get(dev, NULL);
- + if (IS_ERR(clk)) {
- + if (PTR_ERR(clk) != -EPROBE_DEFER)
- dev_err(dev, "failed to get clock\n");
- - return PTR_ERR(wdt->clk);
- + return PTR_ERR(clk);
- + }
- +
- + wdt->freq = clk_get_rate(clk);
- +
- + clk_put(clk);
- +
- + if (!wdt->freq) {
- + dev_err(dev, "Failed to get fck rate.\n");
- + 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 < 32768)
- + wdt->freq = wdt->freq * 9 / 10;
- +
- pm_runtime_enable(dev);
- ret = pm_runtime_get_sync(dev);
- if (ret) {
- + pm_runtime_put_noidle(dev);
- if (ret != -EPROBE_DEFER)
- - dev_err(&pdev->dev, "runtime pm failed\n");
- + dev_err(dev, "runtime pm failed\n");
- return ret;
- }
-
- @@ -175,18 +243,10 @@ static int rti_wdt_probe(struct platform_device *pdev)
- wdd->info = &rti_wdt_info;
- wdd->ops = &rti_wdt_ops;
- wdd->min_timeout = 1;
- - /* Set min heartbeat to 1.1x window size */
- - wdd->min_hw_heartbeat_ms = 11 * DEFAULT_HEARTBEAT * 1000 / 20;
- - wdd->max_hw_heartbeat_ms = MAX_HEARTBEAT * 1000;
- - wdd->timeout = DEFAULT_HEARTBEAT;
- + wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
- + wdt->freq * 1000;
- wdd->parent = dev;
-
- - set_bit(WDOG_RESET_KEEPALIVE, &wdd->status);
- -
- - watchdog_init_timeout(wdd, heartbeat, dev);
- -
- - dev_info(dev, "heartbeat %d sec\n", wdd->timeout);
- -
- watchdog_set_drvdata(wdd, wdt);
- watchdog_set_nowayout(wdd, 1);
- watchdog_set_restart_priority(wdd, 128);
- @@ -198,16 +258,53 @@ static int rti_wdt_probe(struct platform_device *pdev)
- goto err_iomap;
- }
-
- + if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) {
- + u32 time_left_ms;
- + u64 heartbeat_ms;
- + u32 wsize;
- +
- + set_bit(WDOG_HW_RUNNING, &wdd->status);
- + time_left_ms = rti_wdt_get_timeleft_ms(wdd);
- + heartbeat_ms = readl(wdt->base + RTIDWDPRLD);
- + heartbeat_ms <<= WDT_PRELOAD_SHIFT;
- + heartbeat_ms *= 1000;
- + do_div(heartbeat_ms, wdt->freq);
- + if (heartbeat_ms != heartbeat * 1000)
- + dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n");
- +
- + heartbeat = heartbeat_ms;
- + heartbeat /= 1000;
- +
- + wsize = readl(wdt->base + RTIWWDSIZECTRL);
- + ret = rti_wdt_setup_hw_hb(wdd, wsize);
- + if (ret) {
- + dev_err(dev, "bad window size.\n");
- + goto err_iomap;
- + }
- +
- + last_ping = heartbeat_ms - time_left_ms;
- + if (time_left_ms > heartbeat_ms) {
- + dev_warn(dev, "time_left > heartbeat? Assuming last ping just before now.\n");
- + last_ping = 0;
- + }
- + }
- +
- + watchdog_init_timeout(wdd, heartbeat, dev);
- +
- ret = watchdog_register_device(wdd);
- if (ret) {
- dev_err(dev, "cannot register watchdog device\n");
- goto err_iomap;
- }
-
- + if (last_ping)
- + watchdog_set_last_hw_keepalive(wdd, last_ping);
- +
- return 0;
-
- err_iomap:
- pm_runtime_put_sync(&pdev->dev);
- + pm_runtime_disable(&pdev->dev);
-
- return ret;
- }
- @@ -218,12 +315,13 @@ static int rti_wdt_remove(struct platform_device *pdev)
-
- watchdog_unregister_device(&wdt->wdd);
- pm_runtime_put(&pdev->dev);
- + pm_runtime_disable(&pdev->dev);
-
- return 0;
- }
-
- static const struct of_device_id rti_wdt_of_match[] = {
- - { .compatible = "ti,rti-wdt", },
- + { .compatible = "ti,j7-rti-wdt", },
- {},
- };
- MODULE_DEVICE_TABLE(of, rti_wdt_of_match);
- --
- 2.31.1
|