source: rtems/bsps/shared/dev/rtc/ds1375.c @ 762fa62

5
Last change on this file since 762fa62 was 27de4e1f, checked in by Sebastian Huber <sebastian.huber@…>, on 04/03/18 at 05:20:11

bsps: Move libchip to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

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