]>
Commit | Line | Data |
---|---|---|
87e9963d HS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright 2020, Heinrich Schuchardt <[email protected]> | |
4 | * | |
5 | * This driver emulates a real time clock based on timer ticks. | |
6 | */ | |
7 | ||
8 | #include <common.h> | |
9 | #include <div64.h> | |
10 | #include <dm.h> | |
fb71c3f4 | 11 | #include <env.h> |
87e9963d HS |
12 | #include <generated/timestamp_autogenerated.h> |
13 | #include <rtc.h> | |
14 | ||
15 | /** | |
16 | * struct emul_rtc - private data for emulated RTC driver | |
17 | */ | |
18 | struct emul_rtc { | |
19 | /** | |
20 | * @offset_us: microseconds from 1970-01-01 to timer_get_us() base | |
21 | */ | |
22 | u64 offset_us; | |
23 | /** | |
24 | * @isdst: daylight saving time | |
25 | */ | |
26 | int isdst; | |
27 | }; | |
28 | ||
29 | static int emul_rtc_get(struct udevice *dev, struct rtc_time *time) | |
30 | { | |
31 | struct emul_rtc *priv = dev_get_priv(dev); | |
32 | u64 now; | |
33 | ||
87e9963d HS |
34 | now = timer_get_us() + priv->offset_us; |
35 | do_div(now, 1000000); | |
36 | rtc_to_tm(now, time); | |
37 | time->tm_isdst = priv->isdst; | |
38 | ||
39 | return 0; | |
40 | } | |
41 | ||
42 | static int emul_rtc_set(struct udevice *dev, const struct rtc_time *time) | |
43 | { | |
44 | struct emul_rtc *priv = dev_get_priv(dev); | |
45 | ||
46 | if (time->tm_year < 1970) | |
47 | return -EINVAL; | |
48 | ||
49 | priv->offset_us = rtc_mktime(time) * 1000000ULL - timer_get_us(); | |
50 | ||
51 | if (time->tm_isdst > 0) | |
52 | priv->isdst = 1; | |
53 | else if (time->tm_isdst < 0) | |
54 | priv->isdst = -1; | |
55 | else | |
56 | priv->isdst = 0; | |
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
0ca4b558 HS |
61 | int emul_rtc_probe(struct udevice *dev) |
62 | { | |
63 | struct emul_rtc *priv = dev_get_priv(dev); | |
fb71c3f4 HS |
64 | const char *epoch_str; |
65 | u64 epoch; | |
66 | ||
67 | epoch_str = env_get("rtc_emul_epoch"); | |
68 | ||
69 | if (epoch_str) { | |
70 | epoch = simple_strtoull(epoch_str, NULL, 10); | |
71 | } else { | |
72 | /* Use the build date as initial time */ | |
73 | epoch = U_BOOT_EPOCH; | |
74 | } | |
75 | priv->offset_us = epoch * 1000000ULL - timer_get_us(); | |
0ca4b558 HS |
76 | priv->isdst = -1; |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
87e9963d HS |
81 | static const struct rtc_ops emul_rtc_ops = { |
82 | .get = emul_rtc_get, | |
83 | .set = emul_rtc_set, | |
84 | }; | |
85 | ||
86 | U_BOOT_DRIVER(rtc_emul) = { | |
87 | .name = "rtc_emul", | |
88 | .id = UCLASS_RTC, | |
89 | .ops = &emul_rtc_ops, | |
0ca4b558 | 90 | .probe = emul_rtc_probe, |
41575d8e | 91 | .priv_auto = sizeof(struct emul_rtc), |
87e9963d HS |
92 | }; |
93 | ||
94 | U_BOOT_DEVICE(rtc_emul) = { | |
95 | .name = "rtc_emul", | |
96 | }; |