source: rtems/c/src/libchip/rtc/ds1375.c @ 381a2f18

4.104.114.9
Last change on this file since 381a2f18 was 381a2f18, checked in by Till Straumann <strauman@…>, on Nov 20, 2007 at 8:16:43 PM

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

  • libchip/rtc/ds1375.c, libchip/rtc/ds1375-rtc.h,
  • libchip/Makefile.am, libchip/preinstall.am: added new driver for Maxim DS1375 i2c RTC.
  • Property mode set to 100644
File size: 11.0 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 <stdio.h>
58#include <inttypes.h>
59
60
61#define STATIC static
62#undef  DEBUG
63
64STATIC uint8_t ds1375_bcd2bin(uint8_t x)
65{
66uint8_t h = x & 0xf0;
67
68        /* 8*hi + 2*hi + lo */
69        return ( h >> 1 ) + ( h >> 3 ) + ( x & 0xf );
70}
71
72STATIC uint8_t ds1375_bin2bcd(uint8_t x)
73{
74uint8_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
149STATIC int
150rd_bytes( int fd, uint32_t off, uint8_t *buf, int len )
151{
152uint8_t ptr = off;
153
154        return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1;
155}
156
157STATIC int
158wr_bytes( int fd, uint32_t off, uint8_t *buf, int len )
159{
160uint8_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
177static int
178getfd( int minor )
179{
180        return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR );
181}
182
183/* Driver Access Functions               */
184
185STATIC void
186ds1375_initialize( int minor )
187{
188int     fd;
189uint8_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
204STATIC int
205ds1375_get_time( int minor, rtems_time_of_day *time )
206{
207int     rval = -1;
208int     fd;
209uint8_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
236STATIC int
237ds1375_set_time( int minor, rtems_time_of_day *time )
238{
239int       rval = -1;
240int       fd   = -1;
241time_t    secs;
242struct tm tm;
243uint8_t   buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
244uint8_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       
295cleanup:
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
318uint32_t
319ds1375_get_time_tst()
320{
321rtems_time_of_day rtod;
322time_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
330int
331ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt )
332{
333struct tm         tm;
334time_t            secs;
335rtems_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
365uint32_t
366rtc_ds1375_get_register( uint32_t port, uint8_t reg )
367{
368int      fd;
369uint8_t  v;
370uint32_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
383void
384rtc_ds1375_set_register( uint32_t port, uint8_t reg, uint32_t value )
385{
386int     fd;
387uint8_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
396boolean
397rtc_ds1375_device_probe( int minor )
398{
399int 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
421rtc_fns rtc_ds1375_fns = {
422        deviceInitialize:       ds1375_initialize,
423        deviceGetTime:          ds1375_get_time,
424        deviceSetTime:          ds1375_set_time,
425};
Note: See TracBrowser for help on using the repository browser.