mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:19:30 +02:00
Merge tag 'watchdog-for-v7.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull watchdog fixes from Guenter Roeck: - core: Do not start hrtimer when pretimeout is zero - msc313e: Various fixes for issues reported by Sashiko - MAINTAINERS: Update URI for watchdog tree - sunxi_wdt: preserve boot-enabled watchdog * tag 'watchdog-for-v7.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: watchdog: msc313e: Sync timeout value if WDT was running at boot watchdog: msc313e: Fix undefined behavior watchdog: msc313e: Fix spurious reset on suspend watchdog: msc313e: Enable clock before accessing hardware registers watchdog: msc313e: Fix clock leak and spurious timer in settimeout() watchdog: msc313e: Avoid division by zero watchdog: fix hrtimer start when pretimeout is zero MAINTAINERS: Update URI for watchdog tree watchdog: msc313e: Fix NULL pointer dereference in PM callbacks watchdog: sunxi_wdt: preserve boot-enabled watchdog
This commit is contained in:
+1
-1
@@ -29357,7 +29357,7 @@ M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-watchdog@vger.kernel.org
|
||||
S: Maintained
|
||||
W: http://www.linux-watchdog.org/
|
||||
T: git git://www.linux-watchdog.org/linux-watchdog.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
|
||||
F: Documentation/devicetree/bindings/watchdog/
|
||||
F: Documentation/watchdog/
|
||||
F: drivers/watchdog/
|
||||
|
||||
@@ -31,20 +31,36 @@ struct msc313e_wdt_priv {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static u32 msc313e_wdt_get_hw_timeout(struct msc313e_wdt_priv *priv)
|
||||
{
|
||||
u16 low, high;
|
||||
|
||||
low = readw(priv->base + REG_WDT_MAX_PRD_L);
|
||||
high = readw(priv->base + REG_WDT_MAX_PRD_H);
|
||||
|
||||
return ((u32)high << 16) | low;
|
||||
}
|
||||
|
||||
static void msc313e_wdt_set_hw_timeout(struct msc313e_wdt_priv *priv,
|
||||
unsigned int timeout)
|
||||
{
|
||||
u32 t = timeout * clk_get_rate(priv->clk);
|
||||
|
||||
writew(t & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
|
||||
writew((t >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
|
||||
writew(1, priv->base + REG_WDT_CLR);
|
||||
}
|
||||
|
||||
static int msc313e_wdt_start(struct watchdog_device *wdev)
|
||||
{
|
||||
struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
u32 timeout;
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
timeout = wdev->timeout * clk_get_rate(priv->clk);
|
||||
writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
|
||||
writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
|
||||
writew(1, priv->base + REG_WDT_CLR);
|
||||
msc313e_wdt_set_hw_timeout(priv, wdev->timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -69,9 +85,13 @@ static int msc313e_wdt_stop(struct watchdog_device *wdev)
|
||||
|
||||
static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time)
|
||||
{
|
||||
struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
|
||||
wdev->timeout = new_time;
|
||||
|
||||
return msc313e_wdt_start(wdev);
|
||||
if (watchdog_hw_running(wdev) || watchdog_active(wdev))
|
||||
msc313e_wdt_set_hw_timeout(priv, wdev->timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info msc313e_wdt_ident = {
|
||||
@@ -97,6 +117,8 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct msc313e_wdt_priv *priv;
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@@ -116,27 +138,51 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
|
||||
priv->wdev.ops = &msc313e_wdt_ops,
|
||||
priv->wdev.parent = dev;
|
||||
priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT;
|
||||
priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
|
||||
rate = clk_get_rate(priv->clk);
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
priv->wdev.max_timeout = U32_MAX / rate;
|
||||
priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
|
||||
|
||||
/* If the period is non-zero the WDT is running */
|
||||
if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16))
|
||||
set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
|
||||
|
||||
watchdog_set_drvdata(&priv->wdev, priv);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
watchdog_init_timeout(&priv->wdev, timeout, dev);
|
||||
watchdog_stop_on_reboot(&priv->wdev);
|
||||
watchdog_stop_on_unregister(&priv->wdev);
|
||||
watchdog_stop_ping_on_suspend(&priv->wdev);
|
||||
|
||||
return devm_watchdog_register_device(dev, &priv->wdev);
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If the period is non-zero the WDT is running */
|
||||
if (msc313e_wdt_get_hw_timeout(priv)) {
|
||||
msc313e_wdt_set_hw_timeout(priv, priv->wdev.timeout);
|
||||
set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
|
||||
/*
|
||||
* Keep the clock enabled. The watchdog core will skip the next
|
||||
* start() and a future stop() will balance the CCF reference
|
||||
* count.
|
||||
*/
|
||||
} else {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &priv->wdev);
|
||||
|
||||
/* If the WDT is running and anything goes wrong, disable the clock. */
|
||||
if (ret && test_bit(WDOG_HW_RUNNING, &priv->wdev.status))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&priv->wdev))
|
||||
if (watchdog_active(&priv->wdev) || watchdog_hw_running(&priv->wdev))
|
||||
msc313e_wdt_stop(&priv->wdev);
|
||||
|
||||
return 0;
|
||||
@@ -146,7 +192,7 @@ static int __maybe_unused msc313e_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&priv->wdev))
|
||||
if (watchdog_active(&priv->wdev) || watchdog_hw_running(&priv->wdev))
|
||||
msc313e_wdt_start(&priv->wdev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -128,6 +128,38 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sunxi_wdt_is_running(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
|
||||
|
||||
return readl(sunxi_wdt->wdt_base + regs->wdt_mode) & WDT_MODE_EN;
|
||||
}
|
||||
|
||||
static unsigned int sunxi_wdt_get_timeout(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
|
||||
const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
|
||||
unsigned int timeout;
|
||||
u32 interval;
|
||||
|
||||
interval = readl(sunxi_wdt->wdt_base + regs->wdt_mode);
|
||||
interval >>= regs->wdt_timeout_shift;
|
||||
interval &= WDT_TIMEOUT_MASK;
|
||||
/* Round the 0.5-second interval up to the minimum representable timeout. */
|
||||
if (!interval)
|
||||
return WDT_MIN_TIMEOUT;
|
||||
|
||||
for (timeout = WDT_MIN_TIMEOUT;
|
||||
timeout < ARRAY_SIZE(wdt_timeout_map); timeout++) {
|
||||
if (wdt_timeout_map[timeout] == interval)
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/* Reserved interval encoding. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
@@ -259,6 +291,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sunxi_wdt_dev *sunxi_wdt;
|
||||
unsigned int running_timeout;
|
||||
int err;
|
||||
|
||||
sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL);
|
||||
@@ -286,7 +319,17 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt);
|
||||
|
||||
sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
|
||||
if (sunxi_wdt_is_running(&sunxi_wdt->wdt_dev)) {
|
||||
running_timeout = sunxi_wdt_get_timeout(&sunxi_wdt->wdt_dev);
|
||||
if (running_timeout)
|
||||
sunxi_wdt->wdt_dev.timeout = running_timeout;
|
||||
|
||||
err = sunxi_wdt_start(&sunxi_wdt->wdt_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
set_bit(WDOG_HW_RUNNING, &sunxi_wdt->wdt_dev.status);
|
||||
}
|
||||
|
||||
watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev);
|
||||
err = devm_watchdog_register_device(dev, &sunxi_wdt->wdt_dev);
|
||||
|
||||
@@ -30,6 +30,7 @@ void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd)
|
||||
void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd)
|
||||
{
|
||||
if (!(wdd->info->options & WDIOF_PRETIMEOUT) &&
|
||||
wdd->pretimeout &&
|
||||
!watchdog_pretimeout_invalid(wdd, wdd->pretimeout))
|
||||
hrtimer_start(&wdd->wd_data->pretimeout_timer,
|
||||
ktime_set(wdd->timeout - wdd->pretimeout, 0),
|
||||
|
||||
Reference in New Issue
Block a user