1 // SPDX-License-Identifier: GPL-2.0
3 * Watchdog driver for the UniPhier watchdog timer
5 * (c) Copyright 2014 Panasonic Corporation
6 * (c) Copyright 2016 Socionext Inc.
10 #include <linux/bitops.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
16 #include <linux/watchdog.h>
18 /* WDT timer setting register */
19 #define WDTTIMSET 0x3004
20 #define WDTTIMSET_PERIOD_MASK (0xf << 0)
21 #define WDTTIMSET_PERIOD_1_SEC (0x3 << 0)
23 /* WDT reset selection register */
24 #define WDTRSTSEL 0x3008
25 #define WDTRSTSEL_RSTSEL_MASK (0x3 << 0)
26 #define WDTRSTSEL_RSTSEL_BOTH (0x0 << 0)
27 #define WDTRSTSEL_RSTSEL_IRQ_ONLY (0x2 << 0)
29 /* WDT control register */
30 #define WDTCTRL 0x300c
31 #define WDTCTRL_STATUS BIT(8)
32 #define WDTCTRL_CLEAR BIT(1)
33 #define WDTCTRL_ENABLE BIT(0)
35 #define SEC_TO_WDTTIMSET_PRD(sec) \
36 (ilog2(sec) + WDTTIMSET_PERIOD_1_SEC)
38 #define WDTST_TIMEOUT 1000 /* usec */
40 #define WDT_DEFAULT_TIMEOUT 64 /* Default is 64 seconds */
41 #define WDT_PERIOD_MIN 1
42 #define WDT_PERIOD_MAX 128
44 static unsigned int timeout
= 0;
45 static bool nowayout
= WATCHDOG_NOWAYOUT
;
47 struct uniphier_wdt_dev
{
48 struct watchdog_device wdt_dev
;
49 struct regmap
*regmap
;
53 * UniPhier Watchdog operations
55 static int uniphier_watchdog_ping(struct watchdog_device
*w
)
57 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
62 ret
= regmap_write_bits(wdev
->regmap
, WDTCTRL
,
63 WDTCTRL_CLEAR
, WDTCTRL_CLEAR
);
66 * As SoC specification, after clear counter,
67 * it needs to wait until counter status is 1.
69 ret
= regmap_read_poll_timeout(wdev
->regmap
, WDTCTRL
, val
,
70 (val
& WDTCTRL_STATUS
),
76 static int __uniphier_watchdog_start(struct regmap
*regmap
, unsigned int sec
)
81 ret
= regmap_read_poll_timeout(regmap
, WDTCTRL
, val
,
82 !(val
& WDTCTRL_STATUS
),
88 ret
= regmap_write(regmap
, WDTTIMSET
,
89 SEC_TO_WDTTIMSET_PRD(sec
));
93 /* Enable and clear watchdog */
94 ret
= regmap_write(regmap
, WDTCTRL
, WDTCTRL_ENABLE
| WDTCTRL_CLEAR
);
97 * As SoC specification, after clear counter,
98 * it needs to wait until counter status is 1.
100 ret
= regmap_read_poll_timeout(regmap
, WDTCTRL
, val
,
101 (val
& WDTCTRL_STATUS
),
107 static int __uniphier_watchdog_stop(struct regmap
*regmap
)
109 /* Disable and stop watchdog */
110 return regmap_write_bits(regmap
, WDTCTRL
, WDTCTRL_ENABLE
, 0);
113 static int __uniphier_watchdog_restart(struct regmap
*regmap
, unsigned int sec
)
117 ret
= __uniphier_watchdog_stop(regmap
);
121 return __uniphier_watchdog_start(regmap
, sec
);
124 static int uniphier_watchdog_start(struct watchdog_device
*w
)
126 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
127 unsigned int tmp_timeout
;
129 tmp_timeout
= roundup_pow_of_two(w
->timeout
);
131 return __uniphier_watchdog_start(wdev
->regmap
, tmp_timeout
);
134 static int uniphier_watchdog_stop(struct watchdog_device
*w
)
136 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
138 return __uniphier_watchdog_stop(wdev
->regmap
);
141 static int uniphier_watchdog_set_timeout(struct watchdog_device
*w
,
144 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
145 unsigned int tmp_timeout
;
148 tmp_timeout
= roundup_pow_of_two(t
);
149 if (tmp_timeout
== w
->timeout
)
152 if (watchdog_active(w
)) {
153 ret
= __uniphier_watchdog_restart(wdev
->regmap
, tmp_timeout
);
158 w
->timeout
= tmp_timeout
;
166 static const struct watchdog_info uniphier_wdt_info
= {
167 .identity
= "uniphier-wdt",
168 .options
= WDIOF_SETTIMEOUT
|
169 WDIOF_KEEPALIVEPING
|
174 static const struct watchdog_ops uniphier_wdt_ops
= {
175 .owner
= THIS_MODULE
,
176 .start
= uniphier_watchdog_start
,
177 .stop
= uniphier_watchdog_stop
,
178 .ping
= uniphier_watchdog_ping
,
179 .set_timeout
= uniphier_watchdog_set_timeout
,
182 static int uniphier_wdt_probe(struct platform_device
*pdev
)
184 struct device
*dev
= &pdev
->dev
;
185 struct uniphier_wdt_dev
*wdev
;
186 struct regmap
*regmap
;
187 struct device_node
*parent
;
190 wdev
= devm_kzalloc(dev
, sizeof(*wdev
), GFP_KERNEL
);
194 platform_set_drvdata(pdev
, wdev
);
196 parent
= of_get_parent(dev
->of_node
); /* parent should be syscon node */
197 regmap
= syscon_node_to_regmap(parent
);
200 return PTR_ERR(regmap
);
202 wdev
->regmap
= regmap
;
203 wdev
->wdt_dev
.info
= &uniphier_wdt_info
;
204 wdev
->wdt_dev
.ops
= &uniphier_wdt_ops
;
205 wdev
->wdt_dev
.max_timeout
= WDT_PERIOD_MAX
;
206 wdev
->wdt_dev
.min_timeout
= WDT_PERIOD_MIN
;
207 wdev
->wdt_dev
.timeout
= WDT_DEFAULT_TIMEOUT
;
208 wdev
->wdt_dev
.parent
= dev
;
210 watchdog_init_timeout(&wdev
->wdt_dev
, timeout
, dev
);
211 watchdog_set_nowayout(&wdev
->wdt_dev
, nowayout
);
212 watchdog_stop_on_reboot(&wdev
->wdt_dev
);
214 watchdog_set_drvdata(&wdev
->wdt_dev
, wdev
);
216 uniphier_watchdog_stop(&wdev
->wdt_dev
);
217 ret
= regmap_write(wdev
->regmap
, WDTRSTSEL
, WDTRSTSEL_RSTSEL_BOTH
);
221 ret
= devm_watchdog_register_device(dev
, &wdev
->wdt_dev
);
225 dev_info(dev
, "watchdog driver (timeout=%d sec, nowayout=%d)\n",
226 wdev
->wdt_dev
.timeout
, nowayout
);
231 static const struct of_device_id uniphier_wdt_dt_ids
[] = {
232 { .compatible
= "socionext,uniphier-wdt" },
235 MODULE_DEVICE_TABLE(of
, uniphier_wdt_dt_ids
);
237 static struct platform_driver uniphier_wdt_driver
= {
238 .probe
= uniphier_wdt_probe
,
240 .name
= "uniphier-wdt",
241 .of_match_table
= uniphier_wdt_dt_ids
,
245 module_platform_driver(uniphier_wdt_driver
);
247 module_param(timeout
, uint
, 0000);
248 MODULE_PARM_DESC(timeout
,
249 "Watchdog timeout seconds in power of 2. (0 < timeout < 128, default="
250 __MODULE_STRING(WDT_DEFAULT_TIMEOUT
) ")");
252 module_param(nowayout
, bool, 0000);
253 MODULE_PARM_DESC(nowayout
,
254 "Watchdog cannot be stopped once started (default="
255 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
257 MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>");
258 MODULE_DESCRIPTION("UniPhier Watchdog Device Driver");
259 MODULE_LICENSE("GPL v2");