Home Home > GIT Browse > openSUSE-42.3
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKernel Build Daemon <kbuild@suse.de>2018-07-12 07:14:32 +0200
committerKernel Build Daemon <kbuild@suse.de>2018-07-12 07:14:32 +0200
commit27667421e5aa7d36e7387dfd0c1bf250704033db (patch)
treea6cf203bc77c9355793492bec87c1aa5b0792158
parent7c38217594c2d48d9ae1d9fba53af29f168ef60f (diff)
parent9619a0f1c7f17e6c8458f79155819213049033db (diff)
Merge branch 'SLE12-SP3' into openSUSE-42.3
-rw-r--r--drivers/watchdog/da9063_wdt.c64
1 files changed, 60 insertions, 4 deletions
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index 6bf130bd863d..2b2326586258 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -54,8 +54,43 @@ static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
return DA9063_TWDSCALE_MAX;
}
+/*
+ * Return 0 if watchdog is disabled, else non zero.
+ */
+static unsigned int da9063_wdt_is_running(struct da9063 *da9063)
+{
+ unsigned int val;
+
+ regmap_read(da9063->regmap, DA9063_REG_CONTROL_D, &val);
+
+ return val & DA9063_TWDSCALE_MASK;
+}
+
+static int da9063_wdt_disable_timer(struct da9063 *da9063)
+{
+ return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
+ DA9063_TWDSCALE_MASK,
+ DA9063_TWDSCALE_DISABLE);
+}
+
static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval)
{
+ int ret;
+
+ /*
+ * The watchdog triggers a reboot if a timeout value is already
+ * programmed because the timeout value combines two functions
+ * in one: indicating the counter limit and starting the watchdog.
+ * The watchdog must be disabled to be able to change the timeout
+ * value if the watchdog is already running. Then we can set the
+ * new timeout value which enables the watchdog again.
+ */
+ ret = da9063_wdt_disable_timer(da9063);
+ if (ret)
+ return ret;
+
+ usleep_range(150, 300);
+
return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
DA9063_TWDSCALE_MASK, regval);
}
@@ -80,8 +115,7 @@ static int da9063_wdt_stop(struct watchdog_device *wdd)
struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
int ret;
- ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D,
- DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE);
+ ret = da9063_wdt_disable_timer(da9063);
if (ret)
dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n",
ret);
@@ -108,10 +142,23 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
{
struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
unsigned int selector;
- int ret;
+ int ret = 0;
selector = da9063_wdt_timeout_to_sel(timeout);
- ret = _da9063_wdt_set_timeout(wdt->da9063, selector);
+
+ /*
+ * There are two cases when a set_timeout() will be called:
+ * 1. The watchdog is off and someone wants to set the timeout for the
+ * further use.
+ * 2. The watchdog is already running and a new timeout value should be
+ * set.
+ *
+ * The watchdog can't store a timeout value not equal zero without
+ * enabling the watchdog, so the timeout must be buffered by the driver.
+ */
+ if (watchdog_active(wdd))
+ ret = _da9063_wdt_set_timeout(da9063, selector);
+
if (ret)
dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n",
ret);
@@ -204,6 +251,15 @@ static int da9063_wdt_remove(struct platform_device *pdev)
watchdog_unregister_device(&wdt->wdtdev);
+ /* Change the timeout to the default value if the watchdog is running */
+ if (da9063_wdt_is_running(da9063)) {
+ unsigned int timeout;
+
+ timeout = da9063_wdt_timeout_to_sel(DA9063_WDG_TIMEOUT);
+ _da9063_wdt_set_timeout(da9063, timeout);
+ set_bit(WDOG_HW_RUNNING, &wdd->status);
+ }
+
return 0;
}