xref: /linux/drivers/watchdog/tegra_wdt.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12e62c498SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0
2c33a1597SAndrew Chew /*
3c33a1597SAndrew Chew  * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
4c33a1597SAndrew Chew  */
5c33a1597SAndrew Chew 
6c33a1597SAndrew Chew #include <linux/kernel.h>
7c33a1597SAndrew Chew #include <linux/module.h>
8c33a1597SAndrew Chew #include <linux/interrupt.h>
9c33a1597SAndrew Chew #include <linux/io.h>
10c33a1597SAndrew Chew #include <linux/of.h>
11c33a1597SAndrew Chew #include <linux/platform_device.h>
12c33a1597SAndrew Chew #include <linux/watchdog.h>
13c33a1597SAndrew Chew 
14c33a1597SAndrew Chew /* minimum and maximum watchdog trigger timeout, in seconds */
15c33a1597SAndrew Chew #define MIN_WDT_TIMEOUT			1
16c33a1597SAndrew Chew #define MAX_WDT_TIMEOUT			255
17c33a1597SAndrew Chew 
18c33a1597SAndrew Chew /*
19c33a1597SAndrew Chew  * Base of the WDT registers, from the timer base address.  There are
20c33a1597SAndrew Chew  * actually 5 watchdogs that can be configured (by pairing with an available
21c33a1597SAndrew Chew  * timer), at bases 0x100 + (WDT ID) * 0x20, where WDT ID is 0 through 4.
22c33a1597SAndrew Chew  * This driver only configures the first watchdog (WDT ID 0).
23c33a1597SAndrew Chew  */
24c33a1597SAndrew Chew #define WDT_BASE			0x100
25c33a1597SAndrew Chew #define WDT_ID				0
26c33a1597SAndrew Chew 
27c33a1597SAndrew Chew /*
28c33a1597SAndrew Chew  * Register base of the timer that's selected for pairing with the watchdog.
29c33a1597SAndrew Chew  * This driver arbitrarily uses timer 5, which is currently unused by
30c33a1597SAndrew Chew  * other drivers (in particular, the Tegra clocksource driver).  If this
31c33a1597SAndrew Chew  * needs to change, take care that the new timer is not used by the
32c33a1597SAndrew Chew  * clocksource driver.
33c33a1597SAndrew Chew  */
34c33a1597SAndrew Chew #define WDT_TIMER_BASE			0x60
35c33a1597SAndrew Chew #define WDT_TIMER_ID			5
36c33a1597SAndrew Chew 
37c33a1597SAndrew Chew /* WDT registers */
38c33a1597SAndrew Chew #define WDT_CFG				0x0
39c33a1597SAndrew Chew #define WDT_CFG_PERIOD_SHIFT		4
40c33a1597SAndrew Chew #define WDT_CFG_PERIOD_MASK		0xff
41c33a1597SAndrew Chew #define WDT_CFG_INT_EN			(1 << 12)
42c33a1597SAndrew Chew #define WDT_CFG_PMC2CAR_RST_EN		(1 << 15)
43c33a1597SAndrew Chew #define WDT_STS				0x4
44c33a1597SAndrew Chew #define WDT_STS_COUNT_SHIFT		4
45c33a1597SAndrew Chew #define WDT_STS_COUNT_MASK		0xff
46c33a1597SAndrew Chew #define WDT_STS_EXP_SHIFT		12
47c33a1597SAndrew Chew #define WDT_STS_EXP_MASK		0x3
48c33a1597SAndrew Chew #define WDT_CMD				0x8
49c33a1597SAndrew Chew #define WDT_CMD_START_COUNTER		(1 << 0)
50c33a1597SAndrew Chew #define WDT_CMD_DISABLE_COUNTER		(1 << 1)
51c33a1597SAndrew Chew #define WDT_UNLOCK			(0xc)
52c33a1597SAndrew Chew #define WDT_UNLOCK_PATTERN		(0xc45a << 0)
53c33a1597SAndrew Chew 
54c33a1597SAndrew Chew /* Timer registers */
55c33a1597SAndrew Chew #define TIMER_PTV			0x0
56c33a1597SAndrew Chew #define TIMER_EN			(1 << 31)
57c33a1597SAndrew Chew #define TIMER_PERIODIC			(1 << 30)
58c33a1597SAndrew Chew 
59c33a1597SAndrew Chew struct tegra_wdt {
60c33a1597SAndrew Chew 	struct watchdog_device	wdd;
61c33a1597SAndrew Chew 	void __iomem		*wdt_regs;
62c33a1597SAndrew Chew 	void __iomem		*tmr_regs;
63c33a1597SAndrew Chew };
64c33a1597SAndrew Chew 
65c33a1597SAndrew Chew #define WDT_HEARTBEAT 120
66c33a1597SAndrew Chew static int heartbeat = WDT_HEARTBEAT;
67c33a1597SAndrew Chew module_param(heartbeat, int, 0);
68c33a1597SAndrew Chew MODULE_PARM_DESC(heartbeat,
69c33a1597SAndrew Chew 	"Watchdog heartbeats in seconds. (default = "
70c33a1597SAndrew Chew 	__MODULE_STRING(WDT_HEARTBEAT) ")");
71c33a1597SAndrew Chew 
72c33a1597SAndrew Chew static bool nowayout = WATCHDOG_NOWAYOUT;
73c33a1597SAndrew Chew module_param(nowayout, bool, 0);
74c33a1597SAndrew Chew MODULE_PARM_DESC(nowayout,
75c33a1597SAndrew Chew 	"Watchdog cannot be stopped once started (default="
76c33a1597SAndrew Chew 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
77c33a1597SAndrew Chew 
tegra_wdt_start(struct watchdog_device * wdd)78c33a1597SAndrew Chew static int tegra_wdt_start(struct watchdog_device *wdd)
79c33a1597SAndrew Chew {
80c33a1597SAndrew Chew 	struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
81c33a1597SAndrew Chew 	u32 val;
82c33a1597SAndrew Chew 
83c33a1597SAndrew Chew 	/*
84c33a1597SAndrew Chew 	 * This thing has a fixed 1MHz clock.  Normally, we would set the
85c33a1597SAndrew Chew 	 * period to 1 second by writing 1000000ul, but the watchdog system
86c33a1597SAndrew Chew 	 * reset actually occurs on the 4th expiration of this counter,
87c33a1597SAndrew Chew 	 * so we set the period to 1/4 of this amount.
88c33a1597SAndrew Chew 	 */
89c33a1597SAndrew Chew 	val = 1000000ul / 4;
90c33a1597SAndrew Chew 	val |= (TIMER_EN | TIMER_PERIODIC);
91c33a1597SAndrew Chew 	writel(val, wdt->tmr_regs + TIMER_PTV);
92c33a1597SAndrew Chew 
93c33a1597SAndrew Chew 	/*
94c33a1597SAndrew Chew 	 * Set number of periods and start counter.
95c33a1597SAndrew Chew 	 *
96c33a1597SAndrew Chew 	 * Interrupt handler is not required for user space
97c33a1597SAndrew Chew 	 * WDT accesses, since the caller is responsible to ping the
98c33a1597SAndrew Chew 	 * WDT to reset the counter before expiration, through ioctls.
99c33a1597SAndrew Chew 	 */
100c33a1597SAndrew Chew 	val = WDT_TIMER_ID |
101c33a1597SAndrew Chew 	      (wdd->timeout << WDT_CFG_PERIOD_SHIFT) |
102c33a1597SAndrew Chew 	      WDT_CFG_PMC2CAR_RST_EN;
103c33a1597SAndrew Chew 	writel(val, wdt->wdt_regs + WDT_CFG);
104c33a1597SAndrew Chew 
105c33a1597SAndrew Chew 	writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
106c33a1597SAndrew Chew 
107c33a1597SAndrew Chew 	return 0;
108c33a1597SAndrew Chew }
109c33a1597SAndrew Chew 
tegra_wdt_stop(struct watchdog_device * wdd)110c33a1597SAndrew Chew static int tegra_wdt_stop(struct watchdog_device *wdd)
111c33a1597SAndrew Chew {
112c33a1597SAndrew Chew 	struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
113c33a1597SAndrew Chew 
114c33a1597SAndrew Chew 	writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK);
115c33a1597SAndrew Chew 	writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD);
116c33a1597SAndrew Chew 	writel(0, wdt->tmr_regs + TIMER_PTV);
117c33a1597SAndrew Chew 
118c33a1597SAndrew Chew 	return 0;
119c33a1597SAndrew Chew }
120c33a1597SAndrew Chew 
tegra_wdt_ping(struct watchdog_device * wdd)121c33a1597SAndrew Chew static int tegra_wdt_ping(struct watchdog_device *wdd)
122c33a1597SAndrew Chew {
123c33a1597SAndrew Chew 	struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
124c33a1597SAndrew Chew 
125c33a1597SAndrew Chew 	writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
126c33a1597SAndrew Chew 
127c33a1597SAndrew Chew 	return 0;
128c33a1597SAndrew Chew }
129c33a1597SAndrew Chew 
tegra_wdt_set_timeout(struct watchdog_device * wdd,unsigned int timeout)130c33a1597SAndrew Chew static int tegra_wdt_set_timeout(struct watchdog_device *wdd,
131c33a1597SAndrew Chew 				 unsigned int timeout)
132c33a1597SAndrew Chew {
133c33a1597SAndrew Chew 	wdd->timeout = timeout;
134c33a1597SAndrew Chew 
1350879eee1SAndrew Chew 	if (watchdog_active(wdd)) {
1360879eee1SAndrew Chew 		tegra_wdt_stop(wdd);
137c33a1597SAndrew Chew 		return tegra_wdt_start(wdd);
1380879eee1SAndrew Chew 	}
139c33a1597SAndrew Chew 
140c33a1597SAndrew Chew 	return 0;
141c33a1597SAndrew Chew }
142c33a1597SAndrew Chew 
tegra_wdt_get_timeleft(struct watchdog_device * wdd)143c33a1597SAndrew Chew static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd)
144c33a1597SAndrew Chew {
145c33a1597SAndrew Chew 	struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
146c33a1597SAndrew Chew 	u32 val;
147c33a1597SAndrew Chew 	int count;
148c33a1597SAndrew Chew 	int exp;
149c33a1597SAndrew Chew 
150c33a1597SAndrew Chew 	val = readl(wdt->wdt_regs + WDT_STS);
151c33a1597SAndrew Chew 
152c33a1597SAndrew Chew 	/* Current countdown (from timeout) */
153c33a1597SAndrew Chew 	count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK;
154c33a1597SAndrew Chew 
155c33a1597SAndrew Chew 	/* Number of expirations (we are waiting for the 4th expiration) */
156c33a1597SAndrew Chew 	exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK;
157c33a1597SAndrew Chew 
158c33a1597SAndrew Chew 	/*
159c33a1597SAndrew Chew 	 * The entire thing is divided by 4 because we are ticking down 4 times
160c33a1597SAndrew Chew 	 * faster due to needing to wait for the 4th expiration.
161c33a1597SAndrew Chew 	 */
162c33a1597SAndrew Chew 	return (((3 - exp) * wdd->timeout) + count) / 4;
163c33a1597SAndrew Chew }
164c33a1597SAndrew Chew 
165c33a1597SAndrew Chew static const struct watchdog_info tegra_wdt_info = {
166c33a1597SAndrew Chew 	.options	= WDIOF_SETTIMEOUT |
167c33a1597SAndrew Chew 			  WDIOF_MAGICCLOSE |
168c33a1597SAndrew Chew 			  WDIOF_KEEPALIVEPING,
169c33a1597SAndrew Chew 	.firmware_version = 0,
170c33a1597SAndrew Chew 	.identity	= "Tegra Watchdog",
171c33a1597SAndrew Chew };
172c33a1597SAndrew Chew 
1737123f253SJulia Lawall static const struct watchdog_ops tegra_wdt_ops = {
174c33a1597SAndrew Chew 	.owner = THIS_MODULE,
175c33a1597SAndrew Chew 	.start = tegra_wdt_start,
176c33a1597SAndrew Chew 	.stop = tegra_wdt_stop,
177c33a1597SAndrew Chew 	.ping = tegra_wdt_ping,
178c33a1597SAndrew Chew 	.set_timeout = tegra_wdt_set_timeout,
179c33a1597SAndrew Chew 	.get_timeleft = tegra_wdt_get_timeleft,
180c33a1597SAndrew Chew };
181c33a1597SAndrew Chew 
tegra_wdt_probe(struct platform_device * pdev)182c33a1597SAndrew Chew static int tegra_wdt_probe(struct platform_device *pdev)
183c33a1597SAndrew Chew {
184edad7528SGuenter Roeck 	struct device *dev = &pdev->dev;
185c33a1597SAndrew Chew 	struct watchdog_device *wdd;
186c33a1597SAndrew Chew 	struct tegra_wdt *wdt;
187c33a1597SAndrew Chew 	void __iomem *regs;
188c33a1597SAndrew Chew 	int ret;
189c33a1597SAndrew Chew 
190c33a1597SAndrew Chew 	/* This is the timer base. */
1910f0a6a28SGuenter Roeck 	regs = devm_platform_ioremap_resource(pdev, 0);
192c33a1597SAndrew Chew 	if (IS_ERR(regs))
193c33a1597SAndrew Chew 		return PTR_ERR(regs);
194c33a1597SAndrew Chew 
195c33a1597SAndrew Chew 	/*
196c33a1597SAndrew Chew 	 * Allocate our watchdog driver data, which has the
197c33a1597SAndrew Chew 	 * struct watchdog_device nested within it.
198c33a1597SAndrew Chew 	 */
199edad7528SGuenter Roeck 	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
200c33a1597SAndrew Chew 	if (!wdt)
201c33a1597SAndrew Chew 		return -ENOMEM;
202c33a1597SAndrew Chew 
203c33a1597SAndrew Chew 	/* Initialize struct tegra_wdt. */
204c33a1597SAndrew Chew 	wdt->wdt_regs = regs + WDT_BASE;
205c33a1597SAndrew Chew 	wdt->tmr_regs = regs + WDT_TIMER_BASE;
206c33a1597SAndrew Chew 
207c33a1597SAndrew Chew 	/* Initialize struct watchdog_device. */
208c33a1597SAndrew Chew 	wdd = &wdt->wdd;
209c33a1597SAndrew Chew 	wdd->timeout = heartbeat;
210c33a1597SAndrew Chew 	wdd->info = &tegra_wdt_info;
211c33a1597SAndrew Chew 	wdd->ops = &tegra_wdt_ops;
212c33a1597SAndrew Chew 	wdd->min_timeout = MIN_WDT_TIMEOUT;
213c33a1597SAndrew Chew 	wdd->max_timeout = MAX_WDT_TIMEOUT;
214edad7528SGuenter Roeck 	wdd->parent = dev;
215c33a1597SAndrew Chew 
216c33a1597SAndrew Chew 	watchdog_set_drvdata(wdd, wdt);
217c33a1597SAndrew Chew 
218c33a1597SAndrew Chew 	watchdog_set_nowayout(wdd, nowayout);
219c33a1597SAndrew Chew 
220edad7528SGuenter Roeck 	watchdog_stop_on_unregister(wdd);
221edad7528SGuenter Roeck 	ret = devm_watchdog_register_device(dev, wdd);
222e290eb8cSWolfram Sang 	if (ret)
223c33a1597SAndrew Chew 		return ret;
224c33a1597SAndrew Chew 
225c33a1597SAndrew Chew 	platform_set_drvdata(pdev, wdt);
226c33a1597SAndrew Chew 
227edad7528SGuenter Roeck 	dev_info(dev, "initialized (heartbeat = %d sec, nowayout = %d)\n",
228c33a1597SAndrew Chew 		 heartbeat, nowayout);
229c33a1597SAndrew Chew 
230c33a1597SAndrew Chew 	return 0;
231c33a1597SAndrew Chew }
232c33a1597SAndrew Chew 
tegra_wdt_suspend(struct device * dev)233*c1eaa8c5SPaul Cercueil static int tegra_wdt_suspend(struct device *dev)
234c33a1597SAndrew Chew {
235c33a1597SAndrew Chew 	struct tegra_wdt *wdt = dev_get_drvdata(dev);
236c33a1597SAndrew Chew 
237c33a1597SAndrew Chew 	if (watchdog_active(&wdt->wdd))
238c33a1597SAndrew Chew 		tegra_wdt_stop(&wdt->wdd);
239c33a1597SAndrew Chew 
240c33a1597SAndrew Chew 	return 0;
241c33a1597SAndrew Chew }
242c33a1597SAndrew Chew 
tegra_wdt_resume(struct device * dev)243*c1eaa8c5SPaul Cercueil static int tegra_wdt_resume(struct device *dev)
244c33a1597SAndrew Chew {
245c33a1597SAndrew Chew 	struct tegra_wdt *wdt = dev_get_drvdata(dev);
246c33a1597SAndrew Chew 
247c33a1597SAndrew Chew 	if (watchdog_active(&wdt->wdd))
248c33a1597SAndrew Chew 		tegra_wdt_start(&wdt->wdd);
249c33a1597SAndrew Chew 
250c33a1597SAndrew Chew 	return 0;
251c33a1597SAndrew Chew }
252c33a1597SAndrew Chew 
253c33a1597SAndrew Chew static const struct of_device_id tegra_wdt_of_match[] = {
254c33a1597SAndrew Chew 	{ .compatible = "nvidia,tegra30-timer", },
255c33a1597SAndrew Chew 	{ },
256c33a1597SAndrew Chew };
257c33a1597SAndrew Chew MODULE_DEVICE_TABLE(of, tegra_wdt_of_match);
258c33a1597SAndrew Chew 
259*c1eaa8c5SPaul Cercueil static DEFINE_SIMPLE_DEV_PM_OPS(tegra_wdt_pm_ops,
260*c1eaa8c5SPaul Cercueil 				tegra_wdt_suspend, tegra_wdt_resume);
261c33a1597SAndrew Chew 
262c33a1597SAndrew Chew static struct platform_driver tegra_wdt_driver = {
263c33a1597SAndrew Chew 	.probe		= tegra_wdt_probe,
264c33a1597SAndrew Chew 	.driver		= {
265c33a1597SAndrew Chew 		.name	= "tegra-wdt",
266*c1eaa8c5SPaul Cercueil 		.pm	= pm_sleep_ptr(&tegra_wdt_pm_ops),
267c33a1597SAndrew Chew 		.of_match_table = tegra_wdt_of_match,
268c33a1597SAndrew Chew 	},
269c33a1597SAndrew Chew };
270c33a1597SAndrew Chew module_platform_driver(tegra_wdt_driver);
271c33a1597SAndrew Chew 
272c33a1597SAndrew Chew MODULE_AUTHOR("NVIDIA Corporation");
273c33a1597SAndrew Chew MODULE_DESCRIPTION("Tegra Watchdog Driver");
274c33a1597SAndrew Chew MODULE_LICENSE("GPL v2");
275