Skip to content

Commit a44a455

Browse files
mbggWim Van Sebroeck
authored andcommitted
watchdog: Add driver for Mediatek watchdog
This patch adds a driver for the Mediatek SoC integrated watchdog. This driver supports watchdog and software reset for mt65xx and mt81xx SoCs. Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> Tested-by: Eddie Huang <eddie.huang@mediatek.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
1 parent fb1cbea commit a44a455

File tree

3 files changed

+262
-0
lines changed

3 files changed

+262
-0
lines changed

drivers/watchdog/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,16 @@ config MESON_WATCHDOG
505505
To compile this driver as a module, choose M here: the
506506
module will be called meson_wdt.
507507

508+
config MEDIATEK_WATCHDOG
509+
tristate "Mediatek SoCs watchdog support"
510+
depends on ARCH_MEDIATEK
511+
select WATCHDOG_CORE
512+
help
513+
Say Y here to include support for the watchdog timer
514+
in Mediatek SoCs.
515+
To compile this driver as a module, choose M here: the
516+
module will be called mtk_wdt.
517+
508518
# AVR32 Architecture
509519

510520
config AT32AP700X_WDT

drivers/watchdog/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
6363
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
6464
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
6565
obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
66+
obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
6667

6768
# AVR32 Architecture
6869
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o

