xref: /linux/drivers/watchdog/stmp3xxx_rtc_wdt.c (revision bc8fdfbe75058c6569dd6a08bdc2a411db533c47)
1de6303abSWolfram Sang /*
2de6303abSWolfram Sang  * Watchdog driver for the RTC based watchdog in STMP3xxx and i.MX23/28
3de6303abSWolfram Sang  *
4de6303abSWolfram Sang  * Author: Wolfram Sang <w.sang@pengutronix.de>
5de6303abSWolfram Sang  *
6de6303abSWolfram Sang  * Copyright (C) 2011-12 Wolfram Sang, Pengutronix
7de6303abSWolfram Sang  *
8de6303abSWolfram Sang  * This program is free software; you can redistribute it and/or modify it
9de6303abSWolfram Sang  * under the terms of the GNU General Public License version 2 as published by
10de6303abSWolfram Sang  * the Free Software Foundation.
11de6303abSWolfram Sang  */
12de6303abSWolfram Sang #include <linux/init.h>
13de6303abSWolfram Sang #include <linux/kernel.h>
14de6303abSWolfram Sang #include <linux/module.h>
15de6303abSWolfram Sang #include <linux/miscdevice.h>
16de6303abSWolfram Sang #include <linux/watchdog.h>
17de6303abSWolfram Sang #include <linux/platform_device.h>
18de6303abSWolfram Sang #include <linux/stmp3xxx_rtc_wdt.h>
19de6303abSWolfram Sang 
20de6303abSWolfram Sang #define WDOG_TICK_RATE 1000 /* 1 kHz clock */
21de6303abSWolfram Sang #define STMP3XXX_DEFAULT_TIMEOUT 19
22de6303abSWolfram Sang #define STMP3XXX_MAX_TIMEOUT (UINT_MAX / WDOG_TICK_RATE)
23de6303abSWolfram Sang 
24de6303abSWolfram Sang static int heartbeat = STMP3XXX_DEFAULT_TIMEOUT;
25de6303abSWolfram Sang module_param(heartbeat, uint, 0);
26de6303abSWolfram Sang MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to "
27de6303abSWolfram Sang 		 __MODULE_STRING(STMP3XXX_MAX_TIMEOUT) ", default "
28de6303abSWolfram Sang 		 __MODULE_STRING(STMP3XXX_DEFAULT_TIMEOUT));
29de6303abSWolfram Sang 
30de6303abSWolfram Sang static int wdt_start(struct watchdog_device *wdd)
31de6303abSWolfram Sang {
32de6303abSWolfram Sang 	struct device *dev = watchdog_get_drvdata(wdd);
33*bc8fdfbeSJingoo Han 	struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
34de6303abSWolfram Sang 
35de6303abSWolfram Sang 	pdata->wdt_set_timeout(dev->parent, wdd->timeout * WDOG_TICK_RATE);
36de6303abSWolfram Sang 	return 0;
37de6303abSWolfram Sang }
38de6303abSWolfram Sang 
39de6303abSWolfram Sang static int wdt_stop(struct watchdog_device *wdd)
40de6303abSWolfram Sang {
41de6303abSWolfram Sang 	struct device *dev = watchdog_get_drvdata(wdd);
42*bc8fdfbeSJingoo Han 	struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
43de6303abSWolfram Sang 
44de6303abSWolfram Sang 	pdata->wdt_set_timeout(dev->parent, 0);
45de6303abSWolfram Sang 	return 0;
46de6303abSWolfram Sang }
47de6303abSWolfram Sang 
48de6303abSWolfram Sang static int wdt_set_timeout(struct watchdog_device *wdd, unsigned new_timeout)
49de6303abSWolfram Sang {
50de6303abSWolfram Sang 	wdd->timeout = new_timeout;
51de6303abSWolfram Sang 	return wdt_start(wdd);
52de6303abSWolfram Sang }
53de6303abSWolfram Sang 
54de6303abSWolfram Sang static const struct watchdog_info stmp3xxx_wdt_ident = {
55de6303abSWolfram Sang 	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
56de6303abSWolfram Sang 	.identity = "STMP3XXX RTC Watchdog",
57de6303abSWolfram Sang };
58de6303abSWolfram Sang 
59de6303abSWolfram Sang static const struct watchdog_ops stmp3xxx_wdt_ops = {
60de6303abSWolfram Sang 	.owner = THIS_MODULE,
61de6303abSWolfram Sang 	.start = wdt_start,
62de6303abSWolfram Sang 	.stop = wdt_stop,
63de6303abSWolfram Sang 	.set_timeout = wdt_set_timeout,
64de6303abSWolfram Sang };
65de6303abSWolfram Sang 
66de6303abSWolfram Sang static struct watchdog_device stmp3xxx_wdd = {
67de6303abSWolfram Sang 	.info = &stmp3xxx_wdt_ident,
68de6303abSWolfram Sang 	.ops = &stmp3xxx_wdt_ops,
69de6303abSWolfram Sang 	.min_timeout = 1,
70de6303abSWolfram Sang 	.max_timeout = STMP3XXX_MAX_TIMEOUT,
71de6303abSWolfram Sang 	.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
72de6303abSWolfram Sang };
73de6303abSWolfram Sang 
74de6303abSWolfram Sang static int stmp3xxx_wdt_probe(struct platform_device *pdev)
75de6303abSWolfram Sang {
76de6303abSWolfram Sang 	int ret;
77de6303abSWolfram Sang 
78de6303abSWolfram Sang 	watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
79de6303abSWolfram Sang 
80de6303abSWolfram Sang 	stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
81de6303abSWolfram Sang 
82de6303abSWolfram Sang 	ret = watchdog_register_device(&stmp3xxx_wdd);
83de6303abSWolfram Sang 	if (ret < 0) {
84de6303abSWolfram Sang 		dev_err(&pdev->dev, "cannot register watchdog device\n");
85de6303abSWolfram Sang 		return ret;
86de6303abSWolfram Sang 	}
87de6303abSWolfram Sang 
88de6303abSWolfram Sang 	dev_info(&pdev->dev, "initialized watchdog with heartbeat %ds\n",
89de6303abSWolfram Sang 			stmp3xxx_wdd.timeout);
90de6303abSWolfram Sang 	return 0;
91de6303abSWolfram Sang }
92de6303abSWolfram Sang 
93de6303abSWolfram Sang static int stmp3xxx_wdt_remove(struct platform_device *pdev)
94de6303abSWolfram Sang {
95de6303abSWolfram Sang 	watchdog_unregister_device(&stmp3xxx_wdd);
96de6303abSWolfram Sang 	return 0;
97de6303abSWolfram Sang }
98de6303abSWolfram Sang 
99de6303abSWolfram Sang static struct platform_driver stmp3xxx_wdt_driver = {
100de6303abSWolfram Sang 	.driver = {
101de6303abSWolfram Sang 		.name = "stmp3xxx_rtc_wdt",
102de6303abSWolfram Sang 	},
103de6303abSWolfram Sang 	.probe = stmp3xxx_wdt_probe,
104de6303abSWolfram Sang 	.remove = stmp3xxx_wdt_remove,
105de6303abSWolfram Sang };
106de6303abSWolfram Sang module_platform_driver(stmp3xxx_wdt_driver);
107de6303abSWolfram Sang 
108de6303abSWolfram Sang MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver");
109de6303abSWolfram Sang MODULE_LICENSE("GPL v2");
110de6303abSWolfram Sang MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
111de6303abSWolfram Sang MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
112