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