1 | /* Driver for the Maxim 1375 i2c RTC (TOD only; very simple...) */ |
---|
2 | |
---|
3 | /* |
---|
4 | * Authorship |
---|
5 | * ---------- |
---|
6 | * This software was created by |
---|
7 | * |
---|
8 | * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, |
---|
9 | * Stanford Linear Accelerator Center, Stanford University. |
---|
10 | * |
---|
11 | * Acknowledgement of sponsorship |
---|
12 | * ------------------------------ |
---|
13 | * The software was produced by |
---|
14 | * the Stanford Linear Accelerator Center, Stanford University, |
---|
15 | * under Contract DE-AC03-76SFO0515 with the Department of Energy. |
---|
16 | * |
---|
17 | * Government disclaimer of liability |
---|
18 | * ---------------------------------- |
---|
19 | * Neither the United States nor the United States Department of Energy, |
---|
20 | * nor any of their employees, makes any warranty, express or implied, or |
---|
21 | * assumes any legal liability or responsibility for the accuracy, |
---|
22 | * completeness, or usefulness of any data, apparatus, product, or process |
---|
23 | * disclosed, or represents that its use would not infringe privately owned |
---|
24 | * rights. |
---|
25 | * |
---|
26 | * Stanford disclaimer of liability |
---|
27 | * -------------------------------- |
---|
28 | * Stanford University makes no representations or warranties, express or |
---|
29 | * implied, nor assumes any liability for the use of this software. |
---|
30 | * |
---|
31 | * Stanford disclaimer of copyright |
---|
32 | * -------------------------------- |
---|
33 | * Stanford University, owner of the copyright, hereby disclaims its |
---|
34 | * copyright and all other rights in this software. Hence, anyone may |
---|
35 | * freely use it for any purpose without restriction. |
---|
36 | * |
---|
37 | * Maintenance of notices |
---|
38 | * ---------------------- |
---|
39 | * In the interest of clarity regarding the origin and status of this |
---|
40 | * SLAC software, this and all the preceding Stanford University notices |
---|
41 | * are to remain affixed to any copy or derivative of this software made |
---|
42 | * or distributed by the recipient and are to be affixed to any copy of |
---|
43 | * software made or distributed by the recipient that contains a copy or |
---|
44 | * derivative of this software. |
---|
45 | * |
---|
46 | * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 |
---|
47 | */ |
---|
48 | |
---|
49 | /* This driver uses the file-system interface to the i2c bus */ |
---|
50 | |
---|
51 | #include <rtems.h> |
---|
52 | #include <rtems/rtc.h> |
---|
53 | #include <libchip/rtc.h> |
---|
54 | #include <libchip/ds1375-rtc.h> |
---|
55 | |
---|
56 | #include <sys/fcntl.h> |
---|
57 | #include <stdio.h> |
---|
58 | #include <inttypes.h> |
---|
59 | |
---|
60 | |
---|
61 | #define STATIC static |
---|
62 | #undef DEBUG |
---|
63 | |
---|
64 | STATIC uint8_t ds1375_bcd2bin(uint8_t x) |
---|
65 | { |
---|
66 | uint8_t h = x & 0xf0; |
---|
67 | |
---|
68 | /* 8*hi + 2*hi + lo */ |
---|
69 | return ( h >> 1 ) + ( h >> 3 ) + ( x & 0xf ); |
---|
70 | } |
---|
71 | |
---|
72 | STATIC uint8_t ds1375_bin2bcd(uint8_t x) |
---|
73 | { |
---|
74 | uint8_t h = x/10; |
---|
75 | |
---|
76 | return ( h << 4 ) + ( x - ( ( h << 3 ) + ( h << 1 ) ) ); |
---|
77 | } |
---|
78 | |
---|
79 | /* |
---|
80 | * Register Definitions and Access Macros |
---|
81 | * |
---|
82 | * The xxx_REG macros are offsets into the register files |
---|
83 | * The xxx_OFF macros are offsets into a in-memory buffer |
---|
84 | * starting at the seconds (for the 1375 both, |
---|
85 | * _REG and _OFF happen to be identical). |
---|
86 | */ |
---|
87 | |
---|
88 | |
---|
89 | #define DS1375_SEC_REG 0x0 |
---|
90 | #define DS1375_SEC_OFF (DS1375_SEC_REG-DS1375_SEC_REG) |
---|
91 | /* Extract seconds and convert to binary */ |
---|
92 | #define DS1375_SEC(x) ds1375_bcd2bin( ((x)[DS1375_SEC_OFF]) & 0x7f ) |
---|
93 | |
---|
94 | #define DS1375_MIN_REG 0x1 |
---|
95 | #define DS1375_MIN_OFF (DS1375_MIN_REG-DS1375_SEC_REG) |
---|
96 | /* Extract minutes and convert to binary */ |
---|
97 | #define DS1375_MIN(x) ds1375_bcd2bin( ((x)[DS1375_MIN_OFF]) & 0x7f ) |
---|
98 | |
---|
99 | #define DS1375_HR_REG 0x2 |
---|
100 | #define DS1375_HR_OFF (DS1375_HR_REG-DS1375_SEC_REG) |
---|
101 | #define DS1375_HR_1224 (1<<6) |
---|
102 | #define DS1375_HR_AMPM (1<<5) |
---|
103 | /* Are hours in AM/PM representation ? */ |
---|
104 | #define DS1375_IS_AMPM(x) (DS1375_HR_1224 & ((x)[DS1375_HR_OFF])) |
---|
105 | /* Are we PM ? */ |
---|
106 | #define DS1375_IS_PM(x) (DS1375_HR_AMPM & ((x)[DS1375_HR_OFF])) |
---|
107 | /* Extract hours (12h mode) and convert to binary */ |
---|
108 | #define DS1375_HR_12(x) ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x1f ) |
---|
109 | /* Extract hours (24h mode) and convert to binary */ |
---|
110 | #define DS1375_HR_24(x) ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x3f ) |
---|
111 | |
---|
112 | #define DS1375_DAY_REG 0x3 |
---|
113 | #define DS1375_DAY_OFF (DS1375_DAY_REG-DS1375_SEC_REG) |
---|
114 | #define DS1375_DAT_REG 0x4 |
---|
115 | #define DS1375_DAT_OFF (DS1375_DAT_REG-DS1375_SEC_REG) |
---|
116 | /* Extract date and convert to binary */ |
---|
117 | #define DS1375_DAT(x) ds1375_bcd2bin( ((x)[DS1375_DAT_OFF]) & 0x3f ) |
---|
118 | #define DS1375_MON_REG 0x5 |
---|
119 | #define DS1375_MON_OFF (DS1375_MON_REG-DS1375_SEC_REG) |
---|
120 | #define DS1375_MON_CTRY (1<<7) |
---|
121 | /* Is century bit set ? */ |
---|
122 | #define DS1375_IS_CTRY(x) (((x)[DS1375_MON_OFF]) & DS1375_MON_CTRY) |
---|
123 | /* Extract month and convert to binary */ |
---|
124 | #define DS1375_MON(x) ds1375_bcd2bin( ((x)[DS1375_MON_OFF]) & 0x1f ) |
---|
125 | |
---|
126 | #define DS1375_YR_REG 0x6 |
---|
127 | #define DS1375_YR_OFF (DS1375_YR_REG-DS1375_SEC_REG) |
---|
128 | /* Extract year and convert to binary */ |
---|
129 | #define DS1375_YR(x) ds1375_bcd2bin( ((x)[DS1375_YR_OFF]) & 0xff ) |
---|
130 | |
---|
131 | /* CR Register and bit definitions */ |
---|
132 | #define DS1375_CR_REG 0xe |
---|
133 | #define DS1375_CR_ECLK (1<<7) |
---|
134 | #define DS1375_CR_CLKSEL1 (1<<6) |
---|
135 | #define DS1375_CR_CLKSEL0 (1<<5) |
---|
136 | #define DS1375_CR_RS2 (1<<4) |
---|
137 | #define DS1375_CR_RS1 (1<<3) |
---|
138 | #define DS1375_CR_INTCN (1<<2) |
---|
139 | #define DS1375_CR_A2IE (1<<1) |
---|
140 | #define DS1375_CR_A1IE (1<<0) |
---|
141 | |
---|
142 | #define DS1375_CSR_REG 0xf |
---|
143 | |
---|
144 | /* User SRAM (8 bytes) */ |
---|
145 | #define DS1375_RAM 0x10 /* start of 8 bytes user ram */ |
---|
146 | |
---|
147 | /* Access Primitives */ |
---|
148 | |
---|
149 | STATIC int |
---|
150 | rd_bytes( int fd, uint32_t off, uint8_t *buf, int len ) |
---|
151 | { |
---|
152 | uint8_t ptr = off; |
---|
153 | |
---|
154 | return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1; |
---|
155 | } |
---|
156 | |
---|
157 | STATIC int |
---|
158 | wr_bytes( int fd, uint32_t off, uint8_t *buf, int len ) |
---|
159 | { |
---|
160 | uint8_t d[ len + 1 ]; |
---|
161 | |
---|
162 | /* Must not break up writing of the register pointer and |
---|
163 | * the data to-be-written into multiple write() calls |
---|
164 | * because every 'write()' operation sends START and |
---|
165 | * the chip interprets the first byte after START as |
---|
166 | * the register pointer. |
---|
167 | */ |
---|
168 | |
---|
169 | d[0] = off; |
---|
170 | memcpy( d + 1, buf, len ); |
---|
171 | |
---|
172 | return len + 1 == write( fd, d, len + 1 ) ? 0 : -1; |
---|
173 | } |
---|
174 | |
---|
175 | /* Helpers */ |
---|
176 | |
---|
177 | static int |
---|
178 | getfd( int minor ) |
---|
179 | { |
---|
180 | return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR ); |
---|
181 | } |
---|
182 | |
---|
183 | /* Driver Access Functions */ |
---|
184 | |
---|
185 | STATIC void |
---|
186 | ds1375_initialize( int minor ) |
---|
187 | { |
---|
188 | int fd; |
---|
189 | uint8_t cr; |
---|
190 | |
---|
191 | if ( ( fd = getfd( minor ) ) >= 0 ) { |
---|
192 | if ( 0 == rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) { |
---|
193 | /* make sure clock is enabled */ |
---|
194 | if ( ! ( DS1375_CR_ECLK & cr ) ) { |
---|
195 | cr |= DS1375_CR_ECLK; |
---|
196 | wr_bytes( fd, DS1375_CR_REG, &cr, 1 ); |
---|
197 | } |
---|
198 | } |
---|
199 | close( fd ); |
---|
200 | } |
---|
201 | |
---|
202 | } |
---|
203 | |
---|
204 | STATIC int |
---|
205 | ds1375_get_time( int minor, rtems_time_of_day *time ) |
---|
206 | { |
---|
207 | int rval = -1; |
---|
208 | int fd; |
---|
209 | uint8_t buf[DS1375_YR_REG + 1 - DS1375_SEC_REG]; |
---|
210 | |
---|
211 | if ( time && ( ( fd = getfd( minor ) ) >= 0 ) ) { |
---|
212 | if ( 0 == rd_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) { |
---|
213 | time->year = DS1375_IS_CTRY( buf ) ? 2000 : 1900; |
---|
214 | time->year += DS1375_YR ( buf ); |
---|
215 | time->month = DS1375_MON( buf ); |
---|
216 | time->day = DS1375_DAT( buf ); /* DAY is weekday */ |
---|
217 | |
---|
218 | if ( DS1375_IS_AMPM( buf ) ) { |
---|
219 | time->hour = DS1375_HR_12 ( buf ); |
---|
220 | if ( DS1375_IS_PM( buf ) ) |
---|
221 | time->hour += 12; |
---|
222 | } else { |
---|
223 | time->hour = DS1375_HR_24 ( buf ); |
---|
224 | } |
---|
225 | |
---|
226 | time->minute = DS1375_MIN( buf ); |
---|
227 | time->second = DS1375_SEC( buf ); |
---|
228 | time->ticks = 0; |
---|
229 | rval = 0; |
---|
230 | } |
---|
231 | close( fd ); |
---|
232 | } |
---|
233 | return rval; |
---|
234 | } |
---|
235 | |
---|
236 | STATIC int |
---|
237 | ds1375_set_time( int minor, rtems_time_of_day *time ) |
---|
238 | { |
---|
239 | int rval = -1; |
---|
240 | int fd = -1; |
---|
241 | time_t secs; |
---|
242 | struct tm tm; |
---|
243 | uint8_t buf[DS1375_YR_REG + 1 - DS1375_SEC_REG]; |
---|
244 | uint8_t cr = 0xff; |
---|
245 | /* |
---|
246 | * The clock hardware maintains the day-of-week as a separate counter |
---|
247 | * so we must compute it ourselves (rtems_time_of_day doesn't come |
---|
248 | * with a day of week). |
---|
249 | */ |
---|
250 | secs = _TOD_To_seconds( time ); |
---|
251 | /* we're only interested in tm_wday... */ |
---|
252 | gmtime_r( &secs, &tm ); |
---|
253 | |
---|
254 | buf[DS1375_SEC_OFF] = ds1375_bin2bcd( time->second ); |
---|
255 | buf[DS1375_MIN_OFF] = ds1375_bin2bcd( time->minute ); |
---|
256 | /* bin2bcd(hour) implicitly selects 24h mode since ms-bit is clear */ |
---|
257 | buf[DS1375_HR_OFF] = ds1375_bin2bcd( time->hour ); |
---|
258 | buf[DS1375_DAY_OFF] = tm.tm_wday + 1; |
---|
259 | buf[DS1375_DAT_OFF] = ds1375_bin2bcd( time->day ); |
---|
260 | buf[DS1375_MON_OFF] = ds1375_bin2bcd( time->month ); |
---|
261 | |
---|
262 | if ( time->year >= 2000 ) { |
---|
263 | buf[DS1375_YR_OFF] = ds1375_bin2bcd( time->year - 2000 ); |
---|
264 | buf[DS1375_MON_OFF] |= DS1375_MON_CTRY; |
---|
265 | } else { |
---|
266 | buf[DS1375_YR_OFF] = ds1375_bin2bcd( time->year - 1900 ); |
---|
267 | } |
---|
268 | |
---|
269 | /* |
---|
270 | * Stop clock; update registers and restart. This is slightly |
---|
271 | * slower than just writing everyting but if we did that we |
---|
272 | * could get inconsistent registers if this routine would not |
---|
273 | * complete in less than 1s (says the datasheet) and we don't |
---|
274 | * know if we are going to be pre-empted for some time... |
---|
275 | */ |
---|
276 | if ( ( fd = getfd( minor ) ) < 0 ) { |
---|
277 | goto cleanup; |
---|
278 | } |
---|
279 | |
---|
280 | if ( rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) |
---|
281 | goto cleanup; |
---|
282 | |
---|
283 | cr &= ~DS1375_CR_ECLK; |
---|
284 | |
---|
285 | /* This stops the clock */ |
---|
286 | if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) |
---|
287 | goto cleanup; |
---|
288 | |
---|
289 | /* write new contents */ |
---|
290 | if ( wr_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) |
---|
291 | goto cleanup; |
---|
292 | |
---|
293 | rval = 0; |
---|
294 | |
---|
295 | cleanup: |
---|
296 | if ( fd >= 0 ) { |
---|
297 | if ( ! ( DS1375_CR_ECLK & cr ) ) { |
---|
298 | /* start clock; this handles some cases of failure |
---|
299 | * after stopping the clock by restarting it again |
---|
300 | */ |
---|
301 | cr |= DS1375_CR_ECLK; |
---|
302 | if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) |
---|
303 | rval = -1; |
---|
304 | } |
---|
305 | close( fd ); |
---|
306 | } |
---|
307 | return rval; |
---|
308 | } |
---|
309 | |
---|
310 | /* Debugging / Testing */ |
---|
311 | |
---|
312 | #ifdef DEBUG |
---|
313 | |
---|
314 | /* Don't forget to set "TZ" when using these test routines */ |
---|
315 | |
---|
316 | /* What is rtems_time_of_day good for ? Why not use std types ? */ |
---|
317 | |
---|
318 | uint32_t |
---|
319 | ds1375_get_time_tst() |
---|
320 | { |
---|
321 | rtems_time_of_day rtod; |
---|
322 | time_t secs; |
---|
323 | |
---|
324 | ds1375_get_time( 0, &rtod ); |
---|
325 | secs = _TOD_To_seconds( &rtod ); |
---|
326 | printf( "%s\n", ctime( &secs ) ); |
---|
327 | return secs; |
---|
328 | } |
---|
329 | |
---|
330 | int |
---|
331 | ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt ) |
---|
332 | { |
---|
333 | struct tm tm; |
---|
334 | time_t secs; |
---|
335 | rtems_time_of_day rt; |
---|
336 | |
---|
337 | if ( !datstr ) |
---|
338 | return -1; |
---|
339 | |
---|
340 | if ( ! strptime( datstr, "%Y-%m-%d/%T", &tm ) ) |
---|
341 | return -2; |
---|
342 | |
---|
343 | if ( ! prt ) |
---|
344 | prt = &rt; |
---|
345 | |
---|
346 | secs = mktime( &tm ); |
---|
347 | |
---|
348 | /* convert to UTC */ |
---|
349 | gmtime_r( &secs, &tm ); |
---|
350 | |
---|
351 | printf("Y: %"PRIu32" ", (prt->year = tm.tm_year + 1900) ); |
---|
352 | printf("M: %"PRIu32" ", (prt->month = tm.tm_mon + 1) ); |
---|
353 | printf("D: %"PRIu32" ", (prt->day = tm.tm_mday ) ); |
---|
354 | printf("h: %"PRIu32" ", (prt->hour = tm.tm_hour ) ); |
---|
355 | printf("m: %"PRIu32" ", (prt->minute = tm.tm_min ) ); |
---|
356 | printf("s: %"PRIu32"\n", (prt->second = tm.tm_sec ) ); |
---|
357 | prt->ticks = 0; |
---|
358 | |
---|
359 | return ( prt == &rt ) ? ds1375_set_time( 0, &rt ) : 0; |
---|
360 | } |
---|
361 | |
---|
362 | #endif |
---|
363 | |
---|
364 | |
---|
365 | uint32_t |
---|
366 | rtc_ds1375_get_register( uint32_t port, uint8_t reg ) |
---|
367 | { |
---|
368 | int fd; |
---|
369 | uint8_t v; |
---|
370 | uint32_t rval = -1; |
---|
371 | |
---|
372 | if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) { |
---|
373 | |
---|
374 | if ( 0 == rd_bytes( fd, reg, &v, 1 ) ) { |
---|
375 | rval = v; |
---|
376 | } |
---|
377 | close( fd ); |
---|
378 | } |
---|
379 | |
---|
380 | return rval; |
---|
381 | } |
---|
382 | |
---|
383 | void |
---|
384 | rtc_ds1375_set_register( uint32_t port, uint8_t reg, uint32_t value ) |
---|
385 | { |
---|
386 | int fd; |
---|
387 | uint8_t v = value; |
---|
388 | |
---|
389 | if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) { |
---|
390 | wr_bytes( fd, reg, &v, 1 ); |
---|
391 | close( fd ); |
---|
392 | } |
---|
393 | |
---|
394 | } |
---|
395 | |
---|
396 | boolean |
---|
397 | rtc_ds1375_device_probe( int minor ) |
---|
398 | { |
---|
399 | int fd; |
---|
400 | |
---|
401 | if ( ( fd = getfd( minor ) ) < 0 ) { |
---|
402 | perror("ds1375_probe (open)"); |
---|
403 | return FALSE; |
---|
404 | } |
---|
405 | |
---|
406 | /* Try to set file pointer */ |
---|
407 | if ( 0 != wr_bytes( fd, DS1375_SEC_REG, 0, 0 ) ) { |
---|
408 | perror( "ds1375_probe (wr_bytes)" ); |
---|
409 | close( fd ); |
---|
410 | return FALSE; |
---|
411 | } |
---|
412 | |
---|
413 | if ( close( fd ) ) { |
---|
414 | perror( "ds1375_probe (close)" ); |
---|
415 | return FALSE; |
---|
416 | } |
---|
417 | |
---|
418 | return TRUE; |
---|
419 | } |
---|
420 | |
---|
421 | rtc_fns rtc_ds1375_fns = { |
---|
422 | deviceInitialize: ds1375_initialize, |
---|
423 | deviceGetTime: ds1375_get_time, |
---|
424 | deviceSetTime: ds1375_set_time, |
---|
425 | }; |
---|