Merge tag 'xilinx-for-v2020.01' of https://gitlab.denx.de/u-boot/custodians/u-boot...
[oweals/u-boot.git] / drivers / watchdog / omap_wdt.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * omap_wdt.c
4  *
5  * (C) Copyright 2013
6  * Heiko Schocher, DENX Software Engineering, hs@denx.de.
7  *
8  * Based on:
9  *
10  * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
11  *
12  * commit 2d991a164a61858012651e13c59521975504e260
13  * Author: Bill Pemberton <wfp5p@virginia.edu>
14  * Date:   Mon Nov 19 13:21:41 2012 -0500
15  *
16  * watchdog: remove use of __devinit
17  *
18  * CONFIG_HOTPLUG is going away as an option so __devinit is no longer
19  * needed.
20  *
21  * Author: MontaVista Software, Inc.
22  *       <gdavis@mvista.com> or <source@mvista.com>
23  *
24  * History:
25  *
26  * 20030527: George G. Davis <gdavis@mvista.com>
27  *      Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
28  *      (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
29  *      Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
30  *
31  * Copyright (c) 2004 Texas Instruments.
32  *      1. Modified to support OMAP1610 32-KHz watchdog timer
33  *      2. Ported to 2.6 kernel
34  *
35  * Copyright (c) 2005 David Brownell
36  *      Use the driver model and standard identifiers; handle bigger timeouts.
37  */
38
39 #include <common.h>
40 #include <watchdog.h>
41 #include <asm/arch/hardware.h>
42 #include <asm/io.h>
43 #include <asm/processor.h>
44 #include <asm/arch/cpu.h>
45 #include <wdt.h>
46 #include <dm.h>
47 #include <errno.h>
48
49 /* Hardware timeout in seconds */
50 #define WDT_HW_TIMEOUT 60
51
52 #if !CONFIG_IS_ENABLED(WDT)
53 static unsigned int wdt_trgr_pattern = 0x1234;
54
55 void hw_watchdog_reset(void)
56 {
57         struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
58
59         /*
60          * Somebody just triggered watchdog reset and write to WTGR register
61          * is in progress. It is resetting right now, no need to trigger it
62          * again
63          */
64         if ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WTGR)
65                 return;
66
67         wdt_trgr_pattern = ~wdt_trgr_pattern;
68         writel(wdt_trgr_pattern, &wdt->wdtwtgr);
69
70         /*
71          * Don't wait for posted write to complete, i.e. don't check
72          * WDT_WWPS_PEND_WTGR bit in WWPS register. There is no writes to
73          * WTGR register outside of this func, and if entering it
74          * we see WDT_WWPS_PEND_WTGR bit set, it means watchdog reset
75          * was just triggered. This prevents us from wasting time in busy
76          * polling of WDT_WWPS_PEND_WTGR bit.
77          */
78 }
79
80 static int omap_wdt_set_timeout(unsigned int timeout)
81 {
82         struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
83         u32 pre_margin = GET_WLDR_VAL(timeout);
84
85         /* just count up at 32 KHz */
86         while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WLDR)
87                 ;
88
89         writel(pre_margin, &wdt->wdtwldr);
90         while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WLDR)
91                 ;
92
93         return 0;
94 }
95
96 void hw_watchdog_disable(void)
97 {
98         struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
99
100         /*
101          * Disable watchdog
102          */
103         writel(0xAAAA, &wdt->wdtwspr);
104         while (readl(&wdt->wdtwwps) != 0x0)
105                 ;
106         writel(0x5555, &wdt->wdtwspr);
107         while (readl(&wdt->wdtwwps) != 0x0)
108                 ;
109 }
110
111 void hw_watchdog_init(void)
112 {
113         struct wd_timer *wdt = (struct wd_timer *)WDT_BASE;
114
115         /*
116          * Make sure the watchdog is disabled. This is unfortunately required
117          * because writing to various registers with the watchdog running has no
118          * effect.
119          */
120         hw_watchdog_disable();
121
122         /* initialize prescaler */
123         while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WCLR)
124                 ;
125
126         writel(WDT_WCLR_PRE | (PTV << WDT_WCLR_PTV_OFF), &wdt->wdtwclr);
127         while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WCLR)
128                 ;
129
130         omap_wdt_set_timeout(WDT_HW_TIMEOUT);
131
132         /* Sequence to enable the watchdog */
133         writel(0xBBBB, &wdt->wdtwspr);
134         while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR)
135                 ;
136
137         writel(0x4444, &wdt->wdtwspr);
138         while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR)
139                 ;
140 }
141
142 void watchdog_reset(void)
143 {
144         hw_watchdog_reset();
145 }
146
147 #else
148
149 static int omap3_wdt_reset(struct udevice *dev)
150 {
151         struct omap3_wdt_priv *priv = dev_get_priv(dev);
152
153 /*
154  * Somebody just triggered watchdog reset and write to WTGR register
155  * is in progress. It is resetting right now, no need to trigger it
156  * again
157  */
158         if ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WTGR)
159                 return 0;
160
161         priv->wdt_trgr_pattern = ~(priv->wdt_trgr_pattern);
162         writel(priv->wdt_trgr_pattern, &priv->regs->wdtwtgr);
163 /*
164  * Don't wait for posted write to complete, i.e. don't check
165  * WDT_WWPS_PEND_WTGR bit in WWPS register. There is no writes to
166  * WTGR register outside of this func, and if entering it
167  * we see WDT_WWPS_PEND_WTGR bit set, it means watchdog reset
168  * was just triggered. This prevents us from wasting time in busy
169  * polling of WDT_WWPS_PEND_WTGR bit.
170  */
171         return 0;
172 }
173
174 static int omap3_wdt_stop(struct udevice *dev)
175 {
176         struct omap3_wdt_priv *priv = dev_get_priv(dev);
177
178 /* disable watchdog */
179         writel(0xAAAA, &priv->regs->wdtwspr);
180         while (readl(&priv->regs->wdtwwps) != 0x0)
181                 ;
182         writel(0x5555, &priv->regs->wdtwspr);
183         while (readl(&priv->regs->wdtwwps) != 0x0)
184                 ;
185         return 0;
186 }
187
188 static int omap3_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
189 {
190         struct omap3_wdt_priv *priv = dev_get_priv(dev);
191         u32 pre_margin = GET_WLDR_VAL(timeout_ms);
192 /*
193  * Make sure the watchdog is disabled. This is unfortunately required
194  * because writing to various registers with the watchdog running has no
195  * effect.
196  */
197         omap3_wdt_stop(dev);
198
199 /* initialize prescaler */
200         while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WCLR)
201                 ;
202
203         writel(WDT_WCLR_PRE | (PTV << WDT_WCLR_PTV_OFF), &priv->regs->wdtwclr);
204         while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WCLR)
205                 ;
206 /* just count up at 32 KHz */
207         while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WLDR)
208                 ;
209
210         writel(pre_margin, &priv->regs->wdtwldr);
211         while (readl(&priv->regs->wdtwwps) & WDT_WWPS_PEND_WLDR)
212                 ;
213 /* Sequence to enable the watchdog */
214         writel(0xBBBB, &priv->regs->wdtwspr);
215         while ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WSPR)
216                 ;
217
218         writel(0x4444, &priv->regs->wdtwspr);
219         while ((readl(&priv->regs->wdtwwps)) & WDT_WWPS_PEND_WSPR)
220                 ;
221
222         return 0;
223 }
224
225 static int omap3_wdt_probe(struct udevice *dev)
226 {
227         struct omap3_wdt_priv *priv = dev_get_priv(dev);
228
229         priv->regs = (struct wd_timer *)devfdt_get_addr(dev);
230         if (!priv->regs)
231                 return -EINVAL;
232
233         priv->wdt_trgr_pattern = 0x1234;
234         debug("%s: Probing wdt%u\n", __func__, dev->seq);
235         return 0;
236 }
237
238 static const struct wdt_ops omap3_wdt_ops = {
239         .start = omap3_wdt_start,
240         .stop = omap3_wdt_stop,
241         .reset = omap3_wdt_reset,
242 };
243
244 static const struct udevice_id omap3_wdt_ids[] = {
245         { .compatible = "ti,omap3-wdt" },
246         { }
247 };
248
249 U_BOOT_DRIVER(omap3_wdt) = {
250         .name = "omap3_wdt",
251         .id = UCLASS_WDT,
252         .of_match = omap3_wdt_ids,
253         .ops = &omap3_wdt_ops,
254         .probe = omap3_wdt_probe,
255         .priv_auto_alloc_size = sizeof(struct omap3_wdt_priv),
256 };
257 #endif /* !CONFIG_IS_ENABLED(WDT) */