1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Renesas RZ/G2L WDT Watchdog Driver |
4 | * |
5 | * Copyright (C) 2021 Renesas Electronics Corporation |
6 | */ |
7 | #include <linux/bitops.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/io.h> |
11 | #include <linux/iopoll.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/reset.h> |
18 | #include <linux/units.h> |
19 | #include <linux/watchdog.h> |
20 | |
21 | #define WDTCNT 0x00 |
22 | #define WDTSET 0x04 |
23 | #define WDTTIM 0x08 |
24 | #define WDTINT 0x0C |
25 | #define PECR 0x10 |
26 | #define PEEN 0x14 |
27 | #define WDTCNT_WDTEN BIT(0) |
28 | #define WDTINT_INTDISP BIT(0) |
29 | #define PEEN_FORCE BIT(0) |
30 | |
31 | #define WDT_DEFAULT_TIMEOUT 60U |
32 | |
33 | /* Setting period time register only 12 bit set in WDTSET[31:20] */ |
34 | #define WDTSET_COUNTER_MASK (0xFFF00000) |
35 | #define WDTSET_COUNTER_VAL(f) ((f) << 20) |
36 | |
37 | #define F2CYCLE_NSEC(f) (1000000000 / (f)) |
38 | |
39 | #define RZV2M_A_NSEC 730 |
40 | |
41 | static bool nowayout = WATCHDOG_NOWAYOUT; |
42 | module_param(nowayout, bool, 0); |
43 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
44 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")" ); |
45 | |
46 | enum rz_wdt_type { |
47 | WDT_RZG2L, |
48 | WDT_RZV2M, |
49 | }; |
50 | |
51 | struct rzg2l_wdt_priv { |
52 | void __iomem *base; |
53 | struct watchdog_device wdev; |
54 | struct reset_control *rstc; |
55 | unsigned long osc_clk_rate; |
56 | unsigned long delay; |
57 | unsigned long minimum_assertion_period; |
58 | struct clk *pclk; |
59 | struct clk *osc_clk; |
60 | enum rz_wdt_type devtype; |
61 | }; |
62 | |
63 | static int rzg2l_wdt_reset(struct rzg2l_wdt_priv *priv) |
64 | { |
65 | int err, status; |
66 | |
67 | if (priv->devtype == WDT_RZV2M) { |
68 | /* WDT needs TYPE-B reset control */ |
69 | err = reset_control_assert(rstc: priv->rstc); |
70 | if (err) |
71 | return err; |
72 | ndelay(priv->minimum_assertion_period); |
73 | err = reset_control_deassert(rstc: priv->rstc); |
74 | if (err) |
75 | return err; |
76 | err = read_poll_timeout(reset_control_status, status, |
77 | status != 1, 0, 1000, false, |
78 | priv->rstc); |
79 | } else { |
80 | err = reset_control_reset(rstc: priv->rstc); |
81 | } |
82 | |
83 | return err; |
84 | } |
85 | |
86 | static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) |
87 | { |
88 | /* delay timer when change the setting register */ |
89 | ndelay(priv->delay); |
90 | } |
91 | |
92 | static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime) |
93 | { |
94 | u64 timer_cycle_us = 1024 * 1024ULL * (wdttime + 1) * MICRO; |
95 | |
96 | return div64_ul(timer_cycle_us, cycle); |
97 | } |
98 | |
99 | static void rzg2l_wdt_write(struct rzg2l_wdt_priv *priv, u32 val, unsigned int reg) |
100 | { |
101 | if (reg == WDTSET) |
102 | val &= WDTSET_COUNTER_MASK; |
103 | |
104 | writel_relaxed(val, priv->base + reg); |
105 | /* Registers other than the WDTINT is always synchronized with WDT_CLK */ |
106 | if (reg != WDTINT) |
107 | rzg2l_wdt_wait_delay(priv); |
108 | } |
109 | |
110 | static void rzg2l_wdt_init_timeout(struct watchdog_device *wdev) |
111 | { |
112 | struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdd: wdev); |
113 | u32 time_out; |
114 | |
115 | /* Clear Lapsed Time Register and clear Interrupt */ |
116 | rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT); |
117 | /* 2 consecutive overflow cycle needed to trigger reset */ |
118 | time_out = (wdev->timeout * (MICRO / 2)) / |
119 | rzg2l_wdt_get_cycle_usec(cycle: priv->osc_clk_rate, wdttime: 0); |
120 | rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(time_out), WDTSET); |
121 | } |
122 | |
123 | static int rzg2l_wdt_start(struct watchdog_device *wdev) |
124 | { |
125 | struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdd: wdev); |
126 | |
127 | pm_runtime_get_sync(dev: wdev->parent); |
128 | |
129 | /* Initialize time out */ |
130 | rzg2l_wdt_init_timeout(wdev); |
131 | |
132 | /* Initialize watchdog counter register */ |
133 | rzg2l_wdt_write(priv, val: 0, WDTTIM); |
134 | |
135 | /* Enable watchdog timer*/ |
136 | rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT); |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static int rzg2l_wdt_stop(struct watchdog_device *wdev) |
142 | { |
143 | struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdd: wdev); |
144 | |
145 | rzg2l_wdt_reset(priv); |
146 | pm_runtime_put(dev: wdev->parent); |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) |
152 | { |
153 | wdev->timeout = timeout; |
154 | |
155 | /* |
156 | * If the watchdog is active, reset the module for updating the WDTSET |
157 | * register by calling rzg2l_wdt_stop() (which internally calls reset_control_reset() |
158 | * to reset the module) so that it is updated with new timeout values. |
159 | */ |
160 | if (watchdog_active(wdd: wdev)) { |
161 | rzg2l_wdt_stop(wdev); |
162 | rzg2l_wdt_start(wdev); |
163 | } |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static int rzg2l_wdt_restart(struct watchdog_device *wdev, |
169 | unsigned long action, void *data) |
170 | { |
171 | struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdd: wdev); |
172 | |
173 | clk_prepare_enable(clk: priv->pclk); |
174 | clk_prepare_enable(clk: priv->osc_clk); |
175 | |
176 | if (priv->devtype == WDT_RZG2L) { |
177 | /* Generate Reset (WDTRSTB) Signal on parity error */ |
178 | rzg2l_wdt_write(priv, val: 0, PECR); |
179 | |
180 | /* Force parity error */ |
181 | rzg2l_wdt_write(priv, PEEN_FORCE, PEEN); |
182 | } else { |
183 | /* RZ/V2M doesn't have parity error registers */ |
184 | rzg2l_wdt_reset(priv); |
185 | |
186 | wdev->timeout = 0; |
187 | |
188 | /* Initialize time out */ |
189 | rzg2l_wdt_init_timeout(wdev); |
190 | |
191 | /* Initialize watchdog counter register */ |
192 | rzg2l_wdt_write(priv, val: 0, WDTTIM); |
193 | |
194 | /* Enable watchdog timer*/ |
195 | rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT); |
196 | |
197 | /* Wait 2 consecutive overflow cycles for reset */ |
198 | mdelay(DIV_ROUND_UP(2 * 0xFFFFF * 1000, priv->osc_clk_rate)); |
199 | } |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static const struct watchdog_info rzg2l_wdt_ident = { |
205 | .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, |
206 | .identity = "Renesas RZ/G2L WDT Watchdog" , |
207 | }; |
208 | |
209 | static int rzg2l_wdt_ping(struct watchdog_device *wdev) |
210 | { |
211 | struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdd: wdev); |
212 | |
213 | rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT); |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static const struct watchdog_ops rzg2l_wdt_ops = { |
219 | .owner = THIS_MODULE, |
220 | .start = rzg2l_wdt_start, |
221 | .stop = rzg2l_wdt_stop, |
222 | .ping = rzg2l_wdt_ping, |
223 | .set_timeout = rzg2l_wdt_set_timeout, |
224 | .restart = rzg2l_wdt_restart, |
225 | }; |
226 | |
227 | static void rzg2l_wdt_reset_assert_pm_disable(void *data) |
228 | { |
229 | struct watchdog_device *wdev = data; |
230 | struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdd: wdev); |
231 | |
232 | pm_runtime_disable(dev: wdev->parent); |
233 | reset_control_assert(rstc: priv->rstc); |
234 | } |
235 | |
236 | static int rzg2l_wdt_probe(struct platform_device *pdev) |
237 | { |
238 | struct device *dev = &pdev->dev; |
239 | struct rzg2l_wdt_priv *priv; |
240 | unsigned long pclk_rate; |
241 | int ret; |
242 | |
243 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
244 | if (!priv) |
245 | return -ENOMEM; |
246 | |
247 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
248 | if (IS_ERR(ptr: priv->base)) |
249 | return PTR_ERR(ptr: priv->base); |
250 | |
251 | /* Get watchdog main clock */ |
252 | priv->osc_clk = devm_clk_get(dev: &pdev->dev, id: "oscclk" ); |
253 | if (IS_ERR(ptr: priv->osc_clk)) |
254 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: priv->osc_clk), fmt: "no oscclk" ); |
255 | |
256 | priv->osc_clk_rate = clk_get_rate(clk: priv->osc_clk); |
257 | if (!priv->osc_clk_rate) |
258 | return dev_err_probe(dev: &pdev->dev, err: -EINVAL, fmt: "oscclk rate is 0" ); |
259 | |
260 | /* Get Peripheral clock */ |
261 | priv->pclk = devm_clk_get(dev: &pdev->dev, id: "pclk" ); |
262 | if (IS_ERR(ptr: priv->pclk)) |
263 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: priv->pclk), fmt: "no pclk" ); |
264 | |
265 | pclk_rate = clk_get_rate(clk: priv->pclk); |
266 | if (!pclk_rate) |
267 | return dev_err_probe(dev: &pdev->dev, err: -EINVAL, fmt: "pclk rate is 0" ); |
268 | |
269 | priv->delay = F2CYCLE_NSEC(priv->osc_clk_rate) * 6 + F2CYCLE_NSEC(pclk_rate) * 9; |
270 | |
271 | priv->rstc = devm_reset_control_get_exclusive(dev: &pdev->dev, NULL); |
272 | if (IS_ERR(ptr: priv->rstc)) |
273 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: priv->rstc), |
274 | fmt: "failed to get cpg reset" ); |
275 | |
276 | ret = reset_control_deassert(rstc: priv->rstc); |
277 | if (ret) |
278 | return dev_err_probe(dev, err: ret, fmt: "failed to deassert" ); |
279 | |
280 | priv->devtype = (uintptr_t)of_device_get_match_data(dev); |
281 | |
282 | if (priv->devtype == WDT_RZV2M) { |
283 | priv->minimum_assertion_period = RZV2M_A_NSEC + |
284 | 3 * F2CYCLE_NSEC(pclk_rate) + 5 * |
285 | max(F2CYCLE_NSEC(priv->osc_clk_rate), |
286 | F2CYCLE_NSEC(pclk_rate)); |
287 | } |
288 | |
289 | pm_runtime_enable(dev: &pdev->dev); |
290 | |
291 | priv->wdev.info = &rzg2l_wdt_ident; |
292 | priv->wdev.ops = &rzg2l_wdt_ops; |
293 | priv->wdev.parent = dev; |
294 | priv->wdev.min_timeout = 1; |
295 | priv->wdev.max_timeout = rzg2l_wdt_get_cycle_usec(cycle: priv->osc_clk_rate, wdttime: 0xfff) / |
296 | USEC_PER_SEC; |
297 | priv->wdev.timeout = WDT_DEFAULT_TIMEOUT; |
298 | |
299 | watchdog_set_drvdata(wdd: &priv->wdev, data: priv); |
300 | ret = devm_add_action_or_reset(&pdev->dev, |
301 | rzg2l_wdt_reset_assert_pm_disable, |
302 | &priv->wdev); |
303 | if (ret < 0) |
304 | return ret; |
305 | |
306 | watchdog_set_nowayout(wdd: &priv->wdev, nowayout); |
307 | watchdog_stop_on_unregister(wdd: &priv->wdev); |
308 | |
309 | ret = watchdog_init_timeout(wdd: &priv->wdev, timeout_parm: 0, dev); |
310 | if (ret) |
311 | dev_warn(dev, "Specified timeout invalid, using default" ); |
312 | |
313 | return devm_watchdog_register_device(dev: &pdev->dev, &priv->wdev); |
314 | } |
315 | |
316 | static const struct of_device_id rzg2l_wdt_ids[] = { |
317 | { .compatible = "renesas,rzg2l-wdt" , .data = (void *)WDT_RZG2L }, |
318 | { .compatible = "renesas,rzv2m-wdt" , .data = (void *)WDT_RZV2M }, |
319 | { /* sentinel */ } |
320 | }; |
321 | MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids); |
322 | |
323 | static struct platform_driver rzg2l_wdt_driver = { |
324 | .driver = { |
325 | .name = "rzg2l_wdt" , |
326 | .of_match_table = rzg2l_wdt_ids, |
327 | }, |
328 | .probe = rzg2l_wdt_probe, |
329 | }; |
330 | module_platform_driver(rzg2l_wdt_driver); |
331 | |
332 | MODULE_DESCRIPTION("Renesas RZ/G2L WDT Watchdog Driver" ); |
333 | MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>" ); |
334 | MODULE_LICENSE("GPL v2" ); |
335 | |