GNU Linux-libre 4.19.314-gnu1
[releases.git] / drivers / watchdog / bcm2835_wdt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Watchdog driver for Broadcom BCM2835
4  *
5  * "bcm2708_wdog" driver written by Luke Diamand that was obtained from
6  * branch "rpi-3.6.y" of git://github.com/raspberrypi/linux.git was used
7  * as a hardware reference for the Broadcom BCM2835 watchdog timer.
8  *
9  * Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk>
10  *
11  */
12
13 #include <linux/delay.h>
14 #include <linux/types.h>
15 #include <linux/module.h>
16 #include <linux/io.h>
17 #include <linux/watchdog.h>
18 #include <linux/platform_device.h>
19 #include <linux/of_address.h>
20 #include <linux/of_platform.h>
21
22 #define PM_RSTC                         0x1c
23 #define PM_RSTS                         0x20
24 #define PM_WDOG                         0x24
25
26 #define PM_PASSWORD                     0x5a000000
27
28 #define PM_WDOG_TIME_SET                0x000fffff
29 #define PM_RSTC_WRCFG_CLR               0xffffffcf
30 #define PM_RSTS_HADWRH_SET              0x00000040
31 #define PM_RSTC_WRCFG_SET               0x00000030
32 #define PM_RSTC_WRCFG_FULL_RESET        0x00000020
33 #define PM_RSTC_RESET                   0x00000102
34
35 /*
36  * The Raspberry Pi firmware uses the RSTS register to know which partition
37  * to boot from. The partition value is spread into bits 0, 2, 4, 6, 8, 10.
38  * Partition 63 is a special partition used by the firmware to indicate halt.
39  */
40 #define PM_RSTS_RASPBERRYPI_HALT        0x555
41
42 #define SECS_TO_WDOG_TICKS(x) ((x) << 16)
43 #define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
44 #define WDOG_TICKS_TO_MSECS(x) ((x) * 1000 >> 16)
45
46 struct bcm2835_wdt {
47         void __iomem            *base;
48         spinlock_t              lock;
49 };
50
51 static unsigned int heartbeat;
52 static bool nowayout = WATCHDOG_NOWAYOUT;
53
54 static bool bcm2835_wdt_is_running(struct bcm2835_wdt *wdt)
55 {
56         uint32_t cur;
57
58         cur = readl(wdt->base + PM_RSTC);
59
60         return !!(cur & PM_RSTC_WRCFG_FULL_RESET);
61 }
62
63 static int bcm2835_wdt_start(struct watchdog_device *wdog)
64 {
65         struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
66         uint32_t cur;
67         unsigned long flags;
68
69         spin_lock_irqsave(&wdt->lock, flags);
70
71         writel_relaxed(PM_PASSWORD | (SECS_TO_WDOG_TICKS(wdog->timeout) &
72                                 PM_WDOG_TIME_SET), wdt->base + PM_WDOG);
73         cur = readl_relaxed(wdt->base + PM_RSTC);
74         writel_relaxed(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
75                   PM_RSTC_WRCFG_FULL_RESET, wdt->base + PM_RSTC);
76
77         spin_unlock_irqrestore(&wdt->lock, flags);
78
79         return 0;
80 }
81
82 static int bcm2835_wdt_stop(struct watchdog_device *wdog)
83 {
84         struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
85
86         writel_relaxed(PM_PASSWORD | PM_RSTC_RESET, wdt->base + PM_RSTC);
87         return 0;
88 }
89
90 static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
91 {
92         struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
93
94         uint32_t ret = readl_relaxed(wdt->base + PM_WDOG);
95         return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
96 }
97
98 static void __bcm2835_restart(struct bcm2835_wdt *wdt)
99 {
100         u32 val;
101
102         /* use a timeout of 10 ticks (~150us) */
103         writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG);
104         val = readl_relaxed(wdt->base + PM_RSTC);
105         val &= PM_RSTC_WRCFG_CLR;
106         val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
107         writel_relaxed(val, wdt->base + PM_RSTC);
108
109         /* No sleeping, possibly atomic. */
110         mdelay(1);
111 }
112
113 static int bcm2835_restart(struct watchdog_device *wdog,
114                            unsigned long action, void *data)
115 {
116         struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
117
118         __bcm2835_restart(wdt);
119
120         return 0;
121 }
122
123 static const struct watchdog_ops bcm2835_wdt_ops = {
124         .owner =        THIS_MODULE,
125         .start =        bcm2835_wdt_start,
126         .stop =         bcm2835_wdt_stop,
127         .get_timeleft = bcm2835_wdt_get_timeleft,
128         .restart =      bcm2835_restart,
129 };
130
131 static const struct watchdog_info bcm2835_wdt_info = {
132         .options =      WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
133                         WDIOF_KEEPALIVEPING,
134         .identity =     "Broadcom BCM2835 Watchdog timer",
135 };
136
137 static struct watchdog_device bcm2835_wdt_wdd = {
138         .info =         &bcm2835_wdt_info,
139         .ops =          &bcm2835_wdt_ops,
140         .min_timeout =  1,
141         .max_hw_heartbeat_ms =  WDOG_TICKS_TO_MSECS(PM_WDOG_TIME_SET),
142         .timeout =      WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
143 };
144
145 /*
146  * We can't really power off, but if we do the normal reset scheme, and
147  * indicate to bootcode.bin not to reboot, then most of the chip will be
148  * powered off.
149  */
150 static void bcm2835_power_off(void)
151 {
152         struct device_node *np =
153                 of_find_compatible_node(NULL, NULL, "brcm,bcm2835-pm-wdt");
154         struct platform_device *pdev = of_find_device_by_node(np);
155         struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);
156         u32 val;
157
158         /*
159          * We set the watchdog hard reset bit here to distinguish this reset
160          * from the normal (full) reset. bootcode.bin will not reboot after a
161          * hard reset.
162          */
163         val = readl_relaxed(wdt->base + PM_RSTS);
164         val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
165         writel_relaxed(val, wdt->base + PM_RSTS);
166
167         /* Continue with normal reset mechanism */
168         __bcm2835_restart(wdt);
169 }
170
171 static int bcm2835_wdt_probe(struct platform_device *pdev)
172 {
173         struct resource *res;
174         struct device *dev = &pdev->dev;
175         struct bcm2835_wdt *wdt;
176         int err;
177
178         wdt = devm_kzalloc(dev, sizeof(struct bcm2835_wdt), GFP_KERNEL);
179         if (!wdt)
180                 return -ENOMEM;
181         platform_set_drvdata(pdev, wdt);
182
183         spin_lock_init(&wdt->lock);
184
185         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
186         wdt->base = devm_ioremap_resource(dev, res);
187         if (IS_ERR(wdt->base))
188                 return PTR_ERR(wdt->base);
189
190         watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
191         watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
192         watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
193         bcm2835_wdt_wdd.parent = dev;
194         if (bcm2835_wdt_is_running(wdt)) {
195                 /*
196                  * The currently active timeout value (set by the
197                  * bootloader) may be different from the module
198                  * heartbeat parameter or the value in device
199                  * tree. But we just need to set WDOG_HW_RUNNING,
200                  * because then the framework will "immediately" ping
201                  * the device, updating the timeout.
202                  */
203                 set_bit(WDOG_HW_RUNNING, &bcm2835_wdt_wdd.status);
204         }
205
206         watchdog_set_restart_priority(&bcm2835_wdt_wdd, 128);
207
208         watchdog_stop_on_reboot(&bcm2835_wdt_wdd);
209         err = devm_watchdog_register_device(dev, &bcm2835_wdt_wdd);
210         if (err) {
211                 dev_err(dev, "Failed to register watchdog device");
212                 return err;
213         }
214
215         if (pm_power_off == NULL)
216                 pm_power_off = bcm2835_power_off;
217
218         dev_info(dev, "Broadcom BCM2835 watchdog timer");
219         return 0;
220 }
221
222 static int bcm2835_wdt_remove(struct platform_device *pdev)
223 {
224         if (pm_power_off == bcm2835_power_off)
225                 pm_power_off = NULL;
226
227         return 0;
228 }
229
230 static const struct of_device_id bcm2835_wdt_of_match[] = {
231         { .compatible = "brcm,bcm2835-pm-wdt", },
232         {},
233 };
234 MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match);
235
236 static struct platform_driver bcm2835_wdt_driver = {
237         .probe          = bcm2835_wdt_probe,
238         .remove         = bcm2835_wdt_remove,
239         .driver = {
240                 .name =         "bcm2835-wdt",
241                 .of_match_table = bcm2835_wdt_of_match,
242         },
243 };
244 module_platform_driver(bcm2835_wdt_driver);
245
246 module_param(heartbeat, uint, 0);
247 MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
248
249 module_param(nowayout, bool, 0);
250 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
251                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
252
253 MODULE_ALIAS("platform:bcm2835-wdt");
254 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
255 MODULE_DESCRIPTION("Driver for Broadcom BCM2835 watchdog timer");
256 MODULE_LICENSE("GPL");