summaryrefslogblamecommitdiffstats
path: root/bsps/shared/dev/rtc/ds1375.c
blob: e51744355f41c9f965b9ddcec5d2cc9d735322ad (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                  
  




                                                              
                                                                
  



                                                                   
                                                                       
  







                                                                           
  



                                                                         
  



                                                                      

                                                     







                                                                        
  
                                                                        
   


                                                               

                                            
                  
                        
                      
                                 
                                  



                               
                  
                  
                   





                     





                                                          







                                                         
 
 

                                        
                       
 

                                               



                                        
                   
 
                                                          









                                                          

                                                       
                                           
                                                                     
 

                                                       
                                           
                                                                     
 



                                                     
                                           
                                                                  
                                           
                                                                  
                                                    
                                                                      
                                                    
                                                                      
 



                                                       
                                           



                                                                     
                                           
                                                                    
                                           
                                                                     
 

                                                     
                                           
                                                                   

                                           










                                 

                                           
                                                           


                                           





                    
 
                    
 
                                                                           

 





                    
 







                                                          
 



                                                     



                                           


                 
 
                                                                    



                                           


                              
 












                                                       


 



                           
 


























                                                                  

 



                                 
 






















                                                                       
 































                                                               
 
        











                                                        














                                                                  

                              


                                   








                                                                 

                
 

                                                 
 

              
 
                       
 

                         
 






                                                                
 
                                                        





        
                                                      




                   
                                                          
 




                                            
 
              


    
                                                                      


                  
 



                                                          


 


                             
 



















                                                                    


                          


                                        
  
/* Driver for the Maxim 1375 i2c RTC (TOD only; very simple...) */

/*
 * Authorship
 * ----------
 * This software was created by
 *
 *     Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
 *      Stanford Linear Accelerator Center, Stanford University.
 *
 * Acknowledgement of sponsorship
 * ------------------------------
 * The software was produced by
 *     the Stanford Linear Accelerator Center, Stanford University,
 *      under Contract DE-AC03-76SFO0515 with the Department of Energy.
 *
 * Government disclaimer of liability
 * ----------------------------------
 * Neither the United States nor the United States Department of Energy,
 * nor any of their employees, makes any warranty, express or implied, or
 * assumes any legal liability or responsibility for the accuracy,
 * completeness, or usefulness of any data, apparatus, product, or process
 * disclosed, or represents that its use would not infringe privately owned
 * rights.
 *
 * Stanford disclaimer of liability
 * --------------------------------
 * Stanford University makes no representations or warranties, express or
 * implied, nor assumes any liability for the use of this software.
 *
 * Stanford disclaimer of copyright
 * --------------------------------
 * Stanford University, owner of the copyright, hereby disclaims its
 * copyright and all other rights in this software.  Hence, anyone may
 * freely use it for any purpose without restriction.
 *
 * Maintenance of notices
 * ----------------------
 * In the interest of clarity regarding the origin and status of this
 * SLAC software, this and all the preceding Stanford University notices
 * are to remain affixed to any copy or derivative of this software made
 * or distributed by the recipient and are to be affixed to any copy of
 * software made or distributed by the recipient that contains a copy or
 * derivative of this software.
 *
 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
 */

/* This driver uses the file-system interface to the i2c bus */

#include <unistd.h> /* write, read, close */

#include <rtems.h>
#include <rtems/bspIo.h>
#include <rtems/rtc.h>
#include <rtems/score/sysstate.h>
#include <rtems/rtems/clockimpl.h>
#include <libchip/rtc.h>
#include <libchip/ds1375-rtc.h>

#include <sys/fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>


#define STATIC static
#undef  DEBUG

/* The RTC driver routines are possibly called during
 * system initialization -- that would be prior to opening
 * the console. At this point it is not safe to use stdio
 * (printf, perror etc.).
 * Our file descriptors may even be 0..2
 */
#define STDIOSAFE(fmt,args...)        \
  do {                                \
    if ( _System_state_Is_up( _System_state_Get() ) ) { \
      fprintf(stderr,fmt,args);   \
    } else {                        \
      printk(fmt,args);           \
    }                               \
  } while (0)


STATIC uint8_t ds1375_bcd2bin(uint8_t x)
{
  uint8_t h = x & 0xf0;

  /* 8*hi + 2*hi + lo */
  return ( h >> 1 ) + ( h >> 3 ) + ( x & 0xf );
}

STATIC uint8_t ds1375_bin2bcd(uint8_t x)
{
  uint8_t h = x/10;

  return ( h << 4 ) + ( x - ( ( h << 3 ) + ( h << 1 ) ) );
}

/*
 * Register Definitions and Access Macros
 *
 * The xxx_REG macros are offsets into the register files
 * The xxx_OFF macros are offsets into a in-memory buffer
 *             starting at the seconds (for the 1375 both,
 *             _REG and _OFF happen to be identical).
 */
#define DS1375_SEC_REG  0x0
#define DS1375_SEC_OFF  (DS1375_SEC_REG-DS1375_SEC_REG)
/* Extract seconds and convert to binary */
#define DS1375_SEC(x)  ds1375_bcd2bin( ((x)[DS1375_SEC_OFF]) & 0x7f )

#define DS1375_MIN_REG  0x1
#define DS1375_MIN_OFF  (DS1375_MIN_REG-DS1375_SEC_REG)
/* Extract minutes and convert to binary */
#define DS1375_MIN(x)  ds1375_bcd2bin( ((x)[DS1375_MIN_OFF]) & 0x7f )

#define DS1375_HR_REG  0x2
#define DS1375_HR_OFF  (DS1375_HR_REG-DS1375_SEC_REG)
#define DS1375_HR_1224  (1<<6)
#define DS1375_HR_AMPM  (1<<5)
/* Are hours in AM/PM representation ?   */
#define DS1375_IS_AMPM(x)  (DS1375_HR_1224 & ((x)[DS1375_HR_OFF]))
/* Are we PM ?                           */
#define DS1375_IS_PM(x)    (DS1375_HR_AMPM & ((x)[DS1375_HR_OFF]))
/* Extract hours (12h mode) and convert to binary */
#define DS1375_HR_12(x)  ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x1f )
/* Extract hours (24h mode) and convert to binary */
#define DS1375_HR_24(x)  ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x3f )

#define DS1375_DAY_REG  0x3
#define DS1375_DAY_OFF  (DS1375_DAY_REG-DS1375_SEC_REG)
#define DS1375_DAT_REG  0x4
#define DS1375_DAT_OFF  (DS1375_DAT_REG-DS1375_SEC_REG)
/* Extract date and convert to binary    */
#define DS1375_DAT(x)  ds1375_bcd2bin( ((x)[DS1375_DAT_OFF]) & 0x3f )
#define DS1375_MON_REG  0x5
#define DS1375_MON_OFF  (DS1375_MON_REG-DS1375_SEC_REG)
#define DS1375_MON_CTRY    (1<<7)
/* Is century bit set ?                  */
#define DS1375_IS_CTRY(x)  (((x)[DS1375_MON_OFF]) & DS1375_MON_CTRY)
/* Extract month and convert to binary   */
#define DS1375_MON(x)  ds1375_bcd2bin( ((x)[DS1375_MON_OFF]) & 0x1f )

#define DS1375_YR_REG  0x6
#define DS1375_YR_OFF  (DS1375_YR_REG-DS1375_SEC_REG)
/* Extract year and convert to binary    */
#define DS1375_YR(x)  ds1375_bcd2bin( ((x)[DS1375_YR_OFF]) & 0xff )

/* CR Register and bit definitions       */
#define DS1375_CR_REG  0xe
#define DS1375_CR_ECLK    (1<<7)
#define DS1375_CR_CLKSEL1  (1<<6)
#define DS1375_CR_CLKSEL0  (1<<5)
#define DS1375_CR_RS2    (1<<4)
#define DS1375_CR_RS1    (1<<3)
#define DS1375_CR_INTCN    (1<<2)
#define DS1375_CR_A2IE    (1<<1)
#define DS1375_CR_A1IE    (1<<0)

#define DS1375_CSR_REG  0xf

/* User SRAM (8 bytes)                   */
#define DS1375_RAM    0x10  /* start of 8 bytes user ram */

/* Access Primitives                     */

STATIC int rd_bytes(
  int      fd,
  uint32_t off,
  uint8_t *buf,
  int      len
)
{
  uint8_t ptr = off;

  return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1;
}

STATIC int wr_bytes(
  int      fd,
  uint32_t off,
  uint8_t *buf,
  int      len
)
{
  uint8_t d[ len + 1 ];

  /* Must not break up writing of the register pointer and
   * the data to-be-written into multiple write() calls
   * because every 'write()' operation sends START and
   * the chip interprets the first byte after START as
   * the register pointer.
   */

  d[0] = off;
  memcpy( d + 1, buf, len );

  return len + 1 == write( fd, d, len + 1 ) ? 0 : -1;
}

/* Helpers                               */

static int getfd(
  int minor
)
{
  return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR );
}

