diff options
Diffstat (limited to 'firmware/drivers/rtc/rtc_jz4740.c')
-rw-r--r-- | firmware/drivers/rtc/rtc_jz4740.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/firmware/drivers/rtc/rtc_jz4740.c b/firmware/drivers/rtc/rtc_jz4740.c new file mode 100644 index 0000000000..b9cceec9af --- /dev/null +++ b/firmware/drivers/rtc/rtc_jz4740.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2008 by Maurus Cuelenaere | ||
11 | * | ||
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. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* | ||
23 | * Jz OnChip Real Time Clock interface for Linux | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "config.h" | ||
28 | #include "jz4740.h" | ||
29 | #include "rtc.h" | ||
30 | #include "timefuncs.h" | ||
31 | |||
32 | static unsigned int epoch = 1900; | ||
33 | static const unsigned char days_in_mo[] = { | ||
34 | 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
35 | }; | ||
36 | |||
37 | static const unsigned int yearday[5] = {0, 366, 366+365, 366+365*2, 366+365*3}; | ||
38 | static const unsigned int sweekday = 6; | ||
39 | static const unsigned int sum_monthday[13] = { | ||
40 | 0, | ||
41 | 31, | ||
42 | 31+28, | ||
43 | 31+28+31, | ||
44 | 31+28+31+30, | ||
45 | 31+28+31+30+31, | ||
46 | 31+28+31+30+31+30, | ||
47 | 31+28+31+30+31+30+31, | ||
48 | 31+28+31+30+31+30+31+31, | ||
49 | 31+28+31+30+31+30+31+31+30, | ||
50 | 31+28+31+30+31+30+31+31+30+31, | ||
51 | 31+28+31+30+31+30+31+31+30+31+30, | ||
52 | 365 | ||
53 | }; | ||
54 | |||
55 | static unsigned int jz_mktime(int year, int mon, int day, int hour, int min, int sec) | ||
56 | { | ||
57 | unsigned int seccounter; | ||
58 | |||
59 | if (year < 2000) | ||
60 | year = 2000; | ||
61 | year -= 2000; | ||
62 | seccounter = (year/4)*(365*3+366); | ||
63 | seccounter += yearday[year%4]; | ||
64 | if (year%4) | ||
65 | seccounter += sum_monthday[mon-1]; | ||
66 | else | ||
67 | if (mon >= 3) | ||
68 | seccounter += sum_monthday[mon-1]+1; | ||
69 | else | ||
70 | seccounter += sum_monthday[mon-1]; | ||
71 | seccounter += day-1; | ||
72 | seccounter *= 24; | ||
73 | seccounter += hour; | ||
74 | seccounter *= 60; | ||
75 | seccounter += min; | ||
76 | seccounter *= 60; | ||
77 | seccounter += sec; | ||
78 | |||
79 | return seccounter; | ||
80 | } | ||
81 | |||
82 | static void jz_gettime(unsigned int rtc, int *year, int *mon, int *day, int *hour, | ||
83 | int *min, int *sec, int *weekday) | ||
84 | { | ||
85 | unsigned int tday, tsec, i, tmp; | ||
86 | |||
87 | tday = rtc/(24*3600); | ||
88 | *weekday = ((tday % 7) + sweekday) % 7; | ||
89 | *year = (tday/(366+365*3)) * 4; | ||
90 | tday = tday%(366+365*3); | ||
91 | for (i=0;i<5;i++) | ||
92 | { | ||
93 | if (tday<yearday[i]) | ||
94 | { | ||
95 | *year += i-1; | ||
96 | tday -= yearday[i-1]; | ||
97 | break; | ||
98 | } | ||
99 | } | ||
100 | for (i=0;i<13;i++) | ||
101 | { | ||
102 | tmp = sum_monthday[i]; | ||
103 | if (((*year%4) == 0) && (i>=2)) | ||
104 | tmp += 1; | ||
105 | if (tday<tmp) | ||
106 | { | ||
107 | *mon = i; | ||
108 | tmp = sum_monthday[i-1]; | ||
109 | if (((*year%4) == 0) && ((i-1)>=2)) | ||
110 | tmp += 1; | ||
111 | *day = tday - tmp + 1; | ||
112 | break; | ||
113 | } | ||
114 | } | ||
115 | tsec = rtc % (24 * 3600); | ||
116 | *hour = tsec / 3600; | ||
117 | *min = (tsec / 60) % 60; | ||
118 | *sec = tsec - *hour*3600 - *min*60; | ||
119 | *year += 2000; | ||
120 | } | ||
121 | |||
122 | int rtc_read_datetime(unsigned char* buf) | ||
123 | { | ||
124 | struct tm rtc_tm; | ||
125 | unsigned int sec,mon,mday,wday,year,hour,min; | ||
126 | |||
127 | /* | ||
128 | * Only the values that we read from the RTC are set. We leave | ||
129 | * tm_wday, tm_yday and tm_isdst untouched. Even though the | ||
130 | * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated | ||
131 | * by the RTC when initially set to a non-zero value. | ||
132 | */ | ||
133 | jz_gettime(REG_RTC_RSR, &year, &mon, &mday, &hour, &min, &sec, &wday); | ||
134 | |||
135 | year -= 2000; | ||
136 | |||
137 | rtc_tm.tm_sec = sec; | ||
138 | rtc_tm.tm_min = min; | ||
139 | rtc_tm.tm_hour = hour; | ||
140 | rtc_tm.tm_mday = mday; | ||
141 | rtc_tm.tm_wday = wday; | ||
142 | /* Don't use centry, but start from year 1970 */ | ||
143 | rtc_tm.tm_mon = mon; | ||
144 | if ((year += (epoch - 1900)) <= 69) | ||
145 | year += 100; | ||
146 | rtc_tm.tm_year = year + 1900; | ||
147 | |||
148 | rtc_tm.tm_yday = 0; /* Not implemented for now */ | ||
149 | rtc_tm.tm_isdst = -1; /* Not implemented for now */ | ||
150 | |||
151 | (*((struct tm*)buf)) = rtc_tm; | ||
152 | return 1; | ||
153 | } | ||
154 | |||
155 | #if 0 | ||
156 | void get_rtc_alm_time(struct rtc_time *alm_tm) | ||
157 | { | ||
158 | unsigned int sec,mon,mday,wday,year,hour,min; | ||
159 | unsigned int lval; | ||
160 | unsigned long flags; | ||
161 | /* | ||
162 | * Only the values that we read from the RTC are set. That | ||
163 | * means only tm_hour, tm_min, and tm_sec. | ||
164 | */ | ||
165 | lval = REG_RTC_RSAR; | ||
166 | jz_gettime(lval, &year, &mon, &mday, &hour, &min, &sec, &wday); | ||
167 | |||
168 | alm_tm->tm_sec = sec; | ||
169 | alm_tm->tm_min = min; | ||
170 | alm_tm->tm_hour = hour; | ||
171 | } | ||
172 | |||
173 | |||
174 | int rtc_ioctl(unsigned int cmd,struct rtc_time *val,unsigned int epo) | ||
175 | { | ||
176 | struct rtc_time wtime; | ||
177 | switch (cmd) { | ||
178 | case RTC_ALM_READ: /* Read the present alarm time */ | ||
179 | /* | ||
180 | * This returns a struct rtc_time. Reading >= 0xc0 | ||
181 | * means "don't care" or "match all". Only the tm_hour, | ||
182 | * tm_min, and tm_sec values are filled in. | ||
183 | */ | ||
184 | get_rtc_alm_time(val); | ||
185 | break; | ||
186 | case RTC_ALM_SET: /* Store a time into the alarm */ | ||
187 | { | ||
188 | unsigned char ahrs, amin, asec; | ||
189 | unsigned int sec,mon,mday,wday,year,hour,min; | ||
190 | unsigned int lval; | ||
191 | unsigned long flags; | ||
192 | struct rtc_time alm_tm; | ||
193 | |||
194 | alm_tm = *val; | ||
195 | ahrs = alm_tm.tm_hour; | ||
196 | amin = alm_tm.tm_min; | ||
197 | asec = alm_tm.tm_sec; | ||
198 | |||
199 | |||
200 | |||
201 | if (ahrs >= 24) | ||
202 | return -1; | ||
203 | |||
204 | if (amin >= 60) | ||
205 | return -1; | ||
206 | |||
207 | if (asec >= 60) | ||
208 | return -1; | ||
209 | |||
210 | flags = spin_lock_irqsave(); | ||
211 | lval = REG_RTC_RSR; | ||
212 | jz_gettime(lval, &year, &mon, &mday, &hour, &min, &sec, &wday); | ||
213 | hour = ahrs; | ||
214 | min = amin; | ||
215 | sec = asec; | ||
216 | lval = jz_mktime(year, mon, mday, hour, min, sec); | ||
217 | REG_RTC_RSAR = lval; | ||
218 | spin_unlock_irqrestore(flags); | ||
219 | break; | ||
220 | } | ||
221 | case RTC_RD_TIME: /* Read the time/date from RTC */ | ||
222 | get_rtc_time(val); | ||
223 | break; | ||
224 | case RTC_SET_TIME: /* Set the RTC */ | ||
225 | { | ||
226 | struct rtc_time rtc_tm; | ||
227 | unsigned int mon, day, hrs, min, sec, leap_yr, date; | ||
228 | unsigned int yrs; | ||
229 | unsigned int lval; | ||
230 | unsigned long flags; | ||
231 | |||
232 | rtc_tm = *val; | ||
233 | yrs = rtc_tm.tm_year;// + 1900; | ||
234 | mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ | ||
235 | day = rtc_tm.tm_wday; | ||
236 | date = rtc_tm.tm_mday; | ||
237 | hrs = rtc_tm.tm_hour; | ||
238 | min = rtc_tm.tm_min; | ||
239 | sec = rtc_tm.tm_sec; | ||
240 | |||
241 | |||
242 | if (yrs < 1970) | ||
243 | return -EINVAL; | ||
244 | leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); | ||
245 | |||
246 | if ((mon > 12) || (date == 0)) | ||
247 | return -EINVAL; | ||
248 | |||
249 | if (date > (days_in_mo[mon] + ((mon == 2) && leap_yr))) | ||
250 | return -EINVAL; | ||
251 | |||
252 | if ((hrs >= 24) || (min >= 60) || (sec >= 60)) | ||
253 | return -EINVAL; | ||
254 | |||
255 | if ((yrs -= epoch) > 255) /* They are unsigned */ | ||
256 | return -EINVAL; | ||
257 | |||
258 | flags = spin_lock_irqsave(); | ||
259 | /* These limits and adjustments are independant of | ||
260 | * whether the chip is in binary mode or not. | ||
261 | */ | ||
262 | |||
263 | if (yrs > 169) { | ||
264 | spin_unlock_irqrestore(flags); | ||
265 | return -EINVAL; | ||
266 | } | ||
267 | |||
268 | yrs += epoch; | ||
269 | lval = jz_mktime(yrs, mon, date, hrs, min, sec); | ||
270 | REG_RTC_RSR = lval; | ||
271 | /* FIXME: maybe we need to write alarm register here. */ | ||
272 | spin_unlock_irqrestore(flags); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | break; | ||
277 | case RTC_EPOCH_READ: /* Read the epoch. */ | ||
278 | epo = epoch; | ||
279 | return 0; | ||
280 | case RTC_EPOCH_SET: /* Set the epoch. */ | ||
281 | /* | ||
282 | * There were no RTC clocks before 1900. | ||
283 | */ | ||
284 | if (epo < 1900) | ||
285 | return -EINVAL; | ||
286 | |||
287 | epoch = epo; | ||
288 | return 0; | ||
289 | default: | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | return -EINVAL; | ||
293 | } | ||
294 | #endif | ||
295 | |||
296 | #define udelay(x) for(i=0; i<x*100000; i++) asm("nop"); | ||
297 | void rtc_init(void) | ||
298 | { | ||
299 | int i; | ||
300 | REG_RTC_RSR = 0; | ||
301 | while(!(REG_RTC_RCR & 0x80)); | ||
302 | REG_RTC_RCR = 1; | ||
303 | udelay(70); | ||
304 | while(!(REG_RTC_RCR & 0x80)); | ||
305 | REG_RTC_RGR = 0x7fff; | ||
306 | udelay(70); | ||
307 | } | ||