forked from Miroirs/x49gp
448 lines
12 KiB
C
448 lines
12 KiB
C
/* $Id: s3c2410_rtc.c,v 1.5 2008/12/11 12:18:17 ecd Exp $
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include "x49gp.h"
|
|
#include "s3c2410.h"
|
|
#include "s3c2410_intc.h"
|
|
|
|
typedef struct {
|
|
uint32_t rtccon;
|
|
uint32_t ticnt;
|
|
uint32_t rtcalm;
|
|
uint32_t almsec;
|
|
uint32_t almmin;
|
|
uint32_t almhour;
|
|
uint32_t almdate;
|
|
uint32_t almmon;
|
|
uint32_t almyear;
|
|
uint32_t rtcrst;
|
|
uint32_t bcdsec;
|
|
uint32_t bcdmin;
|
|
uint32_t bcdhour;
|
|
uint32_t bcddate;
|
|
uint32_t bcdday;
|
|
uint32_t bcdmon;
|
|
uint32_t bcdyear;
|
|
|
|
unsigned int nr_regs;
|
|
s3c2410_offset_t* regs;
|
|
|
|
x49gp_t* x49gp;
|
|
x49gp_timer_t* tick_timer;
|
|
x49gp_timer_t* alarm_timer;
|
|
int64_t interval; /* us */
|
|
int64_t expires; /* us */
|
|
} s3c2410_rtc_t;
|
|
|
|
static int s3c2410_rtc_data_init( s3c2410_rtc_t* rtc )
|
|
{
|
|
s3c2410_offset_t regs[] = { S3C2410_OFFSET( RTC, RTCCON, 0x00, rtc->rtccon ), S3C2410_OFFSET( RTC, TICNT, 0x00, rtc->ticnt ),
|
|
S3C2410_OFFSET( RTC, RTCALM, 0x00, rtc->rtcalm ), S3C2410_OFFSET( RTC, ALMSEC, 0x00, rtc->almsec ),
|
|
S3C2410_OFFSET( RTC, ALMMIN, 0x00, rtc->almmin ), S3C2410_OFFSET( RTC, ALMHOUR, 0x00, rtc->almhour ),
|
|
S3C2410_OFFSET( RTC, ALMDATE, 0x01, rtc->almdate ), S3C2410_OFFSET( RTC, ALMMON, 0x01, rtc->almmon ),
|
|
S3C2410_OFFSET( RTC, ALMYEAR, 0x00, rtc->almyear ), S3C2410_OFFSET( RTC, RTCRST, 0x00, rtc->rtcrst ),
|
|
S3C2410_OFFSET( RTC, BCDSEC, 0, rtc->bcdsec ), S3C2410_OFFSET( RTC, BCDMIN, 0, rtc->bcdmin ),
|
|
S3C2410_OFFSET( RTC, BCDHOUR, 0, rtc->bcdhour ), S3C2410_OFFSET( RTC, BCDDATE, 0, rtc->bcddate ),
|
|
S3C2410_OFFSET( RTC, BCDDAY, 0, rtc->bcdday ), S3C2410_OFFSET( RTC, BCDMON, 0, rtc->bcdmon ),
|
|
S3C2410_OFFSET( RTC, BCDYEAR, 0, rtc->bcdyear ) };
|
|
|
|
memset( rtc, 0, sizeof( s3c2410_rtc_t ) );
|
|
|
|
rtc->regs = malloc( sizeof( regs ) );
|
|
if ( NULL == rtc->regs ) {
|
|
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy( rtc->regs, regs, sizeof( regs ) );
|
|
rtc->nr_regs = sizeof( regs ) / sizeof( regs[ 0 ] );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __inline__ uint32_t bin2bcd( uint32_t bin ) { return ( ( bin / 10 ) << 4 ) | ( bin % 10 ); }
|
|
|
|
static __inline__ uint32_t bcd2bin( uint32_t bcd ) { return ( ( bcd >> 4 ) * 10 ) + ( bcd & 0x0f ); }
|
|
|
|
static void s3c2410_rtc_timeout( void* user_data )
|
|
{
|
|
s3c2410_rtc_t* rtc = user_data;
|
|
x49gp_t* x49gp = rtc->x49gp;
|
|
int64_t now, us;
|
|
|
|
if ( !( rtc->ticnt & 0x80 ) ) {
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: assert TICK interrupt\n" );
|
|
#endif
|
|
|
|
s3c2410_intc_assert( x49gp, INT_TICK, 0 );
|
|
|
|
now = x49gp_get_clock();
|
|
while ( rtc->expires <= now ) {
|
|
rtc->expires += rtc->interval;
|
|
}
|
|
|
|
us = rtc->expires - now;
|
|
if ( us < 1000 )
|
|
us = 1000;
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: restart TICK timer (%llu us)\n", ( unsigned long long )us );
|
|
#endif
|
|
|
|
x49gp_mod_timer( rtc->tick_timer, rtc->expires );
|
|
}
|
|
|
|
static int s3c2410_rtc_set_ticnt( s3c2410_rtc_t* rtc )
|
|
{
|
|
int64_t now, us;
|
|
|
|
if ( x49gp_timer_pending( rtc->tick_timer ) ) {
|
|
x49gp_del_timer( rtc->tick_timer );
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: stop TICK timer\n" );
|
|
#endif
|
|
}
|
|
|
|
if ( !( rtc->ticnt & 0x80 ) ) {
|
|
return 0;
|
|
}
|
|
|
|
us = ( ( ( rtc->ticnt & 0x7f ) + 1 ) * 1000000 ) / 128;
|
|
|
|
rtc->interval = us;
|
|
if ( rtc->interval < 1000 )
|
|
rtc->interval = 1000;
|
|
|
|
now = x49gp_get_clock();
|
|
rtc->expires = now + rtc->interval;
|
|
|
|
us = rtc->expires - now;
|
|
if ( us < 1000 )
|
|
us = 1000;
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: start TICK timer (%lld us)\n", ( unsigned long long )us );
|
|
#endif
|
|
|
|
x49gp_mod_timer( rtc->tick_timer, rtc->expires );
|
|
return 0;
|
|
}
|
|
|
|
static void s3c2410_rtc_alarm( void* user_data )
|
|
{
|
|
s3c2410_rtc_t* rtc = user_data;
|
|
x49gp_t* x49gp = rtc->x49gp;
|
|
struct tm* tm;
|
|
struct timeval tv;
|
|
int64_t now, us;
|
|
int match = 1;
|
|
|
|
if ( !( rtc->rtcalm & 0x40 ) ) {
|
|
return;
|
|
}
|
|
|
|
gettimeofday( &tv, NULL );
|
|
tm = localtime( &tv.tv_sec );
|
|
|
|
now = x49gp_get_clock();
|
|
us = 1000000LL - tv.tv_usec;
|
|
|
|
if ( match && ( rtc->rtcalm & 0x01 ) ) {
|
|
if ( tm->tm_sec != bcd2bin( rtc->almsec ) )
|
|
match = 0;
|
|
}
|
|
if ( match && ( rtc->rtcalm & 0x02 ) ) {
|
|
if ( tm->tm_min != bcd2bin( rtc->almmin ) )
|
|
match = 0;
|
|
}
|
|
if ( match && ( rtc->rtcalm & 0x04 ) ) {
|
|
if ( tm->tm_hour != bcd2bin( rtc->almhour ) )
|
|
match = 0;
|
|
}
|
|
if ( match && ( rtc->rtcalm & 0x08 ) ) {
|
|
if ( tm->tm_mday != bcd2bin( rtc->almdate ) )
|
|
match = 0;
|
|
}
|
|
if ( match && ( rtc->rtcalm & 0x10 ) ) {
|
|
if ( ( tm->tm_mon + 1 ) != bcd2bin( rtc->almmon ) )
|
|
match = 0;
|
|
}
|
|
if ( match && ( rtc->rtcalm & 0x20 ) ) {
|
|
if ( ( tm->tm_year % 100 ) != bcd2bin( rtc->almyear ) )
|
|
match = 0;
|
|
}
|
|
|
|
if ( match ) {
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: assert ALARM interrupt\n" );
|
|
#endif
|
|
s3c2410_intc_assert( x49gp, INT_RTC, 0 );
|
|
}
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: reload ALARM timer (%lld us)\n", ( unsigned long long )us );
|
|
#endif
|
|
|
|
x49gp_mod_timer( rtc->alarm_timer, now + us );
|
|
}
|
|
|
|
static int s3c2410_rtc_set_rtcalm( s3c2410_rtc_t* rtc )
|
|
{
|
|
struct timeval tv;
|
|
int64_t now, us;
|
|
|
|
if ( !( rtc->rtcalm & 0x40 ) ) {
|
|
x49gp_del_timer( rtc->alarm_timer );
|
|
return 0;
|
|
}
|
|
|
|
gettimeofday( &tv, NULL );
|
|
|
|
now = x49gp_get_clock();
|
|
us = 1000000LL - tv.tv_usec;
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "RTC: start ALARM timer (%lld us)\n", ( unsigned long long )us );
|
|
#endif
|
|
|
|
x49gp_mod_timer( rtc->alarm_timer, now + us );
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t s3c2410_rtc_read( void* opaque, target_phys_addr_t offset )
|
|
{
|
|
s3c2410_rtc_t* rtc = opaque;
|
|
s3c2410_offset_t* reg;
|
|
|
|
if ( !S3C2410_OFFSET_OK( rtc, offset ) ) {
|
|
return ~( 0 );
|
|
}
|
|
|
|
reg = S3C2410_OFFSET_ENTRY( rtc, offset );
|
|
|
|
if ( S3C2410_RTC_BCDSEC <= offset && offset <= S3C2410_RTC_BCDYEAR ) {
|
|
struct tm* tm;
|
|
struct timeval tv;
|
|
|
|
gettimeofday( &tv, NULL );
|
|
tm = localtime( &tv.tv_sec );
|
|
|
|
switch ( offset ) {
|
|
case S3C2410_RTC_BCDSEC:
|
|
*( reg->datap ) = bin2bcd( tm->tm_sec );
|
|
break;
|
|
case S3C2410_RTC_BCDMIN:
|
|
*( reg->datap ) = bin2bcd( tm->tm_min );
|
|
break;
|
|
case S3C2410_RTC_BCDHOUR:
|
|
*( reg->datap ) = bin2bcd( tm->tm_hour );
|
|
break;
|
|
case S3C2410_RTC_BCDDATE:
|
|
*( reg->datap ) = bin2bcd( tm->tm_mday );
|
|
break;
|
|
case S3C2410_RTC_BCDDAY:
|
|
*( reg->datap ) = bin2bcd( tm->tm_wday + 1 );
|
|
break;
|
|
case S3C2410_RTC_BCDMON:
|
|
*( reg->datap ) = bin2bcd( tm->tm_mon + 1 );
|
|
break;
|
|
case S3C2410_RTC_BCDYEAR:
|
|
*( reg->datap ) = bin2bcd( tm->tm_year % 100 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "read %s [%08x] %s [%08lx] data %08x\n", "s3c2410-rtc", S3C2410_RTC_BASE, reg->name, ( unsigned long )offset,
|
|
*( reg->datap ) );
|
|
#endif
|
|
|
|
return *( reg->datap );
|
|
}
|
|
|
|
static void s3c2410_rtc_write( void* opaque, target_phys_addr_t offset, uint32_t data )
|
|
{
|
|
s3c2410_rtc_t* rtc = opaque;
|
|
s3c2410_offset_t* reg;
|
|
|
|
if ( !S3C2410_OFFSET_OK( rtc, offset ) ) {
|
|
return;
|
|
}
|
|
|
|
reg = S3C2410_OFFSET_ENTRY( rtc, offset );
|
|
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "write %s [%08x] %s [%08lx] data %08x\n", "s3c2410-rtc", S3C2410_RTC_BASE, reg->name, ( unsigned long )offset, data );
|
|
#endif
|
|
|
|
switch ( offset ) {
|
|
case S3C2410_RTC_TICNT:
|
|
*( reg->datap ) = data;
|
|
s3c2410_rtc_set_ticnt( rtc );
|
|
break;
|
|
case S3C2410_RTC_RTCALM:
|
|
*( reg->datap ) = data;
|
|
s3c2410_rtc_set_rtcalm( rtc );
|
|
break;
|
|
default:
|
|
*( reg->datap ) = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int s3c2410_rtc_load( x49gp_module_t* module, GKeyFile* key )
|
|
{
|
|
s3c2410_rtc_t* rtc = module->user_data;
|
|
s3c2410_offset_t* reg;
|
|
int error = 0;
|
|
int i;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
for ( i = 0; i < rtc->nr_regs; i++ ) {
|
|
reg = &rtc->regs[ i ];
|
|
|
|
if ( NULL == reg->name )
|
|
continue;
|
|
|
|
if ( x49gp_module_get_u32( module, key, reg->name, reg->reset, reg->datap ) )
|
|
error = -EAGAIN;
|
|
}
|
|
|
|
s3c2410_rtc_set_ticnt( rtc );
|
|
s3c2410_rtc_set_rtcalm( rtc );
|
|
|
|
return error;
|
|
}
|
|
|
|
static int s3c2410_rtc_save( x49gp_module_t* module, GKeyFile* key )
|
|
{
|
|
s3c2410_rtc_t* rtc = module->user_data;
|
|
s3c2410_offset_t* reg;
|
|
int i;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
for ( i = 0; i < rtc->nr_regs; i++ ) {
|
|
reg = &rtc->regs[ i ];
|
|
|
|
if ( NULL == reg->name )
|
|
continue;
|
|
|
|
x49gp_module_set_u32( module, key, reg->name, *( reg->datap ) );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s3c2410_rtc_reset( x49gp_module_t* module, x49gp_reset_t reset )
|
|
{
|
|
s3c2410_rtc_t* rtc = module->user_data;
|
|
s3c2410_offset_t* reg;
|
|
int i;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
for ( i = 0; i < ( S3C2410_RTC_RTCALM >> 2 ); i++ ) {
|
|
reg = &rtc->regs[ i ];
|
|
|
|
if ( NULL == reg->name )
|
|
continue;
|
|
|
|
*( reg->datap ) = reg->reset;
|
|
}
|
|
|
|
s3c2410_rtc_set_ticnt( rtc );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static CPUReadMemoryFunc* s3c2410_rtc_readfn[] = { s3c2410_rtc_read, s3c2410_rtc_read, s3c2410_rtc_read };
|
|
|
|
static CPUWriteMemoryFunc* s3c2410_rtc_writefn[] = { s3c2410_rtc_write, s3c2410_rtc_write, s3c2410_rtc_write };
|
|
|
|
static int s3c2410_rtc_init( x49gp_module_t* module )
|
|
{
|
|
s3c2410_rtc_t* rtc;
|
|
int iotype;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
rtc = malloc( sizeof( s3c2410_rtc_t ) );
|
|
if ( NULL == rtc ) {
|
|
fprintf( stderr, "%s: %s:%u: Out of memory\n", module->x49gp->progname, __FUNCTION__, __LINE__ );
|
|
return -ENOMEM;
|
|
}
|
|
if ( s3c2410_rtc_data_init( rtc ) ) {
|
|
free( rtc );
|
|
return -ENOMEM;
|
|
}
|
|
|
|
module->user_data = rtc;
|
|
rtc->x49gp = module->x49gp;
|
|
|
|
rtc->tick_timer = x49gp_new_timer( X49GP_TIMER_REALTIME, s3c2410_rtc_timeout, rtc );
|
|
rtc->alarm_timer = x49gp_new_timer( X49GP_TIMER_REALTIME, s3c2410_rtc_alarm, rtc );
|
|
|
|
iotype = cpu_register_io_memory( s3c2410_rtc_readfn, s3c2410_rtc_writefn, rtc );
|
|
#ifdef DEBUG_S3C2410_RTC
|
|
printf( "%s: iotype %08x\n", __FUNCTION__, iotype );
|
|
#endif
|
|
cpu_register_physical_memory( S3C2410_RTC_BASE, S3C2410_MAP_SIZE, iotype );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s3c2410_rtc_exit( x49gp_module_t* module )
|
|
{
|
|
s3c2410_rtc_t* rtc;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
if ( module->user_data ) {
|
|
rtc = module->user_data;
|
|
if ( rtc->regs )
|
|
free( rtc->regs );
|
|
free( rtc );
|
|
}
|
|
|
|
x49gp_module_unregister( module );
|
|
free( module );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int x49gp_s3c2410_rtc_init( x49gp_t* x49gp )
|
|
{
|
|
x49gp_module_t* module;
|
|
|
|
if ( x49gp_module_init( x49gp, "s3c2410-rtc", s3c2410_rtc_init, s3c2410_rtc_exit, s3c2410_rtc_reset, s3c2410_rtc_load, s3c2410_rtc_save,
|
|
NULL, &module ) ) {
|
|
return -1;
|
|
}
|
|
|
|
return x49gp_module_register( module );
|
|
}
|