xref: /linux/drivers/watchdog/mt7621_wdt.c (revision b893e344bfbd01a3df5df32ecb9f7bf8f1271d46)
1ab3f09feSJohn Crispin /*
2ab3f09feSJohn Crispin  * Ralink MT7621/MT7628 built-in hardware watchdog timer
3ab3f09feSJohn Crispin  *
4f3519a66SJohn Crispin  * Copyright (C) 2014 John Crispin <john@phrozen.org>
5ab3f09feSJohn Crispin  *
6ab3f09feSJohn Crispin  * This driver was based on: drivers/watchdog/rt2880_wdt.c
7ab3f09feSJohn Crispin  *
8ab3f09feSJohn Crispin  * This program is free software; you can redistribute it and/or modify it
9ab3f09feSJohn Crispin  * under the terms of the GNU General Public License version 2 as published
10ab3f09feSJohn Crispin  * by the Free Software Foundation.
11ab3f09feSJohn Crispin  */
12ab3f09feSJohn Crispin 
13ab3f09feSJohn Crispin #include <linux/clk.h>
14ab3f09feSJohn Crispin #include <linux/reset.h>
15ab3f09feSJohn Crispin #include <linux/module.h>
16ab3f09feSJohn Crispin #include <linux/kernel.h>
17ab3f09feSJohn Crispin #include <linux/watchdog.h>
18ab3f09feSJohn Crispin #include <linux/moduleparam.h>
19ab3f09feSJohn Crispin #include <linux/platform_device.h>
20ab3f09feSJohn Crispin 
21ab3f09feSJohn Crispin #include <asm/mach-ralink/ralink_regs.h>
22ab3f09feSJohn Crispin 
23ab3f09feSJohn Crispin #define SYSC_RSTSTAT			0x38
24ab3f09feSJohn Crispin #define WDT_RST_CAUSE			BIT(1)
25ab3f09feSJohn Crispin 
26ab3f09feSJohn Crispin #define RALINK_WDT_TIMEOUT		30
27ab3f09feSJohn Crispin 
28ab3f09feSJohn Crispin #define TIMER_REG_TMRSTAT		0x00
29ab3f09feSJohn Crispin #define TIMER_REG_TMR1LOAD		0x24
30ab3f09feSJohn Crispin #define TIMER_REG_TMR1CTL		0x20
31ab3f09feSJohn Crispin 
32ab3f09feSJohn Crispin #define TMR1CTL_ENABLE			BIT(7)
33ab3f09feSJohn Crispin #define TMR1CTL_RESTART			BIT(9)
34ab3f09feSJohn Crispin #define TMR1CTL_PRESCALE_SHIFT		16
35ab3f09feSJohn Crispin 
36ab3f09feSJohn Crispin static void __iomem *mt7621_wdt_base;
37ab3f09feSJohn Crispin static struct reset_control *mt7621_wdt_reset;
38ab3f09feSJohn Crispin 
39ab3f09feSJohn Crispin static bool nowayout = WATCHDOG_NOWAYOUT;
40ab3f09feSJohn Crispin module_param(nowayout, bool, 0);
41ab3f09feSJohn Crispin MODULE_PARM_DESC(nowayout,
42ab3f09feSJohn Crispin 		 "Watchdog cannot be stopped once started (default="
43ab3f09feSJohn Crispin 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
44ab3f09feSJohn Crispin 
45ab3f09feSJohn Crispin static inline void rt_wdt_w32(unsigned reg, u32 val)
46ab3f09feSJohn Crispin {
47ab3f09feSJohn Crispin 	iowrite32(val, mt7621_wdt_base + reg);
48ab3f09feSJohn Crispin }
49ab3f09feSJohn Crispin 
50ab3f09feSJohn Crispin static inline u32 rt_wdt_r32(unsigned reg)
51ab3f09feSJohn Crispin {
52ab3f09feSJohn Crispin 	return ioread32(mt7621_wdt_base + reg);
53ab3f09feSJohn Crispin }
54ab3f09feSJohn Crispin 
55ab3f09feSJohn Crispin static int mt7621_wdt_ping(struct watchdog_device *w)
56ab3f09feSJohn Crispin {
57ab3f09feSJohn Crispin 	rt_wdt_w32(TIMER_REG_TMRSTAT, TMR1CTL_RESTART);
58ab3f09feSJohn Crispin 
59ab3f09feSJohn Crispin 	return 0;
60ab3f09feSJohn Crispin }
61ab3f09feSJohn Crispin 
62ab3f09feSJohn Crispin static int mt7621_wdt_set_timeout(struct watchdog_device *w, unsigned int t)
63ab3f09feSJohn Crispin {
64ab3f09feSJohn Crispin 	w->timeout = t;
65ab3f09feSJohn Crispin 	rt_wdt_w32(TIMER_REG_TMR1LOAD, t * 1000);
66ab3f09feSJohn Crispin 	mt7621_wdt_ping(w);
67ab3f09feSJohn Crispin 
68ab3f09feSJohn Crispin 	return 0;
69ab3f09feSJohn Crispin }
70ab3f09feSJohn Crispin 
71ab3f09feSJohn Crispin static int mt7621_wdt_start(struct watchdog_device *w)
72ab3f09feSJohn Crispin {
73ab3f09feSJohn Crispin 	u32 t;
74ab3f09feSJohn Crispin 
75ab3f09feSJohn Crispin 	/* set the prescaler to 1ms == 1000us */
76ab3f09feSJohn Crispin 	rt_wdt_w32(TIMER_REG_TMR1CTL, 1000 << TMR1CTL_PRESCALE_SHIFT);
77ab3f09feSJohn Crispin 
78ab3f09feSJohn Crispin 	mt7621_wdt_set_timeout(w, w->timeout);
79ab3f09feSJohn Crispin 
80ab3f09feSJohn Crispin 	t = rt_wdt_r32(TIMER_REG_TMR1CTL);
81ab3f09feSJohn Crispin 	t |= TMR1CTL_ENABLE;
82ab3f09feSJohn Crispin 	rt_wdt_w32(TIMER_REG_TMR1CTL, t);
83ab3f09feSJohn Crispin 
84ab3f09feSJohn Crispin 	return 0;
85ab3f09feSJohn Crispin }
86ab3f09feSJohn Crispin 
87ab3f09feSJohn Crispin static int mt7621_wdt_stop(struct watchdog_device *w)
88ab3f09feSJohn Crispin {
89ab3f09feSJohn Crispin 	u32 t;
90ab3f09feSJohn Crispin 
91ab3f09feSJohn Crispin 	mt7621_wdt_ping(w);
92ab3f09feSJohn Crispin 
93ab3f09feSJohn Crispin 	t = rt_wdt_r32(TIMER_REG_TMR1CTL);
94ab3f09feSJohn Crispin 	t &= ~TMR1CTL_ENABLE;
95ab3f09feSJohn Crispin 	rt_wdt_w32(TIMER_REG_TMR1CTL, t);
96ab3f09feSJohn Crispin 
97ab3f09feSJohn Crispin 	return 0;
98ab3f09feSJohn Crispin }
99ab3f09feSJohn Crispin 
100ab3f09feSJohn Crispin static int mt7621_wdt_bootcause(void)
101ab3f09feSJohn Crispin {
102ab3f09feSJohn Crispin 	if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE)
103ab3f09feSJohn Crispin 		return WDIOF_CARDRESET;
104ab3f09feSJohn Crispin 
105ab3f09feSJohn Crispin 	return 0;
106ab3f09feSJohn Crispin }
107ab3f09feSJohn Crispin 
108ab3f09feSJohn Crispin static struct watchdog_info mt7621_wdt_info = {
109ab3f09feSJohn Crispin 	.identity = "Mediatek Watchdog",
110ab3f09feSJohn Crispin 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
111ab3f09feSJohn Crispin };
112ab3f09feSJohn Crispin 
113*b893e344SBhumika Goyal static const struct watchdog_ops mt7621_wdt_ops = {
114ab3f09feSJohn Crispin 	.owner = THIS_MODULE,
115ab3f09feSJohn Crispin 	.start = mt7621_wdt_start,
116ab3f09feSJohn Crispin 	.stop = mt7621_wdt_stop,
117ab3f09feSJohn Crispin 	.ping = mt7621_wdt_ping,
118ab3f09feSJohn Crispin 	.set_timeout = mt7621_wdt_set_timeout,
119ab3f09feSJohn Crispin };
120ab3f09feSJohn Crispin 
121ab3f09feSJohn Crispin static struct watchdog_device mt7621_wdt_dev = {
122ab3f09feSJohn Crispin 	.info = &mt7621_wdt_info,
123ab3f09feSJohn Crispin 	.ops = &mt7621_wdt_ops,
124ab3f09feSJohn Crispin 	.min_timeout = 1,
125ab3f09feSJohn Crispin 	.max_timeout = 0xfffful / 1000,
126ab3f09feSJohn Crispin };
127ab3f09feSJohn Crispin 
128ab3f09feSJohn Crispin static int mt7621_wdt_probe(struct platform_device *pdev)
129ab3f09feSJohn Crispin {
130ab3f09feSJohn Crispin 	struct resource *res;
131ab3f09feSJohn Crispin 	int ret;
132ab3f09feSJohn Crispin 
133ab3f09feSJohn Crispin 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
134ab3f09feSJohn Crispin 	mt7621_wdt_base = devm_ioremap_resource(&pdev->dev, res);
135ab3f09feSJohn Crispin 	if (IS_ERR(mt7621_wdt_base))
136ab3f09feSJohn Crispin 		return PTR_ERR(mt7621_wdt_base);
137ab3f09feSJohn Crispin 
138ab3f09feSJohn Crispin 	mt7621_wdt_reset = devm_reset_control_get(&pdev->dev, NULL);
139ab3f09feSJohn Crispin 	if (!IS_ERR(mt7621_wdt_reset))
140ab3f09feSJohn Crispin 		reset_control_deassert(mt7621_wdt_reset);
141ab3f09feSJohn Crispin 
142ab3f09feSJohn Crispin 	mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause();
143ab3f09feSJohn Crispin 
144ab3f09feSJohn Crispin 	watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
145ab3f09feSJohn Crispin 			      &pdev->dev);
146ab3f09feSJohn Crispin 	watchdog_set_nowayout(&mt7621_wdt_dev, nowayout);
147ab3f09feSJohn Crispin 
148ab3f09feSJohn Crispin 	ret = watchdog_register_device(&mt7621_wdt_dev);
149ab3f09feSJohn Crispin 
150ab3f09feSJohn Crispin 	return 0;
151ab3f09feSJohn Crispin }
152ab3f09feSJohn Crispin 
153ab3f09feSJohn Crispin static int mt7621_wdt_remove(struct platform_device *pdev)
154ab3f09feSJohn Crispin {
155ab3f09feSJohn Crispin 	watchdog_unregister_device(&mt7621_wdt_dev);
156ab3f09feSJohn Crispin 
157ab3f09feSJohn Crispin 	return 0;
158ab3f09feSJohn Crispin }
159ab3f09feSJohn Crispin 
160ab3f09feSJohn Crispin static void mt7621_wdt_shutdown(struct platform_device *pdev)
161ab3f09feSJohn Crispin {
162ab3f09feSJohn Crispin 	mt7621_wdt_stop(&mt7621_wdt_dev);
163ab3f09feSJohn Crispin }
164ab3f09feSJohn Crispin 
165ab3f09feSJohn Crispin static const struct of_device_id mt7621_wdt_match[] = {
166ab3f09feSJohn Crispin 	{ .compatible = "mediatek,mt7621-wdt" },
167ab3f09feSJohn Crispin 	{},
168ab3f09feSJohn Crispin };
169ab3f09feSJohn Crispin MODULE_DEVICE_TABLE(of, mt7621_wdt_match);
170ab3f09feSJohn Crispin 
171ab3f09feSJohn Crispin static struct platform_driver mt7621_wdt_driver = {
172ab3f09feSJohn Crispin 	.probe		= mt7621_wdt_probe,
173ab3f09feSJohn Crispin 	.remove		= mt7621_wdt_remove,
174ab3f09feSJohn Crispin 	.shutdown	= mt7621_wdt_shutdown,
175ab3f09feSJohn Crispin 	.driver		= {
176ab3f09feSJohn Crispin 		.name		= KBUILD_MODNAME,
177ab3f09feSJohn Crispin 		.of_match_table	= mt7621_wdt_match,
178ab3f09feSJohn Crispin 	},
179ab3f09feSJohn Crispin };
180ab3f09feSJohn Crispin 
181ab3f09feSJohn Crispin module_platform_driver(mt7621_wdt_driver);
182ab3f09feSJohn Crispin 
183ab3f09feSJohn Crispin MODULE_DESCRIPTION("MediaTek MT762x hardware watchdog driver");
184f3519a66SJohn Crispin MODULE_AUTHOR("John Crispin <john@phrozen.org");
185ab3f09feSJohn Crispin MODULE_LICENSE("GPL v2");
186