/* Driver Access Functions               */

STATIC void ds1375_initialize(
  int minor
)
{
  int     fd;
  uint8_t cr;

  if ( ( fd = getfd( minor ) ) >= 0 ) {
    if ( 0 == rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) {
      /* make sure clock is enabled */
      if ( ! ( DS1375_CR_ECLK & cr ) ) {
        cr |= DS1375_CR_ECLK;
        wr_bytes( fd, DS1375_CR_REG, &cr, 1 );
      }
    }
    close( fd );
  }

}

STATIC int ds1375_get_time(
  int                minor,
  rtems_time_of_day *time
)
{
  int   rval = -1;
  int   fd;
  uint8_t  buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];

  if ( time && ( ( fd = getfd( minor ) ) >= 0 ) ) {
    if ( 0 == rd_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) {
      time->year    =  DS1375_IS_CTRY( buf ) ? 2000 : 1900;
      time->year  +=  DS1375_YR ( buf );
      time->month  =  DS1375_MON( buf );
      time->day    =  DS1375_DAT( buf );  /* DAY is weekday */

      if ( DS1375_IS_AMPM( buf ) ) {
        time->hour   = DS1375_HR_12 ( buf );
        if ( DS1375_IS_PM( buf ) )
          time->hour += 12;
      } else {
        time->hour   = DS1375_HR_24 ( buf );
      }

      time->minute =  DS1375_MIN( buf );
      time->second =  DS1375_SEC( buf );
      time->ticks  = 0;
      rval = 0;
    }
    close( fd );
  }
  return rval;
}

