Merge tag 'drm-intel-fixes-2022-06-29' of git://anongit.freedesktop.org/drm/drm-intel...
[linux-2.6-block.git] / drivers / watchdog / rzg2l_wdt.c
CommitLineData
2cbc5cd0
BD
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/kernel.h>
12#include <linux/module.h>
13#include <linux/of.h>
14#include <linux/platform_device.h>
15#include <linux/pm_runtime.h>
16#include <linux/reset.h>
17#include <linux/units.h>
18#include <linux/watchdog.h>
19
20#define WDTCNT 0x00
21#define WDTSET 0x04
22#define WDTTIM 0x08
23#define WDTINT 0x0C
f43e6ddb
BD
24#define PECR 0x10
25#define PEEN 0x14
2cbc5cd0
BD
26#define WDTCNT_WDTEN BIT(0)
27#define WDTINT_INTDISP BIT(0)
f43e6ddb 28#define PEEN_FORCE BIT(0)
2cbc5cd0
BD
29
30#define WDT_DEFAULT_TIMEOUT 60U
31
32/* Setting period time register only 12 bit set in WDTSET[31:20] */
33#define WDTSET_COUNTER_MASK (0xFFF00000)
34#define WDTSET_COUNTER_VAL(f) ((f) << 20)
35
36#define F2CYCLE_NSEC(f) (1000000000 / (f))
37
38static bool nowayout = WATCHDOG_NOWAYOUT;
39module_param(nowayout, bool, 0);
40MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
41 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
42
43struct rzg2l_wdt_priv {
44 void __iomem *base;
45 struct watchdog_device wdev;
46 struct reset_control *rstc;
47 unsigned long osc_clk_rate;
48 unsigned long delay;
e4cf8959
BD
49 struct clk *pclk;
50 struct clk *osc_clk;
2cbc5cd0
BD
51};
52
53static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
54{
55 /* delay timer when change the setting register */
56 ndelay(priv->delay);
57}
58
59static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime)
60{
ea2949df 61 u64 timer_cycle_us = 1024 * 1024ULL * (wdttime + 1) * MICRO;
2cbc5cd0
BD
62
63 return div64_ul(timer_cycle_us, cycle);
64}
65
66static void rzg2l_wdt_write(struct rzg2l_wdt_priv *priv, u32 val, unsigned int reg)
67{
68 if (reg == WDTSET)
69 val &= WDTSET_COUNTER_MASK;
70
71 writel_relaxed(val, priv->base + reg);
72 /* Registers other than the WDTINT is always synchronized with WDT_CLK */
73 if (reg != WDTINT)
74 rzg2l_wdt_wait_delay(priv);
75}
76
77static void rzg2l_wdt_init_timeout(struct watchdog_device *wdev)
78{
79 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
80 u32 time_out;
81
82 /* Clear Lapsed Time Register and clear Interrupt */
83 rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);
84 /* 2 consecutive overflow cycle needed to trigger reset */
85 time_out = (wdev->timeout * (MICRO / 2)) /
86 rzg2l_wdt_get_cycle_usec(priv->osc_clk_rate, 0);
87 rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(time_out), WDTSET);
88}
89
90static int rzg2l_wdt_start(struct watchdog_device *wdev)
91{
92 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
93
2cbc5cd0
BD
94 pm_runtime_get_sync(wdev->parent);
95
96 /* Initialize time out */
97 rzg2l_wdt_init_timeout(wdev);
98
99 /* Initialize watchdog counter register */
100 rzg2l_wdt_write(priv, 0, WDTTIM);
101
102 /* Enable watchdog timer*/
103 rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);
104
105 return 0;
106}
107
108static int rzg2l_wdt_stop(struct watchdog_device *wdev)
109{
110 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
111
112 pm_runtime_put(wdev->parent);
33d04d0f 113 reset_control_reset(priv->rstc);
2cbc5cd0
BD
114
115 return 0;
116}
117
4055ee81
BD
118static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
119{
120 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
121
122 wdev->timeout = timeout;
123
124 /*
125 * If the watchdog is active, reset the module for updating the WDTSET
126 * register so that it is updated with new timeout values.
127 */
128 if (watchdog_active(wdev)) {
129 pm_runtime_put(wdev->parent);
130 reset_control_reset(priv->rstc);
131 rzg2l_wdt_start(wdev);
132 }
133
134 return 0;
135}
136
2cbc5cd0
BD
137static int rzg2l_wdt_restart(struct watchdog_device *wdev,
138 unsigned long action, void *data)
139{
140 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
141
e4cf8959
BD
142 clk_prepare_enable(priv->pclk);
143 clk_prepare_enable(priv->osc_clk);
2cbc5cd0 144
f43e6ddb
BD
145 /* Generate Reset (WDTRSTB) Signal on parity error */
146 rzg2l_wdt_write(priv, 0, PECR);
2cbc5cd0 147
f43e6ddb
BD
148 /* Force parity error */
149 rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
2cbc5cd0
BD
150
151 return 0;
152}
153
154static const struct watchdog_info rzg2l_wdt_ident = {
155 .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
156 .identity = "Renesas RZ/G2L WDT Watchdog",
157};
158
159static int rzg2l_wdt_ping(struct watchdog_device *wdev)
160{
161 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
162
163 rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);
164
165 return 0;
166}
167
168static const struct watchdog_ops rzg2l_wdt_ops = {
169 .owner = THIS_MODULE,
170 .start = rzg2l_wdt_start,
171 .stop = rzg2l_wdt_stop,
172 .ping = rzg2l_wdt_ping,
4055ee81 173 .set_timeout = rzg2l_wdt_set_timeout,
2cbc5cd0
BD
174 .restart = rzg2l_wdt_restart,
175};
176
95abafe7 177static void rzg2l_wdt_reset_assert_pm_disable(void *data)
2cbc5cd0
BD
178{
179 struct watchdog_device *wdev = data;
180 struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
181
2cbc5cd0
BD
182 pm_runtime_disable(wdev->parent);
183 reset_control_assert(priv->rstc);
184}
185
186static int rzg2l_wdt_probe(struct platform_device *pdev)
187{
188 struct device *dev = &pdev->dev;
189 struct rzg2l_wdt_priv *priv;
190 unsigned long pclk_rate;
2cbc5cd0
BD
191 int ret;
192
193 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
194 if (!priv)
195 return -ENOMEM;
196
197 priv->base = devm_platform_ioremap_resource(pdev, 0);
198 if (IS_ERR(priv->base))
199 return PTR_ERR(priv->base);
200
201 /* Get watchdog main clock */
e4cf8959
BD
202 priv->osc_clk = devm_clk_get(&pdev->dev, "oscclk");
203 if (IS_ERR(priv->osc_clk))
204 return dev_err_probe(&pdev->dev, PTR_ERR(priv->osc_clk), "no oscclk");
2cbc5cd0 205
e4cf8959 206 priv->osc_clk_rate = clk_get_rate(priv->osc_clk);
2cbc5cd0
BD
207 if (!priv->osc_clk_rate)
208 return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0");
209
210 /* Get Peripheral clock */
e4cf8959
BD
211 priv->pclk = devm_clk_get(&pdev->dev, "pclk");
212 if (IS_ERR(priv->pclk))
213 return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), "no pclk");
2cbc5cd0 214
e4cf8959 215 pclk_rate = clk_get_rate(priv->pclk);
2cbc5cd0
BD
216 if (!pclk_rate)
217 return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0");
218
219 priv->delay = F2CYCLE_NSEC(priv->osc_clk_rate) * 6 + F2CYCLE_NSEC(pclk_rate) * 9;
220
221 priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
222 if (IS_ERR(priv->rstc))
223 return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc),
224 "failed to get cpg reset");
225
baf1aace
BD
226 ret = reset_control_deassert(priv->rstc);
227 if (ret)
228 return dev_err_probe(dev, ret, "failed to deassert");
229
2cbc5cd0 230 pm_runtime_enable(&pdev->dev);
2cbc5cd0
BD
231
232 priv->wdev.info = &rzg2l_wdt_ident;
233 priv->wdev.ops = &rzg2l_wdt_ops;
234 priv->wdev.parent = dev;
235 priv->wdev.min_timeout = 1;
236 priv->wdev.max_timeout = rzg2l_wdt_get_cycle_usec(priv->osc_clk_rate, 0xfff) /
237 USEC_PER_SEC;
238 priv->wdev.timeout = WDT_DEFAULT_TIMEOUT;
239
240 watchdog_set_drvdata(&priv->wdev, priv);
241 ret = devm_add_action_or_reset(&pdev->dev,
95abafe7 242 rzg2l_wdt_reset_assert_pm_disable,
2cbc5cd0
BD
243 &priv->wdev);
244 if (ret < 0)
245 return ret;
246
247 watchdog_set_nowayout(&priv->wdev, nowayout);
248 watchdog_stop_on_unregister(&priv->wdev);
249
250 ret = watchdog_init_timeout(&priv->wdev, 0, dev);
251 if (ret)
252 dev_warn(dev, "Specified timeout invalid, using default");
253
254 return devm_watchdog_register_device(&pdev->dev, &priv->wdev);
2cbc5cd0
BD
255}
256
257static const struct of_device_id rzg2l_wdt_ids[] = {
258 { .compatible = "renesas,rzg2l-wdt", },
259 { /* sentinel */ }
260};
261MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids);
262
263static struct platform_driver rzg2l_wdt_driver = {
264 .driver = {
265 .name = "rzg2l_wdt",
266 .of_match_table = rzg2l_wdt_ids,
267 },
268 .probe = rzg2l_wdt_probe,
269};
270module_platform_driver(rzg2l_wdt_driver);
271
272MODULE_DESCRIPTION("Renesas RZ/G2L WDT Watchdog Driver");
273MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
274MODULE_LICENSE("GPL v2");