2 * Watchdog driver for the UniPhier watchdog timer
4 * (c) Copyright 2014 Panasonic Corporation
5 * (c) Copyright 2016 Socionext Inc.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
18 #include <linux/bitops.h>
19 #include <linux/mfd/syscon.h>
20 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/regmap.h>
24 #include <linux/watchdog.h>
26 /* WDT timer setting register */
27 #define WDTTIMSET 0x3004
28 #define WDTTIMSET_PERIOD_MASK (0xf << 0)
29 #define WDTTIMSET_PERIOD_1_SEC (0x3 << 0)
31 /* WDT reset selection register */
32 #define WDTRSTSEL 0x3008
33 #define WDTRSTSEL_RSTSEL_MASK (0x3 << 0)
34 #define WDTRSTSEL_RSTSEL_BOTH (0x0 << 0)
35 #define WDTRSTSEL_RSTSEL_IRQ_ONLY (0x2 << 0)
37 /* WDT control register */
38 #define WDTCTRL 0x300c
39 #define WDTCTRL_STATUS BIT(8)
40 #define WDTCTRL_CLEAR BIT(1)
41 #define WDTCTRL_ENABLE BIT(0)
43 #define SEC_TO_WDTTIMSET_PRD(sec) \
44 (ilog2(sec) + WDTTIMSET_PERIOD_1_SEC)
46 #define WDTST_TIMEOUT 1000 /* usec */
48 #define WDT_DEFAULT_TIMEOUT 64 /* Default is 64 seconds */
49 #define WDT_PERIOD_MIN 1
50 #define WDT_PERIOD_MAX 128
52 static unsigned int timeout
= 0;
53 static bool nowayout
= WATCHDOG_NOWAYOUT
;
55 struct uniphier_wdt_dev
{
56 struct watchdog_device wdt_dev
;
57 struct regmap
*regmap
;
61 * UniPhier Watchdog operations
63 static int uniphier_watchdog_ping(struct watchdog_device
*w
)
65 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
70 ret
= regmap_write_bits(wdev
->regmap
, WDTCTRL
,
71 WDTCTRL_CLEAR
, WDTCTRL_CLEAR
);
74 * As SoC specification, after clear counter,
75 * it needs to wait until counter status is 1.
77 ret
= regmap_read_poll_timeout(wdev
->regmap
, WDTCTRL
, val
,
78 (val
& WDTCTRL_STATUS
),
84 static int __uniphier_watchdog_start(struct regmap
*regmap
, unsigned int sec
)
89 ret
= regmap_read_poll_timeout(regmap
, WDTCTRL
, val
,
90 !(val
& WDTCTRL_STATUS
),
96 ret
= regmap_write(regmap
, WDTTIMSET
,
97 SEC_TO_WDTTIMSET_PRD(sec
));
101 /* Enable and clear watchdog */
102 ret
= regmap_write(regmap
, WDTCTRL
, WDTCTRL_ENABLE
| WDTCTRL_CLEAR
);
105 * As SoC specification, after clear counter,
106 * it needs to wait until counter status is 1.
108 ret
= regmap_read_poll_timeout(regmap
, WDTCTRL
, val
,
109 (val
& WDTCTRL_STATUS
),
115 static int __uniphier_watchdog_stop(struct regmap
*regmap
)
117 /* Disable and stop watchdog */
118 return regmap_write_bits(regmap
, WDTCTRL
, WDTCTRL_ENABLE
, 0);
121 static int __uniphier_watchdog_restart(struct regmap
*regmap
, unsigned int sec
)
125 ret
= __uniphier_watchdog_stop(regmap
);
129 return __uniphier_watchdog_start(regmap
, sec
);
132 static int uniphier_watchdog_start(struct watchdog_device
*w
)
134 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
135 unsigned int tmp_timeout
;
137 tmp_timeout
= roundup_pow_of_two(w
->timeout
);
139 return __uniphier_watchdog_start(wdev
->regmap
, tmp_timeout
);
142 static int uniphier_watchdog_stop(struct watchdog_device
*w
)
144 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
146 return __uniphier_watchdog_stop(wdev
->regmap
);
149 static int uniphier_watchdog_set_timeout(struct watchdog_device
*w
,
152 struct uniphier_wdt_dev
*wdev
= watchdog_get_drvdata(w
);
153 unsigned int tmp_timeout
;
156 tmp_timeout
= roundup_pow_of_two(t
);
157 if (tmp_timeout
== w
->timeout
)
160 if (watchdog_active(w
)) {
161 ret
= __uniphier_watchdog_restart(wdev
->regmap
, tmp_timeout
);
166 w
->timeout
= tmp_timeout
;
174 static const struct watchdog_info uniphier_wdt_info
= {
175 .identity
= "uniphier-wdt",
176 .options
= WDIOF_SETTIMEOUT
|
177 WDIOF_KEEPALIVEPING
|
182 static const struct watchdog_ops uniphier_wdt_ops
= {
183 .owner
= THIS_MODULE
,
184 .start
= uniphier_watchdog_start
,
185 .stop
= uniphier_watchdog_stop
,
186 .ping
= uniphier_watchdog_ping
,
187 .set_timeout
= uniphier_watchdog_set_timeout
,
190 static int uniphier_wdt_probe(struct platform_device
*pdev
)
192 struct device
*dev
= &pdev
->dev
;
193 struct uniphier_wdt_dev
*wdev
;
194 struct regmap
*regmap
;
195 struct device_node
*parent
;
198 wdev
= devm_kzalloc(dev
, sizeof(*wdev
), GFP_KERNEL
);
202 platform_set_drvdata(pdev
, wdev
);
204 parent
= of_get_parent(dev
->of_node
); /* parent should be syscon node */
205 regmap
= syscon_node_to_regmap(parent
);
208 return PTR_ERR(regmap
);
210 wdev
->regmap
= regmap
;
211 wdev
->wdt_dev
.info
= &uniphier_wdt_info
;
212 wdev
->wdt_dev
.ops
= &uniphier_wdt_ops
;
213 wdev
->wdt_dev
.max_timeout
= WDT_PERIOD_MAX
;
214 wdev
->wdt_dev
.min_timeout
= WDT_PERIOD_MIN
;
215 wdev
->wdt_dev
.parent
= dev
;
217 if (watchdog_init_timeout(&wdev
->wdt_dev
, timeout
, dev
) < 0) {
218 wdev
->wdt_dev
.timeout
= WDT_DEFAULT_TIMEOUT
;
220 watchdog_set_nowayout(&wdev
->wdt_dev
, nowayout
);
221 watchdog_stop_on_reboot(&wdev
->wdt_dev
);
223 watchdog_set_drvdata(&wdev
->wdt_dev
, wdev
);
225 uniphier_watchdog_stop(&wdev
->wdt_dev
);
226 ret
= regmap_write(wdev
->regmap
, WDTRSTSEL
, WDTRSTSEL_RSTSEL_BOTH
);
230 ret
= devm_watchdog_register_device(dev
, &wdev
->wdt_dev
);
234 dev_info(dev
, "watchdog driver (timeout=%d sec, nowayout=%d)\n",
235 wdev
->wdt_dev
.timeout
, nowayout
);
240 static const struct of_device_id uniphier_wdt_dt_ids
[] = {
241 { .compatible
= "socionext,uniphier-wdt" },
244 MODULE_DEVICE_TABLE(of
, uniphier_wdt_dt_ids
);
246 static struct platform_driver uniphier_wdt_driver
= {
247 .probe
= uniphier_wdt_probe
,
249 .name
= "uniphier-wdt",
250 .of_match_table
= uniphier_wdt_dt_ids
,
254 module_platform_driver(uniphier_wdt_driver
);
256 module_param(timeout
, uint
, 0000);
257 MODULE_PARM_DESC(timeout
,
258 "Watchdog timeout seconds in power of 2. (0 < timeout < 128, default="
259 __MODULE_STRING(WDT_DEFAULT_TIMEOUT
) ")");
261 module_param(nowayout
, bool, 0000);
262 MODULE_PARM_DESC(nowayout
,
263 "Watchdog cannot be stopped once started (default="
264 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
266 MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>");
267 MODULE_DESCRIPTION("UniPhier Watchdog Device Driver");
268 MODULE_LICENSE("GPL v2");