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