summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-12-24 17:06:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-12-24 17:06:35 +0000
commitc332bba905cc4fcf030c340f5a46fb167eb6034f (patch)
tree03bbafcc4e701588c139c1f480a4560226b43bca
parent78a11cf648442816c346cac9a756b782c918067b (diff)
downloadrockbox-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
-rw-r--r--firmware/drivers/rtc/rtc_mc13783.c230
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
49enum rtc_registers_indexes 54enum 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 ? */ 63static const unsigned char rtc_registers[RTC_NUM_REGS_RD] =
58static bool alarm_start = false;
59
60static 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
67static const unsigned char month_table[2][12] = 70/* was it an alarm that triggered power on ? */
71static bool alarm_start = false;
72
73static 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 */ 80static bool read_time_and_day(uint32_t regs[RTC_NUM_REGS_RD])
74static 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
82static 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
98int rtc_read_datetime(struct tm *tm) 110int 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
166int rtc_write_datetime(const struct tm *tm) 181int 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
239void rtc_set_alarm(int h, int m) 247void 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
251void rtc_get_alarm(int *h, int *m) 264void 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