drivers/watchdog/mtk_wdt.c

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/*
2+
* Mediatek Watchdog Driver
3+
*
4+
* Copyright (C) 2014 Matthias Brugger
5+
*
6+
* Matthias Brugger <matthias.bgg@gmail.com>
7+
*
8+
* This program is free software; you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation; either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* Based on sunxi_wdt.c
19+
*/
20+
21+
#include <linux/err.h>
22+
#include <linux/init.h>
23+
#include <linux/io.h>
24+
#include <linux/kernel.h>
25+
#include <linux/module.h>
26+
#include <linux/moduleparam.h>
27+
#include <linux/of.h>
28+
#include <linux/platform_device.h>
29+
#include <linux/types.h>
30+
#include <linux/watchdog.h>
31+
#include <linux/notifier.h>
32+
#include <linux/reboot.h>
33+
#include <linux/delay.h>
34+
35+
#define WDT_MAX_TIMEOUT 31
36+
#define WDT_MIN_TIMEOUT 1
37+
#define WDT_LENGTH_TIMEOUT(n) ((n) << 5)
38+
39+
#define WDT_LENGTH 0x04
40+
#define WDT_LENGTH_KEY 0x8
41+
42+
#define WDT_RST 0x08
43+
#define WDT_RST_RELOAD 0x1971
44+
45+
#define WDT_MODE 0x00
46+
#define WDT_MODE_EN (1 << 0)
47+
#define WDT_MODE_EXT_POL_LOW (0 << 1)
48+
#define WDT_MODE_EXT_POL_HIGH (1 << 1)
49+
#define WDT_MODE_EXRST_EN (1 << 2)
50+
#define WDT_MODE_IRQ_EN (1 << 3)
51+
#define WDT_MODE_AUTO_START (1 << 4)
52+
#define WDT_MODE_DUAL_EN (1 << 6)
53+
#define WDT_MODE_KEY 0x22000000
54+
55+
#define WDT_SWRST 0x14
56+
#define WDT_SWRST_KEY 0x1209
57+
58+
#define DRV_NAME "mtk-wdt"
59+
#define DRV_VERSION "1.0"
60+
61+
static bool nowayout = WATCHDOG_NOWAYOUT;
62+
static unsigned int timeout = WDT_MAX_TIMEOUT;
63+
64+
struct mtk_wdt_dev {
65+
struct watchdog_device wdt_dev;
66+
void __iomem *wdt_base;
67+
struct notifier_block restart_handler;
68+
};
69+
70+
static int mtk_reset_handler(struct notifier_block *this, unsigned long mode,
71+
void *cmd)
72+
{
73+
struct mtk_wdt_dev *mtk_wdt;
74+
void __iomem *wdt_base;
75+
76+
mtk_wdt = container_of(this, struct mtk_wdt_dev, restart_handler);
77+
wdt_base = mtk_wdt->wdt_base;
78+
79+
while (1) {
80+
writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
81+
mdelay(5);
82+
}
83+
84+
return NOTIFY_DONE;
85+
}
86+
87+
static int mtk_wdt_ping(struct watchdog_device *wdt_dev)
88+
{
89+
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
90+
void __iomem *wdt_base = mtk_wdt->wdt_base;
91+
92+
iowrite32(WDT_RST_RELOAD, wdt_base + WDT_RST);
93+
94+
return 0;
95+
}
96+
97+
static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev,
98+
unsigned int timeout)
99+
{
100+
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
101+
void __iomem *wdt_base = mtk_wdt->wdt_base;
102+
u32 reg;
103+
104+
wdt_dev->timeout = timeout;
105+
106+
/*
107+
* One bit is the value of 512 ticks
108+
* The clock has 32 KHz
109+
*/
110+
reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY;
111+
iowrite32(reg, wdt_base + WDT_LENGTH);
112+
113+
mtk_wdt_ping(wdt_dev);
114+
115+
return 0;
116+
}
117+
118+
static int mtk_wdt_stop(struct watchdog_device *wdt_dev)
119+
{
120+
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
121+
void __iomem *wdt_base = mtk_wdt->wdt_base;
122+
u32 reg;
123+
124+
reg = readl(wdt_base + WDT_MODE);
125+
reg &= ~WDT_MODE_EN;
126+
iowrite32(reg, wdt_base + WDT_MODE);
127+
128+
return 0;
129+
}
130+
131+
static int mtk_wdt_start(struct watchdog_device *wdt_dev)
132+
{
133+
u32 reg;
134+
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
135+
void __iomem *wdt_base = mtk_wdt->wdt_base;
136+
u32 ret;
137+
138+
ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
139+
if (ret < 0)
140+
return ret;
141+
142+
reg = ioread32(wdt_base + WDT_MODE);
143+
reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
144+
reg |= (WDT_MODE_EN | WDT_MODE_KEY);
145+
iowrite32(reg, wdt_base + WDT_MODE);
146+
147+
return 0;
148+
}
149+
150+
static const struct watchdog_info mtk_wdt_info = {
151+
.identity = DRV_NAME,
152+
.options = WDIOF_SETTIMEOUT |
153+
WDIOF_KEEPALIVEPING |
154+
WDIOF_MAGICCLOSE,
155+
};
156+
157+
static const struct watchdog_ops mtk_wdt_ops = {
158+
.owner = THIS_MODULE,
159+
.start = mtk_wdt_start,
160+
.stop = mtk_wdt_stop,
161+
.ping = mtk_wdt_ping,
162+
.set_timeout = mtk_wdt_set_timeout,
163+
};
164+
165+
static int mtk_wdt_probe(struct platform_device *pdev)
166+
{
167+
struct mtk_wdt_dev *mtk_wdt;
168+
struct resource *res;
169+
int err;
170+
171+
mtk_wdt = devm_kzalloc(&pdev->dev, sizeof(*mtk_wdt), GFP_KERNEL);
172+
if (!mtk_wdt)
173+
return -ENOMEM;
174+
175+
platform_set_drvdata(pdev, mtk_wdt);
176+
177+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
178+
mtk_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
179+
if (IS_ERR(mtk_wdt->wdt_base))
180+
return PTR_ERR(mtk_wdt->wdt_base);
181+
182+
mtk_wdt->wdt_dev.info = &mtk_wdt_info;
183+
mtk_wdt->wdt_dev.ops = &mtk_wdt_ops;
184+
mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;
185+
mtk_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT;
186+
mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT;
187+
mtk_wdt->wdt_dev.parent = &pdev->dev;
188+
189+
watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, &pdev->dev);
190+
watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout);
191+
192+
watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt);
193+
194+
mtk_wdt_stop(&mtk_wdt->wdt_dev);
195+
196+
err = watchdog_register_device(&mtk_wdt->wdt_dev);
197+
if (unlikely(err))
198+
return err;
199+
200+
mtk_wdt->restart_handler.notifier_call = mtk_reset_handler;
201+
mtk_wdt->restart_handler.priority = 128;
202+
err = register_restart_handler(&mtk_wdt->restart_handler);
203+
if (err)
204+
dev_warn(&pdev->dev,
205+
"cannot register restart handler (err=%d)\n", err);
206+
207+
dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n",
208+
mtk_wdt->wdt_dev.timeout, nowayout);
209+
210+
return 0;
211+
}
212+
213+
static int mtk_wdt_remove(struct platform_device *pdev)
214+
{
215+
struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
216+
217+
unregister_restart_handler(&mtk_wdt->restart_handler);
218+
219+
watchdog_unregister_device(&mtk_wdt->wdt_dev);
220+
221+
return 0;
222+
}
223+
224+
static const struct of_device_id mtk_wdt_dt_ids[] = {
225+
{ .compatible = "mediatek,mt6589-wdt" },
226+
{ /* sentinel */ }
227+
};
228+
MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
229+
230+
static struct platform_driver mtk_wdt_driver = {
231+
.probe = mtk_wdt_probe,
232+
.remove = mtk_wdt_remove,
233+
.driver = {
234+
.name = DRV_NAME,
235+
.of_match_table = mtk_wdt_dt_ids,
236+
},
237+
};
238+
239+
module_platform_driver(mtk_wdt_driver);
240+
241+
module_param(timeout, uint, 0);
242+
MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
243+
244+
module_param(nowayout, bool, 0);
245+
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
246+
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
247+
248+
MODULE_LICENSE("GPL");
249+
MODULE_AUTHOR("Matthias Brugger <matthias.bgg@gmail.com>");
250+
MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver");
251+
MODULE_VERSION(DRV_VERSION);

0 commit comments

Comments
 (0)