xref: /openbmc/linux/drivers/watchdog/sbsa_gwdt.c (revision 16da1e7c)
19f806850SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
257d2caaaSFu Wei /*
357d2caaaSFu Wei  * SBSA(Server Base System Architecture) Generic Watchdog driver
457d2caaaSFu Wei  *
557d2caaaSFu Wei  * Copyright (c) 2015, Linaro Ltd.
657d2caaaSFu Wei  * Author: Fu Wei <fu.wei@linaro.org>
757d2caaaSFu Wei  *         Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
857d2caaaSFu Wei  *         Al Stone <al.stone@linaro.org>
957d2caaaSFu Wei  *         Timur Tabi <timur@codeaurora.org>
1057d2caaaSFu Wei  *
1157d2caaaSFu Wei  * ARM SBSA Generic Watchdog has two stage timeouts:
1257d2caaaSFu Wei  * the first signal (WS0) is for alerting the system by interrupt,
1357d2caaaSFu Wei  * the second one (WS1) is a real hardware reset.
1457d2caaaSFu Wei  * More details about the hardware specification of this device:
1557d2caaaSFu Wei  * ARM DEN0029B - Server Base System Architecture (SBSA)
1657d2caaaSFu Wei  *
1757d2caaaSFu Wei  * This driver can operate ARM SBSA Generic Watchdog as a single stage watchdog
1857d2caaaSFu Wei  * or a two stages watchdog, it's set up by the module parameter "action".
1957d2caaaSFu Wei  * In the single stage mode, when the timeout is reached, your system
2057d2caaaSFu Wei  * will be reset by WS1. The first signal (WS0) is ignored.
2157d2caaaSFu Wei  * In the two stages mode, when the timeout is reached, the first signal (WS0)
2257d2caaaSFu Wei  * will trigger panic. If the system is getting into trouble and cannot be reset
2357d2caaaSFu Wei  * by panic or restart properly by the kdump kernel(if supported), then the
2457d2caaaSFu Wei  * second stage (as long as the first stage) will be reached, system will be
2557d2caaaSFu Wei  * reset by WS1. This function can help administrator to backup the system
2657d2caaaSFu Wei  * context info by panic console output or kdump.
2757d2caaaSFu Wei  *
2857d2caaaSFu Wei  * SBSA GWDT:
2957d2caaaSFu Wei  * if action is 1 (the two stages mode):
3057d2caaaSFu Wei  * |--------WOR-------WS0--------WOR-------WS1
3157d2caaaSFu Wei  * |----timeout-----(panic)----timeout-----reset
3257d2caaaSFu Wei  *
3357d2caaaSFu Wei  * if action is 0 (the single stage mode):
3457d2caaaSFu Wei  * |------WOR-----WS0(ignored)-----WOR------WS1
3557d2caaaSFu Wei  * |--------------timeout-------------------reset
3657d2caaaSFu Wei  *
3757d2caaaSFu Wei  * Note: Since this watchdog timer has two stages, and each stage is determined
3857d2caaaSFu Wei  * by WOR, in the single stage mode, the timeout is (WOR * 2); in the two
3957d2caaaSFu Wei  * stages mode, the timeout is WOR. The maximum timeout in the two stages mode
4057d2caaaSFu Wei  * is half of that in the single stage mode.
4157d2caaaSFu Wei  */
4257d2caaaSFu Wei 
4357d2caaaSFu Wei #include <linux/io.h>
4493ac3debSJayachandran C #include <linux/io-64-nonatomic-lo-hi.h>
4557d2caaaSFu Wei #include <linux/interrupt.h>
46cc85f87aSRob Herring #include <linux/mod_devicetable.h>
4757d2caaaSFu Wei #include <linux/module.h>
4857d2caaaSFu Wei #include <linux/moduleparam.h>
4957d2caaaSFu Wei #include <linux/platform_device.h>
5057d2caaaSFu Wei #include <linux/uaccess.h>
5157d2caaaSFu Wei #include <linux/watchdog.h>
5257d2caaaSFu Wei #include <asm/arch_timer.h>
5357d2caaaSFu Wei 
5457d2caaaSFu Wei #define DRV_NAME		"sbsa-gwdt"
5557d2caaaSFu Wei #define WATCHDOG_NAME		"SBSA Generic Watchdog"
5657d2caaaSFu Wei 
5757d2caaaSFu Wei /* SBSA Generic Watchdog register definitions */
5857d2caaaSFu Wei /* refresh frame */
5957d2caaaSFu Wei #define SBSA_GWDT_WRR		0x000
6057d2caaaSFu Wei 
6157d2caaaSFu Wei /* control frame */
6257d2caaaSFu Wei #define SBSA_GWDT_WCS		0x000
6357d2caaaSFu Wei #define SBSA_GWDT_WOR		0x008
6457d2caaaSFu Wei #define SBSA_GWDT_WCV		0x010
6557d2caaaSFu Wei 
6657d2caaaSFu Wei /* refresh/control frame */
6757d2caaaSFu Wei #define SBSA_GWDT_W_IIDR	0xfcc
6857d2caaaSFu Wei #define SBSA_GWDT_IDR		0xfd0
6957d2caaaSFu Wei 
7057d2caaaSFu Wei /* Watchdog Control and Status Register */
7157d2caaaSFu Wei #define SBSA_GWDT_WCS_EN	BIT(0)
7257d2caaaSFu Wei #define SBSA_GWDT_WCS_WS0	BIT(1)
7357d2caaaSFu Wei #define SBSA_GWDT_WCS_WS1	BIT(2)
7457d2caaaSFu Wei 
75abd3ac79SShaokun Zhang #define SBSA_GWDT_VERSION_MASK  0xF
76abd3ac79SShaokun Zhang #define SBSA_GWDT_VERSION_SHIFT 16
77abd3ac79SShaokun Zhang 
7857d2caaaSFu Wei /**
7957d2caaaSFu Wei  * struct sbsa_gwdt - Internal representation of the SBSA GWDT
8057d2caaaSFu Wei  * @wdd:		kernel watchdog_device structure
8157d2caaaSFu Wei  * @clk:		store the System Counter clock frequency, in Hz.
82abd3ac79SShaokun Zhang  * @version:            store the architecture version
8357d2caaaSFu Wei  * @refresh_base:	Virtual address of the watchdog refresh frame
8457d2caaaSFu Wei  * @control_base:	Virtual address of the watchdog control frame
8557d2caaaSFu Wei  */
8657d2caaaSFu Wei struct sbsa_gwdt {
8757d2caaaSFu Wei 	struct watchdog_device	wdd;
8857d2caaaSFu Wei 	u32			clk;
89abd3ac79SShaokun Zhang 	int			version;
9057d2caaaSFu Wei 	void __iomem		*refresh_base;
9157d2caaaSFu Wei 	void __iomem		*control_base;
9257d2caaaSFu Wei };
9357d2caaaSFu Wei 
9457d2caaaSFu Wei #define DEFAULT_TIMEOUT		10 /* seconds */
9557d2caaaSFu Wei 
9657d2caaaSFu Wei static unsigned int timeout;
9757d2caaaSFu Wei module_param(timeout, uint, 0);
9857d2caaaSFu Wei MODULE_PARM_DESC(timeout,
9957d2caaaSFu Wei 		 "Watchdog timeout in seconds. (>=0, default="
10057d2caaaSFu Wei 		 __MODULE_STRING(DEFAULT_TIMEOUT) ")");
10157d2caaaSFu Wei 
10257d2caaaSFu Wei /*
10357d2caaaSFu Wei  * action refers to action taken when watchdog gets WS0
10457d2caaaSFu Wei  * 0 = skip
10557d2caaaSFu Wei  * 1 = panic
10657d2caaaSFu Wei  * defaults to skip (0)
10757d2caaaSFu Wei  */
10857d2caaaSFu Wei static int action;
10957d2caaaSFu Wei module_param(action, int, 0);
11057d2caaaSFu Wei MODULE_PARM_DESC(action, "after watchdog gets WS0 interrupt, do: "
11157d2caaaSFu Wei 		 "0 = skip(*)  1 = panic");
11257d2caaaSFu Wei 
11357d2caaaSFu Wei static bool nowayout = WATCHDOG_NOWAYOUT;
11457d2caaaSFu Wei module_param(nowayout, bool, S_IRUGO);
11557d2caaaSFu Wei MODULE_PARM_DESC(nowayout,
11657d2caaaSFu Wei 		 "Watchdog cannot be stopped once started (default="
11757d2caaaSFu Wei 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
11857d2caaaSFu Wei 
11957d2caaaSFu Wei /*
120abd3ac79SShaokun Zhang  * Arm Base System Architecture 1.0 introduces watchdog v1 which
121abd3ac79SShaokun Zhang  * increases the length watchdog offset register to 48 bits.
122abd3ac79SShaokun Zhang  * - For version 0: WOR is 32 bits;
123abd3ac79SShaokun Zhang  * - For version 1: WOR is 48 bits which comprises the register
124abd3ac79SShaokun Zhang  * offset 0x8 and 0xC, and the bits [63:48] are reserved which are
125abd3ac79SShaokun Zhang  * Read-As-Zero and Writes-Ignored.
126abd3ac79SShaokun Zhang  */
sbsa_gwdt_reg_read(struct sbsa_gwdt * gwdt)127abd3ac79SShaokun Zhang static u64 sbsa_gwdt_reg_read(struct sbsa_gwdt *gwdt)
128abd3ac79SShaokun Zhang {
129abd3ac79SShaokun Zhang 	if (gwdt->version == 0)
130abd3ac79SShaokun Zhang 		return readl(gwdt->control_base + SBSA_GWDT_WOR);
131abd3ac79SShaokun Zhang 	else
132f31afb50SJamie Iles 		return lo_hi_readq(gwdt->control_base + SBSA_GWDT_WOR);
133abd3ac79SShaokun Zhang }
134abd3ac79SShaokun Zhang 
sbsa_gwdt_reg_write(u64 val,struct sbsa_gwdt * gwdt)135abd3ac79SShaokun Zhang static void sbsa_gwdt_reg_write(u64 val, struct sbsa_gwdt *gwdt)
136abd3ac79SShaokun Zhang {
137abd3ac79SShaokun Zhang 	if (gwdt->version == 0)
138abd3ac79SShaokun Zhang 		writel((u32)val, gwdt->control_base + SBSA_GWDT_WOR);
139abd3ac79SShaokun Zhang 	else
140f31afb50SJamie Iles 		lo_hi_writeq(val, gwdt->control_base + SBSA_GWDT_WOR);
141abd3ac79SShaokun Zhang }
142abd3ac79SShaokun Zhang 
143abd3ac79SShaokun Zhang /*
14457d2caaaSFu Wei  * watchdog operation functions
14557d2caaaSFu Wei  */
sbsa_gwdt_set_timeout(struct watchdog_device * wdd,unsigned int timeout)14657d2caaaSFu Wei static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
14757d2caaaSFu Wei 				 unsigned int timeout)
14857d2caaaSFu Wei {
14957d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
15057d2caaaSFu Wei 
15157d2caaaSFu Wei 	wdd->timeout = timeout;
152000987a3SGeorge Cherian 	timeout = clamp_t(unsigned int, timeout, 1, wdd->max_hw_heartbeat_ms / 1000);
15357d2caaaSFu Wei 
15457d2caaaSFu Wei 	if (action)
155*16da1e7cSDarren Hart 		sbsa_gwdt_reg_write((u64)gwdt->clk * timeout, gwdt);
15657d2caaaSFu Wei 	else
15757d2caaaSFu Wei 		/*
15857d2caaaSFu Wei 		 * In the single stage mode, The first signal (WS0) is ignored,
15957d2caaaSFu Wei 		 * the timeout is (WOR * 2), so the WOR should be configured
16057d2caaaSFu Wei 		 * to half value of timeout.
16157d2caaaSFu Wei 		 */
162*16da1e7cSDarren Hart 		sbsa_gwdt_reg_write(((u64)gwdt->clk / 2) * timeout, gwdt);
16357d2caaaSFu Wei 
16457d2caaaSFu Wei 	return 0;
16557d2caaaSFu Wei }
16657d2caaaSFu Wei 
sbsa_gwdt_get_timeleft(struct watchdog_device * wdd)16757d2caaaSFu Wei static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd)
16857d2caaaSFu Wei {
16957d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
17057d2caaaSFu Wei 	u64 timeleft = 0;
17157d2caaaSFu Wei 
17257d2caaaSFu Wei 	/*
17357d2caaaSFu Wei 	 * In the single stage mode, if WS0 is deasserted
17457d2caaaSFu Wei 	 * (watchdog is in the first stage),
17557d2caaaSFu Wei 	 * timeleft = WOR + (WCV - system counter)
17657d2caaaSFu Wei 	 */
17757d2caaaSFu Wei 	if (!action &&
17857d2caaaSFu Wei 	    !(readl(gwdt->control_base + SBSA_GWDT_WCS) & SBSA_GWDT_WCS_WS0))
179abd3ac79SShaokun Zhang 		timeleft += sbsa_gwdt_reg_read(gwdt);
18057d2caaaSFu Wei 
18193ac3debSJayachandran C 	timeleft += lo_hi_readq(gwdt->control_base + SBSA_GWDT_WCV) -
182eae1ddc6SMarc Zyngier 		    arch_timer_read_counter();
18357d2caaaSFu Wei 
18457d2caaaSFu Wei 	do_div(timeleft, gwdt->clk);
18557d2caaaSFu Wei 
18657d2caaaSFu Wei 	return timeleft;
18757d2caaaSFu Wei }
18857d2caaaSFu Wei 
sbsa_gwdt_keepalive(struct watchdog_device * wdd)18957d2caaaSFu Wei static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
19057d2caaaSFu Wei {
19157d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
19257d2caaaSFu Wei 
19357d2caaaSFu Wei 	/*
19457d2caaaSFu Wei 	 * Writing WRR for an explicit watchdog refresh.
19557d2caaaSFu Wei 	 * You can write anyting (like 0).
19657d2caaaSFu Wei 	 */
19757d2caaaSFu Wei 	writel(0, gwdt->refresh_base + SBSA_GWDT_WRR);
19857d2caaaSFu Wei 
19957d2caaaSFu Wei 	return 0;
20057d2caaaSFu Wei }
20157d2caaaSFu Wei 
sbsa_gwdt_get_version(struct watchdog_device * wdd)202abd3ac79SShaokun Zhang static void sbsa_gwdt_get_version(struct watchdog_device *wdd)
203abd3ac79SShaokun Zhang {
204abd3ac79SShaokun Zhang 	struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
205abd3ac79SShaokun Zhang 	int ver;
206abd3ac79SShaokun Zhang 
207abd3ac79SShaokun Zhang 	ver = readl(gwdt->control_base + SBSA_GWDT_W_IIDR);
208abd3ac79SShaokun Zhang 	ver = (ver >> SBSA_GWDT_VERSION_SHIFT) & SBSA_GWDT_VERSION_MASK;
209abd3ac79SShaokun Zhang 
210abd3ac79SShaokun Zhang 	gwdt->version = ver;
211abd3ac79SShaokun Zhang }
212abd3ac79SShaokun Zhang 
sbsa_gwdt_start(struct watchdog_device * wdd)21357d2caaaSFu Wei static int sbsa_gwdt_start(struct watchdog_device *wdd)
21457d2caaaSFu Wei {
21557d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
21657d2caaaSFu Wei 
21757d2caaaSFu Wei 	/* writing WCS will cause an explicit watchdog refresh */
21857d2caaaSFu Wei 	writel(SBSA_GWDT_WCS_EN, gwdt->control_base + SBSA_GWDT_WCS);
21957d2caaaSFu Wei 
22057d2caaaSFu Wei 	return 0;
22157d2caaaSFu Wei }
22257d2caaaSFu Wei 
sbsa_gwdt_stop(struct watchdog_device * wdd)22357d2caaaSFu Wei static int sbsa_gwdt_stop(struct watchdog_device *wdd)
22457d2caaaSFu Wei {
22557d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
22657d2caaaSFu Wei 
22757d2caaaSFu Wei 	/* Simply write 0 to WCS to clean WCS_EN bit */
22857d2caaaSFu Wei 	writel(0, gwdt->control_base + SBSA_GWDT_WCS);
22957d2caaaSFu Wei 
23057d2caaaSFu Wei 	return 0;
23157d2caaaSFu Wei }
23257d2caaaSFu Wei 
sbsa_gwdt_interrupt(int irq,void * dev_id)23357d2caaaSFu Wei static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id)
23457d2caaaSFu Wei {
23557d2caaaSFu Wei 	panic(WATCHDOG_NAME " timeout");
23657d2caaaSFu Wei 
23757d2caaaSFu Wei 	return IRQ_HANDLED;
23857d2caaaSFu Wei }
23957d2caaaSFu Wei 
2406c368932SBhumika Goyal static const struct watchdog_info sbsa_gwdt_info = {
24157d2caaaSFu Wei 	.identity	= WATCHDOG_NAME,
24257d2caaaSFu Wei 	.options	= WDIOF_SETTIMEOUT |
24357d2caaaSFu Wei 			  WDIOF_KEEPALIVEPING |
24457d2caaaSFu Wei 			  WDIOF_MAGICCLOSE |
24557d2caaaSFu Wei 			  WDIOF_CARDRESET,
24657d2caaaSFu Wei };
24757d2caaaSFu Wei 
248b893e344SBhumika Goyal static const struct watchdog_ops sbsa_gwdt_ops = {
24957d2caaaSFu Wei 	.owner		= THIS_MODULE,
25057d2caaaSFu Wei 	.start		= sbsa_gwdt_start,
25157d2caaaSFu Wei 	.stop		= sbsa_gwdt_stop,
25257d2caaaSFu Wei 	.ping		= sbsa_gwdt_keepalive,
25357d2caaaSFu Wei 	.set_timeout	= sbsa_gwdt_set_timeout,
25457d2caaaSFu Wei 	.get_timeleft	= sbsa_gwdt_get_timeleft,
25557d2caaaSFu Wei };
25657d2caaaSFu Wei 
sbsa_gwdt_probe(struct platform_device * pdev)25757d2caaaSFu Wei static int sbsa_gwdt_probe(struct platform_device *pdev)
25857d2caaaSFu Wei {
25957d2caaaSFu Wei 	void __iomem *rf_base, *cf_base;
26057d2caaaSFu Wei 	struct device *dev = &pdev->dev;
26157d2caaaSFu Wei 	struct watchdog_device *wdd;
26257d2caaaSFu Wei 	struct sbsa_gwdt *gwdt;
26357d2caaaSFu Wei 	int ret, irq;
26457d2caaaSFu Wei 	u32 status;
26557d2caaaSFu Wei 
26657d2caaaSFu Wei 	gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
26757d2caaaSFu Wei 	if (!gwdt)
26857d2caaaSFu Wei 		return -ENOMEM;
26957d2caaaSFu Wei 	platform_set_drvdata(pdev, gwdt);
27057d2caaaSFu Wei 
2710f0a6a28SGuenter Roeck 	cf_base = devm_platform_ioremap_resource(pdev, 0);
27257d2caaaSFu Wei 	if (IS_ERR(cf_base))
27357d2caaaSFu Wei 		return PTR_ERR(cf_base);
27457d2caaaSFu Wei 
2750f0a6a28SGuenter Roeck 	rf_base = devm_platform_ioremap_resource(pdev, 1);
27657d2caaaSFu Wei 	if (IS_ERR(rf_base))
27757d2caaaSFu Wei 		return PTR_ERR(rf_base);
27857d2caaaSFu Wei 
27957d2caaaSFu Wei 	/*
28057d2caaaSFu Wei 	 * Get the frequency of system counter from the cp15 interface of ARM
28157d2caaaSFu Wei 	 * Generic timer. We don't need to check it, because if it returns "0",
28257d2caaaSFu Wei 	 * system would panic in very early stage.
28357d2caaaSFu Wei 	 */
28457d2caaaSFu Wei 	gwdt->clk = arch_timer_get_cntfrq();
28557d2caaaSFu Wei 	gwdt->refresh_base = rf_base;
28657d2caaaSFu Wei 	gwdt->control_base = cf_base;
28757d2caaaSFu Wei 
28857d2caaaSFu Wei 	wdd = &gwdt->wdd;
28957d2caaaSFu Wei 	wdd->parent = dev;
29057d2caaaSFu Wei 	wdd->info = &sbsa_gwdt_info;
29157d2caaaSFu Wei 	wdd->ops = &sbsa_gwdt_ops;
29257d2caaaSFu Wei 	wdd->min_timeout = 1;
29357d2caaaSFu Wei 	wdd->timeout = DEFAULT_TIMEOUT;
29457d2caaaSFu Wei 	watchdog_set_drvdata(wdd, gwdt);
29557d2caaaSFu Wei 	watchdog_set_nowayout(wdd, nowayout);
296abd3ac79SShaokun Zhang 	sbsa_gwdt_get_version(wdd);
297abd3ac79SShaokun Zhang 	if (gwdt->version == 0)
298abd3ac79SShaokun Zhang 		wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
299abd3ac79SShaokun Zhang 	else
300abd3ac79SShaokun Zhang 		wdd->max_hw_heartbeat_ms = GENMASK_ULL(47, 0) / gwdt->clk * 1000;
30157d2caaaSFu Wei 
30257d2caaaSFu Wei 	status = readl(cf_base + SBSA_GWDT_WCS);
30357d2caaaSFu Wei 	if (status & SBSA_GWDT_WCS_WS1) {
30457d2caaaSFu Wei 		dev_warn(dev, "System reset by WDT.\n");
30557d2caaaSFu Wei 		wdd->bootstatus |= WDIOF_CARDRESET;
30657d2caaaSFu Wei 	}
307c3c1e29cSPratyush Anand 	if (status & SBSA_GWDT_WCS_EN)
308c3c1e29cSPratyush Anand 		set_bit(WDOG_HW_RUNNING, &wdd->status);
30957d2caaaSFu Wei 
31057d2caaaSFu Wei 	if (action) {
31157d2caaaSFu Wei 		irq = platform_get_irq(pdev, 0);
31257d2caaaSFu Wei 		if (irq < 0) {
31357d2caaaSFu Wei 			action = 0;
31457d2caaaSFu Wei 			dev_warn(dev, "unable to get ws0 interrupt.\n");
31557d2caaaSFu Wei 		} else {
31657d2caaaSFu Wei 			/*
31757d2caaaSFu Wei 			 * In case there is a pending ws0 interrupt, just ping
31857d2caaaSFu Wei 			 * the watchdog before registering the interrupt routine
31957d2caaaSFu Wei 			 */
32057d2caaaSFu Wei 			writel(0, rf_base + SBSA_GWDT_WRR);
32157d2caaaSFu Wei 			if (devm_request_irq(dev, irq, sbsa_gwdt_interrupt, 0,
32257d2caaaSFu Wei 					     pdev->name, gwdt)) {
32357d2caaaSFu Wei 				action = 0;
32457d2caaaSFu Wei 				dev_warn(dev, "unable to request IRQ %d.\n",
32557d2caaaSFu Wei 					 irq);
32657d2caaaSFu Wei 			}
32757d2caaaSFu Wei 		}
32857d2caaaSFu Wei 		if (!action)
32957d2caaaSFu Wei 			dev_warn(dev, "falling back to single stage mode.\n");
33057d2caaaSFu Wei 	}
33157d2caaaSFu Wei 	/*
33257d2caaaSFu Wei 	 * In the single stage mode, The first signal (WS0) is ignored,
33357d2caaaSFu Wei 	 * the timeout is (WOR * 2), so the maximum timeout should be doubled.
33457d2caaaSFu Wei 	 */
33557d2caaaSFu Wei 	if (!action)
336e05e80ebSPratyush Anand 		wdd->max_hw_heartbeat_ms *= 2;
33757d2caaaSFu Wei 
33857d2caaaSFu Wei 	watchdog_init_timeout(wdd, timeout, dev);
33957d2caaaSFu Wei 	/*
34057d2caaaSFu Wei 	 * Update timeout to WOR.
34157d2caaaSFu Wei 	 * Because of the explicit watchdog refresh mechanism,
34257d2caaaSFu Wei 	 * it's also a ping, if watchdog is enabled.
34357d2caaaSFu Wei 	 */
34457d2caaaSFu Wei 	sbsa_gwdt_set_timeout(wdd, wdd->timeout);
34557d2caaaSFu Wei 
346f7daaa8dSGuenter Roeck 	watchdog_stop_on_reboot(wdd);
347f7daaa8dSGuenter Roeck 	ret = devm_watchdog_register_device(dev, wdd);
34857d2caaaSFu Wei 	if (ret)
34957d2caaaSFu Wei 		return ret;
35057d2caaaSFu Wei 
35157d2caaaSFu Wei 	dev_info(dev, "Initialized with %ds timeout @ %u Hz, action=%d.%s\n",
35257d2caaaSFu Wei 		 wdd->timeout, gwdt->clk, action,
35357d2caaaSFu Wei 		 status & SBSA_GWDT_WCS_EN ? " [enabled]" : "");
35457d2caaaSFu Wei 
35557d2caaaSFu Wei 	return 0;
35657d2caaaSFu Wei }
35757d2caaaSFu Wei 
35857d2caaaSFu Wei /* Disable watchdog if it is active during suspend */
sbsa_gwdt_suspend(struct device * dev)35957d2caaaSFu Wei static int __maybe_unused sbsa_gwdt_suspend(struct device *dev)
36057d2caaaSFu Wei {
36157d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
36257d2caaaSFu Wei 
36374d6c68cSWang Wensheng 	if (watchdog_hw_running(&gwdt->wdd))
36457d2caaaSFu Wei 		sbsa_gwdt_stop(&gwdt->wdd);
36557d2caaaSFu Wei 
36657d2caaaSFu Wei 	return 0;
36757d2caaaSFu Wei }
36857d2caaaSFu Wei 
36957d2caaaSFu Wei /* Enable watchdog if necessary */
sbsa_gwdt_resume(struct device * dev)37057d2caaaSFu Wei static int __maybe_unused sbsa_gwdt_resume(struct device *dev)
37157d2caaaSFu Wei {
37257d2caaaSFu Wei 	struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
37357d2caaaSFu Wei 
37474d6c68cSWang Wensheng 	if (watchdog_hw_running(&gwdt->wdd))
37557d2caaaSFu Wei 		sbsa_gwdt_start(&gwdt->wdd);
37657d2caaaSFu Wei 
37757d2caaaSFu Wei 	return 0;
37857d2caaaSFu Wei }
37957d2caaaSFu Wei 
38057d2caaaSFu Wei static const struct dev_pm_ops sbsa_gwdt_pm_ops = {
38157d2caaaSFu Wei 	SET_SYSTEM_SLEEP_PM_OPS(sbsa_gwdt_suspend, sbsa_gwdt_resume)
38257d2caaaSFu Wei };
38357d2caaaSFu Wei 
38457d2caaaSFu Wei static const struct of_device_id sbsa_gwdt_of_match[] = {
38557d2caaaSFu Wei 	{ .compatible = "arm,sbsa-gwdt", },
38657d2caaaSFu Wei 	{},
38757d2caaaSFu Wei };
38857d2caaaSFu Wei MODULE_DEVICE_TABLE(of, sbsa_gwdt_of_match);
38957d2caaaSFu Wei 
39057d2caaaSFu Wei static const struct platform_device_id sbsa_gwdt_pdev_match[] = {
39157d2caaaSFu Wei 	{ .name = DRV_NAME, },
39257d2caaaSFu Wei 	{},
39357d2caaaSFu Wei };
39457d2caaaSFu Wei MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match);
39557d2caaaSFu Wei 
39657d2caaaSFu Wei static struct platform_driver sbsa_gwdt_driver = {
39757d2caaaSFu Wei 	.driver = {
39857d2caaaSFu Wei 		.name = DRV_NAME,
39957d2caaaSFu Wei 		.pm = &sbsa_gwdt_pm_ops,
40057d2caaaSFu Wei 		.of_match_table = sbsa_gwdt_of_match,
40157d2caaaSFu Wei 	},
40257d2caaaSFu Wei 	.probe = sbsa_gwdt_probe,
40357d2caaaSFu Wei 	.id_table = sbsa_gwdt_pdev_match,
40457d2caaaSFu Wei };
40557d2caaaSFu Wei 
40657d2caaaSFu Wei module_platform_driver(sbsa_gwdt_driver);
40757d2caaaSFu Wei 
40857d2caaaSFu Wei MODULE_DESCRIPTION("SBSA Generic Watchdog Driver");
40957d2caaaSFu Wei MODULE_AUTHOR("Fu Wei <fu.wei@linaro.org>");
41057d2caaaSFu Wei MODULE_AUTHOR("Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>");
41157d2caaaSFu Wei MODULE_AUTHOR("Al Stone <al.stone@linaro.org>");
41257d2caaaSFu Wei MODULE_AUTHOR("Timur Tabi <timur@codeaurora.org>");
41357d2caaaSFu Wei MODULE_LICENSE("GPL v2");
414