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

4.115
Last change on this file since 6279149 was 39046f7, checked in by Sebastian Huber <sebastian.huber@…>, on 07/24/13 at 09:09:23

score: Merge sysstate API into one file

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