blob: a29b42d607eca6f54e49a60a781528f479234da6 [file] [log] [blame]
Randolph4350e992024-01-24 14:21:32 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2023 Andes Technology Corporation.
4 *
5 */
6
7#include <asm/io.h>
8#include <dm.h>
9#include <hang.h>
10#include <linux/bitops.h>
11#include <wdt.h>
12
13#define NODE_NOT_FOUND 0xFFFFFFFF
14
15#define WDT_WP_MAGIC 0x5aa5
16#define WDT_RESTART_MAGIC 0xcafe
17
18/* Control Register */
19#define REG_WDT_ID 0x00
20#define REG_WDT_CFG 0x10
21#define REG_WDT_RS 0x14
22#define REG_WDT_WE 0x18
23#define REG_WDT_STA 0x1C
24
25#define RST_TIME_OFF 8
26#define RST_TIME_MSK (0x7 << RST_TIME_OFF)
27#define RST_CLK_128 (0 << RST_TIME_OFF)
28#define RST_CLK_256 (1 << RST_TIME_OFF)
29#define RST_CLK_512 (2 << RST_TIME_OFF)
30#define RST_CLK_1024 (3 << RST_TIME_OFF)
31#define INT_TIME_OFF 4
32#define INT_TIME_MSK (0xf << INT_TIME_OFF)
33#define INT_CLK_2_6 (0 << INT_TIME_OFF) /* clk period*2^6 */
34#define INT_CLK_2_8 (1 << INT_TIME_OFF) /* clk period*2^8 */
35#define INT_CLK_2_10 (2 << INT_TIME_OFF) /* clk period*2^10 */
36#define INT_CLK_2_11 (3 << INT_TIME_OFF) /* clk period*2^11 */
37#define INT_CLK_2_12 (4 << INT_TIME_OFF) /* clk period*2^12 */
38#define INT_CLK_2_13 (5 << INT_TIME_OFF) /* clk period*2^13 */
39#define INT_CLK_2_14 (6 << INT_TIME_OFF) /* clk period*2^14 */
40#define INT_CLK_2_15 (7 << INT_TIME_OFF) /* clk period*2^15 */
41#define INT_CLK_2_17 (8 << INT_TIME_OFF) /* clk period*2^17 */
42#define INT_CLK_2_19 (9 << INT_TIME_OFF) /* clk period*2^19 */
43#define INT_CLK_2_21 (10 << INT_TIME_OFF) /* clk period*2^21 */
44#define INT_CLK_2_23 (11 << INT_TIME_OFF) /* clk period*2^23 */
45#define INT_CLK_2_25 (12 << INT_TIME_OFF) /* clk period*2^25 */
46#define INT_CLK_2_27 (13 << INT_TIME_OFF) /* clk period*2^27 */
47#define INT_CLK_2_29 (14 << INT_TIME_OFF) /* clk period*2^29 */
48#define INT_CLK_2_31 (15 << INT_TIME_OFF) /* clk period*2^31 */
49#define INT_CLK_MIN 0x0
50#define INT_CLK_MAX_16B 0x7
51#define INT_CLK_MAX_32B 0xF
52#define RST_EN BIT(3)
53#define INT_EN BIT(2)
54#define CLK_PCLK BIT(1)
55#define WDT_EN BIT(0)
56#define INT_EXPIRED BIT(0)
57
58#define INT_TIME_ARRAY 16
59#define RST_TIME_ARRAY 8
60
61struct wdt_priv {
62 void __iomem *base;
63 u32 wdt_clk_src;
64 u32 clk_freq;
65 u8 max_clk;
66};
67
68static inline u8 atcwdt_get_2_power_of_n(u8 index, u8 type)
69{
70 const u8 div_int[INT_TIME_ARRAY] = {6, 8, 10, 11, 12, 13, 14, 15,
71 17, 19, 21, 23, 25, 27, 29, 31};
72 const u8 div_rst[RST_TIME_ARRAY] = {7, 8, 9, 10, 11, 12, 13, 14};
73 const u8 *pdiv;
74
75 if (type == RST_TIME_ARRAY)
76 pdiv = div_rst;
77 else
78 pdiv = div_int;
79
80 if (index >= type)
81 index = type - 1;
82
83 return pdiv[index];
84}
85
86static u8 atcwdt_search_msb(u64 freq_ms, u8 type)
87{
88 u64 result;
89 u64 freq_sec;
90 u8 index;
91
92 freq_sec = freq_ms / 1000;
93 for (index = 0; index < type; index++) {
94 result = freq_sec >> atcwdt_get_2_power_of_n(index, type);
95
96 if (result <= 1)
97 break;
98 }
99
100 return index;
101}
102
103static int atcwdt_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
104{
105 struct wdt_priv *priv = dev_get_priv(dev);
106 u64 rst_max_count;
107 u32 rst_max_time_ms;
108 u64 rst_time_ms;
109 u64 int_time_ms;
110 u8 rst_time;
111 u8 int_time;
112
113 rst_max_count = 1 << atcwdt_get_2_power_of_n(RST_TIME_ARRAY, RST_TIME_ARRAY);
114 rst_max_time_ms = (rst_max_count * 1000) / priv->clk_freq;
115
116 if (timeout > rst_max_time_ms) {
117 int_time_ms = timeout - rst_max_time_ms;
118 rst_time_ms = rst_max_time_ms;
119 } else {
120 int_time_ms = 0;
121 rst_time_ms = timeout;
122 }
123
124 rst_time = atcwdt_search_msb(rst_time_ms * priv->clk_freq, RST_TIME_ARRAY);
125
126 if (int_time_ms) {
127 int_time = atcwdt_search_msb(int_time_ms * priv->clk_freq, INT_TIME_ARRAY);
128 if (int_time > priv->max_clk)
129 int_time = priv->max_clk;
130 } else {
131 int_time = 0;
132 }
133
134 writel(WDT_WP_MAGIC, priv->base + REG_WDT_WE);
135 writel(((rst_time << RST_TIME_OFF) & RST_TIME_MSK) | ((int_time << INT_TIME_OFF) &
136 INT_TIME_MSK) | INT_EN | RST_EN | priv->wdt_clk_src | WDT_EN,
137 priv->base + REG_WDT_CFG);
138
139 return 0;
140}
141
142static int atcwdt_wdt_stop(struct udevice *dev)
143{
144 struct wdt_priv *priv = dev_get_priv(dev);
145
146 writel(WDT_WP_MAGIC, priv->base + REG_WDT_WE);
147 writel(0, priv->base + REG_WDT_CFG);
148
149 return 0;
150}
151
152static int atcwdt_wdt_restart(struct udevice *dev)
153{
154 struct wdt_priv *priv = dev_get_priv(dev);
155
156 writel(WDT_WP_MAGIC, priv->base + REG_WDT_WE);
157 writel(WDT_RESTART_MAGIC, priv->base + REG_WDT_RS);
158 setbits_le32(priv->base + REG_WDT_STA, INT_EXPIRED);
159
160 return 0;
161}
162
163static int atcwdt_wdt_expire_now(struct udevice *dev, ulong flags)
164{
165 atcwdt_wdt_start(dev, 0, 0);
166 hang();
167
168 return 0;
169}
170
171static int atcwdt_wdt_probe(struct udevice *dev)
172{
173 struct wdt_priv *priv = dev_get_priv(dev);
174 int timer_16bit;
175
176 priv->base = dev_remap_addr_index(dev, 0);
177 if (!priv->base)
178 return -EFAULT;
179
180 priv->wdt_clk_src = dev_read_u32_default(dev, "clock-source", NODE_NOT_FOUND);
181 if (priv->wdt_clk_src == NODE_NOT_FOUND || priv->wdt_clk_src > 1)
182 priv->wdt_clk_src = CLK_PCLK;
183
184 timer_16bit = dev_read_u32_default(dev, "16bit_timer", NODE_NOT_FOUND);
185 if (timer_16bit == 1 || timer_16bit == NODE_NOT_FOUND)
186 priv->max_clk = INT_CLK_MAX_16B;
187 else
188 priv->max_clk = INT_CLK_MAX_32B;
189
190 priv->clk_freq = dev_read_u32_default(dev, "clock-frequency", NODE_NOT_FOUND);
191 if (priv->clk_freq == NODE_NOT_FOUND) {
192 printf("atcwdt200: Please provide a valid \"clock-frequency\" in DTB\n");
193 return -EINVAL;
194 }
195
196 atcwdt_wdt_stop(dev);
197
198 return 0;
199}
200
201static const struct wdt_ops atcwdt_wdt_ops = {
202 .start = atcwdt_wdt_start,
203 .reset = atcwdt_wdt_restart,
204 .stop = atcwdt_wdt_stop,
205 .expire_now = atcwdt_wdt_expire_now,
206};
207
208static const struct udevice_id atcwdt_wdt_ids[] = {
209 {.compatible = "andestech,atcwdt200"},
210 {}
211};
212
213U_BOOT_DRIVER(atcwdt) = {
214 .name = "atcwdt200",
215 .id = UCLASS_WDT,
216 .probe = atcwdt_wdt_probe,
217 .of_match = atcwdt_wdt_ids,
218 .ops = &atcwdt_wdt_ops,
219 .priv_auto = sizeof(struct wdt_priv),
220};