Skip to content

Commit ef9e7fe

Browse files
Bharat BhushanWim Van Sebroeck
authored andcommitted
Watchdog: Add marvell GTI watchdog driver
This patch add support for Marvell GTI watchdog. Global timer unit (GTI) support hardware watchdog timer. Software programs watchdog timer to generate interrupt on first timeout, second timeout is configured to be ignored and system reboots on third timeout. Signed-off-by: Bharat Bhushan <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] [groeck: MODULE_LICENSE GPL v2 -> GPL since that is sufficient] Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent e81e57f commit ef9e7fe

File tree

3 files changed

+354
-0
lines changed

3 files changed

+354
-0
lines changed

drivers/watchdog/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,19 @@ config OCTEON_WDT
17971797
from the first interrupt, it is then only poked when the
17981798
device is written.
17991799

1800+
config MARVELL_GTI_WDT
1801+
tristate "Marvell GTI Watchdog driver"
1802+
depends on ARCH_THUNDER || (COMPILE_TEST && 64BIT)
1803+
default y
1804+
select WATCHDOG_CORE
1805+
help
1806+
Marvell GTI hardware supports watchdog timer. First timeout
1807+
works as watchdog pretimeout and installed interrupt handler
1808+
will be called on first timeout. Hardware can generate interrupt
1809+
to SCP on second timeout but it is not enabled, so second
1810+
timeout is ignored. If device poke does not happen then system
1811+
will reboot on third timeout.
1812+
18001813
config BCM2835_WDT
18011814
tristate "Broadcom BCM2835 hardware watchdog"
18021815
depends on ARCH_BCM2835 || (OF && COMPILE_TEST)

drivers/watchdog/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
9898
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
9999
obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
100100
obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
101+
obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o
101102

102103
# X86 (i386 + ia64 + x86_64) Architecture
103104
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o

