1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2020 TOSHIBA CORPORATION
4 * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation
5 * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/watchdog.h>
21 #define WDT_CMD_CLEAR 0x4352
22 #define WDT_CMD_START_STOP 0x5354
25 #define VISCONTI_WDT_FREQ 2000000 /* 2MHz */
26 #define WDT_DEFAULT_TIMEOUT 10U /* in seconds */
28 static bool nowayout
= WATCHDOG_NOWAYOUT
;
29 module_param(nowayout
, bool, 0);
32 "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT
)")");
34 struct visconti_wdt_priv
{
35 struct watchdog_device wdev
;
40 static int visconti_wdt_start(struct watchdog_device
*wdev
)
42 struct visconti_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
43 u32 timeout
= wdev
->timeout
* VISCONTI_WDT_FREQ
;
45 writel(priv
->div
, priv
->base
+ WDT_DIV
);
46 writel(0, priv
->base
+ WDT_MIN
);
47 writel(timeout
, priv
->base
+ WDT_MAX
);
48 writel(0, priv
->base
+ WDT_CTL
);
49 writel(WDT_CMD_START_STOP
, priv
->base
+ WDT_CMD
);
54 static int visconti_wdt_stop(struct watchdog_device
*wdev
)
56 struct visconti_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
58 writel(1, priv
->base
+ WDT_CTL
);
59 writel(WDT_CMD_START_STOP
, priv
->base
+ WDT_CMD
);
64 static int visconti_wdt_ping(struct watchdog_device
*wdd
)
66 struct visconti_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
68 writel(WDT_CMD_CLEAR
, priv
->base
+ WDT_CMD
);
73 static unsigned int visconti_wdt_get_timeleft(struct watchdog_device
*wdev
)
75 struct visconti_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
76 u32 timeout
= wdev
->timeout
* VISCONTI_WDT_FREQ
;
77 u32 cnt
= readl(priv
->base
+ WDT_CNT
);
83 return timeout
/ VISCONTI_WDT_FREQ
;
86 static int visconti_wdt_set_timeout(struct watchdog_device
*wdev
, unsigned int timeout
)
89 struct visconti_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
91 wdev
->timeout
= timeout
;
92 val
= wdev
->timeout
* VISCONTI_WDT_FREQ
;
94 /* Clear counter before setting timeout because WDT expires */
95 writel(WDT_CMD_CLEAR
, priv
->base
+ WDT_CMD
);
96 writel(val
, priv
->base
+ WDT_MAX
);
101 static const struct watchdog_info visconti_wdt_info
= {
102 .options
= WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
,
103 .identity
= "Visconti Watchdog",
106 static const struct watchdog_ops visconti_wdt_ops
= {
107 .owner
= THIS_MODULE
,
108 .start
= visconti_wdt_start
,
109 .stop
= visconti_wdt_stop
,
110 .ping
= visconti_wdt_ping
,
111 .get_timeleft
= visconti_wdt_get_timeleft
,
112 .set_timeout
= visconti_wdt_set_timeout
,
115 static int visconti_wdt_probe(struct platform_device
*pdev
)
117 struct watchdog_device
*wdev
;
118 struct visconti_wdt_priv
*priv
;
119 struct device
*dev
= &pdev
->dev
;
122 unsigned long clk_freq
;
124 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
128 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
129 if (IS_ERR(priv
->base
))
130 return PTR_ERR(priv
->base
);
132 clk
= devm_clk_get_enabled(dev
, NULL
);
134 return dev_err_probe(dev
, PTR_ERR(clk
), "Could not get clock\n");
136 clk_freq
= clk_get_rate(clk
);
140 priv
->div
= clk_freq
/ VISCONTI_WDT_FREQ
;
142 /* Initialize struct watchdog_device. */
144 wdev
->info
= &visconti_wdt_info
;
145 wdev
->ops
= &visconti_wdt_ops
;
147 wdev
->min_timeout
= 1;
148 wdev
->max_timeout
= 0xffffffff / VISCONTI_WDT_FREQ
;
149 wdev
->timeout
= min(wdev
->max_timeout
, WDT_DEFAULT_TIMEOUT
);
151 watchdog_set_drvdata(wdev
, priv
);
152 watchdog_set_nowayout(wdev
, nowayout
);
153 watchdog_stop_on_unregister(wdev
);
155 /* This overrides the default timeout only if DT configuration was found */
156 ret
= watchdog_init_timeout(wdev
, 0, dev
);
158 dev_warn(dev
, "Specified timeout value invalid, using default\n");
160 return devm_watchdog_register_device(dev
, wdev
);
163 static const struct of_device_id visconti_wdt_of_match
[] = {
164 { .compatible
= "toshiba,visconti-wdt", },
167 MODULE_DEVICE_TABLE(of
, visconti_wdt_of_match
);
169 static struct platform_driver visconti_wdt_driver
= {
171 .name
= "visconti_wdt",
172 .of_match_table
= visconti_wdt_of_match
,
174 .probe
= visconti_wdt_probe
,
176 module_platform_driver(visconti_wdt_driver
);
178 MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
179 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
180 MODULE_LICENSE("GPL v2");