diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-12-24 17:06:35 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-12-24 17:06:35 +0000 |
commit | c332bba905cc4fcf030c340f5a46fb167eb6034f (patch) | |
tree | 03bbafcc4e701588c139c1f480a4560226b43bca /firmware/drivers/rtc | |
parent | 78a11cf648442816c346cac9a756b782c918067b (diff) | |
download | rockbox-c332bba905cc4fcf030c340f5a46fb167eb6034f.tar.gz rockbox-c332bba905cc4fcf030c340f5a46fb167eb6034f.zip |
mc13783 RTC: Handle years in a better way for the use of struct tm. Make the code less general because all years evenly divisible by 4 in the day range are leap years.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28891 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/rtc')
-rw-r--r-- | firmware/drivers/rtc/rtc_mc13783.c | 230 |
1 files changed, 121 insertions, 109 deletions
diff --git a/firmware/drivers/rtc/rtc_mc13783.c b/firmware/drivers/rtc/rtc_mc13783.c index d98ae9ba06..a1f78f738d 100644 --- a/firmware/drivers/rtc/rtc_mc13783.c +++ b/firmware/drivers/rtc/rtc_mc13783.c | |||
@@ -26,62 +26,74 @@ | |||
26 | /* NOTE: Defined the base to be original firmware compatible if needed - | 26 | /* NOTE: Defined the base to be original firmware compatible if needed - |
27 | * ie. the day and year as it would interpret a DAY register value of zero. */ | 27 | * ie. the day and year as it would interpret a DAY register value of zero. */ |
28 | 28 | ||
29 | /* Days passed since midnight 01 Jan, 1601 to midnight on the base date. */ | 29 | /* None of this code concerns itself with (year mod 100) = 0 leap year |
30 | * exceptions because all (year mod 4) = 0 years in the relevant range are | ||
31 | * leap years. A base year of 1901 to an end date of 28 Feb 2100 are ok. */ | ||
32 | |||
30 | #ifdef TOSHIBA_GIGABEAT_S | 33 | #ifdef TOSHIBA_GIGABEAT_S |
31 | /* Gigabeat S seems to be 1 day behind the ususual - this will | 34 | /* Gigabeat S seems to be 1 day behind the ususual - this will |
32 | * make the RTC match file dates created by retailos. */ | 35 | * make the RTC match file dates created by retailos. */ |
33 | #define RTC_BASE_DAY_COUNT 138425 | 36 | #define RTC_BASE_WDAY 1 /* Monday */ |
34 | #define RTC_BASE_MONTH 12 | 37 | #define RTC_BASE_YDAY 364 /* 31 Dec */ |
35 | #define RTC_BASE_DAY 31 | ||
36 | #define RTC_BASE_YEAR 1979 | 38 | #define RTC_BASE_YEAR 1979 |
37 | #elif 1 | 39 | #elif 1 |
38 | #define RTC_BASE_DAY_COUNT 138426 | 40 | #define RTC_BASE_WDAY 2 /* Tuesday */ |
39 | #define RTC_BASE_MONTH 1 | 41 | #define RTC_BASE_YDAY 0 /* 01 Jan */ |
40 | #define RTC_BASE_DAY 1 | ||
41 | #define RTC_BASE_YEAR 1980 | 42 | #define RTC_BASE_YEAR 1980 |
42 | #else | 43 | #else |
43 | #define RTC_BASE_DAY_COUNT 134774 | 44 | #define RTC_BASE_WDAY 4 /* Thursday */ |
44 | #define RTC_BASE_MONTH 1 | 45 | #define RTC_BASE_YDAY 0 /* 01 Jan */ |
45 | #define RTC_BASE_DAY 1 | ||
46 | #define RTC_BASE_YEAR 1970 | 46 | #define RTC_BASE_YEAR 1970 |
47 | #endif | 47 | #endif |
48 | 48 | ||
49 | /* Reference year for leap calculation - year that is on or before the base | ||
50 | * year and immediately follows a leap year */ | ||
51 | #define RTC_REF_YEAR_OFFS ((RTC_BASE_YEAR - 1) & 3) | ||
52 | #define RTC_REF_YEAR (RTC_BASE_YEAR - RTC_REF_YEAR_OFFS) | ||
53 | |||
49 | enum rtc_registers_indexes | 54 | enum rtc_registers_indexes |
50 | { | 55 | { |
51 | RTC_REG_TIME = 0, | 56 | RTC_REG_TIME = 0, |
52 | RTC_REG_DAY, | 57 | RTC_REG_DAY, |
53 | RTC_REG_TIME2, | 58 | RTC_REG_TIME2, |
54 | RTC_NUM_REGS, | 59 | RTC_NUM_REGS_RD = 3, |
60 | RTC_NUM_REGS_WR = 2, | ||
55 | }; | 61 | }; |
56 | 62 | ||
57 | /* was it an alarm that triggered power on ? */ | 63 | static const unsigned char rtc_registers[RTC_NUM_REGS_RD] = |
58 | static bool alarm_start = false; | ||
59 | |||
60 | static const unsigned char rtc_registers[RTC_NUM_REGS] = | ||
61 | { | 64 | { |
62 | [RTC_REG_TIME] = MC13783_RTC_TIME, | 65 | [RTC_REG_TIME] = MC13783_RTC_TIME, |
63 | [RTC_REG_DAY] = MC13783_RTC_DAY, | 66 | [RTC_REG_DAY] = MC13783_RTC_DAY, |
64 | [RTC_REG_TIME2] = MC13783_RTC_TIME, | 67 | [RTC_REG_TIME2] = MC13783_RTC_TIME, |
65 | }; | 68 | }; |
66 | 69 | ||
67 | static const unsigned char month_table[2][12] = | 70 | /* was it an alarm that triggered power on ? */ |
71 | static bool alarm_start = false; | ||
72 | |||
73 | static const unsigned short month_table[13] = | ||
68 | { | 74 | { |
69 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, | 75 | /* Since 1 Jan, how many days have passed this year? (non-leap) |
70 | { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, | 76 | +31 28 31 30 31 30 31 31 30 31 30 31 */ |
77 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 | ||
71 | }; | 78 | }; |
72 | 79 | ||
73 | /* Get number of leaps since the reference date of 1601/01/01 */ | 80 | static bool read_time_and_day(uint32_t regs[RTC_NUM_REGS_RD]) |
74 | static int get_leap_count(int d) | ||
75 | { | 81 | { |
76 | int lm = (d + 1) / 146097; | 82 | /* Read time, day, time - 2nd read of time should be the same or |
77 | int lc = (d + 1 - lm) / 36524; | 83 | * greater */ |
78 | int ly = (d + 1 - lm + lc) / 1461; | 84 | do |
79 | return ly - lc + lm; | 85 | { |
80 | } | 86 | if (mc13783_read_regs(rtc_registers, regs, |
87 | RTC_NUM_REGS_RD) < RTC_NUM_REGS_RD) | ||
88 | { | ||
89 | /* Couldn't read registers */ | ||
90 | return false; | ||
91 | } | ||
92 | } | ||
93 | /* If TOD counter turned over - reread */ | ||
94 | while (regs[RTC_REG_TIME2] < regs[RTC_REG_TIME]); | ||
81 | 95 | ||
82 | static int is_leap_year(int y) | 96 | return true; |
83 | { | ||
84 | return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0) ? 1 : 0; | ||
85 | } | 97 | } |
86 | 98 | ||
87 | /** Public APIs **/ | 99 | /** Public APIs **/ |
@@ -97,114 +109,110 @@ void rtc_init(void) | |||
97 | 109 | ||
98 | int rtc_read_datetime(struct tm *tm) | 110 | int rtc_read_datetime(struct tm *tm) |
99 | { | 111 | { |
100 | uint32_t regs[RTC_NUM_REGS]; | 112 | uint32_t regs[RTC_NUM_REGS_RD]; |
101 | int year, leap, month, day, hour, min; | 113 | int year, month, yday, x, n; |
102 | 114 | ||
103 | /* Read time, day, time - 2nd read of time should be the same or | 115 | if (!read_time_and_day(regs)) |
104 | * greater */ | 116 | return 0; |
105 | do | 117 | |
106 | { | 118 | /* TOD: 0 to 86399 */ |
107 | if (mc13783_read_regs(rtc_registers, regs, | 119 | x = regs[RTC_REG_TIME]; |
108 | RTC_NUM_REGS) < RTC_NUM_REGS) | ||
109 | { | ||
110 | /* Couldn't read registers */ | ||
111 | return 0; | ||
112 | } | ||
113 | } | ||
114 | /* If TOD counter turned over - reread */ | ||
115 | while (regs[RTC_REG_TIME2] < regs[RTC_REG_TIME]); | ||
116 | 120 | ||
117 | /* TOD: = 0 to 86399 */ | 121 | /* Hour */ |
118 | hour = regs[RTC_REG_TIME] / 3600; | 122 | n = x / 3600; |
119 | regs[RTC_REG_TIME] -= hour*3600; | 123 | tm->tm_hour = n; |
120 | tm->tm_hour = hour; | ||
121 | 124 | ||
122 | min = regs[RTC_REG_TIME] / 60; | 125 | /* Minute */ |
123 | regs[RTC_REG_TIME] -= min*60; | 126 | x -= n*3600; |
124 | tm->tm_min = min; | 127 | n = x / 60; |
128 | tm->tm_min = n; | ||
125 | 129 | ||
126 | tm->tm_sec = regs[RTC_REG_TIME]; | 130 | /* Second */ |
131 | x -= n*60; | ||
132 | tm->tm_sec = x; | ||
127 | 133 | ||
128 | /* DAY: 0 to 32767 */ | 134 | /* DAY: 0 to 32767 */ |
129 | day = regs[RTC_REG_DAY] + RTC_BASE_DAY_COUNT; | 135 | x = regs[RTC_REG_DAY]; |
130 | 136 | ||
131 | /* Weekday */ | 137 | /* Weekday */ |
132 | tm->tm_wday = (day + 1) % 7; /* 1601/01/01 = Monday */ | 138 | tm->tm_wday = (x + RTC_BASE_WDAY) % 7; |
133 | 139 | ||
134 | /* Get number of leaps for today */ | 140 | /* Year */ |
135 | leap = get_leap_count(day); | 141 | x += RTC_REF_YEAR_OFFS*365 + RTC_BASE_YDAY; |
136 | year = (day - leap) / 365; | ||
137 | 142 | ||
138 | /* Get number of leaps for yesterday */ | 143 | /* Lag year increment by subtracting leaps since the reference year |
139 | leap = get_leap_count(day - 1); | 144 | * on 31 Dec of each leap year, essentially removing them from the |
145 | * calculation */ | ||
146 | n = (x + 1) / 1461; | ||
147 | year = (x - n) / 365; | ||
140 | 148 | ||
141 | /* Get day number for year 0-364|365 */ | 149 | /* Year day */ |
142 | day = day - leap - year * 365; | 150 | yday = x - n - year*365; |
143 | 151 | ||
144 | year += 1601; | 152 | /* If (x + 1) mod 1461 == 0, then it is yday 365 of a leap year */ |
153 | if (n * 1461 - 1 == x) | ||
154 | yday++; | ||
145 | 155 | ||
146 | /* Get the current month */ | 156 | tm->tm_yday = x = yday; |
147 | leap = is_leap_year(year); | ||
148 | 157 | ||
149 | for (month = 0; month < 12; month++) | 158 | if (((year + RTC_REF_YEAR) & 3) == 0 && x >= month_table[2]) |
150 | { | 159 | { |
151 | int days = month_table[leap][month]; | 160 | if (x > month_table[2]) |
152 | 161 | yday--; /* 1 Mar or after; lag date by one day */ | |
153 | if (day < days) | ||
154 | break; | ||
155 | 162 | ||
156 | day -= days; | 163 | x--; /* 29 Feb or after, lag month by one day */ |
157 | } | 164 | } |
158 | 165 | ||
159 | tm->tm_mday = day + 1; /* 1 to 31 */ | 166 | /* Get the current month */ |
167 | month = x >> 5; /* yday / 32, close enough */ | ||
168 | |||
169 | if (month_table[month + 1] <= x) | ||
170 | month++; /* Round to next */ | ||
171 | |||
172 | tm->tm_mday = yday - month_table[month] + 1; /* 1 to 31 */ | ||
160 | tm->tm_mon = month; /* 0 to 11 */ | 173 | tm->tm_mon = month; /* 0 to 11 */ |
161 | tm->tm_year = year % 100 + 100; | 174 | |
175 | /* {BY to (BY+89 or 90)} - 1900 */ | ||
176 | tm->tm_year = year + RTC_REF_YEAR - 1900; | ||
162 | 177 | ||
163 | return 7; | 178 | return 7; |
164 | } | 179 | } |
165 | 180 | ||
166 | int rtc_write_datetime(const struct tm *tm) | 181 | int rtc_write_datetime(const struct tm *tm) |
167 | { | 182 | { |
168 | uint32_t regs[2]; | 183 | uint32_t regs[RTC_NUM_REGS_WR]; |
169 | int year, leap, month, day, i, base_yearday; | 184 | int year, month, x; |
170 | 185 | ||
171 | regs[RTC_REG_TIME] = tm->tm_sec + | 186 | /* Convert time of day into seconds since midnight */ |
172 | tm->tm_min*60 + | 187 | x = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600; |
173 | tm->tm_hour*3600; | ||
174 | |||
175 | year = tm->tm_year - 100; | ||
176 | |||
177 | if (year < RTC_BASE_YEAR - 1900) | ||
178 | year += 2000; | ||
179 | else | ||
180 | year += 1900; | ||
181 | 188 | ||
182 | /* Get number of leaps for day before base */ | 189 | /* Keep in range (it throws off the PMIC counters otherwise) */ |
183 | leap = get_leap_count(RTC_BASE_DAY_COUNT - 1); | 190 | regs[RTC_REG_TIME] = MAX(0, MIN(86399, x)); |
184 | 191 | ||
185 | /* Get day number for base year 0-364|365 */ | 192 | year = tm->tm_year + 1900; |
186 | base_yearday = RTC_BASE_DAY_COUNT - leap - | ||
187 | (RTC_BASE_YEAR - 1601) * 365; | ||
188 | 193 | ||
189 | /* Get the number of days elapsed from reference */ | 194 | /* Get the number of days elapsed from 1 Jan of reference year to 1 Jan of |
190 | for (i = RTC_BASE_YEAR, day = 0; i < year; i++) | 195 | * this year */ |
191 | { | 196 | x = year - RTC_REF_YEAR; |
192 | day += is_leap_year(i) ? 366 : 365; | 197 | x = x*365 + (x >> 2); |
193 | } | ||
194 | 198 | ||
195 | /* Find the number of days passed this year up to the 1st of the | 199 | /* Add the number of days passed this year since 1 Jan and offset by the |
196 | * month. */ | 200 | * base yearday and the reference offset in days from the base */ |
197 | leap = is_leap_year(year); | ||
198 | month = tm->tm_mon; | 201 | month = tm->tm_mon; |
202 | x += month_table[month] + tm->tm_mday - RTC_REF_YEAR_OFFS*365 | ||
203 | - RTC_BASE_YDAY; | ||
199 | 204 | ||
200 | for (i = 0; i < month; i++) | 205 | if ((year & 3) != 0 || month < 2) { |
201 | { | 206 | /* Sub one day because tm_mday starts at 1, otherwise the offset is |
202 | day += month_table[leap][i]; | 207 | * required because of 29 Feb */ |
208 | x--; | ||
203 | } | 209 | } |
204 | 210 | ||
205 | regs[RTC_REG_DAY] = day + tm->tm_mday - 1 - base_yearday; | 211 | /* Keep in range */ |
212 | regs[RTC_REG_DAY] = MAX(0, MIN(32767, x)); | ||
206 | 213 | ||
207 | if (mc13783_write_regs(rtc_registers, regs, 2) == 2) | 214 | if (mc13783_write_regs(rtc_registers, regs, RTC_NUM_REGS_WR) |
215 | == RTC_NUM_REGS_WR) | ||
208 | { | 216 | { |
209 | return 7; | 217 | return 7; |
210 | } | 218 | } |
@@ -238,20 +246,24 @@ bool rtc_check_alarm_started(bool release_alarm) | |||
238 | 246 | ||
239 | void rtc_set_alarm(int h, int m) | 247 | void rtc_set_alarm(int h, int m) |
240 | { | 248 | { |
241 | int day = mc13783_read(MC13783_RTC_DAY); | 249 | uint32_t regs[RTC_NUM_REGS_RD]; |
242 | int tod = mc13783_read(MC13783_RTC_TIME); | 250 | uint32_t tod; |
243 | 251 | ||
244 | if (h*3600 + m*60 < tod) | 252 | if (!read_time_and_day(regs)) |
245 | day++; | 253 | return; |
246 | 254 | ||
247 | mc13783_write(MC13783_RTC_DAY_ALARM, day); | 255 | tod = h*3600 + m*60; |
248 | mc13783_write(MC13783_RTC_ALARM, h*3600 + m*60); | 256 | |
257 | if (tod < regs[RTC_REG_TIME]) | ||
258 | regs[RTC_REG_DAY]++; | ||
259 | |||
260 | mc13783_write(MC13783_RTC_DAY_ALARM, regs[RTC_REG_DAY]); | ||
261 | mc13783_write(MC13783_RTC_ALARM, tod); | ||
249 | } | 262 | } |
250 | 263 | ||
251 | void rtc_get_alarm(int *h, int *m) | 264 | void rtc_get_alarm(int *h, int *m) |
252 | { | 265 | { |
253 | int tod = mc13783_read(MC13783_RTC_ALARM); | 266 | uint32_t tod = mc13783_read(MC13783_RTC_ALARM); |
254 | *h = tod / 3600; | 267 | *h = tod / 3600; |
255 | *m = tod % 3600 / 60; | 268 | *m = tod % 3600 / 60; |
256 | } | 269 | } |
257 | |||