xref: /openbmc/linux/drivers/watchdog/armada_37xx_wdt.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
154e3d9b5SMarek Behún // SPDX-License-Identifier: GPL-2.0+
254e3d9b5SMarek Behún /*
354e3d9b5SMarek Behún  * Watchdog driver for Marvell Armada 37xx SoCs
454e3d9b5SMarek Behún  *
5b37c3848SMarek Behún  * Author: Marek Behún <kabel@kernel.org>
654e3d9b5SMarek Behún  */
754e3d9b5SMarek Behún 
854e3d9b5SMarek Behún #include <linux/clk.h>
954e3d9b5SMarek Behún #include <linux/err.h>
1054e3d9b5SMarek Behún #include <linux/interrupt.h>
1154e3d9b5SMarek Behún #include <linux/io.h>
1254e3d9b5SMarek Behún #include <linux/kernel.h>
1354e3d9b5SMarek Behún #include <linux/mfd/syscon.h>
1454e3d9b5SMarek Behún #include <linux/module.h>
1554e3d9b5SMarek Behún #include <linux/moduleparam.h>
1654e3d9b5SMarek Behún #include <linux/of.h>
1754e3d9b5SMarek Behún #include <linux/platform_device.h>
1854e3d9b5SMarek Behún #include <linux/regmap.h>
1954e3d9b5SMarek Behún #include <linux/types.h>
2054e3d9b5SMarek Behún #include <linux/watchdog.h>
2154e3d9b5SMarek Behún 
2254e3d9b5SMarek Behún /*
2354e3d9b5SMarek Behún  * There are four counters that can be used for watchdog on Armada 37xx.
2454e3d9b5SMarek Behún  * The addresses for counter control registers are register base plus ID*0x10,
2554e3d9b5SMarek Behún  * where ID is 0, 1, 2 or 3.
2654e3d9b5SMarek Behún  *
2754e3d9b5SMarek Behún  * In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter,
2854e3d9b5SMarek Behún  * while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is
2954e3d9b5SMarek Behún  * set to restart counting from initial value on counter ID 0 end count event.
3054e3d9b5SMarek Behún  * Pinging is done by forcing immediate end count event on counter ID 0.
3154e3d9b5SMarek Behún  * If only one counter was used, pinging would have to be implemented by
3254e3d9b5SMarek Behún  * disabling and enabling the counter, leaving the system in a vulnerable state
3354e3d9b5SMarek Behún  * for a (really) short period of time.
3454e3d9b5SMarek Behún  *
3554e3d9b5SMarek Behún  * Counters ID 2 and 3 are enabled by default even before U-Boot loads,
3654e3d9b5SMarek Behún  * therefore this driver does not provide a way to use them, eg. by setting a
3754e3d9b5SMarek Behún  * property in device tree.
3854e3d9b5SMarek Behún  */
3954e3d9b5SMarek Behún 
4054e3d9b5SMarek Behún #define CNTR_ID_RETRIGGER		0
4154e3d9b5SMarek Behún #define CNTR_ID_WDOG			1
4254e3d9b5SMarek Behún 
4354e3d9b5SMarek Behún /* relative to cpu_misc */
4454e3d9b5SMarek Behún #define WDT_TIMER_SELECT		0x64
4554e3d9b5SMarek Behún #define WDT_TIMER_SELECT_MASK		0xf
4654e3d9b5SMarek Behún #define WDT_TIMER_SELECT_VAL		BIT(CNTR_ID_WDOG)
4754e3d9b5SMarek Behún 
4854e3d9b5SMarek Behún /* relative to reg */
4954e3d9b5SMarek Behún #define CNTR_CTRL(id)			((id) * 0x10)
5054e3d9b5SMarek Behún #define CNTR_CTRL_ENABLE		0x0001
5154e3d9b5SMarek Behún #define CNTR_CTRL_ACTIVE		0x0002
5254e3d9b5SMarek Behún #define CNTR_CTRL_MODE_MASK		0x000c
5354e3d9b5SMarek Behún #define CNTR_CTRL_MODE_ONESHOT		0x0000
5454e3d9b5SMarek Behún #define CNTR_CTRL_MODE_HWSIG		0x000c
5554e3d9b5SMarek Behún #define CNTR_CTRL_TRIG_SRC_MASK		0x00f0
5654e3d9b5SMarek Behún #define CNTR_CTRL_TRIG_SRC_PREV_CNTR	0x0050
5754e3d9b5SMarek Behún #define CNTR_CTRL_PRESCALE_MASK		0xff00
5854e3d9b5SMarek Behún #define CNTR_CTRL_PRESCALE_MIN		2
5954e3d9b5SMarek Behún #define CNTR_CTRL_PRESCALE_SHIFT	8
6054e3d9b5SMarek Behún 
6154e3d9b5SMarek Behún #define CNTR_COUNT_LOW(id)		(CNTR_CTRL(id) + 0x4)
6254e3d9b5SMarek Behún #define CNTR_COUNT_HIGH(id)		(CNTR_CTRL(id) + 0x8)
6354e3d9b5SMarek Behún 
6454e3d9b5SMarek Behún #define WATCHDOG_TIMEOUT		120
6554e3d9b5SMarek Behún 
6654e3d9b5SMarek Behún static unsigned int timeout;
6754e3d9b5SMarek Behún module_param(timeout, int, 0);
6854e3d9b5SMarek Behún MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
6954e3d9b5SMarek Behún 
7054e3d9b5SMarek Behún static bool nowayout = WATCHDOG_NOWAYOUT;
7154e3d9b5SMarek Behún module_param(nowayout, bool, 0);
7254e3d9b5SMarek Behún MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
7354e3d9b5SMarek Behún 			   __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
7454e3d9b5SMarek Behún 
7554e3d9b5SMarek Behún struct armada_37xx_watchdog {
7654e3d9b5SMarek Behún 	struct watchdog_device wdt;
7754e3d9b5SMarek Behún 	struct regmap *cpu_misc;
7854e3d9b5SMarek Behún 	void __iomem *reg;
7954e3d9b5SMarek Behún 	u64 timeout; /* in clock ticks */
8054e3d9b5SMarek Behún 	unsigned long clk_rate;
8154e3d9b5SMarek Behún 	struct clk *clk;
8254e3d9b5SMarek Behún };
8354e3d9b5SMarek Behún 
get_counter_value(struct armada_37xx_watchdog * dev,int id)8454e3d9b5SMarek Behún static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id)
8554e3d9b5SMarek Behún {
8654e3d9b5SMarek Behún 	u64 val;
8754e3d9b5SMarek Behún 
8854e3d9b5SMarek Behún 	/*
8954e3d9b5SMarek Behún 	 * when low is read, high is latched into flip-flops so that it can be
9054e3d9b5SMarek Behún 	 * read consistently without using software debouncing
9154e3d9b5SMarek Behún 	 */
9254e3d9b5SMarek Behún 	val = readl(dev->reg + CNTR_COUNT_LOW(id));
9354e3d9b5SMarek Behún 	val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32;
9454e3d9b5SMarek Behún 
9554e3d9b5SMarek Behún 	return val;
9654e3d9b5SMarek Behún }
9754e3d9b5SMarek Behún 
set_counter_value(struct armada_37xx_watchdog * dev,int id,u64 val)9854e3d9b5SMarek Behún static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val)
9954e3d9b5SMarek Behún {
10054e3d9b5SMarek Behún 	writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id));
10154e3d9b5SMarek Behún 	writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id));
10254e3d9b5SMarek Behún }
10354e3d9b5SMarek Behún 
counter_enable(struct armada_37xx_watchdog * dev,int id)10454e3d9b5SMarek Behún static void counter_enable(struct armada_37xx_watchdog *dev, int id)
10554e3d9b5SMarek Behún {
10654e3d9b5SMarek Behún 	u32 reg;
10754e3d9b5SMarek Behún 
10854e3d9b5SMarek Behún 	reg = readl(dev->reg + CNTR_CTRL(id));
10954e3d9b5SMarek Behún 	reg |= CNTR_CTRL_ENABLE;
11054e3d9b5SMarek Behún 	writel(reg, dev->reg + CNTR_CTRL(id));
11154e3d9b5SMarek Behún }
11254e3d9b5SMarek Behún 
counter_disable(struct armada_37xx_watchdog * dev,int id)11354e3d9b5SMarek Behún static void counter_disable(struct armada_37xx_watchdog *dev, int id)
11454e3d9b5SMarek Behún {
11554e3d9b5SMarek Behún 	u32 reg;
11654e3d9b5SMarek Behún 
11754e3d9b5SMarek Behún 	reg = readl(dev->reg + CNTR_CTRL(id));
11854e3d9b5SMarek Behún 	reg &= ~CNTR_CTRL_ENABLE;
11954e3d9b5SMarek Behún 	writel(reg, dev->reg + CNTR_CTRL(id));
12054e3d9b5SMarek Behún }
12154e3d9b5SMarek Behún 
init_counter(struct armada_37xx_watchdog * dev,int id,u32 mode,u32 trig_src)12254e3d9b5SMarek Behún static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode,
12354e3d9b5SMarek Behún 			 u32 trig_src)
12454e3d9b5SMarek Behún {
12554e3d9b5SMarek Behún 	u32 reg;
12654e3d9b5SMarek Behún 
12754e3d9b5SMarek Behún 	reg = readl(dev->reg + CNTR_CTRL(id));
12854e3d9b5SMarek Behún 
12954e3d9b5SMarek Behún 	reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
13054e3d9b5SMarek Behún 		 CNTR_CTRL_TRIG_SRC_MASK);
13154e3d9b5SMarek Behún 
13254e3d9b5SMarek Behún 	/* set mode */
13354e3d9b5SMarek Behún 	reg |= mode & CNTR_CTRL_MODE_MASK;
13454e3d9b5SMarek Behún 
13554e3d9b5SMarek Behún 	/* set prescaler to the min value */
13654e3d9b5SMarek Behún 	reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
13754e3d9b5SMarek Behún 
13854e3d9b5SMarek Behún 	/* set trigger source */
13954e3d9b5SMarek Behún 	reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK;
14054e3d9b5SMarek Behún 
14154e3d9b5SMarek Behún 	writel(reg, dev->reg + CNTR_CTRL(id));
14254e3d9b5SMarek Behún }
14354e3d9b5SMarek Behún 
armada_37xx_wdt_ping(struct watchdog_device * wdt)14454e3d9b5SMarek Behún static int armada_37xx_wdt_ping(struct watchdog_device *wdt)
14554e3d9b5SMarek Behún {
14654e3d9b5SMarek Behún 	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
14754e3d9b5SMarek Behún 
14854e3d9b5SMarek Behún 	/* counter 1 is retriggered by forcing end count on counter 0 */
14954e3d9b5SMarek Behún 	counter_disable(dev, CNTR_ID_RETRIGGER);
15054e3d9b5SMarek Behún 	counter_enable(dev, CNTR_ID_RETRIGGER);
15154e3d9b5SMarek Behún 
15254e3d9b5SMarek Behún 	return 0;
15354e3d9b5SMarek Behún }
15454e3d9b5SMarek Behún 
armada_37xx_wdt_get_timeleft(struct watchdog_device * wdt)15554e3d9b5SMarek Behún static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt)
15654e3d9b5SMarek Behún {
15754e3d9b5SMarek Behún 	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
158c8ca6e70SMarek Behún 	u64 res;
15954e3d9b5SMarek Behún 
160c8ca6e70SMarek Behún 	res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN;
161c8ca6e70SMarek Behún 	do_div(res, dev->clk_rate);
16254e3d9b5SMarek Behún 
16354e3d9b5SMarek Behún 	return res;
16454e3d9b5SMarek Behún }
16554e3d9b5SMarek Behún 
armada_37xx_wdt_set_timeout(struct watchdog_device * wdt,unsigned int timeout)16654e3d9b5SMarek Behún static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
16754e3d9b5SMarek Behún 				       unsigned int timeout)
16854e3d9b5SMarek Behún {
16954e3d9b5SMarek Behún 	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
17054e3d9b5SMarek Behún 
17154e3d9b5SMarek Behún 	wdt->timeout = timeout;
17254e3d9b5SMarek Behún 
17354e3d9b5SMarek Behún 	/*
17454e3d9b5SMarek Behún 	 * Compute the timeout in clock rate. We use smallest possible
17554e3d9b5SMarek Behún 	 * prescaler, which divides the clock rate by 2
17654e3d9b5SMarek Behún 	 * (CNTR_CTRL_PRESCALE_MIN).
17754e3d9b5SMarek Behún 	 */
178c8ca6e70SMarek Behún 	dev->timeout = (u64)dev->clk_rate * timeout;
179c8ca6e70SMarek Behún 	do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
18054e3d9b5SMarek Behún 
18180079353SPali Rohár 	set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
18280079353SPali Rohár 
18354e3d9b5SMarek Behún 	return 0;
18454e3d9b5SMarek Behún }
18554e3d9b5SMarek Behún 
armada_37xx_wdt_is_running(struct armada_37xx_watchdog * dev)18654e3d9b5SMarek Behún static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev)
18754e3d9b5SMarek Behún {
18854e3d9b5SMarek Behún 	u32 reg;
18954e3d9b5SMarek Behún 
19054e3d9b5SMarek Behún 	regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, &reg);
19154e3d9b5SMarek Behún 	if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL)
19254e3d9b5SMarek Behún 		return false;
19354e3d9b5SMarek Behún 
19454e3d9b5SMarek Behún 	reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG));
19554e3d9b5SMarek Behún 	return !!(reg & CNTR_CTRL_ACTIVE);
19654e3d9b5SMarek Behún }
19754e3d9b5SMarek Behún 
armada_37xx_wdt_start(struct watchdog_device * wdt)19854e3d9b5SMarek Behún static int armada_37xx_wdt_start(struct watchdog_device *wdt)
19954e3d9b5SMarek Behún {
20054e3d9b5SMarek Behún 	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
20154e3d9b5SMarek Behún 
20254e3d9b5SMarek Behún 	/* select counter 1 as watchdog counter */
20354e3d9b5SMarek Behún 	regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL);
20454e3d9b5SMarek Behún 
20554e3d9b5SMarek Behún 	/* init counter 0 as retrigger counter for counter 1 */
20654e3d9b5SMarek Behún 	init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0);
20754e3d9b5SMarek Behún 	set_counter_value(dev, CNTR_ID_RETRIGGER, 0);
20854e3d9b5SMarek Behún 
20954e3d9b5SMarek Behún 	/* init counter 1 to be retriggerable by counter 0 end count */
21054e3d9b5SMarek Behún 	init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG,
21154e3d9b5SMarek Behún 		     CNTR_CTRL_TRIG_SRC_PREV_CNTR);
21254e3d9b5SMarek Behún 	set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
21354e3d9b5SMarek Behún 
21454e3d9b5SMarek Behún 	/* enable counter 1 */
21554e3d9b5SMarek Behún 	counter_enable(dev, CNTR_ID_WDOG);
21654e3d9b5SMarek Behún 
21754e3d9b5SMarek Behún 	/* start counter 1 by forcing immediate end count on counter 0 */
21854e3d9b5SMarek Behún 	counter_enable(dev, CNTR_ID_RETRIGGER);
21954e3d9b5SMarek Behún 
22054e3d9b5SMarek Behún 	return 0;
22154e3d9b5SMarek Behún }
22254e3d9b5SMarek Behún 
armada_37xx_wdt_stop(struct watchdog_device * wdt)22354e3d9b5SMarek Behún static int armada_37xx_wdt_stop(struct watchdog_device *wdt)
22454e3d9b5SMarek Behún {
22554e3d9b5SMarek Behún 	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
22654e3d9b5SMarek Behún 
22754e3d9b5SMarek Behún 	counter_disable(dev, CNTR_ID_WDOG);
22854e3d9b5SMarek Behún 	counter_disable(dev, CNTR_ID_RETRIGGER);
22954e3d9b5SMarek Behún 	regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0);
23054e3d9b5SMarek Behún 
23154e3d9b5SMarek Behún 	return 0;
23254e3d9b5SMarek Behún }
23354e3d9b5SMarek Behún 
23454e3d9b5SMarek Behún static const struct watchdog_info armada_37xx_wdt_info = {
23554e3d9b5SMarek Behún 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
23654e3d9b5SMarek Behún 	.identity = "Armada 37xx Watchdog",
23754e3d9b5SMarek Behún };
23854e3d9b5SMarek Behún 
23954e3d9b5SMarek Behún static const struct watchdog_ops armada_37xx_wdt_ops = {
24054e3d9b5SMarek Behún 	.owner = THIS_MODULE,
24154e3d9b5SMarek Behún 	.start = armada_37xx_wdt_start,
24254e3d9b5SMarek Behún 	.stop = armada_37xx_wdt_stop,
24354e3d9b5SMarek Behún 	.ping = armada_37xx_wdt_ping,
24454e3d9b5SMarek Behún 	.set_timeout = armada_37xx_wdt_set_timeout,
24554e3d9b5SMarek Behún 	.get_timeleft = armada_37xx_wdt_get_timeleft,
24654e3d9b5SMarek Behún };
24754e3d9b5SMarek Behún 
armada_37xx_wdt_probe(struct platform_device * pdev)24854e3d9b5SMarek Behún static int armada_37xx_wdt_probe(struct platform_device *pdev)
24954e3d9b5SMarek Behún {
25054e3d9b5SMarek Behún 	struct armada_37xx_watchdog *dev;
25154e3d9b5SMarek Behún 	struct resource *res;
25254e3d9b5SMarek Behún 	struct regmap *regmap;
25354e3d9b5SMarek Behún 	int ret;
25454e3d9b5SMarek Behún 
25554e3d9b5SMarek Behún 	dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog),
25654e3d9b5SMarek Behún 			   GFP_KERNEL);
25754e3d9b5SMarek Behún 	if (!dev)
25854e3d9b5SMarek Behún 		return -ENOMEM;
25954e3d9b5SMarek Behún 
26054e3d9b5SMarek Behún 	dev->wdt.info = &armada_37xx_wdt_info;
26154e3d9b5SMarek Behún 	dev->wdt.ops = &armada_37xx_wdt_ops;
26254e3d9b5SMarek Behún 
26354e3d9b5SMarek Behún 	regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
26454e3d9b5SMarek Behún 						 "marvell,system-controller");
26554e3d9b5SMarek Behún 	if (IS_ERR(regmap))
26654e3d9b5SMarek Behún 		return PTR_ERR(regmap);
26754e3d9b5SMarek Behún 	dev->cpu_misc = regmap;
26854e3d9b5SMarek Behún 
26954e3d9b5SMarek Behún 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
27054e3d9b5SMarek Behún 	if (!res)
27154e3d9b5SMarek Behún 		return -ENODEV;
27254e3d9b5SMarek Behún 	dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
2732d27e528SWilliam Dean 	if (!dev->reg)
2742d27e528SWilliam Dean 		return -ENOMEM;
27554e3d9b5SMarek Behún 
27654e3d9b5SMarek Behún 	/* init clock */
277*cd9e5d04SChristophe JAILLET 	dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
27854e3d9b5SMarek Behún 	if (IS_ERR(dev->clk))
27954e3d9b5SMarek Behún 		return PTR_ERR(dev->clk);
28054e3d9b5SMarek Behún 
28154e3d9b5SMarek Behún 	dev->clk_rate = clk_get_rate(dev->clk);
282b09c1539SGuenter Roeck 	if (!dev->clk_rate)
283b09c1539SGuenter Roeck 		return -EINVAL;
28454e3d9b5SMarek Behún 
28554e3d9b5SMarek Behún 	/*
28654e3d9b5SMarek Behún 	 * Since the timeout in seconds is given as 32 bit unsigned int, and
28754e3d9b5SMarek Behún 	 * the counters hold 64 bit values, even after multiplication by clock
28854e3d9b5SMarek Behún 	 * rate the counter can hold timeout of UINT_MAX seconds.
28954e3d9b5SMarek Behún 	 */
29054e3d9b5SMarek Behún 	dev->wdt.min_timeout = 1;
29154e3d9b5SMarek Behún 	dev->wdt.max_timeout = UINT_MAX;
29254e3d9b5SMarek Behún 	dev->wdt.parent = &pdev->dev;
29354e3d9b5SMarek Behún 
29454e3d9b5SMarek Behún 	/* default value, possibly override by module parameter or dtb */
29554e3d9b5SMarek Behún 	dev->wdt.timeout = WATCHDOG_TIMEOUT;
29654e3d9b5SMarek Behún 	watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
29754e3d9b5SMarek Behún 
29854e3d9b5SMarek Behún 	platform_set_drvdata(pdev, &dev->wdt);
29954e3d9b5SMarek Behún 	watchdog_set_drvdata(&dev->wdt, dev);
30054e3d9b5SMarek Behún 
30154e3d9b5SMarek Behún 	armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout);
30254e3d9b5SMarek Behún 
30354e3d9b5SMarek Behún 	if (armada_37xx_wdt_is_running(dev))
30454e3d9b5SMarek Behún 		set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
30554e3d9b5SMarek Behún 
30654e3d9b5SMarek Behún 	watchdog_set_nowayout(&dev->wdt, nowayout);
307b09c1539SGuenter Roeck 	watchdog_stop_on_reboot(&dev->wdt);
308b09c1539SGuenter Roeck 	ret = devm_watchdog_register_device(&pdev->dev, &dev->wdt);
30954e3d9b5SMarek Behún 	if (ret)
310b09c1539SGuenter Roeck 		return ret;
31154e3d9b5SMarek Behún 
31254e3d9b5SMarek Behún 	dev_info(&pdev->dev, "Initial timeout %d sec%s\n",
31354e3d9b5SMarek Behún 		 dev->wdt.timeout, nowayout ? ", nowayout" : "");
31454e3d9b5SMarek Behún 
31554e3d9b5SMarek Behún 	return 0;
31654e3d9b5SMarek Behún }
31754e3d9b5SMarek Behún 
armada_37xx_wdt_suspend(struct device * dev)31854e3d9b5SMarek Behún static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev)
31954e3d9b5SMarek Behún {
32054e3d9b5SMarek Behún 	struct watchdog_device *wdt = dev_get_drvdata(dev);
32154e3d9b5SMarek Behún 
32254e3d9b5SMarek Behún 	return armada_37xx_wdt_stop(wdt);
32354e3d9b5SMarek Behún }
32454e3d9b5SMarek Behún 
armada_37xx_wdt_resume(struct device * dev)32554e3d9b5SMarek Behún static int __maybe_unused armada_37xx_wdt_resume(struct device *dev)
32654e3d9b5SMarek Behún {
32754e3d9b5SMarek Behún 	struct watchdog_device *wdt = dev_get_drvdata(dev);
32854e3d9b5SMarek Behún 
32954e3d9b5SMarek Behún 	if (watchdog_active(wdt))
33054e3d9b5SMarek Behún 		return armada_37xx_wdt_start(wdt);
33154e3d9b5SMarek Behún 
33254e3d9b5SMarek Behún 	return 0;
33354e3d9b5SMarek Behún }
33454e3d9b5SMarek Behún 
33554e3d9b5SMarek Behún static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = {
33654e3d9b5SMarek Behún 	SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend,
33754e3d9b5SMarek Behún 				armada_37xx_wdt_resume)
33854e3d9b5SMarek Behún };
33954e3d9b5SMarek Behún 
34054e3d9b5SMarek Behún #ifdef CONFIG_OF
34154e3d9b5SMarek Behún static const struct of_device_id armada_37xx_wdt_match[] = {
34254e3d9b5SMarek Behún 	{ .compatible = "marvell,armada-3700-wdt", },
34354e3d9b5SMarek Behún 	{},
34454e3d9b5SMarek Behún };
34554e3d9b5SMarek Behún MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match);
34654e3d9b5SMarek Behún #endif
34754e3d9b5SMarek Behún 
34854e3d9b5SMarek Behún static struct platform_driver armada_37xx_wdt_driver = {
34954e3d9b5SMarek Behún 	.probe		= armada_37xx_wdt_probe,
35054e3d9b5SMarek Behún 	.driver		= {
35154e3d9b5SMarek Behún 		.name	= "armada_37xx_wdt",
35254e3d9b5SMarek Behún 		.of_match_table = of_match_ptr(armada_37xx_wdt_match),
35354e3d9b5SMarek Behún 		.pm = &armada_37xx_wdt_dev_pm_ops,
35454e3d9b5SMarek Behún 	},
35554e3d9b5SMarek Behún };
35654e3d9b5SMarek Behún 
35754e3d9b5SMarek Behún module_platform_driver(armada_37xx_wdt_driver);
35854e3d9b5SMarek Behún 
359b37c3848SMarek Behún MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
36054e3d9b5SMarek Behún MODULE_DESCRIPTION("Armada 37xx CPU Watchdog");
36154e3d9b5SMarek Behún 
36254e3d9b5SMarek Behún MODULE_LICENSE("GPL v2");
36354e3d9b5SMarek Behún MODULE_ALIAS("platform:armada_37xx_wdt");
364