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

4.104.114.95
Last change on this file since b4d0eec3 was 6640459d, checked in by Ralf Corsepius <ralf.corsepius@…>, on 09/07/08 at 03:44:14

Convert to "bool".

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