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