watchdog: ep93xx_wdt: Use devm_ioremap_resource()
[deliverable/linux.git] / drivers / watchdog / omap_wdt.c
CommitLineData
7768a13c 1/*
2817142f 2 * omap_wdt.c
7768a13c 3 *
2817142f 4 * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
7768a13c
KS
5 *
6 * Author: MontaVista Software, Inc.
7 * <gdavis@mvista.com> or <source@mvista.com>
8 *
9 * 2003 (c) MontaVista Software, Inc. This file is licensed under the
10 * terms of the GNU General Public License version 2. This program is
11 * licensed "as is" without any warranty of any kind, whether express
12 * or implied.
13 *
14 * History:
15 *
16 * 20030527: George G. Davis <gdavis@mvista.com>
17 * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
18 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
29fa0586 19 * Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
7768a13c
KS
20 *
21 * Copyright (c) 2004 Texas Instruments.
22 * 1. Modified to support OMAP1610 32-KHz watchdog timer
23 * 2. Ported to 2.6 kernel
24 *
25 * Copyright (c) 2005 David Brownell
26 * Use the driver model and standard identifiers; handle bigger timeouts.
27 */
28
27c766aa
JP
29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30
7768a13c 31#include <linux/module.h>
7768a13c
KS
32#include <linux/types.h>
33#include <linux/kernel.h>
7768a13c 34#include <linux/mm.h>
7768a13c
KS
35#include <linux/watchdog.h>
36#include <linux/reboot.h>
7768a13c
KS
37#include <linux/err.h>
38#include <linux/platform_device.h>
39#include <linux/moduleparam.h>
089ab079 40#include <linux/io.h>
5a0e3ad6 41#include <linux/slab.h>
7ec5ad0f 42#include <linux/pm_runtime.h>
129f5577 43#include <linux/platform_data/omap-wd-timer.h>
7768a13c
KS
44
45#include "omap_wdt.h"
46
2dd7b244
PR
47static bool nowayout = WATCHDOG_NOWAYOUT;
48module_param(nowayout, bool, 0);
49MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
50 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
51
7768a13c
KS
52static unsigned timer_margin;
53module_param(timer_margin, uint, 0);
54MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
55
2817142f
FB
56struct omap_wdt_dev {
57 void __iomem *base; /* physical */
58 struct device *dev;
67c0f554 59 bool omap_wdt_users;
2817142f 60 struct resource *mem;
67c0f554
AK
61 int wdt_trgr_pattern;
62 struct mutex lock; /* to avoid races with PM */
2817142f
FB
63};
64
67c0f554 65static void omap_wdt_reload(struct omap_wdt_dev *wdev)
7768a13c 66{
2817142f 67 void __iomem *base = wdev->base;
b3112180 68
7768a13c 69 /* wait for posted write to complete */
4a7e94a0 70 while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08)
7768a13c 71 cpu_relax();
b3112180 72
67c0f554 73 wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern;
4a7e94a0 74 writel_relaxed(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
b3112180 75
7768a13c 76 /* wait for posted write to complete */
4a7e94a0 77 while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08)
7768a13c
KS
78 cpu_relax();
79 /* reloaded WCRR from WLDR */
80}
81
2817142f 82static void omap_wdt_enable(struct omap_wdt_dev *wdev)
7768a13c 83{
b3112180
FB
84 void __iomem *base = wdev->base;
85
7768a13c 86 /* Sequence to enable the watchdog */
4a7e94a0
VK
87 writel_relaxed(0xBBBB, base + OMAP_WATCHDOG_SPR);
88 while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10)
7768a13c 89 cpu_relax();
b3112180 90
4a7e94a0
VK
91 writel_relaxed(0x4444, base + OMAP_WATCHDOG_SPR);
92 while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10)
7768a13c
KS
93 cpu_relax();
94}
95
2817142f 96static void omap_wdt_disable(struct omap_wdt_dev *wdev)
7768a13c 97{
b3112180
FB
98 void __iomem *base = wdev->base;
99
7768a13c 100 /* sequence required to disable watchdog */
4a7e94a0
VK
101 writel_relaxed(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
102 while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10)
7768a13c 103 cpu_relax();
b3112180 104
4a7e94a0
VK
105 writel_relaxed(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
106 while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10)
7768a13c
KS
107 cpu_relax();
108}
109
67c0f554
AK
110static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
111 unsigned int timeout)
7768a13c 112{
67c0f554 113 u32 pre_margin = GET_WLDR_VAL(timeout);
b3112180 114 void __iomem *base = wdev->base;
7768a13c
KS
115
116 /* just count up at 32 KHz */
4a7e94a0 117 while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04)
7768a13c 118 cpu_relax();
b3112180 119
4a7e94a0
VK
120 writel_relaxed(pre_margin, base + OMAP_WATCHDOG_LDR);
121 while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04)
7768a13c
KS
122 cpu_relax();
123}
124
67c0f554 125static int omap_wdt_start(struct watchdog_device *wdog)
7768a13c 126{
67c0f554 127 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
b3112180
FB
128 void __iomem *base = wdev->base;
129
67c0f554
AK
130 mutex_lock(&wdev->lock);
131
132 wdev->omap_wdt_users = true;
7768a13c 133
7ec5ad0f 134 pm_runtime_get_sync(wdev->dev);
7768a13c
KS
135
136 /* initialize prescaler */
4a7e94a0 137 while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
7768a13c 138 cpu_relax();
b3112180 139
4a7e94a0
VK
140 writel_relaxed((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
141 while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01)
7768a13c
KS
142 cpu_relax();
143
67c0f554
AK
144 omap_wdt_set_timer(wdev, wdog->timeout);
145 omap_wdt_reload(wdev); /* trigger loading of new timeout value */
2817142f 146 omap_wdt_enable(wdev);
b3112180 147
67c0f554
AK
148 mutex_unlock(&wdev->lock);
149
150 return 0;
7768a13c
KS
151}
152
67c0f554 153static int omap_wdt_stop(struct watchdog_device *wdog)
7768a13c 154{
67c0f554 155 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
b3112180 156
67c0f554 157 mutex_lock(&wdev->lock);
2817142f 158 omap_wdt_disable(wdev);
7ec5ad0f 159 pm_runtime_put_sync(wdev->dev);
67c0f554
AK
160 wdev->omap_wdt_users = false;
161 mutex_unlock(&wdev->lock);
7768a13c
KS
162 return 0;
163}
164
67c0f554 165static int omap_wdt_ping(struct watchdog_device *wdog)
7768a13c 166{
67c0f554 167 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
b3112180 168
67c0f554
AK
169 mutex_lock(&wdev->lock);
170 omap_wdt_reload(wdev);
171 mutex_unlock(&wdev->lock);
172
173 return 0;
7768a13c
KS
174}
175
67c0f554
AK
176static int omap_wdt_set_timeout(struct watchdog_device *wdog,
177 unsigned int timeout)
7768a13c 178{
67c0f554 179 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
7768a13c 180
67c0f554
AK
181 mutex_lock(&wdev->lock);
182 omap_wdt_disable(wdev);
183 omap_wdt_set_timer(wdev, timeout);
184 omap_wdt_enable(wdev);
185 omap_wdt_reload(wdev);
186 wdog->timeout = timeout;
187 mutex_unlock(&wdev->lock);
188
189 return 0;
7768a13c
KS
190}
191
67c0f554
AK
192static const struct watchdog_info omap_wdt_info = {
193 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
194 .identity = "OMAP Watchdog",
195};
196
197static const struct watchdog_ops omap_wdt_ops = {
198 .owner = THIS_MODULE,
199 .start = omap_wdt_start,
200 .stop = omap_wdt_stop,
201 .ping = omap_wdt_ping,
202 .set_timeout = omap_wdt_set_timeout,
7768a13c
KS
203};
204
2d991a16 205static int omap_wdt_probe(struct platform_device *pdev)
7768a13c 206{
bc8fdfbe 207 struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev);
67c0f554 208 struct watchdog_device *omap_wdt;
7768a13c 209 struct resource *res, *mem;
2817142f 210 struct omap_wdt_dev *wdev;
67c0f554 211 u32 rs;
b3112180 212 int ret;
7768a13c 213
4f4753d9 214 omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL);
67c0f554
AK
215 if (!omap_wdt)
216 return -ENOMEM;
217
7768a13c
KS
218 /* reserve static register mappings */
219 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4f4753d9
AK
220 if (!res)
221 return -ENOENT;
7768a13c 222
4f4753d9
AK
223 mem = devm_request_mem_region(&pdev->dev, res->start,
224 resource_size(res), pdev->name);
225 if (!mem)
226 return -EBUSY;
7768a13c 227
4f4753d9
AK
228 wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
229 if (!wdev)
230 return -ENOMEM;
b3112180 231
67c0f554
AK
232 wdev->omap_wdt_users = false;
233 wdev->mem = mem;
234 wdev->dev = &pdev->dev;
235 wdev->wdt_trgr_pattern = 0x1234;
236 mutex_init(&wdev->lock);
2817142f 237
4f4753d9
AK
238 wdev->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
239 if (!wdev->base)
240 return -ENOMEM;
9f69e3b0 241
67c0f554
AK
242 omap_wdt->info = &omap_wdt_info;
243 omap_wdt->ops = &omap_wdt_ops;
244 omap_wdt->min_timeout = TIMER_MARGIN_MIN;
245 omap_wdt->max_timeout = TIMER_MARGIN_MAX;
246
247 if (timer_margin >= TIMER_MARGIN_MIN &&
248 timer_margin <= TIMER_MARGIN_MAX)
249 omap_wdt->timeout = timer_margin;
250 else
251 omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
252
253 watchdog_set_drvdata(omap_wdt, wdev);
254 watchdog_set_nowayout(omap_wdt, nowayout);
255
256 platform_set_drvdata(pdev, omap_wdt);
7768a13c 257
7ec5ad0f
VC
258 pm_runtime_enable(wdev->dev);
259 pm_runtime_get_sync(wdev->dev);
789cd470 260
67c0f554
AK
261 if (pdata && pdata->read_reset_sources)
262 rs = pdata->read_reset_sources();
263 else
264 rs = 0;
265 omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
266 WDIOF_CARDRESET : 0;
7768a13c 267
67c0f554 268 omap_wdt_disable(wdev);
2817142f 269
67c0f554 270 ret = watchdog_register_device(omap_wdt);
1ba85387
AK
271 if (ret) {
272 pm_runtime_disable(wdev->dev);
273 return ret;
274 }
7768a13c 275
2817142f 276 pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
4a7e94a0 277 readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
67c0f554 278 omap_wdt->timeout);
7768a13c 279
7ec5ad0f 280 pm_runtime_put_sync(wdev->dev);
789cd470 281
7768a13c 282 return 0;
7768a13c
KS
283}
284
285static void omap_wdt_shutdown(struct platform_device *pdev)
286{
67c0f554
AK
287 struct watchdog_device *wdog = platform_get_drvdata(pdev);
288 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
2817142f 289
67c0f554 290 mutex_lock(&wdev->lock);
0503add9 291 if (wdev->omap_wdt_users) {
2817142f 292 omap_wdt_disable(wdev);
0503add9
PW
293 pm_runtime_put_sync(wdev->dev);
294 }
67c0f554 295 mutex_unlock(&wdev->lock);
7768a13c
KS
296}
297
4b12b896 298static int omap_wdt_remove(struct platform_device *pdev)
7768a13c 299{
67c0f554
AK
300 struct watchdog_device *wdog = platform_get_drvdata(pdev);
301 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
2817142f 302
12c583d8 303 pm_runtime_disable(wdev->dev);
67c0f554 304 watchdog_unregister_device(wdog);
b3112180 305
7768a13c
KS
306 return 0;
307}
308
309#ifdef CONFIG_PM
310
311/* REVISIT ... not clear this is the best way to handle system suspend; and
312 * it's very inappropriate for selective device suspend (e.g. suspending this
313 * through sysfs rather than by stopping the watchdog daemon). Also, this
314 * may not play well enough with NOWAYOUT...
315 */
316
317static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
318{
67c0f554
AK
319 struct watchdog_device *wdog = platform_get_drvdata(pdev);
320 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
b3112180 321
67c0f554 322 mutex_lock(&wdev->lock);
0503add9 323 if (wdev->omap_wdt_users) {
2817142f 324 omap_wdt_disable(wdev);
0503add9
PW
325 pm_runtime_put_sync(wdev->dev);
326 }
67c0f554 327 mutex_unlock(&wdev->lock);
b3112180 328
7768a13c
KS
329 return 0;
330}
331
332static int omap_wdt_resume(struct platform_device *pdev)
333{
67c0f554
AK
334 struct watchdog_device *wdog = platform_get_drvdata(pdev);
335 struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
b3112180 336
67c0f554 337 mutex_lock(&wdev->lock);
2817142f 338 if (wdev->omap_wdt_users) {
0503add9 339 pm_runtime_get_sync(wdev->dev);
2817142f 340 omap_wdt_enable(wdev);
67c0f554 341 omap_wdt_reload(wdev);
7768a13c 342 }
67c0f554 343 mutex_unlock(&wdev->lock);
b3112180 344
7768a13c
KS
345 return 0;
346}
347
348#else
349#define omap_wdt_suspend NULL
350#define omap_wdt_resume NULL
351#endif
352
e6ca04ea
XJ
353static const struct of_device_id omap_wdt_of_match[] = {
354 { .compatible = "ti,omap3-wdt", },
355 {},
356};
357MODULE_DEVICE_TABLE(of, omap_wdt_of_match);
358
7768a13c
KS
359static struct platform_driver omap_wdt_driver = {
360 .probe = omap_wdt_probe,
82268714 361 .remove = omap_wdt_remove,
7768a13c
KS
362 .shutdown = omap_wdt_shutdown,
363 .suspend = omap_wdt_suspend,
364 .resume = omap_wdt_resume,
365 .driver = {
366 .owner = THIS_MODULE,
367 .name = "omap_wdt",
e6ca04ea 368 .of_match_table = omap_wdt_of_match,
7768a13c
KS
369 },
370};
371
b8ec6118 372module_platform_driver(omap_wdt_driver);
7768a13c
KS
373
374MODULE_AUTHOR("George G. Davis");
375MODULE_LICENSE("GPL");
f37d193c 376MODULE_ALIAS("platform:omap_wdt");
This page took 0.628769 seconds and 5 git commands to generate.