mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
UPSTREAM: watchdog: dw_wdt: Support devices with asynch clocks
DW Watchdog IP core can be synthesised with asynchronous timer/APB
clocks support (WDT_ASYNC_CLK_MODE_ENABLE == 1). In this case
separate clock signals are supposed to be used to feed watchdog timer
and APB interface of the device. Currently the driver supports
the synchronous mode only. Since there is no way to determine which
mode was actually activated for device from its registers, we have to
rely on the platform device configuration data. If optional "pclk"
clock source is supplied, we consider the device working in asynchronous
mode, otherwise the driver falls back to the synchronous configuration.
Change-Id: I9c3bdc7e7b7b1fc0be0f7335cf3c1950169891d7
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org
Link: https://lore.kernel.org/r/20200530073557.22661-6-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
Signed-off-by: Simon Xue <xxm@rock-chips.com>
(cherry picked from commit a16f58bf15)
This commit is contained in:
@@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||
struct dw_wdt {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
unsigned long rate;
|
||||
struct watchdog_device wdd;
|
||||
struct reset_control *rst;
|
||||
@@ -212,6 +213,7 @@ static int dw_wdt_suspend(struct device *dev)
|
||||
dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
|
||||
dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
|
||||
|
||||
clk_disable_unprepare(dw_wdt->pclk);
|
||||
clk_disable_unprepare(dw_wdt->clk);
|
||||
|
||||
return 0;
|
||||
@@ -225,6 +227,12 @@ static int dw_wdt_resume(struct device *dev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(dw_wdt->pclk);
|
||||
if (err) {
|
||||
clk_disable_unprepare(dw_wdt->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
|
||||
writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
|
||||
|
||||
@@ -253,9 +261,18 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(dw_wdt->regs))
|
||||
return PTR_ERR(dw_wdt->regs);
|
||||
|
||||
dw_wdt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(dw_wdt->clk))
|
||||
return PTR_ERR(dw_wdt->clk);
|
||||
/*
|
||||
* Try to request the watchdog dedicated timer clock source. It must
|
||||
* be supplied if asynchronous mode is enabled. Otherwise fallback
|
||||
* to the common timer/bus clocks configuration, in which the very
|
||||
* first found clock supply both timer and APB signals.
|
||||
*/
|
||||
dw_wdt->clk = devm_clk_get(dev, "tclk");
|
||||
if (IS_ERR(dw_wdt->clk)) {
|
||||
dw_wdt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(dw_wdt->clk))
|
||||
return PTR_ERR(dw_wdt->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dw_wdt->clk);
|
||||
if (ret)
|
||||
@@ -267,10 +284,27 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
|
||||
goto out_disable_clk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Request APB clock if device is configured with async clocks mode.
|
||||
* In this case both tclk and pclk clocks are supposed to be specified.
|
||||
* Alas we can't know for sure whether async mode was really activated,
|
||||
* so the pclk phandle reference is left optional. If it couldn't be
|
||||
* found we consider the device configured in synchronous clocks mode.
|
||||
*/
|
||||
dw_wdt->pclk = devm_clk_get_optional(dev, "pclk");
|
||||
if (IS_ERR(dw_wdt->pclk)) {
|
||||
ret = PTR_ERR(dw_wdt->pclk);
|
||||
goto out_disable_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dw_wdt->pclk);
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
|
||||
dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
|
||||
if (IS_ERR(dw_wdt->rst)) {
|
||||
ret = PTR_ERR(dw_wdt->rst);
|
||||
goto out_disable_clk;
|
||||
goto out_disable_pclk;
|
||||
}
|
||||
|
||||
reset_control_deassert(dw_wdt->rst);
|
||||
@@ -306,10 +340,13 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
|
||||
|
||||
ret = watchdog_register_device(wdd);
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
goto out_disable_pclk;
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_pclk:
|
||||
clk_disable_unprepare(dw_wdt->pclk);
|
||||
|
||||
out_disable_clk:
|
||||
clk_disable_unprepare(dw_wdt->clk);
|
||||
return ret;
|
||||
@@ -321,6 +358,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev)
|
||||
|
||||
watchdog_unregister_device(&dw_wdt->wdd);
|
||||
reset_control_assert(dw_wdt->rst);
|
||||
clk_disable_unprepare(dw_wdt->pclk);
|
||||
clk_disable_unprepare(dw_wdt->clk);
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user