drivers/watchdog/marvell_gti_wdt.c

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Marvell GTI Watchdog driver
3+
*
4+
* Copyright (C) 2023 Marvell.
5+
*/
6+
7+
#include <linux/clk.h>
8+
#include <linux/interrupt.h>
9+
#include <linux/io.h>
10+
#include <linux/module.h>
11+
#include <linux/of_platform.h>
12+
#include <linux/platform_device.h>
13+
#include <linux/watchdog.h>
14+
15+
/*
16+
* Hardware supports following mode of operation:
17+
* 1) Interrupt Only:
18+
* This will generate the interrupt to arm core whenever timeout happens.
19+
*
20+
* 2) Interrupt + del3t (Interrupt to firmware (SCP processor)).
21+
* This will generate interrupt to arm core on 1st timeout happens
22+
* This will generate interrupt to SCP processor on 2nd timeout happens
23+
*
24+
* 3) Interrupt + Interrupt to SCP processor (called delt3t) + reboot.
25+
* This will generate interrupt to arm core on 1st timeout happens
26+
* Will generate interrupt to SCP processor on 2nd timeout happens,
27+
* if interrupt is configured.
28+
* Reboot on 3rd timeout.
29+
*
30+
* Driver will use hardware in mode-3 above so that system can reboot in case
31+
* a hardware hang. Also h/w is configured not to generate SCP interrupt, so
32+
* effectively 2nd timeout is ignored within hardware.
33+
*
34+
* First timeout is effectively watchdog pretimeout.
35+
*/
36+
37+
/* GTI CWD Watchdog (GTI_CWD_WDOG) Register */
38+
#define GTI_CWD_WDOG(reg_offset) (0x8 * (reg_offset))
39+
#define GTI_CWD_WDOG_MODE_INT_DEL3T_RST 0x3
40+
#define GTI_CWD_WDOG_MODE_MASK GENMASK_ULL(1, 0)
41+
#define GTI_CWD_WDOG_LEN_SHIFT 4
42+
#define GTI_CWD_WDOG_LEN_MASK GENMASK_ULL(19, 4)
43+
#define GTI_CWD_WDOG_CNT_SHIFT 20
44+
#define GTI_CWD_WDOG_CNT_MASK GENMASK_ULL(43, 20)
45+
46+
/* GTI CWD Watchdog Interrupt (GTI_CWD_INT) Register */
47+
#define GTI_CWD_INT 0x200
48+
#define GTI_CWD_INT_PENDING_STATUS(bit) BIT_ULL(bit)
49+
50+
/* GTI CWD Watchdog Interrupt Enable Clear (GTI_CWD_INT_ENA_CLR) Register */
51+
#define GTI_CWD_INT_ENA_CLR 0x210
52+
#define GTI_CWD_INT_ENA_CLR_VAL(bit) BIT_ULL(bit)
53+
54+
/* GTI CWD Watchdog Interrupt Enable Set (GTI_CWD_INT_ENA_SET) Register */
55+
#define GTI_CWD_INT_ENA_SET 0x218
56+
#define GTI_CWD_INT_ENA_SET_VAL(bit) BIT_ULL(bit)
57+
58+
/* GTI CWD Watchdog Poke (GTI_CWD_POKE) Registers */
59+
#define GTI_CWD_POKE(reg_offset) (0x10000 + 0x8 * (reg_offset))
60+
#define GTI_CWD_POKE_VAL 1
61+
62+
struct gti_match_data {
63+
u32 gti_num_timers;
64+
};
65+
66+
static const struct gti_match_data match_data_octeontx2 = {
67+
.gti_num_timers = 54,
68+
};
69+
70+
static const struct gti_match_data match_data_cn10k = {
71+
.gti_num_timers = 64,
72+
};
73+
74+
struct gti_wdt_priv {
75+
struct watchdog_device wdev;
76+
void __iomem *base;
77+
u32 clock_freq;
78+
struct clk *sclk;
79+
/* wdt_timer_idx used for timer to be used for system watchdog */
80+
u32 wdt_timer_idx;
81+
const struct gti_match_data *data;
82+
};
83+
84+
static irqreturn_t gti_wdt_interrupt(int irq, void *data)
85+
{
86+
struct watchdog_device *wdev = data;
87+
struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev);
88+
89+
/* Clear Interrupt Pending Status */
90+
writeq(GTI_CWD_INT_PENDING_STATUS(priv->wdt_timer_idx),
91+
priv->base + GTI_CWD_INT);
92+
93+
watchdog_notify_pretimeout(wdev);
94+
95+
return IRQ_HANDLED;
96+
}
97+
98+
static int gti_wdt_ping(struct watchdog_device *wdev)
99+
{
100+
struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev);
101+
102+
writeq(GTI_CWD_POKE_VAL,
103+
priv->base + GTI_CWD_POKE(priv->wdt_timer_idx));
104+
105+
return 0;
106+
}
107+
108+
static int gti_wdt_start(struct watchdog_device *wdev)
109+
{
110+
struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev);
111+
u64 regval;
112+
113+
if (!wdev->pretimeout)
114+
return -EINVAL;
115+
116+
set_bit(WDOG_HW_RUNNING, &wdev->status);
117+
118+
/* Clear any pending interrupt */
119+
writeq(GTI_CWD_INT_PENDING_STATUS(priv->wdt_timer_idx),
120+
priv->base + GTI_CWD_INT);
121+
122+
/* Enable Interrupt */
123+
writeq(GTI_CWD_INT_ENA_SET_VAL(priv->wdt_timer_idx),
124+
priv->base + GTI_CWD_INT_ENA_SET);
125+
126+
/* Set (Interrupt + SCP interrupt (DEL3T) + core domain reset) Mode */
127+
regval = readq(priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx));
128+
regval |= GTI_CWD_WDOG_MODE_INT_DEL3T_RST;
129+
writeq(regval, priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx));
130+
131+
return 0;
132+
}
133+
134+
static int gti_wdt_stop(struct watchdog_device *wdev)
135+
{
136+
struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev);
137+
u64 regval;
138+
139+
/* Disable Interrupt */
140+
writeq(GTI_CWD_INT_ENA_CLR_VAL(priv->wdt_timer_idx),
141+
priv->base + GTI_CWD_INT_ENA_CLR);
142+
143+
/* Set GTI_CWD_WDOG.Mode = 0 to stop the timer */
144+
regval = readq(priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx));
145+
regval &= ~GTI_CWD_WDOG_MODE_MASK;
146+
writeq(regval, priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx));
147+
148+
return 0;
149+
}
150+
151+
static int gti_wdt_settimeout(struct watchdog_device *wdev,
152+
unsigned int timeout)
153+
{
154+
struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev);
155+
u64 timeout_wdog, regval;
156+
157+
/* Update new timeout */
158+
wdev->timeout = timeout;
159+
160+
/* Pretimeout is 1/3 of timeout */
161+
wdev->pretimeout = timeout / 3;
162+
163+
/* Get clock cycles from pretimeout */
164+
timeout_wdog = (u64)priv->clock_freq * wdev->pretimeout;
165+
166+
/* Watchdog counts in 1024 cycle steps */
167+
timeout_wdog = timeout_wdog >> 10;
168+
169+
/* GTI_CWD_WDOG.CNT: reload counter is 16-bit */
170+
timeout_wdog = (timeout_wdog + 0xff) >> 8;
171+
if (timeout_wdog >= 0x10000)
172+
timeout_wdog = 0xffff;
173+
174+
/*
175+
* GTI_CWD_WDOG.LEN is 24bit, lower 8-bits should be zero and
176+
* upper 16-bits are same as GTI_CWD_WDOG.CNT
177+
*/
178+
regval = readq(priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx));
179+
regval &= GTI_CWD_WDOG_MODE_MASK;
180+
regval |= (timeout_wdog << (GTI_CWD_WDOG_CNT_SHIFT + 8)) |
181+
(timeout_wdog << GTI_CWD_WDOG_LEN_SHIFT);
182+
writeq(regval, priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx));
183+
184+
return 0;
185+
}
186+
187+
static int gti_wdt_set_pretimeout(struct watchdog_device *wdev,
188+
unsigned int timeout)
189+
{
190+
struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev);
191+
struct watchdog_device *wdog_dev = &priv->wdev;
192+
193+
/* pretimeout should 1/3 of max_timeout */
194+
if (timeout * 3 <= wdog_dev->max_timeout)
195+
return gti_wdt_settimeout(wdev, timeout * 3);
196+
197+
return -EINVAL;
198+
}
199+
200+
static void gti_clk_disable_unprepare(void *data)
201+
{
202+
clk_disable_unprepare(data);
203+
}
204+
205+
static int gti_wdt_get_cntfrq(struct platform_device *pdev,
206+
struct gti_wdt_priv *priv)
207+
{
208+
int err;
209+
210+
priv->sclk = devm_clk_get_enabled(&pdev->dev, NULL);
211+
if (IS_ERR(priv->sclk))
212+
return PTR_ERR(priv->sclk);
213+
214+
err = devm_add_action_or_reset(&pdev->dev,
215+
gti_clk_disable_unprepare, priv->sclk);
216+
if (err)
217+
return err;
218+
219+
priv->clock_freq = clk_get_rate(priv->sclk);
220+
if (!priv->clock_freq)
221+
return -EINVAL;
222+
223+
return 0;
224+
}
225+
226+
static const struct watchdog_info gti_wdt_ident = {
227+
.identity = "Marvell GTI watchdog",
228+
.options = WDIOF_SETTIMEOUT | WDIOF_PRETIMEOUT | WDIOF_KEEPALIVEPING |
229+
WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
230+
};
231+
232+
static const struct watchdog_ops gti_wdt_ops = {
233+
.owner = THIS_MODULE,
234+
.start = gti_wdt_start,
235+
.stop = gti_wdt_stop,
236+
.ping = gti_wdt_ping,
237+
.set_timeout = gti_wdt_settimeout,
238+
.set_pretimeout = gti_wdt_set_pretimeout,
239+
};
240+
241+
static int gti_wdt_probe(struct platform_device *pdev)
242+
{
243+
struct gti_wdt_priv *priv;
244+
struct device *dev = &pdev->dev;
245+
struct watchdog_device *wdog_dev;
246+
u64 max_pretimeout;
247+
u32 wdt_idx;
248+
int irq;
249+
int err;
250+
251+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
252+
if (!priv)
253+
return -ENOMEM;
254+
255+
priv->base = devm_platform_ioremap_resource(pdev, 0);
256+
if (IS_ERR(priv->base))
257+
return dev_err_probe(&pdev->dev, PTR_ERR(priv->base),
258+
"reg property not valid/found\n");
259+
260+
err = gti_wdt_get_cntfrq(pdev, priv);
261+
if (err)
262+
return dev_err_probe(&pdev->dev, err,
263+
"GTI clock frequency not valid/found");
264+
265+
priv->data = of_device_get_match_data(dev);
266+
267+
/* default use last timer for watchdog */
268+
priv->wdt_timer_idx = priv->data->gti_num_timers - 1;
269+
270+
err = of_property_read_u32(dev->of_node, "marvell,wdt-timer-index",
271+
&wdt_idx);
272+
if (!err) {
273+
if (wdt_idx >= priv->data->gti_num_timers)
274+
return dev_err_probe(&pdev->dev, err,
275+
"GTI wdog timer index not valid");
276+
277+
priv->wdt_timer_idx = wdt_idx;
278+
}
279+
280+
wdog_dev = &priv->wdev;
281+
wdog_dev->info = &gti_wdt_ident,
282+
wdog_dev->ops = &gti_wdt_ops,
283+
wdog_dev->parent = dev;
284+
/*
285+
* Watchdog counter is 24 bit where lower 8 bits are zeros
286+
* This counter decrements every 1024 clock cycles.
287+
*/
288+
max_pretimeout = (GTI_CWD_WDOG_CNT_MASK >> GTI_CWD_WDOG_CNT_SHIFT);
289+
max_pretimeout &= ~0xFFUL;
290+
max_pretimeout = (max_pretimeout * 1024) / priv->clock_freq;
291+
wdog_dev->pretimeout = max_pretimeout;
292+
293+
/* Maximum timeout is 3 times the pretimeout */
294+
wdog_dev->max_timeout = max_pretimeout * 3;
295+
/* Minimum first timeout (pretimeout) is 1, so min_timeout as 3 */
296+
wdog_dev->min_timeout = 3;
297+
wdog_dev->timeout = wdog_dev->pretimeout;
298+
299+
watchdog_set_drvdata(wdog_dev, priv);
300+
platform_set_drvdata(pdev, priv);
301+
gti_wdt_settimeout(wdog_dev, wdog_dev->timeout);
302+
watchdog_stop_on_reboot(wdog_dev);
303+
watchdog_stop_on_unregister(wdog_dev);
304+
305+
err = devm_watchdog_register_device(dev, wdog_dev);
306+
if (err)
307+
return err;
308+
309+
irq = platform_get_irq(pdev, 0);
310+
if (irq < 0)
311+
return dev_err_probe(&pdev->dev, irq, "IRQ resource not found\n");
312+
313+
err = devm_request_irq(dev, irq, gti_wdt_interrupt, 0,
314+
pdev->name, &priv->wdev);
315+
if (err)
316+
return dev_err_probe(dev, err, "Failed to register interrupt handler\n");
317+
318+
dev_info(dev, "Watchdog enabled (timeout=%d sec)\n", wdog_dev->timeout);
319+
return 0;
320+
}
321+
322+
static const struct of_device_id gti_wdt_of_match[] = {
323+
{ .compatible = "marvell,cn9670-wdt", .data = &match_data_octeontx2},
324+
{ .compatible = "marvell,cn10624-wdt", .data = &match_data_cn10k},
325+
{ },
326+
};
327+
MODULE_DEVICE_TABLE(of, gti_wdt_of_match);
328+
329+
static struct platform_driver gti_wdt_driver = {
330+
.driver = {
331+
.name = "gti-wdt",
332+
.of_match_table = gti_wdt_of_match,
333+
},
334+
.probe = gti_wdt_probe,
335+
};
336+
module_platform_driver(gti_wdt_driver);
337+
338+
MODULE_AUTHOR("Bharat Bhushan <[email protected]>");
339+
MODULE_DESCRIPTION("Marvell GTI watchdog driver");
340+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)