1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <common.h>
19#include <dm.h>
20#include <rtc.h>
21#include <bcd.h>
22#include <asm/gpio.h>
23#include <linux/delay.h>
24
25struct ht1380_priv {
26 struct gpio_desc rst_desc;
27 struct gpio_desc clk_desc;
28 struct gpio_desc dat_desc;
29};
30
31enum registers {
32 SEC,
33 MIN,
34 HOUR,
35 MDAY,
36 MONTH,
37 WDAY,
38 YEAR,
39 WP,
40 N_REGS
41};
42
43enum hour_mode {
44 AMPM_MODE = 0x80,
45 PM_NOW = 0x20,
46};
47
48static const int BURST = 0xbe;
49static const int READ = 1;
50
51static void ht1380_half_period_delay(void)
52{
53
54
55
56
57 udelay(1);
58}
59
60static int ht1380_send_byte(struct ht1380_priv *priv, int byte)
61{
62 int ret;
63
64 for (int bit = 0; bit < 8; bit++) {
65 ret = dm_gpio_set_value(&priv->dat_desc, byte >> bit & 1);
66 if (ret)
67 break;
68 ht1380_half_period_delay();
69
70 ret = dm_gpio_set_value(&priv->clk_desc, 1);
71 if (ret)
72 break;
73 ht1380_half_period_delay();
74
75 ret = dm_gpio_set_value(&priv->clk_desc, 0);
76 if (ret)
77 break;
78 }
79
80 return ret;
81}
82
83
84
85
86static int ht1380_reset_off(struct ht1380_priv *priv)
87{
88 const unsigned int T_CC = 4;
89 int ret;
90
91
92
93
94
95 ret = dm_gpio_set_value(&priv->rst_desc, 0);
96 udelay(T_CC);
97
98 return ret;
99}
100
101
102
103
104static int ht1380_reset_on(struct ht1380_priv *priv)
105{
106 const unsigned int T_CWH = 4;
107 int ret;
108
109
110
111
112
113 ret = dm_gpio_set_value(&priv->rst_desc, 1);
114 udelay(T_CWH);
115
116 return ret;
117}
118
119static int ht1380_rtc_get(struct udevice *dev, struct rtc_time *tm)
120{
121 struct ht1380_priv *priv = dev_get_priv(dev);
122 int ret, i, bit, reg[N_REGS];
123
124 ret = dm_gpio_set_value(&priv->clk_desc, 0);
125 if (ret)
126 return ret;
127
128 ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT);
129 if (ret)
130 return ret;
131
132 ret = ht1380_reset_off(priv);
133 if (ret)
134 goto exit;
135
136 ret = ht1380_send_byte(priv, BURST + READ);
137 if (ret)
138 goto exit;
139
140 ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_IN);
141 if (ret)
142 goto exit;
143
144 for (i = 0; i < N_REGS; i++) {
145 reg[i] = 0;
146
147 for (bit = 0; bit < 8; bit++) {
148 ht1380_half_period_delay();
149
150 ret = dm_gpio_set_value(&priv->clk_desc, 1);
151 if (ret)
152 goto exit;
153 ht1380_half_period_delay();
154
155 reg[i] |= dm_gpio_get_value(&priv->dat_desc) << bit;
156 ret = dm_gpio_set_value(&priv->clk_desc, 0);
157 if (ret)
158 goto exit;
159 }
160 }
161
162 ret = -EINVAL;
163
164
165 if (reg[MIN] & 0x80 || reg[HOUR] & 0x40 || reg[MDAY] & 0xc0 ||
166 reg[MONTH] & 0xe0 || reg[WDAY] & 0xf8 || reg[WP] & 0x7f)
167 goto exit;
168
169
170 if (!reg[MDAY] || !reg[MONTH] || !reg[WDAY])
171 goto exit;
172
173 tm->tm_sec = bcd2bin(reg[SEC]);
174 tm->tm_min = bcd2bin(reg[MIN]);
175 if (reg[HOUR] & AMPM_MODE) {
176
177 tm->tm_hour = bcd2bin(reg[HOUR] & 0x1f) % 12;
178 if (reg[HOUR] & PM_NOW) {
179
180 tm->tm_hour += 12;
181 }
182 } else {
183
184 tm->tm_hour = bcd2bin(reg[HOUR]);
185 }
186 tm->tm_mday = bcd2bin(reg[MDAY]);
187 tm->tm_mon = bcd2bin(reg[MONTH]);
188 tm->tm_year = 2000 + bcd2bin(reg[YEAR]);
189 tm->tm_wday = bcd2bin(reg[WDAY]) - 1;
190 tm->tm_yday = 0;
191 tm->tm_isdst = 0;
192
193 ret = 0;
194
195exit:
196 ht1380_reset_on(priv);
197
198 return ret;
199}
200
201static int ht1380_write_protection_off(struct ht1380_priv *priv)
202{
203 int ret;
204 const int PROTECT = 0x8e;
205
206 ret = ht1380_reset_off(priv);
207 if (ret)
208 return ret;
209
210 ret = ht1380_send_byte(priv, PROTECT);
211 if (ret)
212 return ret;
213 ret = ht1380_send_byte(priv, 0);
214 if (ret)
215 return ret;
216
217 return ht1380_reset_on(priv);
218}
219
220static int ht1380_rtc_set(struct udevice *dev, const struct rtc_time *tm)
221{
222 struct ht1380_priv *priv = dev_get_priv(dev);
223 int ret, i, reg[N_REGS];
224
225 ret = dm_gpio_set_value(&priv->clk_desc, 0);
226 if (ret)
227 return ret;
228
229 ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT);
230 if (ret)
231 goto exit;
232
233 ret = ht1380_write_protection_off(priv);
234 if (ret)
235 goto exit;
236
237 reg[SEC] = bin2bcd(tm->tm_sec);
238 reg[MIN] = bin2bcd(tm->tm_min);
239 reg[HOUR] = bin2bcd(tm->tm_hour);
240 reg[MDAY] = bin2bcd(tm->tm_mday);
241 reg[MONTH] = bin2bcd(tm->tm_mon);
242 reg[WDAY] = bin2bcd(tm->tm_wday) + 1;
243 reg[YEAR] = bin2bcd(tm->tm_year - 2000);
244 reg[WP] = 0x80;
245
246 ret = ht1380_reset_off(priv);
247 if (ret)
248 goto exit;
249
250 ret = ht1380_send_byte(priv, BURST);
251 for (i = 0; i < N_REGS && ret; i++)
252 ret = ht1380_send_byte(priv, reg[i]);
253
254exit:
255 ht1380_reset_on(priv);
256
257 return ret;
258}
259
260static int ht1380_probe(struct udevice *dev)
261{
262 int ret;
263 struct ht1380_priv *priv;
264
265 priv = dev_get_priv(dev);
266 if (!priv)
267 return -EINVAL;
268
269 ret = gpio_request_by_name(dev, "rst-gpios", 0,
270 &priv->rst_desc, GPIOD_IS_OUT);
271 if (ret)
272 goto fail_rst;
273
274 ret = gpio_request_by_name(dev, "clk-gpios", 0,
275 &priv->clk_desc, GPIOD_IS_OUT);
276 if (ret)
277 goto fail_clk;
278
279 ret = gpio_request_by_name(dev, "dat-gpios", 0,
280 &priv->dat_desc, 0);
281 if (ret)
282 goto fail_dat;
283
284 ret = ht1380_reset_on(priv);
285 if (ret)
286 goto fail;
287
288 return 0;
289
290fail:
291 dm_gpio_free(dev, &priv->dat_desc);
292fail_dat:
293 dm_gpio_free(dev, &priv->clk_desc);
294fail_clk:
295 dm_gpio_free(dev, &priv->rst_desc);
296fail_rst:
297 return ret;
298}
299
300static int ht1380_remove(struct udevice *dev)
301{
302 struct ht1380_priv *priv = dev_get_priv(dev);
303
304 dm_gpio_free(dev, &priv->rst_desc);
305 dm_gpio_free(dev, &priv->clk_desc);
306 dm_gpio_free(dev, &priv->dat_desc);
307
308 return 0;
309}
310
311static const struct rtc_ops ht1380_rtc_ops = {
312 .get = ht1380_rtc_get,
313 .set = ht1380_rtc_set,
314};
315
316static const struct udevice_id ht1380_rtc_ids[] = {
317 { .compatible = "holtek,ht1380" },
318 { }
319};
320
321U_BOOT_DRIVER(rtc_ht1380) = {
322 .name = "rtc-ht1380",
323 .id = UCLASS_RTC,
324 .probe = ht1380_probe,
325 .remove = ht1380_remove,
326 .of_match = ht1380_rtc_ids,
327 .ops = &ht1380_rtc_ops,
328 .priv_auto = sizeof(struct ht1380_priv),
329};
330