]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/blob - drivers/watchdog/tegra_wdt.c
drivers: tegra_wdt: use DT based registration
[hercules2020/nv-tegra/linux-4.4.git] / drivers / watchdog / tegra_wdt.c
1 /*
2  * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  */
13
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/interrupt.h>
17 #include <linux/io.h>
18 #include <linux/of.h>
19 #include <linux/platform_device.h>
20 #include <linux/watchdog.h>
21
22 /* minimum and maximum watchdog trigger timeout, in seconds */
23 #define MIN_WDT_TIMEOUT                 1
24 #define MAX_WDT_TIMEOUT                 255
25
26 /*
27  * Register base of the timer that's selected for pairing with the watchdog.
28  * This driver arbitrarily uses timer 5, which is currently unused by
29  * other drivers (in particular, the Tegra clocksource driver).  If this
30  * needs to change, take care that the new timer is not used by the
31  * clocksource driver.
32  */
33 #define WDT_TIMER_BASE                  0x60
34 #define WDT_TIMER_ID                    5
35
36 /* WDT registers */
37 #define WDT_CFG                         0x0
38 #define WDT_CFG_PERIOD_SHIFT            4
39 #define WDT_CFG_PERIOD_MASK             0xff
40 #define WDT_CFG_INT_EN                  (1 << 12)
41 #define WDT_CFG_PMC2CAR_RST_EN          (1 << 15)
42 #define WDT_STS                         0x4
43 #define WDT_STS_COUNT_SHIFT             4
44 #define WDT_STS_COUNT_MASK              0xff
45 #define WDT_STS_EXP_SHIFT               12
46 #define WDT_STS_EXP_MASK                0x3
47 #define WDT_CMD                         0x8
48 #define WDT_CMD_START_COUNTER           (1 << 0)
49 #define WDT_CMD_DISABLE_COUNTER         (1 << 1)
50 #define WDT_UNLOCK                      (0xc)
51 #define WDT_UNLOCK_PATTERN              (0xc45a << 0)
52
53 /* Timer registers */
54 #define TIMER_PTV                       0x0
55 #define TIMER_EN                        (1 << 31)
56 #define TIMER_PERIODIC                  (1 << 30)
57
58 struct tegra_wdt {
59         struct watchdog_device  wdd;
60         void __iomem            *wdt_regs;
61         void __iomem            *tmr_regs;
62 };
63
64 #define WDT_HEARTBEAT 120
65 static int heartbeat = WDT_HEARTBEAT;
66 module_param(heartbeat, int, 0);
67 MODULE_PARM_DESC(heartbeat,
68         "Watchdog heartbeats in seconds. (default = "
69         __MODULE_STRING(WDT_HEARTBEAT) ")");
70
71 static bool nowayout = WATCHDOG_NOWAYOUT;
72 module_param(nowayout, bool, 0);
73 MODULE_PARM_DESC(nowayout,
74         "Watchdog cannot be stopped once started (default="
75         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
76
77 static int tegra_wdt_start(struct watchdog_device *wdd)
78 {
79         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
80         u32 val;
81
82         /*
83          * This thing has a fixed 1MHz clock.  Normally, we would set the
84          * period to 1 second by writing 1000000ul, but the watchdog system
85          * reset actually occurs on the 4th expiration of this counter,
86          * so we set the period to 1/4 of this amount.
87          */
88         val = 1000000ul / 4;
89         val |= (TIMER_EN | TIMER_PERIODIC);
90         writel(val, wdt->tmr_regs + TIMER_PTV);
91
92         /*
93          * Set number of periods and start counter.
94          *
95          * Interrupt handler is not required for user space
96          * WDT accesses, since the caller is responsible to ping the
97          * WDT to reset the counter before expiration, through ioctls.
98          */
99         val = WDT_TIMER_ID |
100               (wdd->timeout << WDT_CFG_PERIOD_SHIFT) |
101               WDT_CFG_PMC2CAR_RST_EN;
102         writel(val, wdt->wdt_regs + WDT_CFG);
103
104         writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
105
106         return 0;
107 }
108
109 static int tegra_wdt_stop(struct watchdog_device *wdd)
110 {
111         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
112
113         writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK);
114         writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD);
115         writel(0, wdt->tmr_regs + TIMER_PTV);
116
117         return 0;
118 }
119
120 static int tegra_wdt_ping(struct watchdog_device *wdd)
121 {
122         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
123
124         writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
125
126         return 0;
127 }
128
129 static int tegra_wdt_set_timeout(struct watchdog_device *wdd,
130                                  unsigned int timeout)
131 {
132         wdd->timeout = timeout;
133
134         if (watchdog_active(wdd)) {
135                 tegra_wdt_stop(wdd);
136                 return tegra_wdt_start(wdd);
137         }
138
139         return 0;
140 }
141
142 static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd)
143 {
144         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
145         u32 val;
146         int count;
147         int exp;
148
149         val = readl(wdt->wdt_regs + WDT_STS);
150
151         /* Current countdown (from timeout) */
152         count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK;
153
154         /* Number of expirations (we are waiting for the 4th expiration) */
155         exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK;
156
157         /*
158          * The entire thing is divided by 4 because we are ticking down 4 times
159          * faster due to needing to wait for the 4th expiration.
160          */
161         return (((3 - exp) * wdd->timeout) + count) / 4;
162 }
163
164 static const struct watchdog_info tegra_wdt_info = {
165         .options        = WDIOF_SETTIMEOUT |
166                           WDIOF_MAGICCLOSE |
167                           WDIOF_KEEPALIVEPING,
168         .firmware_version = 0,
169         .identity       = "Tegra Watchdog",
170 };
171
172 static struct watchdog_ops tegra_wdt_ops = {
173         .owner = THIS_MODULE,
174         .start = tegra_wdt_start,
175         .stop = tegra_wdt_stop,
176         .ping = tegra_wdt_ping,
177         .set_timeout = tegra_wdt_set_timeout,
178         .get_timeleft = tegra_wdt_get_timeleft,
179 };
180
181 static int tegra_wdt_probe(struct platform_device *pdev)
182 {
183         struct watchdog_device *wdd;
184         struct tegra_wdt *wdt;
185         struct resource *wdt_res, *tmr_res;
186         struct device_node *np = pdev->dev.of_node;
187         u32 pval = 0;
188         int ret = 0;
189
190         wdt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
191         tmr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
192
193         if (!wdt_res || !tmr_res) {
194                 dev_err(&pdev->dev, "incorrect wdt resources\n");
195                 return -ENOENT;
196         }
197
198         ret = of_property_read_u32(np, "nvidia,heartbeat-init", &pval);
199         if (!ret)
200                 heartbeat = pval;
201
202         /*
203          * Allocate our watchdog driver data, which has the
204          * struct watchdog_device nested within it.
205          */
206         wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
207         if (!wdt)
208                 return -ENOMEM;
209
210         wdt->wdt_regs = devm_ioremap_resource(&pdev->dev, wdt_res);
211         if (IS_ERR(wdt->wdt_regs)) {
212                 dev_err(&pdev->dev, "failed ioremap wdt resource\n");
213                 return PTR_ERR(wdt->wdt_regs);
214         }
215
216         wdt->tmr_regs = devm_ioremap_resource(&pdev->dev, tmr_res);
217         if (IS_ERR(wdt->tmr_regs)) {
218                 dev_err(&pdev->dev, "failed ioremap tmr resource\n");
219                 return PTR_ERR(wdt->tmr_regs);
220         }
221
222         /* Initialize struct watchdog_device. */
223         wdd = &wdt->wdd;
224         wdd->timeout = heartbeat;
225         wdd->info = &tegra_wdt_info;
226         wdd->ops = &tegra_wdt_ops;
227         wdd->min_timeout = MIN_WDT_TIMEOUT;
228         wdd->max_timeout = MAX_WDT_TIMEOUT;
229         wdd->parent = &pdev->dev;
230
231         watchdog_set_drvdata(wdd, wdt);
232
233         watchdog_set_nowayout(wdd, nowayout);
234
235         ret = watchdog_register_device(wdd);
236         if (ret) {
237                 dev_err(&pdev->dev,
238                         "failed to register watchdog device\n");
239                 return ret;
240         }
241
242         platform_set_drvdata(pdev, wdt);
243
244         dev_info(&pdev->dev,
245                  "initialized (heartbeat = %d sec, nowayout = %d)\n",
246                  heartbeat, nowayout);
247
248         return 0;
249 }
250
251 static int tegra_wdt_remove(struct platform_device *pdev)
252 {
253         struct tegra_wdt *wdt = platform_get_drvdata(pdev);
254
255         tegra_wdt_stop(&wdt->wdd);
256
257         watchdog_unregister_device(&wdt->wdd);
258
259         dev_info(&pdev->dev, "removed wdt\n");
260
261         return 0;
262 }
263
264 #ifdef CONFIG_PM_SLEEP
265 static int tegra_wdt_runtime_suspend(struct device *dev)
266 {
267         struct tegra_wdt *wdt = dev_get_drvdata(dev);
268
269         if (watchdog_active(&wdt->wdd))
270                 tegra_wdt_stop(&wdt->wdd);
271
272         return 0;
273 }
274
275 static int tegra_wdt_runtime_resume(struct device *dev)
276 {
277         struct tegra_wdt *wdt = dev_get_drvdata(dev);
278
279         if (watchdog_active(&wdt->wdd))
280                 tegra_wdt_start(&wdt->wdd);
281
282         return 0;
283 }
284 #endif
285
286 static const struct of_device_id tegra_wdt_of_match[] = {
287         { .compatible = "nvidia,tegra-wdt", },
288         { },
289 };
290 MODULE_DEVICE_TABLE(of, tegra_wdt_of_match);
291
292 static const struct dev_pm_ops tegra_wdt_pm_ops = {
293         SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend,
294                                 tegra_wdt_runtime_resume)
295 };
296
297 static struct platform_driver tegra_wdt_driver = {
298         .probe          = tegra_wdt_probe,
299         .remove         = tegra_wdt_remove,
300         .driver         = {
301                 .name   = "tegra-wdt",
302                 .pm     = &tegra_wdt_pm_ops,
303                 .of_match_table = tegra_wdt_of_match,
304         },
305 };
306 module_platform_driver(tegra_wdt_driver);
307
308 MODULE_AUTHOR("NVIDIA Corporation");
309 MODULE_DESCRIPTION("Tegra Watchdog Driver");
310 MODULE_LICENSE("GPL v2");