summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/rtc.c
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2019-01-31 11:05:13 -0500
committerSolomon Peachy <pizza@shaftnet.org>2020-04-11 18:02:26 +0200
commit6984a7ce1535422891ce4de57070cc55a486d352 (patch)
tree1f585e50cac0dd3ef4b713c2156d6c501b82f7bf /firmware/target/hosted/rtc.c
parent02d347bc6f6e37ead72986436a6fffdeafeec484 (diff)
downloadrockbox-6984a7ce1535422891ce4de57070cc55a486d352.tar.gz
rockbox-6984a7ce1535422891ce4de57070cc55a486d352.zip
RTC: Add support RTC alarms on hosted targets
Only AGPTeck Rocker is enabled for now, and it doesn't work properly: * No generic way to determine wakeup reason under Linux * No generic way to be asynchronously notified if the alarm is triggered when we're already awake * Shutting down may clobber RTC wakeup (driver/etc dependent) And finally: * AGPTek kernel's RTC driver has some 24h clock and some timezone-related issues. So, the infrastructure is arguably useful, but the only applicable hardware I have is pathologically brain-dead. Change-Id: Iac6a26a9b6e4efec5d0b3030b87f456eb23fc01d
Diffstat (limited to 'firmware/target/hosted/rtc.c')
-rw-r--r--firmware/target/hosted/rtc.c154
1 files changed, 146 insertions, 8 deletions
diff --git a/firmware/target/hosted/rtc.c b/firmware/target/hosted/rtc.c
index 488531c77c..ced298a5c8 100644
--- a/firmware/target/hosted/rtc.c
+++ b/firmware/target/hosted/rtc.c
@@ -27,9 +27,14 @@
27#include <linux/rtc.h> 27#include <linux/rtc.h>
28#include <fcntl.h> 28#include <fcntl.h>
29#include <unistd.h> 29#include <unistd.h>
30#include <stdbool.h>
30#endif 31#endif
32
33#include "config.h"
34
31void rtc_init(void) 35void rtc_init(void)
32{ 36{
37 tzset();
33} 38}
34 39
35int rtc_read_datetime(struct tm *tm) 40int rtc_read_datetime(struct tm *tm)
@@ -46,8 +51,6 @@ int rtc_write_datetime(const struct tm *tm)
46 struct timeval tv; 51 struct timeval tv;
47 struct tm *tm_time; 52 struct tm *tm_time;
48 53
49 int rtc = open("/dev/rtc0", O_WRONLY);
50
51 tv.tv_sec = mktime((struct tm *)tm); 54 tv.tv_sec = mktime((struct tm *)tm);
52 tv.tv_usec = 0; 55 tv.tv_usec = 0;
53 56
@@ -58,12 +61,147 @@ int rtc_write_datetime(const struct tm *tm)
58 time_t now = time(NULL); 61 time_t now = time(NULL);
59 tm_time = gmtime(&now); 62 tm_time = gmtime(&now);
60 63
61 ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); 64 /* Try to write the HW RTC, if present. */
62 close(rtc); 65 int rtc = open("/dev/rtc0", O_WRONLY);
63 66 if (rtc > 0) {
64 return 0; 67 ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time);
68 close(rtc);
69 }
65#else 70#else
66 (void)tm; 71 (void)(*tm);
67 return -1;
68#endif 72#endif
73 return 0;
74}
75
76#if defined(HAVE_RTC_ALARM) && !defined(SIMULATOR)
77void rtc_set_alarm(int h, int m)
78{
79 struct rtc_time tm;
80 long sec;
81
82 int rtc = open("/dev/rtc0", O_WRONLY);
83 if (rtc < 0)
84 return;
85
86 /* Get RTC time */
87 ioctl(rtc, RTC_RD_TIME, &tm);
88
89 /* Convert to seconds into the GMT day. Can be negative! */
90 sec = h * 3600 + m * 60 + timezone;
91 h = sec / 3600;
92 sec -= h * 3600;
93 m = sec / 60;
94
95 /* Handle negative or positive wraps */
96 while (m < 0) {
97 m += 60;
98 h--;
99 }
100 while (m > 59) {
101 m -= 60;
102 h++;
103 }
104 while (h < 0) {
105 h += 24;
106 tm.tm_mday--;
107 }
108 while (h > 23) {
109 h -= 24;
110 tm.tm_mday++;
111 }
112
113 /* Update the struct */
114 tm.tm_sec = 0;
115 tm.tm_hour = h;
116 tm.tm_min = m;
117
118 ioctl(rtc, RTC_ALM_SET, &tm);
119 close(rtc);
120}
121
122void rtc_get_alarm(int *h, int *m)
123{
124 struct rtc_time tm;
125 long sec;
126
127 int rtc = open("/dev/rtc0", O_WRONLY);
128 if (rtc < 0)
129 return;
130
131 ioctl(rtc, RTC_ALM_READ, &tm);
132 close(rtc);
133
134 /* Convert RTC from UTC to local time zone.. */
135 sec = (tm.tm_min * 60) + (tm.tm_hour * 3600) - timezone;
136
137 /* Handle wrapping and negative offsets */
138 *h = (sec / 3600);
139 sec -= *h * 3600;
140 *m = sec / 60;
141
142 while (*m < 0) {
143 *m = *m + 60;
144 *h = *h - 1;
145 }
146 while (*m > 59) {
147 *m = *m - 60;
148 *h = *h + 1;
149 }
150 while (*h < 0) {
151 *h = *h + 24;
152 }
153 while (*h > 23) {
154 *h = *h - 24;
155 }
156}
157
158void rtc_enable_alarm(bool enable)
159{
160 int rtc = open("/dev/rtc0", O_WRONLY);
161 if (rtc < 0)
162 return;
163
164 ioctl(rtc, enable ? RTC_AIE_ON : RTC_AIE_OFF, NULL);
165 close(rtc);
166
167 /* XXX Note that this may or may not work; Linux may need to be suspended
168 or shut down in a special way to keep the RTC alarm active */
69} 169}
170
171/* Returns true if alarm was the reason we started up */
172bool rtc_check_alarm_started(bool release_alarm)
173{
174 int rtc = open("/dev/rtc0", O_WRONLY);
175 if (rtc < 0)
176 return false;
177
178 /* XXX There is no generic way of determining wakeup reason. Will
179 likely need a target-specific hook. */
180
181 /* Disable alarm if requested */
182 if (release_alarm)
183 ioctl(rtc, RTC_AIE_OFF, NULL);
184
185 close(rtc);
186 return false;
187}
188
189/* See if we received an alarm. */
190bool rtc_check_alarm_flag(void)
191{
192 struct rtc_wkalrm alrm;
193
194 int rtc = open("/dev/rtc0", O_WRONLY);
195 if (rtc < 0)
196 return false;
197
198 alrm.pending = 0;
199 /* XXX Documented as "mostly useless on Linux" except with EFI RTCs
200 Will likely need a target-specific hook. */
201 ioctl(rtc, RTC_WKALM_RD, &alrm);
202 close(rtc);
203
204 return alrm.pending;
205}
206
207#endif /* HAVE_RTC_ALARM */