diff options
Diffstat (limited to 'drivers/watchdog/sp805_wdt.c')
-rw-r--r-- | drivers/watchdog/sp805_wdt.c | 63 |
1 files changed, 52 insertions, 11 deletions
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 9849db0743a7..072986d461b7 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -11,6 +11,7 @@ * warranty of any kind, whether express or implied. */ +#include <linux/acpi.h> #include <linux/device.h> #include <linux/resource.h> #include <linux/amba/bus.h> @@ -22,6 +23,7 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/of.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -42,6 +44,7 @@ /* control register masks */ #define INT_ENABLE (1 << 0) #define RESET_ENABLE (1 << 1) + #define ENABLE_MASK (INT_ENABLE | RESET_ENABLE) #define WDTINTCLR 0x00C #define WDTRIS 0x010 #define WDTMIS 0x014 @@ -65,6 +68,7 @@ struct sp805_wdt { spinlock_t lock; void __iomem *base; struct clk *clk; + u64 rate; struct amba_device *adev; unsigned int load_val; }; @@ -74,13 +78,22 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Set to 1 to keep watchdog running after device release"); +/* returns true if wdt is running; otherwise returns false */ +static bool wdt_is_running(struct watchdog_device *wdd) +{ + struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); + u32 wdtcontrol = readl_relaxed(wdt->base + WDTCONTROL); + + return (wdtcontrol & ENABLE_MASK) == ENABLE_MASK; +} + /* This routine finds load value that will reset system in required timout */ static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout) { struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); u64 load, rate; - rate = clk_get_rate(wdt->clk); + rate = wdt->rate; /* * sp805 runs counter with given value twice, after the end of first @@ -106,9 +119,7 @@ static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout) static unsigned int wdt_timeleft(struct watchdog_device *wdd) { struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); - u64 load, rate; - - rate = clk_get_rate(wdt->clk); + u64 load; spin_lock(&wdt->lock); load = readl_relaxed(wdt->base + WDTVALUE); @@ -118,7 +129,7 @@ static unsigned int wdt_timeleft(struct watchdog_device *wdd) load += wdt->load_val + 1; spin_unlock(&wdt->lock); - return div_u64(load, rate); + return div_u64(load, wdt->rate); } static int @@ -228,11 +239,25 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - wdt->clk = devm_clk_get(&adev->dev, NULL); - if (IS_ERR(wdt->clk)) { - dev_warn(&adev->dev, "Clock not found\n"); - ret = PTR_ERR(wdt->clk); - goto err; + if (adev->dev.of_node) { + wdt->clk = devm_clk_get(&adev->dev, NULL); + if (IS_ERR(wdt->clk)) { + dev_err(&adev->dev, "Clock not found\n"); + return PTR_ERR(wdt->clk); + } + wdt->rate = clk_get_rate(wdt->clk); + } else if (has_acpi_companion(&adev->dev)) { + /* + * When Driver probe with ACPI device, clock devices + * are not available, so watchdog rate get from + * clock-frequency property given in _DSD object. + */ + device_property_read_u64(&adev->dev, "clock-frequency", + &wdt->rate); + if (!wdt->rate) { + dev_err(&adev->dev, "no clock-frequency property\n"); + return -ENODEV; + } } wdt->adev = adev; @@ -244,7 +269,23 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) watchdog_set_nowayout(&wdt->wdd, nowayout); watchdog_set_drvdata(&wdt->wdd, wdt); watchdog_set_restart_priority(&wdt->wdd, 128); - wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT); + + /* + * If 'timeout-sec' devicetree property is specified, use that. + * Otherwise, use DEFAULT_TIMEOUT + */ + wdt->wdd.timeout = DEFAULT_TIMEOUT; + watchdog_init_timeout(&wdt->wdd, 0, &adev->dev); + wdt_setload(&wdt->wdd, wdt->wdd.timeout); + + /* + * If HW is already running, enable/reset the wdt and set the running + * bit to tell the wdt subsystem + */ + if (wdt_is_running(&wdt->wdd)) { + wdt_enable(&wdt->wdd); + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + } ret = watchdog_register_device(&wdt->wdd); if (ret) { |