]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
253531bb YL |
2 | /* |
3 | * Copyright (C) 2016 Freescale Semiconductor, Inc. | |
253531bb YL |
4 | */ |
5 | ||
d678a59d | 6 | #include <common.h> |
9a3b4ceb | 7 | #include <cpu_func.h> |
253531bb YL |
8 | #include <asm/io.h> |
9 | #include <asm/arch/imx-regs.h> | |
5e112c7c AG |
10 | #include <dm.h> |
11 | #include <wdt.h> | |
253531bb YL |
12 | |
13 | /* | |
14 | * MX7ULP WDOG Register Map | |
15 | */ | |
16 | struct wdog_regs { | |
edf95bde | 17 | u32 cs; |
253531bb YL |
18 | u32 cnt; |
19 | u32 toval; | |
20 | u32 win; | |
21 | }; | |
22 | ||
5e112c7c AG |
23 | struct ulp_wdt_priv { |
24 | struct wdog_regs *wdog; | |
25 | u32 clk_rate; | |
26 | }; | |
27 | ||
253531bb YL |
28 | #define REFRESH_WORD0 0xA602 /* 1st refresh word */ |
29 | #define REFRESH_WORD1 0xB480 /* 2nd refresh word */ | |
30 | ||
31 | #define UNLOCK_WORD0 0xC520 /* 1st unlock word */ | |
32 | #define UNLOCK_WORD1 0xD928 /* 2nd unlock word */ | |
33 | ||
a79f2007 YL |
34 | #define UNLOCK_WORD 0xD928C520 /* unlock word */ |
35 | #define REFRESH_WORD 0xB480A602 /* refresh word */ | |
36 | ||
edf95bde BL |
37 | #define WDGCS_WDGE BIT(7) |
38 | #define WDGCS_WDGUPDATE BIT(5) | |
253531bb | 39 | |
edf95bde BL |
40 | #define WDGCS_RCS BIT(10) |
41 | #define WDGCS_ULK BIT(11) | |
ef0ad9b0 | 42 | #define WDOG_CS_PRES BIT(12) |
a79f2007 | 43 | #define WDGCS_CMD32EN BIT(13) |
edf95bde | 44 | #define WDGCS_FLG BIT(14) |
a7fd6335 | 45 | #define WDGCS_INT BIT(6) |
253531bb YL |
46 | |
47 | #define WDG_BUS_CLK (0x0) | |
48 | #define WDG_LPO_CLK (0x1) | |
49 | #define WDG_32KHZ_CLK (0x2) | |
50 | #define WDG_EXT_CLK (0x3) | |
51 | ||
5e112c7c AG |
52 | #define CLK_RATE_1KHZ 1000 |
53 | #define CLK_RATE_32KHZ 125 | |
54 | ||
253531bb YL |
55 | void hw_watchdog_set_timeout(u16 val) |
56 | { | |
57 | /* setting timeout value */ | |
58 | struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; | |
59 | ||
60 | writel(val, &wdog->toval); | |
61 | } | |
62 | ||
5e112c7c | 63 | void ulp_watchdog_reset(struct wdog_regs *wdog) |
253531bb | 64 | { |
a79f2007 YL |
65 | if (readl(&wdog->cs) & WDGCS_CMD32EN) { |
66 | writel(REFRESH_WORD, &wdog->cnt); | |
67 | } else { | |
68 | dmb(); | |
69 | __raw_writel(REFRESH_WORD0, &wdog->cnt); | |
70 | __raw_writel(REFRESH_WORD1, &wdog->cnt); | |
71 | dmb(); | |
72 | } | |
253531bb YL |
73 | } |
74 | ||
5e112c7c | 75 | void ulp_watchdog_init(struct wdog_regs *wdog, u16 timeout) |
253531bb | 76 | { |
a79f2007 YL |
77 | u32 cmd32 = 0; |
78 | ||
79 | if (readl(&wdog->cs) & WDGCS_CMD32EN) { | |
80 | writel(UNLOCK_WORD, &wdog->cnt); | |
81 | cmd32 = WDGCS_CMD32EN; | |
82 | } else { | |
83 | dmb(); | |
84 | __raw_writel(UNLOCK_WORD0, &wdog->cnt); | |
85 | __raw_writel(UNLOCK_WORD1, &wdog->cnt); | |
86 | dmb(); | |
87 | } | |
253531bb | 88 | |
edf95bde BL |
89 | /* Wait WDOG Unlock */ |
90 | while (!(readl(&wdog->cs) & WDGCS_ULK)) | |
91 | ; | |
253531bb | 92 | |
5e112c7c | 93 | hw_watchdog_set_timeout(timeout); |
253531bb YL |
94 | writel(0, &wdog->win); |
95 | ||
edf95bde | 96 | /* setting 1-kHz clock source, enable counter running, and clear interrupt */ |
ef0ad9b0 AG |
97 | if (IS_ENABLED(CONFIG_ARCH_IMX9)) |
98 | writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) | | |
a7fd6335 | 99 | WDGCS_FLG | WDOG_CS_PRES | WDGCS_INT), &wdog->cs); |
ef0ad9b0 AG |
100 | else |
101 | writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) | | |
102 | WDGCS_FLG), &wdog->cs); | |
edf95bde BL |
103 | |
104 | /* Wait WDOG reconfiguration */ | |
105 | while (!(readl(&wdog->cs) & WDGCS_RCS)) | |
106 | ; | |
253531bb | 107 | |
5e112c7c AG |
108 | ulp_watchdog_reset(wdog); |
109 | } | |
110 | ||
111 | void hw_watchdog_reset(void) | |
112 | { | |
113 | struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; | |
114 | ||
115 | ulp_watchdog_reset(wdog); | |
116 | } | |
117 | ||
118 | void hw_watchdog_init(void) | |
119 | { | |
120 | struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; | |
121 | ||
122 | ulp_watchdog_init(wdog, CONFIG_WATCHDOG_TIMEOUT_MSECS); | |
253531bb YL |
123 | } |
124 | ||
fee8cf21 | 125 | #if !CONFIG_IS_ENABLED(SYSRESET) |
35b65dd8 | 126 | void reset_cpu(void) |
253531bb YL |
127 | { |
128 | struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR; | |
a79f2007 YL |
129 | u32 cmd32 = 0; |
130 | ||
131 | if (readl(&wdog->cs) & WDGCS_CMD32EN) { | |
132 | writel(UNLOCK_WORD, &wdog->cnt); | |
133 | cmd32 = WDGCS_CMD32EN; | |
134 | } else { | |
135 | dmb(); | |
136 | __raw_writel(UNLOCK_WORD0, &wdog->cnt); | |
137 | __raw_writel(UNLOCK_WORD1, &wdog->cnt); | |
138 | dmb(); | |
139 | } | |
253531bb | 140 | |
edf95bde BL |
141 | /* Wait WDOG Unlock */ |
142 | while (!(readl(&wdog->cs) & WDGCS_ULK)) | |
143 | ; | |
144 | ||
ef0ad9b0 | 145 | hw_watchdog_set_timeout(5); /* 5ms timeout for general; 40ms timeout for imx93 */ |
253531bb YL |
146 | writel(0, &wdog->win); |
147 | ||
edf95bde | 148 | /* enable counter running */ |
ef0ad9b0 | 149 | if (IS_ENABLED(CONFIG_ARCH_IMX9)) |
a7fd6335 AG |
150 | writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8) | WDOG_CS_PRES | |
151 | WDGCS_INT), &wdog->cs); | |
ef0ad9b0 AG |
152 | else |
153 | writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs); | |
edf95bde BL |
154 | |
155 | /* Wait WDOG reconfiguration */ | |
156 | while (!(readl(&wdog->cs) & WDGCS_RCS)) | |
157 | ; | |
253531bb YL |
158 | |
159 | hw_watchdog_reset(); | |
160 | ||
161 | while (1); | |
162 | } | |
fee8cf21 | 163 | #endif |
5e112c7c AG |
164 | |
165 | static int ulp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) | |
166 | { | |
167 | struct ulp_wdt_priv *priv = dev_get_priv(dev); | |
168 | u64 timeout = 0; | |
169 | ||
170 | timeout = (timeout_ms * priv->clk_rate) / 1000; | |
171 | if (timeout > U16_MAX) | |
172 | return -EINVAL; | |
173 | ||
174 | ulp_watchdog_init(priv->wdog, (u16)timeout); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static int ulp_wdt_reset(struct udevice *dev) | |
180 | { | |
181 | struct ulp_wdt_priv *priv = dev_get_priv(dev); | |
182 | ||
183 | ulp_watchdog_reset(priv->wdog); | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | static int ulp_wdt_probe(struct udevice *dev) | |
189 | { | |
190 | struct ulp_wdt_priv *priv = dev_get_priv(dev); | |
191 | ||
192 | priv->wdog = dev_read_addr_ptr(dev); | |
193 | if (!priv->wdog) | |
194 | return -EINVAL; | |
195 | ||
196 | priv->clk_rate = (u32)dev_get_driver_data(dev); | |
197 | if (!priv->clk_rate) | |
198 | return -EINVAL; | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | static const struct wdt_ops ulp_wdt_ops = { | |
204 | .start = ulp_wdt_start, | |
205 | .reset = ulp_wdt_reset, | |
206 | }; | |
207 | ||
208 | static const struct udevice_id ulp_wdt_ids[] = { | |
209 | { .compatible = "fsl,imx7ulp-wdt", .data = CLK_RATE_1KHZ }, | |
210 | { .compatible = "fsl,imx8ulp-wdt", .data = CLK_RATE_1KHZ }, | |
211 | { .compatible = "fsl,imx93-wdt", .data = CLK_RATE_32KHZ }, | |
212 | {} | |
213 | }; | |
214 | ||
215 | U_BOOT_DRIVER(ulp_wdt) = { | |
216 | .name = "ulp_wdt", | |
217 | .id = UCLASS_WDT, | |
218 | .of_match = ulp_wdt_ids, | |
219 | .priv_auto = sizeof(struct ulp_wdt_priv), | |
220 | .probe = ulp_wdt_probe, | |
221 | .ops = &ulp_wdt_ops, | |
222 | }; |