2 * Meson Watchdog Driver
4 * Copyright (c) 2014 Carlo Caione
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
12 #include <linux/clk.h>
13 #include <linux/delay.h>
14 #include <linux/err.h>
15 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/moduleparam.h>
21 #include <linux/of_device.h>
22 #include <linux/platform_device.h>
23 #include <linux/types.h>
24 #include <linux/watchdog.h>
26 #define DRV_NAME "meson_wdt"
28 #define MESON_WDT_TC 0x00
29 #define MESON_WDT_DC_RESET (3 << 24)
31 #define MESON_WDT_RESET 0x04
33 #define MESON_WDT_TIMEOUT 30
34 #define MESON_WDT_MIN_TIMEOUT 1
36 #define MESON_SEC_TO_TC(s, c) ((s) * (c))
38 static bool nowayout
= WATCHDOG_NOWAYOUT
;
39 static unsigned int timeout
= MESON_WDT_TIMEOUT
;
41 struct meson_wdt_data
{
43 unsigned int terminal_count_mask
;
44 unsigned int count_unit
;
47 static struct meson_wdt_data meson6_wdt_data
= {
49 .terminal_count_mask
= 0x3fffff,
50 .count_unit
= 100000, /* 10 us */
53 static struct meson_wdt_data meson8b_wdt_data
= {
55 .terminal_count_mask
= 0xffff,
56 .count_unit
= 7812, /* 128 us */
59 struct meson_wdt_dev
{
60 struct watchdog_device wdt_dev
;
61 void __iomem
*wdt_base
;
62 const struct meson_wdt_data
*data
;
65 static int meson_wdt_restart(struct watchdog_device
*wdt_dev
)
67 struct meson_wdt_dev
*meson_wdt
= watchdog_get_drvdata(wdt_dev
);
68 u32 tc_reboot
= MESON_WDT_DC_RESET
;
70 tc_reboot
|= meson_wdt
->data
->enable
;
73 writel(tc_reboot
, meson_wdt
->wdt_base
+ MESON_WDT_TC
);
80 static int meson_wdt_ping(struct watchdog_device
*wdt_dev
)
82 struct meson_wdt_dev
*meson_wdt
= watchdog_get_drvdata(wdt_dev
);
84 writel(0, meson_wdt
->wdt_base
+ MESON_WDT_RESET
);
89 static void meson_wdt_change_timeout(struct watchdog_device
*wdt_dev
,
92 struct meson_wdt_dev
*meson_wdt
= watchdog_get_drvdata(wdt_dev
);
95 reg
= readl(meson_wdt
->wdt_base
+ MESON_WDT_TC
);
96 reg
&= ~meson_wdt
->data
->terminal_count_mask
;
97 reg
|= MESON_SEC_TO_TC(timeout
, meson_wdt
->data
->count_unit
);
98 writel(reg
, meson_wdt
->wdt_base
+ MESON_WDT_TC
);
101 static int meson_wdt_set_timeout(struct watchdog_device
*wdt_dev
,
102 unsigned int timeout
)
104 wdt_dev
->timeout
= timeout
;
106 meson_wdt_change_timeout(wdt_dev
, timeout
);
107 meson_wdt_ping(wdt_dev
);
112 static int meson_wdt_stop(struct watchdog_device
*wdt_dev
)
114 struct meson_wdt_dev
*meson_wdt
= watchdog_get_drvdata(wdt_dev
);
117 reg
= readl(meson_wdt
->wdt_base
+ MESON_WDT_TC
);
118 reg
&= ~meson_wdt
->data
->enable
;
119 writel(reg
, meson_wdt
->wdt_base
+ MESON_WDT_TC
);
124 static int meson_wdt_start(struct watchdog_device
*wdt_dev
)
126 struct meson_wdt_dev
*meson_wdt
= watchdog_get_drvdata(wdt_dev
);
129 meson_wdt_change_timeout(wdt_dev
, meson_wdt
->wdt_dev
.timeout
);
130 meson_wdt_ping(wdt_dev
);
132 reg
= readl(meson_wdt
->wdt_base
+ MESON_WDT_TC
);
133 reg
|= meson_wdt
->data
->enable
;
134 writel(reg
, meson_wdt
->wdt_base
+ MESON_WDT_TC
);
139 static const struct watchdog_info meson_wdt_info
= {
140 .identity
= DRV_NAME
,
141 .options
= WDIOF_SETTIMEOUT
|
142 WDIOF_KEEPALIVEPING
|
146 static const struct watchdog_ops meson_wdt_ops
= {
147 .owner
= THIS_MODULE
,
148 .start
= meson_wdt_start
,
149 .stop
= meson_wdt_stop
,
150 .ping
= meson_wdt_ping
,
151 .set_timeout
= meson_wdt_set_timeout
,
152 .restart
= meson_wdt_restart
,
155 static const struct of_device_id meson_wdt_dt_ids
[] = {
156 { .compatible
= "amlogic,meson6-wdt", .data
= &meson6_wdt_data
},
157 { .compatible
= "amlogic,meson8b-wdt", .data
= &meson8b_wdt_data
},
160 MODULE_DEVICE_TABLE(of
, meson_wdt_dt_ids
);
162 static int meson_wdt_probe(struct platform_device
*pdev
)
164 struct resource
*res
;
165 struct meson_wdt_dev
*meson_wdt
;
166 const struct of_device_id
*of_id
;
169 meson_wdt
= devm_kzalloc(&pdev
->dev
, sizeof(*meson_wdt
), GFP_KERNEL
);
173 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
174 meson_wdt
->wdt_base
= devm_ioremap_resource(&pdev
->dev
, res
);
175 if (IS_ERR(meson_wdt
->wdt_base
))
176 return PTR_ERR(meson_wdt
->wdt_base
);
178 of_id
= of_match_device(meson_wdt_dt_ids
, &pdev
->dev
);
180 dev_err(&pdev
->dev
, "Unable to initialize WDT data\n");
183 meson_wdt
->data
= of_id
->data
;
185 meson_wdt
->wdt_dev
.parent
= &pdev
->dev
;
186 meson_wdt
->wdt_dev
.info
= &meson_wdt_info
;
187 meson_wdt
->wdt_dev
.ops
= &meson_wdt_ops
;
188 meson_wdt
->wdt_dev
.max_timeout
=
189 meson_wdt
->data
->terminal_count_mask
/ meson_wdt
->data
->count_unit
;
190 meson_wdt
->wdt_dev
.min_timeout
= MESON_WDT_MIN_TIMEOUT
;
191 meson_wdt
->wdt_dev
.timeout
= min_t(unsigned int,
193 meson_wdt
->wdt_dev
.max_timeout
);
195 watchdog_set_drvdata(&meson_wdt
->wdt_dev
, meson_wdt
);
197 watchdog_init_timeout(&meson_wdt
->wdt_dev
, timeout
, &pdev
->dev
);
198 watchdog_set_nowayout(&meson_wdt
->wdt_dev
, nowayout
);
199 watchdog_set_restart_priority(&meson_wdt
->wdt_dev
, 128);
201 meson_wdt_stop(&meson_wdt
->wdt_dev
);
203 err
= watchdog_register_device(&meson_wdt
->wdt_dev
);
207 platform_set_drvdata(pdev
, meson_wdt
);
209 dev_info(&pdev
->dev
, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
210 meson_wdt
->wdt_dev
.timeout
, nowayout
);
215 static int meson_wdt_remove(struct platform_device
*pdev
)
217 struct meson_wdt_dev
*meson_wdt
= platform_get_drvdata(pdev
);
219 watchdog_unregister_device(&meson_wdt
->wdt_dev
);
224 static void meson_wdt_shutdown(struct platform_device
*pdev
)
226 struct meson_wdt_dev
*meson_wdt
= platform_get_drvdata(pdev
);
228 meson_wdt_stop(&meson_wdt
->wdt_dev
);
231 static struct platform_driver meson_wdt_driver
= {
232 .probe
= meson_wdt_probe
,
233 .remove
= meson_wdt_remove
,
234 .shutdown
= meson_wdt_shutdown
,
237 .of_match_table
= meson_wdt_dt_ids
,
241 module_platform_driver(meson_wdt_driver
);
243 module_param(timeout
, uint
, 0);
244 MODULE_PARM_DESC(timeout
, "Watchdog heartbeat in seconds");
246 module_param(nowayout
, bool, 0);
247 MODULE_PARM_DESC(nowayout
,
248 "Watchdog cannot be stopped once started (default="
249 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
251 MODULE_LICENSE("GPL");
252 MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
253 MODULE_DESCRIPTION("Meson Watchdog Timer Driver");