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 void visconti_clk_disable_unprepare(void *data
)
117 clk_disable_unprepare(data
);
120 static int visconti_wdt_probe(struct platform_device
*pdev
)
122 struct watchdog_device
*wdev
;
123 struct visconti_wdt_priv
*priv
;
124 struct device
*dev
= &pdev
->dev
;
127 unsigned long clk_freq
;
129 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
133 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
134 if (IS_ERR(priv
->base
))
135 return PTR_ERR(priv
->base
);
137 clk
= devm_clk_get(dev
, NULL
);
139 return dev_err_probe(dev
, PTR_ERR(clk
), "Could not get clock\n");
141 ret
= clk_prepare_enable(clk
);
143 dev_err(dev
, "Could not enable clock\n");
147 ret
= devm_add_action_or_reset(dev
, visconti_clk_disable_unprepare
, clk
);
151 clk_freq
= clk_get_rate(clk
);
155 priv
->div
= clk_freq
/ VISCONTI_WDT_FREQ
;
157 /* Initialize struct watchdog_device. */
159 wdev
->info
= &visconti_wdt_info
;
160 wdev
->ops
= &visconti_wdt_ops
;
162 wdev
->min_timeout
= 1;
163 wdev
->max_timeout
= 0xffffffff / VISCONTI_WDT_FREQ
;
164 wdev
->timeout
= min(wdev
->max_timeout
, WDT_DEFAULT_TIMEOUT
);
166 watchdog_set_drvdata(wdev
, priv
);
167 watchdog_set_nowayout(wdev
, nowayout
);
168 watchdog_stop_on_unregister(wdev
);
170 /* This overrides the default timeout only if DT configuration was found */
171 ret
= watchdog_init_timeout(wdev
, 0, dev
);
173 dev_warn(dev
, "Specified timeout value invalid, using default\n");
175 return devm_watchdog_register_device(dev
, wdev
);
178 static const struct of_device_id visconti_wdt_of_match
[] = {
179 { .compatible
= "toshiba,visconti-wdt", },
182 MODULE_DEVICE_TABLE(of
, visconti_wdt_of_match
);
184 static struct platform_driver visconti_wdt_driver
= {
186 .name
= "visconti_wdt",
187 .of_match_table
= visconti_wdt_of_match
,
189 .probe
= visconti_wdt_probe
,
191 module_platform_driver(visconti_wdt_driver
);
193 MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
194 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
195 MODULE_LICENSE("GPL v2");