STATIC int ds1375_set_time(
  int                      minor,
  const rtems_time_of_day *time
)
{
  int       rval = -1;
  int       fd   = -1;
  time_t    secs;
  struct tm tm;
  uint8_t   buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
  uint8_t   cr = 0xff;

  /*
   * The clock hardware maintains the day-of-week as a separate counter
   * so we must compute it ourselves (rtems_time_of_day doesn't come
   * with a day of week).
   */
  secs = _TOD_To_seconds( time );
  /* we're only interested in tm_wday... */
  gmtime_r( &secs, &tm );

  buf[DS1375_SEC_OFF] = ds1375_bin2bcd( time->second );
  buf[DS1375_MIN_OFF] = ds1375_bin2bcd( time->minute );
  /* bin2bcd(hour) implicitly selects 24h mode since ms-bit is clear */
  buf[DS1375_HR_OFF]  = ds1375_bin2bcd( time->hour   );
  buf[DS1375_DAY_OFF] = tm.tm_wday + 1;
  buf[DS1375_DAT_OFF] = ds1375_bin2bcd( time->day    );
  buf[DS1375_MON_OFF] = ds1375_bin2bcd( time->month  );

  if ( time->year >= 2000 ) {
    buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 2000 );
    buf[DS1375_MON_OFF] |= DS1375_MON_CTRY;
  } else {
    buf[DS1375_YR_OFF]   = ds1375_bin2bcd( time->year - 1900 );
  }

  /*
   * Stop clock; update registers and restart. This is slightly
   * slower than just writing everyting but if we did that we
   * could get inconsistent registers if this routine would not
   * complete in less than 1s (says the datasheet) and we don't
   * know if we are going to be pre-empted for some time...
   */
  if ( ( fd = getfd( minor ) ) < 0 ) {
    goto cleanup;
  }

  if ( rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
    goto cleanup;

  cr &= ~DS1375_CR_ECLK;

  /* This stops the clock */
  if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
    goto cleanup;

  /* write new contents */
  if ( wr_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) )
    goto cleanup;

  rval = 0;

