source: rtems/c/src/libchip/rtc/ds1375.c @ 9c2eaca

4.104.115
Last change on this file since 9c2eaca was 9c2eaca, checked in by Joel Sherrill <joel.sherrill@…>, on May 4, 2009 at 12:53:45 AM

2009-05-03 Joel Sherrill <joel.sherrill@…>

  • libchip/rtc/ds1375.c, libchip/rtc/mc146818a.c: Fix warnings. Reformat as needed.
  • Property mode set to 100644
File size: 12.1 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 <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{
85  uint8_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{
93  uint8_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#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 rd_bytes(
167  int      fd,
168  uint32_t off,
169  uint8_t *buf,
170  int      len
171)
172{
173  uint8_t ptr = off;
174
175  return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1;
176}
177
178STATIC int wr_bytes(
179  int      fd,
180  uint32_t off,
181  uint8_t *buf,
182  int      len
183)
184{
185  uint8_t d[ len + 1 ];
186
187  /* Must not break up writing of the register pointer and
188   * the data to-be-written into multiple write() calls
189   * because every 'write()' operation sends START and
190   * the chip interprets the first byte after START as
191   * the register pointer.
192   */
193 
194  d[0] = off;
195  memcpy( d + 1, buf, len );
196
197  return len + 1 == write( fd, d, len + 1 ) ? 0 : -1;
198}
199
200/* Helpers                               */
201
202static int getfd(
203  int minor
204)
205{
206  return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR );
207}
208
209/* Driver Access Functions               */
210
211STATIC void ds1375_initialize(
212  int minor
213)
214{
215  int     fd;
216  uint8_t cr;
217
218  if ( ( fd = getfd( minor ) ) >= 0 ) {
219    if ( 0 == rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) {
220      /* make sure clock is enabled */
221      if ( ! ( DS1375_CR_ECLK & cr ) ) {
222        cr |= DS1375_CR_ECLK;
223        wr_bytes( fd, DS1375_CR_REG, &cr, 1 );
224      }
225    }
226    close( fd );
227  }
228
229}
230
231STATIC int ds1375_get_time(
232  int                minor,
233  rtems_time_of_day *time
234)
235{
236  int   rval = -1;
237  int   fd;
238  uint8_t  buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
239
240  if ( time && ( ( fd = getfd( minor ) ) >= 0 ) ) {
241    if ( 0 == rd_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) {
242      time->year    =  DS1375_IS_CTRY( buf ) ? 2000 : 1900;
243      time->year  +=  DS1375_YR ( buf );
244      time->month  =  DS1375_MON( buf );
245      time->day    =  DS1375_DAT( buf );  /* DAY is weekday */
246
247      if ( DS1375_IS_AMPM( buf ) ) {
248        time->hour   = DS1375_HR_12 ( buf );
249        if ( DS1375_IS_PM( buf ) )
250          time->hour += 12;
251      } else {
252        time->hour   = DS1375_HR_24 ( buf );
253      }
254
255      time->minute =  DS1375_MIN( buf );
256      time->second =  DS1375_SEC( buf );
257      time->ticks  = 0;
258      rval = 0;
259    }
260    close( fd );
261  }
262  return rval;
263}
264
265STATIC int ds1375_set_time(
266  int                      minor,
267  const rtems_time_of_day *time
268)
269{
270  int       rval = -1;
271  int       fd   = -1;
272  time_t    secs;
273  struct tm tm;
274  uint8_t   buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
275  uint8_t   cr = 0xff;
276
277  /*
278   * The clock hardware maintains the day-of-week as a separate counter
279   * so we must compute it ourselves (rtems_time_of_day doesn't come
280   * with a day of week).
281   */
282  secs = _TOD_To_seconds( time );
283  /* we're only interested in tm_wday... */
284  gmtime_r( &secs, &tm );
285
286  buf[DS1375_SEC_OFF] = ds1375_bin2bcd( time->second );
287  buf[DS1375_MIN_OFF] = ds1375_bin2bcd( time->minute );
288  /* bin2bcd(hour) implicitly selects 24h mode since ms-bit is clear */
289  buf[DS1375_HR_OFF]  = ds1375_bin2bcd( time->hour   );
290  buf[DS1375_DAY_OFF] = tm.tm_wday + 1;
291  buf[DS1375_DAT_OFF] = ds1375_bin2bcd( time->day    );
292  buf[DS1375_MON_OFF] = ds1375_bin2bcd( time->month  );
293 
294  if ( time->year >= 2000 ) {
295    buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 2000 );
296    buf[DS1375_MON_OFF] |= DS1375_MON_CTRY;
297  } else {
298    buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 1900 );
299  }
300
301  /*
302   * Stop clock; update registers and restart. This is slightly
303   * slower than just writing everyting but if we did that we
304   * could get inconsistent registers if this routine would not
305   * complete in less than 1s (says the datasheet) and we don't
306   * know if we are going to be pre-empted for some time...
307   */
308  if ( ( fd = getfd( minor ) ) < 0 ) {
309    goto cleanup;
310  }
311
312  if ( rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
313    goto cleanup;
314
315  cr &= ~DS1375_CR_ECLK;
316
317  /* This stops the clock */
318  if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
319    goto cleanup;
320
321  /* write new contents */
322  if ( wr_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) )
323    goto cleanup;
324
325  rval = 0;
326 
327cleanup:
328  if ( fd >= 0 ) {
329    if ( ! ( DS1375_CR_ECLK & cr ) ) {
330      /* start clock; this handles some cases of failure
331       * after stopping the clock by restarting it again
332       */
333      cr |= DS1375_CR_ECLK;
334      if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
335        rval = -1;
336    }
337    close( fd );
338  }
339  return rval;
340}
341
342/* Debugging / Testing                   */
343
344#ifdef DEBUG
345
346/* Don't forget to set "TZ" when using these test routines */
347
348/* What is rtems_time_of_day good for ? Why not use std types ? */
349
350uint32_t
351ds1375_get_time_tst()
352{
353rtems_time_of_day rtod;
354time_t            secs;
355 
356  ds1375_get_time( 0, &rtod );   
357  secs = _TOD_To_seconds( &rtod );
358  printf( "%s\n", ctime( &secs ) );
359  return secs;
360}
361
362int
363ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt )
364{
365struct tm         tm;
366time_t            secs;
367rtems_time_of_day rt;
368
369  if ( !datstr )
370    return -1;
371
372  if ( ! strptime( datstr, "%Y-%m-%d/%T", &tm ) )
373    return -2;
374
375  if ( ! prt )
376    prt = &rt;
377 
378  secs = mktime( &tm );
379
380  /* convert to UTC */
381  gmtime_r( &secs, &tm );
382
383  printf("Y: %"PRIu32" ",  (prt->year    = tm.tm_year + 1900) );
384  printf("M: %"PRIu32" ",  (prt->month   = tm.tm_mon  +    1) );
385  printf("D: %"PRIu32" ",  (prt->day     = tm.tm_mday       ) );
386  printf("h: %"PRIu32" ",  (prt->hour    = tm.tm_hour       ) );
387  printf("m: %"PRIu32" ",  (prt->minute  = tm.tm_min        ) );
388  printf("s: %"PRIu32"\n", (prt->second  = tm.tm_sec        ) );
389  prt->ticks = 0;
390
391  return ( prt == &rt ) ? ds1375_set_time( 0, &rt ) : 0;
392}
393
394#endif
395
396
397uint32_t
398rtc_ds1375_get_register( uint32_t port, uint8_t reg )
399{
400int      fd;
401uint8_t  v;
402uint32_t rval = -1;
403
404  if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
405
406    if ( 0 == rd_bytes( fd, reg, &v, 1 ) ) {
407      rval = v;
408    }
409    close( fd );
410  }
411
412  return rval;
413}
414
415void
416rtc_ds1375_set_register( uint32_t port, uint8_t reg, uint32_t value )
417{
418int     fd;
419uint8_t v = value;
420 
421  if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
422    wr_bytes( fd, reg, &v, 1 );
423    close( fd );
424  }
425
426}
427
428bool rtc_ds1375_device_probe(
429 int minor
430)
431{
432  int fd;
433
434  if ( ( fd = getfd( minor ) ) < 0 ) {
435    STDIOSAFE( "ds1375_probe (open): %s\n", strerror( errno ) );
436    return false;
437  }
438
439  /* Try to set file pointer */
440  if ( 0 != wr_bytes( fd, DS1375_SEC_REG, 0, 0 ) ) {
441    STDIOSAFE( "ds1375_probe (wr_bytes): %s\n", strerror( errno ) );
442    close( fd );
443    return false;
444  }
445
446  if ( close( fd ) ) {
447    STDIOSAFE( "ds1375_probe (close): %s\n", strerror( errno ) );
448    return false;
449  }
450
451  return true;
452}
453
454rtc_fns rtc_ds1375_fns = {
455  deviceInitialize:  ds1375_initialize,
456  deviceGetTime:    ds1375_get_time,
457  deviceSetTime:    ds1375_set_time,
458};
Note: See TracBrowser for help on using the repository browser.