source: rtems/c/src/libchip/rtc/ds1375.c @ d371a97

4.104.114.9
Last change on this file since d371a97 was d371a97, checked in by Till Straumann <strauman@…>, on Nov 21, 2007 at 6:23:12 AM

2007-11-20 Till Straumann <strauman@…>

  • libchip/rtc/ds1375.c, libchip/rtc/ds1375-rtc.h: Avoid using 'stdio' before the system is up and fully initialized.
  • Property mode set to 100644
File size: 11.7 KB
Line 
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 <sys/errno.h>
58#include <stdio.h>
59#include <inttypes.h>
60
61
62#define STATIC static
63#undef  DEBUG
64
65/* The RTC driver routines are possibly called during
66 * system initialization -- that would be prior to opening
67 * the console. At this point it is not safe to use stdio
68 * (printf, perror etc.).
69 * Our file descriptors may even be 0..2
70 */
71#define STDIOSAFE(fmt,args...)          \
72        do {                                \
73                if ( _System_state_Is_up( _System_state_Get() ) ) { \
74                        fprintf(stderr,fmt,args);   \
75                } else {                        \
76                        printk(fmt,args);           \
77                }                               \
78        } while (0)
79 
80
81STATIC uint8_t ds1375_bcd2bin(uint8_t x)
82{
83uint8_t h = x & 0xf0;
84
85        /* 8*hi + 2*hi + lo */
86        return ( h >> 1 ) + ( h >> 3 ) + ( x & 0xf );
87}
88
89STATIC uint8_t ds1375_bin2bcd(uint8_t x)
90{
91uint8_t h = x/10;
92
93        return ( h << 4 ) + ( x - ( ( h << 3 ) + ( h << 1 ) ) );
94}
95
96/*
97 * Register Definitions and Access Macros
98 *
99 * The xxx_REG macros are offsets into the register files
100 * The xxx_OFF macros are offsets into a in-memory buffer
101 *             starting at the seconds (for the 1375 both,
102 *             _REG and _OFF happen to be identical).
103 */
104
105
106#define DS1375_SEC_REG  0x0
107#define DS1375_SEC_OFF  (DS1375_SEC_REG-DS1375_SEC_REG)
108/* Extract seconds and convert to binary */
109#define DS1375_SEC(x)   ds1375_bcd2bin( ((x)[DS1375_SEC_OFF]) & 0x7f )
110
111#define DS1375_MIN_REG  0x1
112#define DS1375_MIN_OFF  (DS1375_MIN_REG-DS1375_SEC_REG)
113/* Extract minutes and convert to binary */
114#define DS1375_MIN(x)   ds1375_bcd2bin( ((x)[DS1375_MIN_OFF]) & 0x7f )
115
116#define DS1375_HR_REG   0x2
117#define DS1375_HR_OFF   (DS1375_HR_REG-DS1375_SEC_REG)
118#define DS1375_HR_1224  (1<<6)
119#define DS1375_HR_AMPM  (1<<5)
120/* Are hours in AM/PM representation ?   */
121#define DS1375_IS_AMPM(x)       (DS1375_HR_1224 & ((x)[DS1375_HR_OFF]))
122/* Are we PM ?                           */
123#define DS1375_IS_PM(x)         (DS1375_HR_AMPM & ((x)[DS1375_HR_OFF]))
124/* Extract hours (12h mode) and convert to binary */
125#define DS1375_HR_12(x) ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x1f )
126/* Extract hours (24h mode) and convert to binary */
127#define DS1375_HR_24(x) ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x3f )
128
129#define DS1375_DAY_REG  0x3
130#define DS1375_DAY_OFF  (DS1375_DAY_REG-DS1375_SEC_REG)
131#define DS1375_DAT_REG  0x4
132#define DS1375_DAT_OFF  (DS1375_DAT_REG-DS1375_SEC_REG)
133/* Extract date and convert to binary    */
134#define DS1375_DAT(x)   ds1375_bcd2bin( ((x)[DS1375_DAT_OFF]) & 0x3f )
135#define DS1375_MON_REG  0x5
136#define DS1375_MON_OFF  (DS1375_MON_REG-DS1375_SEC_REG)
137#define DS1375_MON_CTRY         (1<<7)
138/* Is century bit set ?                  */
139#define DS1375_IS_CTRY(x)       (((x)[DS1375_MON_OFF]) & DS1375_MON_CTRY)
140/* Extract month and convert to binary   */
141#define DS1375_MON(x)   ds1375_bcd2bin( ((x)[DS1375_MON_OFF]) & 0x1f )
142
143#define DS1375_YR_REG   0x6
144#define DS1375_YR_OFF   (DS1375_YR_REG-DS1375_SEC_REG)
145/* Extract year and convert to binary    */
146#define DS1375_YR(x)    ds1375_bcd2bin( ((x)[DS1375_YR_OFF]) & 0xff )
147
148/* CR Register and bit definitions       */
149#define DS1375_CR_REG   0xe
150#define DS1375_CR_ECLK          (1<<7)
151#define DS1375_CR_CLKSEL1       (1<<6)
152#define DS1375_CR_CLKSEL0       (1<<5)
153#define DS1375_CR_RS2           (1<<4)
154#define DS1375_CR_RS1           (1<<3)
155#define DS1375_CR_INTCN         (1<<2)
156#define DS1375_CR_A2IE          (1<<1)
157#define DS1375_CR_A1IE          (1<<0)
158
159#define DS1375_CSR_REG  0xf
160
161/* User SRAM (8 bytes)                   */
162#define DS1375_RAM              0x10    /* start of 8 bytes user ram */
163
164/* Access Primitives                     */
165
166STATIC int
167rd_bytes( int fd, uint32_t off, uint8_t *buf, int len )
168{
169uint8_t ptr = off;
170
171        return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1;
172}
173
174STATIC int
175wr_bytes( int fd, uint32_t off, uint8_t *buf, int len )
176{
177uint8_t d[ len + 1 ];
178
179        /* Must not break up writing of the register pointer and
180         * the data to-be-written into multiple write() calls
181         * because every 'write()' operation sends START and
182         * the chip interprets the first byte after START as
183         * the register pointer.
184         */
185       
186        d[0] = off;
187        memcpy( d + 1, buf, len );
188
189        return len + 1 == write( fd, d, len + 1 ) ? 0 : -1;
190}
191
192/* Helpers                               */
193
194static int
195getfd( int minor )
196{
197        return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR );
198}
199
200/* Driver Access Functions               */
201
202STATIC void
203ds1375_initialize( int minor )
204{
205int     fd;
206uint8_t cr;
207
208        if ( ( fd = getfd( minor ) ) >= 0 ) {
209                if ( 0 == rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) {
210                        /* make sure clock is enabled */
211                        if ( ! ( DS1375_CR_ECLK & cr ) ) {
212                                cr |= DS1375_CR_ECLK;
213                                wr_bytes( fd, DS1375_CR_REG, &cr, 1 );
214                        }
215                }
216                close( fd );
217        }
218
219}
220
221STATIC int
222ds1375_get_time( int minor, rtems_time_of_day *time )
223{
224int     rval = -1;
225int     fd;
226uint8_t buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
227
228        if ( time && ( ( fd = getfd( minor ) ) >= 0 ) ) {
229                if ( 0 == rd_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) {
230                        time->year       =  DS1375_IS_CTRY( buf ) ? 2000 : 1900;
231                        time->year  +=  DS1375_YR ( buf );
232                        time->month  =  DS1375_MON( buf );
233                        time->day    =  DS1375_DAT( buf );      /* DAY is weekday */
234
235                        if ( DS1375_IS_AMPM( buf ) ) {
236                                time->hour   = DS1375_HR_12 ( buf );
237                                if ( DS1375_IS_PM( buf ) )
238                                        time->hour += 12;
239                        } else {
240                                time->hour   = DS1375_HR_24 ( buf );
241                        }
242
243                        time->minute =  DS1375_MIN( buf );
244                        time->second =  DS1375_SEC( buf );
245                        time->ticks  = 0;
246                        rval = 0;
247                }
248                close( fd );
249        }
250        return rval;
251}
252
253STATIC int
254ds1375_set_time( int minor, rtems_time_of_day *time )
255{
256int       rval = -1;
257int       fd   = -1;
258time_t    secs;
259struct tm tm;
260uint8_t   buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
261uint8_t   cr = 0xff;
262        /*
263         * The clock hardware maintains the day-of-week as a separate counter
264         * so we must compute it ourselves (rtems_time_of_day doesn't come
265         * with a day of week).
266         */
267        secs = _TOD_To_seconds( time );
268        /* we're only interested in tm_wday... */
269        gmtime_r( &secs, &tm );
270
271        buf[DS1375_SEC_OFF] = ds1375_bin2bcd( time->second );
272        buf[DS1375_MIN_OFF] = ds1375_bin2bcd( time->minute );
273        /* bin2bcd(hour) implicitly selects 24h mode since ms-bit is clear */
274        buf[DS1375_HR_OFF]  = ds1375_bin2bcd( time->hour   );
275        buf[DS1375_DAY_OFF] = tm.tm_wday + 1;
276        buf[DS1375_DAT_OFF] = ds1375_bin2bcd( time->day    );
277        buf[DS1375_MON_OFF] = ds1375_bin2bcd( time->month  );
278       
279        if ( time->year >= 2000 ) {
280                buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 2000 );
281                buf[DS1375_MON_OFF] |= DS1375_MON_CTRY;
282        } else {
283                buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 1900 );
284        }
285
286        /*
287         * Stop clock; update registers and restart. This is slightly
288         * slower than just writing everyting but if we did that we
289         * could get inconsistent registers if this routine would not
290         * complete in less than 1s (says the datasheet) and we don't
291         * know if we are going to be pre-empted for some time...
292         */
293        if ( ( fd = getfd( minor ) ) < 0 ) {
294                goto cleanup;
295        }
296
297        if ( rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
298                goto cleanup;
299
300        cr &= ~DS1375_CR_ECLK;
301
302        /* This stops the clock */
303        if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
304                goto cleanup;
305
306        /* write new contents */
307        if ( wr_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) )
308                goto cleanup;
309
310        rval = 0;
311       
312cleanup:
313        if ( fd >= 0 ) {
314                if ( ! ( DS1375_CR_ECLK & cr ) ) {
315                        /* start clock; this handles some cases of failure
316                         * after stopping the clock by restarting it again
317                         */
318                        cr |= DS1375_CR_ECLK;
319                        if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
320                                rval = -1;
321                }
322                close( fd );
323        }
324        return rval;
325}
326
327/* Debugging / Testing                   */
328
329#ifdef DEBUG
330
331/* Don't forget to set "TZ" when using these test routines */
332
333/* What is rtems_time_of_day good for ? Why not use std types ? */
334
335uint32_t
336ds1375_get_time_tst()
337{
338rtems_time_of_day rtod;
339time_t            secs;
340       
341        ds1375_get_time( 0, &rtod );           
342        secs = _TOD_To_seconds( &rtod );
343        printf( "%s\n", ctime( &secs ) );
344        return secs;
345}
346
347int
348ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt )
349{
350struct tm         tm;
351time_t            secs;
352rtems_time_of_day rt;
353
354        if ( !datstr )
355                return -1;
356
357        if ( ! strptime( datstr, "%Y-%m-%d/%T", &tm ) )
358                return -2;
359
360        if ( ! prt )
361                prt = &rt;
362       
363        secs = mktime( &tm );
364
365        /* convert to UTC */
366        gmtime_r( &secs, &tm );
367
368        printf("Y: %"PRIu32" ",  (prt->year    = tm.tm_year + 1900) );
369        printf("M: %"PRIu32" ",  (prt->month   = tm.tm_mon  +    1) );
370        printf("D: %"PRIu32" ",  (prt->day     = tm.tm_mday       ) );
371        printf("h: %"PRIu32" ",  (prt->hour    = tm.tm_hour       ) );
372        printf("m: %"PRIu32" ",  (prt->minute  = tm.tm_min        ) );
373        printf("s: %"PRIu32"\n", (prt->second  = tm.tm_sec        ) );
374        prt->ticks = 0;
375
376        return ( prt == &rt ) ? ds1375_set_time( 0, &rt ) : 0;
377}
378
379#endif
380
381
382uint32_t
383rtc_ds1375_get_register( uint32_t port, uint8_t reg )
384{
385int      fd;
386uint8_t  v;
387uint32_t rval = -1;
388
389        if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
390
391                if ( 0 == rd_bytes( fd, reg, &v, 1 ) ) {
392                        rval = v;
393                }
394                close( fd );
395        }
396
397        return rval;
398}
399
400void
401rtc_ds1375_set_register( uint32_t port, uint8_t reg, uint32_t value )
402{
403int     fd;
404uint8_t v = value;
405       
406        if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
407                wr_bytes( fd, reg, &v, 1 );
408                close( fd );
409        }
410
411}
412
413boolean
414rtc_ds1375_device_probe( int minor )
415{
416int fd;
417
418        if ( ( fd = getfd( minor ) ) < 0 ) {
419                STDIOSAFE( "ds1375_probe (open): %s\n", strerror( errno ) );
420                return FALSE;
421        }
422
423        /* Try to set file pointer */
424        if ( 0 != wr_bytes( fd, DS1375_SEC_REG, 0, 0 ) ) {
425                STDIOSAFE( "ds1375_probe (wr_bytes): %s\n", strerror( errno ) );
426                close( fd );
427                return FALSE;
428        }
429
430        if ( close( fd ) ) {
431                STDIOSAFE( "ds1375_probe (close): %s\n", strerror( errno ) );
432                return FALSE;
433        }
434
435        return TRUE;
436}
437
438rtc_fns rtc_ds1375_fns = {
439        deviceInitialize:       ds1375_initialize,
440        deviceGetTime:          ds1375_get_time,
441        deviceSetTime:          ds1375_set_time,
442};
Note: See TracBrowser for help on using the repository browser.