Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /* Driver for the Maxim 1375 i2c RTC (TOD only; very simple...) */
0002 
0003 /*
0004  * Authorship
0005  * ----------
0006  * This software was created by
0007  *
0008  *     Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
0009  *      Stanford Linear Accelerator Center, Stanford University.
0010  *
0011  * Acknowledgement of sponsorship
0012  * ------------------------------
0013  * The software was produced by
0014  *     the Stanford Linear Accelerator Center, Stanford University,
0015  *      under Contract DE-AC03-76SFO0515 with the Department of Energy.
0016  *
0017  * Government disclaimer of liability
0018  * ----------------------------------
0019  * Neither the United States nor the United States Department of Energy,
0020  * nor any of their employees, makes any warranty, express or implied, or
0021  * assumes any legal liability or responsibility for the accuracy,
0022  * completeness, or usefulness of any data, apparatus, product, or process
0023  * disclosed, or represents that its use would not infringe privately owned
0024  * rights.
0025  *
0026  * Stanford disclaimer of liability
0027  * --------------------------------
0028  * Stanford University makes no representations or warranties, express or
0029  * implied, nor assumes any liability for the use of this software.
0030  *
0031  * Stanford disclaimer of copyright
0032  * --------------------------------
0033  * Stanford University, owner of the copyright, hereby disclaims its
0034  * copyright and all other rights in this software.  Hence, anyone may
0035  * freely use it for any purpose without restriction.
0036  *
0037  * Maintenance of notices
0038  * ----------------------
0039  * In the interest of clarity regarding the origin and status of this
0040  * SLAC software, this and all the preceding Stanford University notices
0041  * are to remain affixed to any copy or derivative of this software made
0042  * or distributed by the recipient and are to be affixed to any copy of
0043  * software made or distributed by the recipient that contains a copy or
0044  * derivative of this software.
0045  *
0046  * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
0047  */
0048 
0049 /* This driver uses the file-system interface to the i2c bus */
0050 
0051 #include <unistd.h> /* write, read, close */
0052 
0053 #include <rtems.h>
0054 #include <rtems/bspIo.h>
0055 #include <rtems/rtc.h>
0056 #include <rtems/score/sysstate.h>
0057 #include <rtems/rtems/clockimpl.h>
0058 #include <libchip/rtc.h>
0059 #include <libchip/ds1375-rtc.h>
0060 
0061 #include <sys/fcntl.h>
0062 #include <errno.h>
0063 #include <stdio.h>
0064 #include <string.h>
0065 #include <inttypes.h>
0066 
0067 
0068 #define STATIC static
0069 #undef  DEBUG
0070 
0071 /* The RTC driver routines are possibly called during
0072  * system initialization -- that would be prior to opening
0073  * the console. At this point it is not safe to use stdio
0074  * (printf, perror etc.).
0075  * Our file descriptors may even be 0..2
0076  */
0077 #define STDIOSAFE(fmt,args...)        \
0078   do {                                \
0079     if ( _System_state_Is_up( _System_state_Get() ) ) { \
0080       fprintf(stderr,fmt,args);   \
0081     } else {                        \
0082       printk(fmt,args);           \
0083     }                               \
0084   } while (0)
0085 
0086 
0087 STATIC uint8_t ds1375_bcd2bin(uint8_t x)
0088 {
0089   uint8_t h = x & 0xf0;
0090 
0091   /* 8*hi + 2*hi + lo */
0092   return ( h >> 1 ) + ( h >> 3 ) + ( x & 0xf );
0093 }
0094 
0095 STATIC uint8_t ds1375_bin2bcd(uint8_t x)
0096 {
0097   uint8_t h = x/10;
0098 
0099   return ( h << 4 ) + ( x - ( ( h << 3 ) + ( h << 1 ) ) );
0100 }
0101 
0102 /*
0103  * Register Definitions and Access Macros
0104  *
0105  * The xxx_REG macros are offsets into the register files
0106  * The xxx_OFF macros are offsets into a in-memory buffer
0107  *             starting at the seconds (for the 1375 both,
0108  *             _REG and _OFF happen to be identical).
0109  */
0110 #define DS1375_SEC_REG  0x0
0111 #define DS1375_SEC_OFF  (DS1375_SEC_REG-DS1375_SEC_REG)
0112 /* Extract seconds and convert to binary */
0113 #define DS1375_SEC(x)  ds1375_bcd2bin( ((x)[DS1375_SEC_OFF]) & 0x7f )
0114 
0115 #define DS1375_MIN_REG  0x1
0116 #define DS1375_MIN_OFF  (DS1375_MIN_REG-DS1375_SEC_REG)
0117 /* Extract minutes and convert to binary */
0118 #define DS1375_MIN(x)  ds1375_bcd2bin( ((x)[DS1375_MIN_OFF]) & 0x7f )
0119 
0120 #define DS1375_HR_REG  0x2
0121 #define DS1375_HR_OFF  (DS1375_HR_REG-DS1375_SEC_REG)
0122 #define DS1375_HR_1224  (1<<6)
0123 #define DS1375_HR_AMPM  (1<<5)
0124 /* Are hours in AM/PM representation ?   */
0125 #define DS1375_IS_AMPM(x)  (DS1375_HR_1224 & ((x)[DS1375_HR_OFF]))
0126 /* Are we PM ?                           */
0127 #define DS1375_IS_PM(x)    (DS1375_HR_AMPM & ((x)[DS1375_HR_OFF]))
0128 /* Extract hours (12h mode) and convert to binary */
0129 #define DS1375_HR_12(x)  ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x1f )
0130 /* Extract hours (24h mode) and convert to binary */
0131 #define DS1375_HR_24(x)  ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x3f )
0132 
0133 #define DS1375_DAY_REG  0x3
0134 #define DS1375_DAY_OFF  (DS1375_DAY_REG-DS1375_SEC_REG)
0135 #define DS1375_DAT_REG  0x4
0136 #define DS1375_DAT_OFF  (DS1375_DAT_REG-DS1375_SEC_REG)
0137 /* Extract date and convert to binary    */
0138 #define DS1375_DAT(x)  ds1375_bcd2bin( ((x)[DS1375_DAT_OFF]) & 0x3f )
0139 #define DS1375_MON_REG  0x5
0140 #define DS1375_MON_OFF  (DS1375_MON_REG-DS1375_SEC_REG)
0141 #define DS1375_MON_CTRY    (1<<7)
0142 /* Is century bit set ?                  */
0143 #define DS1375_IS_CTRY(x)  (((x)[DS1375_MON_OFF]) & DS1375_MON_CTRY)
0144 /* Extract month and convert to binary   */
0145 #define DS1375_MON(x)  ds1375_bcd2bin( ((x)[DS1375_MON_OFF]) & 0x1f )
0146 
0147 #define DS1375_YR_REG  0x6
0148 #define DS1375_YR_OFF  (DS1375_YR_REG-DS1375_SEC_REG)
0149 /* Extract year and convert to binary    */
0150 #define DS1375_YR(x)  ds1375_bcd2bin( ((x)[DS1375_YR_OFF]) & 0xff )
0151 
0152 /* CR Register and bit definitions       */
0153 #define DS1375_CR_REG  0xe
0154 #define DS1375_CR_ECLK    (1<<7)
0155 #define DS1375_CR_CLKSEL1  (1<<6)
0156 #define DS1375_CR_CLKSEL0  (1<<5)
0157 #define DS1375_CR_RS2    (1<<4)
0158 #define DS1375_CR_RS1    (1<<3)
0159 #define DS1375_CR_INTCN    (1<<2)
0160 #define DS1375_CR_A2IE    (1<<1)
0161 #define DS1375_CR_A1IE    (1<<0)
0162 
0163 #define DS1375_CSR_REG  0xf
0164 
0165 /* User SRAM (8 bytes)                   */
0166 #define DS1375_RAM    0x10  /* start of 8 bytes user ram */
0167 
0168 /* Access Primitives                     */
0169 
0170 STATIC int rd_bytes(
0171   int      fd,
0172   uint32_t off,
0173   uint8_t *buf,
0174   int      len
0175 )
0176 {
0177   uint8_t ptr = off;
0178 
0179   return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1;
0180 }
0181 
0182 STATIC int wr_bytes(
0183   int      fd,
0184   uint32_t off,
0185   uint8_t *buf,
0186   int      len
0187 )
0188 {
0189   uint8_t d[ len + 1 ];
0190 
0191   /* Must not break up writing of the register pointer and
0192    * the data to-be-written into multiple write() calls
0193    * because every 'write()' operation sends START and
0194    * the chip interprets the first byte after START as
0195    * the register pointer.
0196    */
0197 
0198   d[0] = off;
0199   memcpy( d + 1, buf, len );
0200 
0201   return len + 1 == write( fd, d, len + 1 ) ? 0 : -1;
0202 }
0203 
0204 /* Helpers                               */
0205 
0206 static int getfd(
0207   int minor
0208 )
0209 {
0210   return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR );
0211 }
0212 
0213 /* Driver Access Functions               */
0214 
0215 STATIC void ds1375_initialize(
0216   int minor
0217 )
0218 {
0219   int     fd;
0220   uint8_t cr;
0221 
0222   if ( ( fd = getfd( minor ) ) >= 0 ) {
0223     if ( 0 == rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) {
0224       /* make sure clock is enabled */
0225       if ( ! ( DS1375_CR_ECLK & cr ) ) {
0226         cr |= DS1375_CR_ECLK;
0227         wr_bytes( fd, DS1375_CR_REG, &cr, 1 );
0228       }
0229     }
0230     close( fd );
0231   }
0232 
0233 }
0234 
0235 STATIC int ds1375_get_time(
0236   int                minor,
0237   rtems_time_of_day *time
0238 )
0239 {
0240   int   rval = -1;
0241   int   fd;
0242   uint8_t  buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
0243 
0244   if ( time && ( ( fd = getfd( minor ) ) >= 0 ) ) {
0245     if ( 0 == rd_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) {
0246       time->year    =  DS1375_IS_CTRY( buf ) ? 2000 : 1900;
0247       time->year  +=  DS1375_YR ( buf );
0248       time->month  =  DS1375_MON( buf );
0249       time->day    =  DS1375_DAT( buf );  /* DAY is weekday */
0250 
0251       if ( DS1375_IS_AMPM( buf ) ) {
0252         time->hour   = DS1375_HR_12 ( buf );
0253         if ( DS1375_IS_PM( buf ) )
0254           time->hour += 12;
0255       } else {
0256         time->hour   = DS1375_HR_24 ( buf );
0257       }
0258 
0259       time->minute =  DS1375_MIN( buf );
0260       time->second =  DS1375_SEC( buf );
0261       time->ticks  = 0;
0262       rval = 0;
0263     }
0264     close( fd );
0265   }
0266   return rval;
0267 }
0268 
0269 STATIC int ds1375_set_time(
0270   int                      minor,
0271   const rtems_time_of_day *time
0272 )
0273 {
0274   int       rval = -1;
0275   int       fd   = -1;
0276   time_t    secs;
0277   struct tm tm;
0278   uint8_t   buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
0279   uint8_t   cr = 0xff;
0280 
0281   /*
0282    * The clock hardware maintains the day-of-week as a separate counter
0283    * so we must compute it ourselves (rtems_time_of_day doesn't come
0284    * with a day of week).
0285    */
0286   secs = _TOD_To_seconds( time );
0287   /* we're only interested in tm_wday... */
0288   gmtime_r( &secs, &tm );
0289 
0290   buf[DS1375_SEC_OFF] = ds1375_bin2bcd( time->second );
0291   buf[DS1375_MIN_OFF] = ds1375_bin2bcd( time->minute );
0292   /* bin2bcd(hour) implicitly selects 24h mode since ms-bit is clear */
0293   buf[DS1375_HR_OFF]  = ds1375_bin2bcd( time->hour   );
0294   buf[DS1375_DAY_OFF] = tm.tm_wday + 1;
0295   buf[DS1375_DAT_OFF] = ds1375_bin2bcd( time->day    );
0296   buf[DS1375_MON_OFF] = ds1375_bin2bcd( time->month  );
0297 
0298   if ( time->year >= 2000 ) {
0299     buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 2000 );
0300     buf[DS1375_MON_OFF] |= DS1375_MON_CTRY;
0301   } else {
0302     buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 1900 );
0303   }
0304 
0305   /*
0306    * Stop clock; update registers and restart. This is slightly
0307    * slower than just writing everyting but if we did that we
0308    * could get inconsistent registers if this routine would not
0309    * complete in less than 1s (says the datasheet) and we don't
0310    * know if we are going to be pre-empted for some time...
0311    */
0312   if ( ( fd = getfd( minor ) ) < 0 ) {
0313     goto cleanup;
0314   }
0315 
0316   if ( rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
0317     goto cleanup;
0318 
0319   cr &= ~DS1375_CR_ECLK;
0320 
0321   /* This stops the clock */
0322   if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
0323     goto cleanup;
0324 
0325   /* write new contents */
0326   if ( wr_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) )
0327     goto cleanup;
0328 
0329   rval = 0;
0330 
0331 cleanup:
0332   if ( fd >= 0 ) {
0333     if ( ! ( DS1375_CR_ECLK & cr ) ) {
0334       /* start clock; this handles some cases of failure
0335        * after stopping the clock by restarting it again
0336        */
0337       cr |= DS1375_CR_ECLK;
0338       if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
0339         rval = -1;
0340     }
0341     close( fd );
0342   }
0343   return rval;
0344 }
0345 
0346 /* Debugging / Testing                   */
0347 
0348 #ifdef DEBUG
0349 
0350 /* Don't forget to set "TZ" when using these test routines */
0351 
0352 /* What is rtems_time_of_day good for ? Why not use std types ? */
0353 
0354 uint32_t
0355 ds1375_get_time_tst()
0356 {
0357 rtems_time_of_day rtod;
0358 time_t            secs;
0359 
0360   ds1375_get_time( 0, &rtod );
0361   secs = _TOD_To_seconds( &rtod );
0362   printf( "%s\n", ctime( &secs ) );
0363   return secs;
0364 }
0365 
0366 int
0367 ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt )
0368 {
0369 struct tm         tm;
0370 time_t            secs;
0371 rtems_time_of_day rt;
0372 
0373   if ( !datstr )
0374     return -1;
0375 
0376   if ( ! strptime( datstr, "%Y-%m-%d/%T", &tm ) )
0377     return -2;
0378 
0379   if ( ! prt )
0380     prt = &rt;
0381 
0382   secs = mktime( &tm );
0383 
0384   /* convert to UTC */
0385   gmtime_r( &secs, &tm );
0386 
0387   printf("Y: %"PRIu32" ",  (prt->year    = tm.tm_year + 1900) );
0388   printf("M: %"PRIu32" ",  (prt->month   = tm.tm_mon  +    1) );
0389   printf("D: %"PRIu32" ",  (prt->day     = tm.tm_mday       ) );
0390   printf("h: %"PRIu32" ",  (prt->hour    = tm.tm_hour       ) );
0391   printf("m: %"PRIu32" ",  (prt->minute  = tm.tm_min        ) );
0392   printf("s: %"PRIu32"\n", (prt->second  = tm.tm_sec        ) );
0393   prt->ticks = 0;
0394 
0395   return ( prt == &rt ) ? ds1375_set_time( 0, &rt ) : 0;
0396 }
0397 
0398 #endif
0399 
0400 
0401 uint32_t
0402 rtc_ds1375_get_register( uintptr_t port, uint8_t reg )
0403 {
0404 int      fd;
0405 uint8_t  v;
0406 uint32_t rval = -1;
0407 
0408   if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
0409 
0410     if ( 0 == rd_bytes( fd, reg, &v, 1 ) ) {
0411       rval = v;
0412     }
0413     close( fd );
0414   }
0415 
0416   return rval;
0417 }
0418 
0419 void
0420 rtc_ds1375_set_register( uintptr_t port, uint8_t reg, uint32_t value )
0421 {
0422 int     fd;
0423 uint8_t v = value;
0424 
0425   if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
0426     wr_bytes( fd, reg, &v, 1 );
0427     close( fd );
0428   }
0429 
0430 }
0431 
0432 bool rtc_ds1375_device_probe(
0433  int minor
0434 )
0435 {
0436   int fd;
0437 
0438   if ( ( fd = getfd( minor ) ) < 0 ) {
0439     STDIOSAFE( "ds1375_probe (open): %s\n", strerror( errno ) );
0440     return false;
0441   }
0442 
0443   /* Try to set file pointer */
0444   if ( 0 != wr_bytes( fd, DS1375_SEC_REG, 0, 0 ) ) {
0445     STDIOSAFE( "ds1375_probe (wr_bytes): %s\n", strerror( errno ) );
0446     close( fd );
0447     return false;
0448   }
0449 
0450   if ( close( fd ) ) {
0451     STDIOSAFE( "ds1375_probe (close): %s\n", strerror( errno ) );
0452     return false;
0453   }
0454 
0455   return true;
0456 }
0457 
0458 rtc_fns rtc_ds1375_fns = {
0459   .deviceInitialize = ds1375_initialize,
0460   .deviceGetTime =    ds1375_get_time,
0461   .deviceSetTime =    ds1375_set_time,
0462 };