Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | pj | 1 | /* |
2 | * Project: S.Ha.R.K. |
||
3 | * |
||
4 | * Coordinators: |
||
5 | * Giorgio Buttazzo <giorgio@sssup.it> |
||
6 | * Paolo Gai <pj@gandalf.sssup.it> |
||
7 | * |
||
8 | * Authors : |
||
9 | * Paolo Gai <pj@gandalf.sssup.it> |
||
10 | * Massimiliano Giorgi <massy@gandalf.sssup.it> |
||
11 | * Luca Abeni <luca@gandalf.sssup.it> |
||
12 | * (see the web pages for full authors list) |
||
13 | * |
||
14 | * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy) |
||
15 | * |
||
16 | * http://www.sssup.it |
||
17 | * http://retis.sssup.it |
||
18 | * http://shark.sssup.it |
||
19 | */ |
||
20 | |||
21 | /** |
||
22 | ------------ |
||
23 | CVS : $Id: rtc.c,v 1.1.1.1 2002-03-29 14:12:49 pj Exp $ |
||
24 | |||
25 | File: $File$ |
||
26 | Revision: $Revision: 1.1.1.1 $ |
||
27 | Last update: $Date: 2002-03-29 14:12:49 $ |
||
28 | ------------ |
||
29 | |||
30 | Author: Massimiliano Giorgi |
||
31 | |||
32 | A source from Linux 2.2.9 modified to work with S.Ha.R.K. |
||
33 | |||
34 | **/ |
||
35 | |||
36 | /* |
||
37 | * Copyright (C) 2000 Paolo Gai |
||
38 | * |
||
39 | * This program is free software; you can redistribute it and/or modify |
||
40 | * it under the terms of the GNU General Public License as published by |
||
41 | * the Free Software Foundation; either version 2 of the License, or |
||
42 | * (at your option) any later version. |
||
43 | * |
||
44 | * This program is distributed in the hope that it will be useful, |
||
45 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
46 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
47 | * GNU General Public License for more details. |
||
48 | * |
||
49 | * You should have received a copy of the GNU General Public License |
||
50 | * along with this program; if not, write to the Free Software |
||
51 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
52 | * |
||
53 | */ |
||
54 | |||
55 | /* |
||
56 | * Real Time Clock interface for Linux |
||
57 | * |
||
58 | * Copyright (C) 1996 Paul Gortmaker |
||
59 | * |
||
60 | * This driver allows use of the real time clock (built into |
||
61 | * nearly all computers) from user space. It exports the /dev/rtc |
||
62 | * interface supporting various ioctl() and also the /proc/rtc |
||
63 | * pseudo-file for status information. |
||
64 | * |
||
65 | * The ioctls can be used to set the interrupt behaviour and |
||
66 | * generation rate from the RTC via IRQ 8. Then the /dev/rtc |
||
67 | * interface can be used to make use of these timer interrupts, |
||
68 | * be they interval or alarm based. |
||
69 | * |
||
70 | * The /dev/rtc interface will block on reads until an interrupt |
||
71 | * has been received. If a RTC interrupt has already happened, |
||
72 | * it will output an unsigned long and then block. The output value |
||
73 | * contains the interrupt status in the low byte and the number of |
||
74 | * interrupts since the last read in the remaining high bytes. The |
||
75 | * /dev/rtc interface can also be used with the select(2) call. |
||
76 | * |
||
77 | * This program is free software; you can redistribute it and/or |
||
78 | * modify it under the terms of the GNU General Public License |
||
79 | * as published by the Free Software Foundation; either version |
||
80 | * 2 of the License, or (at your option) any later version. |
||
81 | * |
||
82 | * Based on other minimal char device drivers, like Alan's |
||
83 | * watchdog, Ted's random, etc. etc. |
||
84 | * |
||
85 | * 1.07 Paul Gortmaker. |
||
86 | * 1.08 Miquel van Smoorenburg: disallow certain things on the |
||
87 | * DEC Alpha as the CMOS clock is also used for other things. |
||
88 | * 1.09 Nikita Schmidt: epoch support and some Alpha cleanup. |
||
89 | * |
||
90 | */ |
||
91 | |||
92 | #define RTC_VERSION "1.09" |
||
93 | |||
94 | #define RTC_IRQ 8 /* Can't see this changing soon. */ |
||
95 | #define RTC_IO_EXTENT 0x10 /* Only really two ports, but... */ |
||
96 | |||
97 | /* |
||
98 | * Note that *all* calls to CMOS_READ and CMOS_WRITE are done with |
||
99 | * interrupts disabled. Due to the index-port/data-port (0x70/0x71) |
||
100 | * design of the RTC, we don't want two different things trying to |
||
101 | * get to it at once. (e.g. the periodic 11 min sync from time.c vs. |
||
102 | * this driver.) |
||
103 | */ |
||
104 | |||
105 | #include <kernel/kern.h> |
||
106 | #include <drivers/rtc.h> |
||
107 | #include "_rtc.h" |
||
108 | /* |
||
109 | #define CMOS_READ(addr) ({ \ |
||
110 | outb_p((addr),RTC_PORT(0)); \ |
||
111 | inb_p(RTC_PORT(1)); \ |
||
112 | }) |
||
113 | #define CMOS_WRITE(val, addr) ({ \ |
||
114 | outb_p((addr),RTC_PORT(0)); \ |
||
115 | outb_p((val),RTC_PORT(1)); \ |
||
116 | }) |
||
117 | */ |
||
118 | |||
119 | #define CMOS_READ(addr) ( \ |
||
120 | ll_out(RTC_PORT(0),addr), \ |
||
121 | ll_in(RTC_PORT(1)) \ |
||
122 | ) |
||
123 | |||
124 | #define CMOS_WRITE(val, addr) (\ |
||
125 | ll_out(RTC_PORT(0),addr), \ |
||
126 | ll_out(RTC_PORT(1),val) \ |
||
127 | ) |
||
128 | |||
129 | /* |
||
130 | * We sponge a minor off of the misc major. No need slurping |
||
131 | * up another valuable major dev number for this. If you add |
||
132 | * an ioctl, make sure you don't conflict with SPARC's RTC |
||
133 | * ioctls. |
||
134 | */ |
||
135 | |||
136 | |||
137 | int get_rtc_time (struct rtc_time *rtc_tm); |
||
138 | int set_rtc_time (struct rtc_time *rtc_tm); |
||
139 | int get_rtc_alm_time (struct rtc_time *alm_tm); |
||
140 | |||
141 | void set_rtc_irq_bit(unsigned char bit); |
||
142 | void mask_rtc_irq_bit(unsigned char bit); |
||
143 | |||
144 | static inline unsigned char rtc_is_updating(void); |
||
145 | |||
146 | /* |
||
147 | * Bits in rtc_status. (6 bits of room for future expansion) |
||
148 | */ |
||
149 | |||
150 | #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ |
||
151 | #define RTC_TIMER_ON 0x02 /* missed irq timer active */ |
||
152 | |||
153 | unsigned char rtc_status = 0; /* bitmapped status byte. */ |
||
154 | unsigned long rtc_freq = 0; /* Current periodic IRQ rate */ |
||
155 | unsigned long rtc_irq_data = 0; /* our output to the world */ |
||
156 | |||
157 | /* |
||
158 | * If this driver ever becomes modularised, it will be really nice |
||
159 | * to make the epoch retain its value across module reload... |
||
160 | */ |
||
161 | |||
162 | static unsigned long epoch = 1900; /* year corresponding to 0x00 */ |
||
163 | |||
164 | unsigned char days_in_mo[] = |
||
165 | {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
||
166 | |||
167 | /* |
||
168 | * Returns true if a clock update is in progress |
||
169 | */ |
||
170 | static inline unsigned char rtc_is_updating(void) |
||
171 | { |
||
172 | SYS_FLAGS flags; |
||
173 | unsigned char uip; |
||
174 | |||
175 | flags=kern_fsave(); |
||
176 | uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); |
||
177 | kern_frestore(flags); |
||
178 | return uip; |
||
179 | } |
||
180 | |||
181 | int get_rtc_time(struct rtc_time *rtc_tm) |
||
182 | { |
||
183 | SYS_FLAGS flags; |
||
184 | unsigned char ctrl; |
||
185 | unsigned retries=0; |
||
186 | unsigned delay; |
||
187 | |||
188 | /* |
||
189 | * read RTC once any update in progress is done. The update |
||
190 | * can take just over 2ms. We wait 10 to 20ms. There is no need to |
||
191 | * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. |
||
192 | * If you need to know *exactly* when a second has started, enable |
||
193 | * periodic update complete interrupts, (via ioctl) and then |
||
194 | * immediately read /dev/rtc which will block until you get the IRQ. |
||
195 | * Once the read clears, read the RTC time (again via ioctl). Easy. |
||
196 | */ |
||
197 | |||
198 | /* |
||
199 | if (rtc_is_updating() != 0) |
||
200 | while (jiffies - uip_watchdog < 2*HZ/100) |
||
201 | barrier(); |
||
202 | */ |
||
203 | |||
204 | delay=1000; |
||
205 | while (rtc_is_updating()&&++retries<=5) task_delay(delay); |
||
206 | if (retries>5) return -1; |
||
207 | |||
208 | /* |
||
209 | * Only the values that we read from the RTC are set. We leave |
||
210 | * tm_wday, tm_yday and tm_isdst untouched. Even though the |
||
211 | * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated |
||
212 | * by the RTC when initially set to a non-zero value. |
||
213 | */ |
||
214 | flags=kern_fsave(); |
||
215 | rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); |
||
216 | rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); |
||
217 | rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); |
||
218 | rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); |
||
219 | rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); |
||
220 | rtc_tm->tm_year = CMOS_READ(RTC_YEAR); |
||
221 | ctrl = CMOS_READ(RTC_CONTROL); |
||
222 | kern_frestore(flags); |
||
223 | |||
224 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) |
||
225 | { |
||
226 | BCD_TO_BIN(rtc_tm->tm_sec); |
||
227 | BCD_TO_BIN(rtc_tm->tm_min); |
||
228 | BCD_TO_BIN(rtc_tm->tm_hour); |
||
229 | BCD_TO_BIN(rtc_tm->tm_mday); |
||
230 | BCD_TO_BIN(rtc_tm->tm_mon); |
||
231 | BCD_TO_BIN(rtc_tm->tm_year); |
||
232 | } |
||
233 | |||
234 | /* |
||
235 | * Account for differences between how the RTC uses the values |
||
236 | * and how they are defined in a struct rtc_time; |
||
237 | */ |
||
238 | if ((rtc_tm->tm_year += (epoch - 1900)) <= 69) |
||
239 | rtc_tm->tm_year += 100; |
||
240 | |||
241 | rtc_tm->tm_mon--; |
||
242 | |||
243 | return 0; |
||
244 | } |
||
245 | |||
246 | int set_rtc_time(struct rtc_time *rtc_tm) |
||
247 | { |
||
248 | unsigned char mon, day, hrs, min, sec, leap_yr; |
||
249 | unsigned char save_control, save_freq_select; |
||
250 | unsigned int yrs; |
||
251 | SYS_FLAGS flags; |
||
252 | |||
253 | yrs = rtc_tm->tm_year + 1900; |
||
254 | mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */ |
||
255 | day = rtc_tm->tm_mday; |
||
256 | hrs = rtc_tm->tm_hour; |
||
257 | min = rtc_tm->tm_min; |
||
258 | sec = rtc_tm->tm_sec; |
||
259 | |||
260 | if (yrs < 1970) |
||
261 | return -EINVAL; |
||
262 | |||
263 | leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); |
||
264 | |||
265 | if ((mon > 12) || (day == 0)) |
||
266 | return -EINVAL; |
||
267 | |||
268 | if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) |
||
269 | return -EINVAL; |
||
270 | |||
271 | if ((hrs >= 24) || (min >= 60) || (sec >= 60)) |
||
272 | return -EINVAL; |
||
273 | |||
274 | if ((yrs -= epoch) > 255) /* They are unsigned */ |
||
275 | return -EINVAL; |
||
276 | |||
277 | flags=kern_fsave(); |
||
278 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) |
||
279 | || RTC_ALWAYS_BCD) { |
||
280 | if (yrs > 169) { |
||
281 | kern_frestore(flags); |
||
282 | return -EINVAL; |
||
283 | } |
||
284 | if (yrs >= 100) |
||
285 | yrs -= 100; |
||
286 | |||
287 | BIN_TO_BCD(sec); |
||
288 | BIN_TO_BCD(min); |
||
289 | BIN_TO_BCD(hrs); |
||
290 | BIN_TO_BCD(day); |
||
291 | BIN_TO_BCD(mon); |
||
292 | BIN_TO_BCD(yrs); |
||
293 | } |
||
294 | |||
295 | save_control = CMOS_READ(RTC_CONTROL); |
||
296 | CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); |
||
297 | save_freq_select = CMOS_READ(RTC_FREQ_SELECT); |
||
298 | CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); |
||
299 | |||
300 | CMOS_WRITE(yrs, RTC_YEAR); |
||
301 | CMOS_WRITE(mon, RTC_MONTH); |
||
302 | CMOS_WRITE(day, RTC_DAY_OF_MONTH); |
||
303 | CMOS_WRITE(hrs, RTC_HOURS); |
||
304 | CMOS_WRITE(min, RTC_MINUTES); |
||
305 | CMOS_WRITE(sec, RTC_SECONDS); |
||
306 | |||
307 | CMOS_WRITE(save_control, RTC_CONTROL); |
||
308 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); |
||
309 | |||
310 | kern_frestore(flags); |
||
311 | |||
312 | return 0; |
||
313 | } |
||
314 | |||
315 | int get_rtc_alm_time(struct rtc_time *alm_tm) |
||
316 | { |
||
317 | SYS_FLAGS flags; |
||
318 | unsigned char ctrl; |
||
319 | |||
320 | /* |
||
321 | * Only the values that we read from the RTC are set. That |
||
322 | * means only tm_hour, tm_min, and tm_sec. |
||
323 | */ |
||
324 | flags=kern_fsave(); |
||
325 | alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM); |
||
326 | alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM); |
||
327 | alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM); |
||
328 | ctrl = CMOS_READ(RTC_CONTROL); |
||
329 | kern_frestore(flags); |
||
330 | |||
331 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) |
||
332 | { |
||
333 | BCD_TO_BIN(alm_tm->tm_sec); |
||
334 | BCD_TO_BIN(alm_tm->tm_min); |
||
335 | BCD_TO_BIN(alm_tm->tm_hour); |
||
336 | } |
||
337 | |||
338 | return 0; |
||
339 | } |
||
340 | |||
341 | /* |
||
342 | * Used to disable/enable interrupts for any one of UIE, AIE, PIE. |
||
343 | * Rumour has it that if you frob the interrupt enable/disable |
||
344 | * bits in RTC_CONTROL, you should read RTC_INTR_FLAGS, to |
||
345 | * ensure you actually start getting interrupts. Probably for |
||
346 | * compatibility with older/broken chipset RTC implementations. |
||
347 | * We also clear out any old irq data after an ioctl() that |
||
348 | * meddles with the interrupt enable/disable bits. |
||
349 | */ |
||
350 | |||
351 | void mask_rtc_irq_bit(unsigned char bit) |
||
352 | { |
||
353 | unsigned char val; |
||
354 | SYS_FLAGS flags; |
||
355 | |||
356 | flags=kern_fsave(); |
||
357 | //cli(); |
||
358 | val = CMOS_READ(RTC_CONTROL); |
||
359 | val &= ~bit; |
||
360 | CMOS_WRITE(val, RTC_CONTROL); |
||
361 | CMOS_READ(RTC_INTR_FLAGS); |
||
362 | kern_frestore(flags); |
||
363 | //rtc_irq_data = 0; |
||
364 | } |
||
365 | |||
366 | void set_rtc_irq_bit(unsigned char bit) |
||
367 | { |
||
368 | unsigned char val; |
||
369 | SYS_FLAGS flags; |
||
370 | |||
371 | flags=kern_fsave(); |
||
372 | //cli(); |
||
373 | val = CMOS_READ(RTC_CONTROL); |
||
374 | val |= bit; |
||
375 | CMOS_WRITE(val, RTC_CONTROL); |
||
376 | CMOS_READ(RTC_INTR_FLAGS); |
||
377 | //rtc_irq_data = 0; |
||
378 | kern_frestore(flags); |
||
379 | } |
||
380 | |||
381 | /* added by Massy */ |
||
382 | /* to find the date in seconds from the Epoch (1 Gen 1970 00:00 GMT) */ |
||
383 | /* (modifing a source from Linux) */ |
||
384 | |||
385 | static int day_n[]={ |
||
386 | 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 |
||
387 | }; |
||
388 | |||
389 | time_t sys_getdate(void) |
||
390 | { |
||
391 | struct rtc_time rtc; |
||
392 | time_t secs; |
||
393 | |||
394 | get_rtc_time (&rtc); |
||
395 | |||
396 | secs = rtc.tm_sec+60l*rtc.tm_min+rtc.tm_hour*3600l+86400l* |
||
397 | (rtc.tm_mday-1+day_n[rtc.tm_mon]+(rtc.tm_year/4l) |
||
398 | +rtc.tm_year*365l- |
||
399 | ((rtc.tm_year & 3) == 0 && rtc.tm_mon < 2 ? 1 : 0)+3653l); |
||
400 | |||
401 | /* days since 1.1.70 plus 80's leap day */ |
||
402 | /*secs += sys_tz.tz_minuteswest*60;*/ |
||
403 | /*if (sys_tz.tz_dsttime) secs -= 3600;*/ |
||
404 | return secs; |
||
405 | } |