cleanup:
  if ( fd >= 0 ) {
    if ( ! ( DS1375_CR_ECLK & cr ) ) {
      /* start clock; this handles some cases of failure
       * after stopping the clock by restarting it again
       */
      cr |= DS1375_CR_ECLK;
      if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
        rval = -1;
    }
    close( fd );
  }
  return rval;
}

/* Debugging / Testing                   */

#ifdef DEBUG

/* Don't forget to set "TZ" when using these test routines */

/* What is rtems_time_of_day good for ? Why not use std types ? */

uint32_t
ds1375_get_time_tst()
{
rtems_time_of_day rtod;
time_t            secs;

  ds1375_get_time( 0, &rtod );
  secs = _TOD_To_seconds( &rtod );
  printf( "%s\n", ctime( &secs ) );
  return secs;
}

int
ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt )
{
struct tm         tm;
time_t            secs;
rtems_time_of_day rt;

  if ( !datstr )
    return -1;

  if ( ! strptime( datstr, "%Y-%m-%d/%T", &tm ) )
    return -2;

  if ( ! prt )
    prt = &rt;

  secs = mktime( &tm );

  /* convert to UTC */
  gmtime_r( &secs, &tm );

  printf("Y: %"PRIu32" ",  (prt->year    = tm.tm_year + 1900) );
  printf("M: %"PRIu32" ",  (prt->month   = tm.tm_mon  +    1) );
  printf("D: %"PRIu32" ",  (prt->day     = tm.tm_mday       ) );
  printf("h: %"PRIu32" ",  (prt->hour    = tm.tm_hour       ) );
  printf("m: %"PRIu32" ",  (prt->minute  = tm.tm_min        ) );
  printf("s: %"PRIu32"\n", (prt->second  = tm.tm_sec        ) );
  prt->ticks = 0;

  return ( prt == &rt ) ? ds1375_set_time( 0, &rt ) : 0;
}

#endif


uint32_t
rtc_ds1375_get_register( uintptr_t port, uint8_t reg )
{
int      fd;
uint8_t  v;
uint32_t rval = -1;

  if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {

    if ( 0 == rd_bytes( fd, reg, &v, 1 ) ) {
      rval = v;
    }
    close( fd );
  }

  return rval;
}

void
rtc_ds1375_set_register( uintptr_t port, uint8_t reg, uint32_t value )
{
int     fd;
uint8_t v = value;

  if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
    wr_bytes( fd, reg, &v, 1 );
    close( fd );
  }

}

bool rtc_ds1375_device_probe(
 int minor
)
{
  int fd;

  if ( ( fd = getfd( minor ) ) < 0 ) {
    STDIOSAFE( "ds1375_probe (open): %s\n", strerror( errno ) );
    return false;
  }

  /* Try to set file pointer */
  if ( 0 != wr_bytes( fd, DS1375_SEC_REG, 0, 0 ) ) {
    STDIOSAFE( "ds1375_probe (wr_bytes): %s\n", strerror( errno ) );
    close( fd );
    return false;
  }

  if ( close( fd ) ) {
    STDIOSAFE( "ds1375_probe (close): %s\n", strerror( errno ) );
    return false;
  }

  return true;
}

rtc_fns rtc_ds1375_fns = {
  .deviceInitialize = ds1375_initialize,
  .deviceGetTime =    ds1375_get_time,
  .deviceSetTime =    ds1375_set_time,
};