mc13783 RTC: Handle years in a better way for the use of struct tm. Make the code...
[kugel-rb.git] / firmware / drivers / rtc / rtc_mc13783.c
bloba1f78f738dc803ce1c6c44a06f602f33868d40d8
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Michael Sevakis
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "config.h"
22 #include "system.h"
23 #include "rtc.h"
24 #include "mc13783.h"
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. */
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. */
33 #ifdef TOSHIBA_GIGABEAT_S
34 /* Gigabeat S seems to be 1 day behind the ususual - this will
35 * make the RTC match file dates created by retailos. */
36 #define RTC_BASE_WDAY 1 /* Monday */
37 #define RTC_BASE_YDAY 364 /* 31 Dec */
38 #define RTC_BASE_YEAR 1979
39 #elif 1
40 #define RTC_BASE_WDAY 2 /* Tuesday */
41 #define RTC_BASE_YDAY 0 /* 01 Jan */
42 #define RTC_BASE_YEAR 1980
43 #else
44 #define RTC_BASE_WDAY 4 /* Thursday */
45 #define RTC_BASE_YDAY 0 /* 01 Jan */
46 #define RTC_BASE_YEAR 1970
47 #endif
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)
54 enum rtc_registers_indexes
56 RTC_REG_TIME = 0,
57 RTC_REG_DAY,
58 RTC_REG_TIME2,
59 RTC_NUM_REGS_RD = 3,
60 RTC_NUM_REGS_WR = 2,
63 static const unsigned char rtc_registers[RTC_NUM_REGS_RD] =
65 [RTC_REG_TIME] = MC13783_RTC_TIME,
66 [RTC_REG_DAY] = MC13783_RTC_DAY,
67 [RTC_REG_TIME2] = MC13783_RTC_TIME,
70 /* was it an alarm that triggered power on ? */
71 static bool alarm_start = false;
73 static const unsigned short month_table[13] =
75 /* Since 1 Jan, how many days have passed this year? (non-leap)
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
80 static bool read_time_and_day(uint32_t regs[RTC_NUM_REGS_RD])
82 /* Read time, day, time - 2nd read of time should be the same or
83 * greater */
86 if (mc13783_read_regs(rtc_registers, regs,
87 RTC_NUM_REGS_RD) < RTC_NUM_REGS_RD)
89 /* Couldn't read registers */
90 return false;
93 /* If TOD counter turned over - reread */
94 while (regs[RTC_REG_TIME2] < regs[RTC_REG_TIME]);
96 return true;
99 /** Public APIs **/
100 void rtc_init(void)
102 /* only needs to be polled on startup */
103 if (mc13783_read(MC13783_INTERRUPT_STATUS1) & MC13783_TODAI)
105 alarm_start = true;
106 mc13783_write(MC13783_INTERRUPT_STATUS1, MC13783_TODAI);
110 int rtc_read_datetime(struct tm *tm)
112 uint32_t regs[RTC_NUM_REGS_RD];
113 int year, month, yday, x, n;
115 if (!read_time_and_day(regs))
116 return 0;
118 /* TOD: 0 to 86399 */
119 x = regs[RTC_REG_TIME];
121 /* Hour */
122 n = x / 3600;
123 tm->tm_hour = n;
125 /* Minute */
126 x -= n*3600;
127 n = x / 60;
128 tm->tm_min = n;
130 /* Second */
131 x -= n*60;
132 tm->tm_sec = x;
134 /* DAY: 0 to 32767 */
135 x = regs[RTC_REG_DAY];
137 /* Weekday */
138 tm->tm_wday = (x + RTC_BASE_WDAY) % 7;
140 /* Year */
141 x += RTC_REF_YEAR_OFFS*365 + RTC_BASE_YDAY;
143 /* Lag year increment by subtracting leaps since the reference year
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;
149 /* Year day */
150 yday = x - n - year*365;
152 /* If (x + 1) mod 1461 == 0, then it is yday 365 of a leap year */
153 if (n * 1461 - 1 == x)
154 yday++;
156 tm->tm_yday = x = yday;
158 if (((year + RTC_REF_YEAR) & 3) == 0 && x >= month_table[2])
160 if (x > month_table[2])
161 yday--; /* 1 Mar or after; lag date by one day */
163 x--; /* 29 Feb or after, lag month by one day */
166 /* Get the current month */
167 month = x >> 5; /* yday / 32, close enough */
169 if (month_table[month + 1] <= x)
170 month++; /* Round to next */
172 tm->tm_mday = yday - month_table[month] + 1; /* 1 to 31 */
173 tm->tm_mon = month; /* 0 to 11 */
175 /* {BY to (BY+89 or 90)} - 1900 */
176 tm->tm_year = year + RTC_REF_YEAR - 1900;
178 return 7;
181 int rtc_write_datetime(const struct tm *tm)
183 uint32_t regs[RTC_NUM_REGS_WR];
184 int year, month, x;
186 /* Convert time of day into seconds since midnight */
187 x = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600;
189 /* Keep in range (it throws off the PMIC counters otherwise) */
190 regs[RTC_REG_TIME] = MAX(0, MIN(86399, x));
192 year = tm->tm_year + 1900;
194 /* Get the number of days elapsed from 1 Jan of reference year to 1 Jan of
195 * this year */
196 x = year - RTC_REF_YEAR;
197 x = x*365 + (x >> 2);
199 /* Add the number of days passed this year since 1 Jan and offset by the
200 * base yearday and the reference offset in days from the base */
201 month = tm->tm_mon;
202 x += month_table[month] + tm->tm_mday - RTC_REF_YEAR_OFFS*365
203 - RTC_BASE_YDAY;
205 if ((year & 3) != 0 || month < 2) {
206 /* Sub one day because tm_mday starts at 1, otherwise the offset is
207 * required because of 29 Feb */
208 x--;
211 /* Keep in range */
212 regs[RTC_REG_DAY] = MAX(0, MIN(32767, x));
214 if (mc13783_write_regs(rtc_registers, regs, RTC_NUM_REGS_WR)
215 == RTC_NUM_REGS_WR)
217 return 7;
220 return 0;
223 bool rtc_check_alarm_flag(void)
225 /* We don't need to do anything special if it has already fired */
226 return false;
229 void rtc_enable_alarm(bool enable)
231 if (enable)
232 mc13783_clear(MC13783_INTERRUPT_MASK1, MC13783_TODAM);
233 else
234 mc13783_set(MC13783_INTERRUPT_MASK1, MC13783_TODAM);
237 bool rtc_check_alarm_started(bool release_alarm)
239 bool rc = alarm_start;
241 if (release_alarm)
242 alarm_start = false;
244 return rc;
247 void rtc_set_alarm(int h, int m)
249 uint32_t regs[RTC_NUM_REGS_RD];
250 uint32_t tod;
252 if (!read_time_and_day(regs))
253 return;
255 tod = h*3600 + m*60;
257 if (tod < regs[RTC_REG_TIME])
258 regs[RTC_REG_DAY]++;
260 mc13783_write(MC13783_RTC_DAY_ALARM, regs[RTC_REG_DAY]);
261 mc13783_write(MC13783_RTC_ALARM, tod);
264 void rtc_get_alarm(int *h, int *m)
266 uint32_t tod = mc13783_read(MC13783_RTC_ALARM);
267 *h = tod / 3600;
268 *m = tod % 3600 / 60;