]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/blob - drivers/watchdog/tegra_wdt.c
watchdog: tegra: select timer based on timer base
[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 /* WDT registers */
27 #define WDT_CFG                         0x0
28 #define WDT_CFG_PERIOD_SHIFT            4
29 #define WDT_CFG_PERIOD_MASK             0xff
30 #define WDT_CFG_INT_EN                  (1 << 12)
31 #define WDT_CFG_PMC2CAR_RST_EN          (1 << 15)
32 #define WDT_STS                         0x4
33 #define WDT_STS_COUNT_SHIFT             4
34 #define WDT_STS_COUNT_MASK              0xff
35 #define WDT_STS_EXP_SHIFT               12
36 #define WDT_STS_EXP_MASK                0x3
37 #define WDT_CMD                         0x8
38 #define WDT_CMD_START_COUNTER           (1 << 0)
39 #define WDT_CMD_DISABLE_COUNTER         (1 << 1)
40 #define WDT_UNLOCK                      (0xc)
41 #define WDT_UNLOCK_PATTERN              (0xc45a << 0)
42
43 /* Timer registers */
44 #define TIMER_BASE_ADDR                 0x60005088
45 #define TIMER_PTV                       0x0
46 #define TIMER_EN                        (1 << 31)
47 #define TIMER_PERIODIC                  (1 << 30)
48
49 struct tegra_wdt {
50         struct watchdog_device  wdd;
51         void __iomem            *wdt_regs;
52         void __iomem            *tmr_regs;
53         u8                      timer_id;
54 };
55
56 /*
57  * The total expiry count of Tegra WDTs is limited to HW design and depends
58  * on skip configuration if supported. To be safe, we set the default expiry
59  * count to 1. It should be updated later with value specified in device tree.
60  */
61 static int expiry_count = 1;
62
63 /*
64  * Period value: Trigger Value is the time period and period value determines
65  * number of periods for watchdog expiration i.e. effectively after time:
66  * "period value" * "Trigger Vaue"
67  */
68 #define WDT_TRG_PERIOD  1
69 static int trigger_period = WDT_TRG_PERIOD;
70
71 #define WDT_HEARTBEAT 120
72 static int heartbeat = WDT_HEARTBEAT;
73 module_param(heartbeat, int, 0);
74 MODULE_PARM_DESC(heartbeat,
75         "Watchdog heartbeats in seconds. (default = "
76         __MODULE_STRING(WDT_HEARTBEAT) ")");
77
78 static bool nowayout = WATCHDOG_NOWAYOUT;
79 module_param(nowayout, bool, 0);
80 MODULE_PARM_DESC(nowayout,
81         "Watchdog cannot be stopped once started (default="
82         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
83
84 /*
85  * Timer ID is required to be programmed to WDT_CFG register
86  * As timer address and timer ID mapping is non-linear,
87  * hence these function are defined to do the conversion.
88  */
89 static resource_size_t tegra_tmr_addr(int timer_id)
90 {
91         int i;
92         resource_size_t timer_addr;
93
94         if (timer_id == 0)
95                 i = 10;
96         else if (timer_id < 3)
97                 i = timer_id - 8;
98         else if (timer_id > 9)
99                 i = timer_id + 1;
100         else
101                 i = timer_id;
102
103         timer_addr = TIMER_BASE_ADDR - (10 - i) * 8;
104
105         return timer_addr;
106 }
107
108 static int tegra_tmr_index(struct resource *tmr_res)
109 {
110         int timer_id;
111
112         /* Select Timer ID using Timer base address provided in DT:
113          *   [Base Addr:Timer#]
114          *   [0x00 :  1], [0x08 :  2], [0x50 :  3], [0x58 : 4], [0x60 : 5],
115          *   [0x68 :  6], [0x70 :  7], [0x78 :  8], [0x80 : 9], [0x88 : 0],
116          *   [0x90 : 10], [0x98 : 11], [0xA0 : 12], [0xA8 : 13]
117          */
118         timer_id = ((3 + ((tmr_res->start & 0xff) - 0x50) / 8)) % 10;
119         if ((tmr_res->start & 0xff) < 0x50)
120                 timer_id -= 4;
121         if ((tmr_res->start & 0xff) > 0x88)
122                 timer_id += 9;
123
124         return timer_id;
125 }
126
127 static int tegra_wdt_start(struct watchdog_device *wdd)
128 {
129         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
130         u32 val;
131
132         /*
133          * The timeout needs to be divided by expiry_count here so as to
134          * keep the ultimate watchdog reset timeout the same as the program
135          * timeout requested by application. The program timeout should make
136          * sure WDT FIQ will never be asserted in a valid use case.
137          */
138         val = (wdd->timeout * USEC_PER_SEC) / expiry_count;
139         val |= (TIMER_EN | TIMER_PERIODIC);
140         writel(val, wdt->tmr_regs + TIMER_PTV);
141
142         /*
143          * Set number of periods and start counter.
144          *
145          * Interrupt handler is not required for user space
146          * WDT accesses, since the caller is responsible to ping the
147          * WDT to reset the counter before expiration, through ioctls.
148          */
149         val = (wdt->timer_id) |
150               (trigger_period << WDT_CFG_PERIOD_SHIFT) |
151               WDT_CFG_PMC2CAR_RST_EN;
152         writel(val, wdt->wdt_regs + WDT_CFG);
153
154         writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
155
156         return 0;
157 }
158
159 static int tegra_wdt_stop(struct watchdog_device *wdd)
160 {
161         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
162
163         writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK);
164         writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD);
165         writel(0, wdt->tmr_regs + TIMER_PTV);
166
167         return 0;
168 }
169
170 static int tegra_wdt_ping(struct watchdog_device *wdd)
171 {
172         u32 val;
173         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
174
175         /* Disable timer */
176         tegra_wdt_stop(wdd);
177
178         /* Load the timeout value */
179         val = (wdd->timeout * USEC_PER_SEC) / expiry_count;
180         val |= (TIMER_EN | TIMER_PERIODIC);
181         writel(val, wdt->tmr_regs + TIMER_PTV);
182
183         /* Restart */
184         writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD);
185
186         return 0;
187 }
188
189 static int tegra_wdt_set_timeout(struct watchdog_device *wdd,
190                                  unsigned int timeout)
191 {
192         wdd->timeout = timeout;
193
194         if (watchdog_active(wdd)) {
195                 tegra_wdt_stop(wdd);
196                 return tegra_wdt_start(wdd);
197         }
198
199         return 0;
200 }
201
202 static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd)
203 {
204         struct tegra_wdt *wdt = watchdog_get_drvdata(wdd);
205         u32 val;
206         int count;
207         int exp;
208
209         val = readl(wdt->wdt_regs + WDT_STS);
210
211         /* Current countdown (from timeout) */
212         count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK;
213
214         /* Number of expirations */
215         exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK;
216         /*
217          * The entire thing is divided by expiry_count because we are ticking
218          * down expiry_count times  faster due to needing to wait for the
219          * expiry_count'th expiration.
220          */
221         return (((3 - exp) * wdd->timeout) + count) / expiry_count;
222 }
223
224 static const struct watchdog_info tegra_wdt_info = {
225         .options        = WDIOF_SETTIMEOUT |
226                           WDIOF_MAGICCLOSE |
227                           WDIOF_KEEPALIVEPING,
228         .firmware_version = 0,
229         .identity       = "Tegra Watchdog",
230 };
231
232 static struct watchdog_ops tegra_wdt_ops = {
233         .owner = THIS_MODULE,
234         .start = tegra_wdt_start,
235         .stop = tegra_wdt_stop,
236         .ping = tegra_wdt_ping,
237         .set_timeout = tegra_wdt_set_timeout,
238         .get_timeleft = tegra_wdt_get_timeleft,
239 };
240
241 static int tegra_wdt_probe(struct platform_device *pdev)
242 {
243         struct watchdog_device *wdd;
244         struct tegra_wdt *wdt;
245         struct resource *wdt_res, *tmr_res;
246         struct device_node *np = pdev->dev.of_node;
247         u32 pval = 0;
248         int ret = 0;
249
250         wdt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251         tmr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
252
253         if (!wdt_res || !tmr_res) {
254                 dev_err(&pdev->dev, "incorrect wdt resources\n");
255                 return -ENOENT;
256         }
257
258         ret = of_property_read_u32(np, "nvidia,expiry-count", &pval);
259         if (!ret)
260                 expiry_count = pval;
261
262         ret = of_property_read_u32(np, "nvidia,heartbeat-init", &pval);
263         if (!ret)
264                 heartbeat = pval;
265
266         /*
267          * Allocate our watchdog driver data, which has the
268          * struct watchdog_device nested within it.
269          */
270         wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
271         if (!wdt)
272                 return -ENOMEM;
273
274         /*
275          * Get Timer index from (in decending priority):
276          *      from "nvidia,timer-index" DT property
277          *      from Timer Source Address in DT
278          */
279         wdt->timer_id = tegra_tmr_index(tmr_res);
280
281         ret = of_property_read_u32(np, "nvidia,timer-index", &pval);
282         if (!ret) {
283                 /*
284                  * If timer-index is provided then either corresponding
285                  * timer source address or timer base address address
286                  * should be provided.
287                  */
288                 if (wdt->timer_id && (wdt->timer_id != pval)) {
289                         dev_err(&pdev->dev, "Invalid Timer base address\n");
290                         return -EINVAL;
291                 }
292
293                 /* Skip adjust resource if timer source address is provided */
294                 if (!wdt->timer_id) {
295                         ret = adjust_resource(tmr_res,
296                                         tegra_tmr_addr(pval),
297                                         tmr_res->end - tmr_res->start);
298                         if (ret < 0) {
299                                 dev_err(&pdev->dev,
300                                         "Failed to adjust resource:%d\n", ret);
301                                 return ret;
302                         }
303                 }
304                 wdt->timer_id = pval;
305         }
306
307         wdt->wdt_regs = devm_ioremap_resource(&pdev->dev, wdt_res);
308         if (IS_ERR(wdt->wdt_regs)) {
309                 dev_err(&pdev->dev, "failed ioremap wdt resource\n");
310                 return PTR_ERR(wdt->wdt_regs);
311         }
312
313         wdt->tmr_regs = devm_ioremap_resource(&pdev->dev, tmr_res);
314         if (IS_ERR(wdt->tmr_regs)) {
315                 dev_err(&pdev->dev, "failed ioremap tmr resource\n");
316                 return PTR_ERR(wdt->tmr_regs);
317         }
318
319         /* Initialize struct watchdog_device. */
320         wdd = &wdt->wdd;
321         wdd->timeout = heartbeat;
322         wdd->info = &tegra_wdt_info;
323         wdd->ops = &tegra_wdt_ops;
324         wdd->min_timeout = MIN_WDT_TIMEOUT * expiry_count;
325         wdd->max_timeout = MAX_WDT_TIMEOUT * expiry_count;
326         wdd->parent = &pdev->dev;
327
328         watchdog_set_drvdata(wdd, wdt);
329
330         watchdog_init_timeout(wdd, heartbeat, &pdev->dev);
331         watchdog_set_nowayout(wdd, nowayout);
332
333         ret = watchdog_register_device(wdd);
334         if (ret) {
335                 dev_err(&pdev->dev,
336                         "failed to register watchdog device\n");
337                 return ret;
338         }
339
340         platform_set_drvdata(pdev, wdt);
341
342         dev_info(&pdev->dev,
343                  "initialized (timeout = %d sec, nowayout = %d)\n",
344                  wdt->wdd.timeout, nowayout);
345
346         return 0;
347 }
348
349 static int tegra_wdt_remove(struct platform_device *pdev)
350 {
351         struct tegra_wdt *wdt = platform_get_drvdata(pdev);
352
353         tegra_wdt_stop(&wdt->wdd);
354
355         watchdog_unregister_device(&wdt->wdd);
356
357         dev_info(&pdev->dev, "removed wdt\n");
358
359         return 0;
360 }
361
362 #ifdef CONFIG_PM_SLEEP
363 static int tegra_wdt_runtime_suspend(struct device *dev)
364 {
365         struct tegra_wdt *wdt = dev_get_drvdata(dev);
366
367         if (watchdog_active(&wdt->wdd))
368                 tegra_wdt_stop(&wdt->wdd);
369
370         return 0;
371 }
372
373 static int tegra_wdt_runtime_resume(struct device *dev)
374 {
375         struct tegra_wdt *wdt = dev_get_drvdata(dev);
376
377         if (watchdog_active(&wdt->wdd))
378                 tegra_wdt_start(&wdt->wdd);
379
380         return 0;
381 }
382 #endif
383
384 static const struct of_device_id tegra_wdt_of_match[] = {
385         { .compatible = "nvidia,tegra-wdt", },
386         { },
387 };
388 MODULE_DEVICE_TABLE(of, tegra_wdt_of_match);
389
390 static const struct dev_pm_ops tegra_wdt_pm_ops = {
391         SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend,
392                                 tegra_wdt_runtime_resume)
393 };
394
395 static struct platform_driver tegra_wdt_driver = {
396         .probe          = tegra_wdt_probe,
397         .remove         = tegra_wdt_remove,
398         .driver         = {
399                 .name   = "tegra-wdt",
400                 .pm     = &tegra_wdt_pm_ops,
401                 .of_match_table = tegra_wdt_of_match,
402         },
403 };
404 module_platform_driver(tegra_wdt_driver);
405
406 MODULE_AUTHOR("NVIDIA Corporation");
407 MODULE_DESCRIPTION("Tegra Watchdog Driver");
408 MODULE_LICENSE("GPL v2");