#include "pxa255_RTC.h"
#include "mem.h"



void pxa255rtcPrvUpdate(Pxa255rtc* rtc){
	
	UInt32 time = rtcCurTime();
	
	if(rtc->lastSeenTime != time){	//do not triger alarm more than once per second please
		
		if((rtc->RTSR & 0x4) && (time + rtc->RCNR_offset == rtc->RTAR)){	//check alarm
			rtc->RTSR |= 1;
		}
		if(rtc->RTSR & 0x8){							//send HZ interrupt
			rtc->RTSR |= 2;
		}
	}
	pxa255icInt(rtc->ic, PXA255_I_RTC_ALM, (rtc->RTSR & 1) != 0);
	pxa255icInt(rtc->ic, PXA255_I_RTC_HZ, (rtc->RTSR & 2) != 0);
}

static Boolean pxa255rtcPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){

	Pxa255rtc* rtc = userData;
	UInt32 val = 0;
	
	if(size != 4) {
		err_str(__FILE__ ": Unexpected ");
	//	err_str(write ? "write" : "read");
	//	err_str(" of ");
	//	err_dec(size);
	//	err_str(" bytes to 0x");
	//	err_hex(pa);
	//	err_str("\r\n");
		return true;		//we do not support non-word accesses
	}
	
	pa = (pa - PXA255_RTC_BASE) >> 2;
	
	if(write){
		val = *(UInt32*)buf;
		
		switch(pa){
			case 0:
				rtc->RCNR_offset = rtcCurTime() - val;
				break;
			
			case 1:
				rtc->RTAR = val;
				pxa255rtcPrvUpdate(rtc);
				break;
			
			case 2:
				rtc->RTSR = (val &~ 3UL) | ((rtc->RTSR &~ val) & 3UL);
				pxa255rtcPrvUpdate(rtc);
				break;
			
			case 3:
				if(!(rtc->RTTR & 0x80000000UL)) rtc->RTTR = val;
				break;
		}
	}
	else{
		switch(pa){
			case 0:
				val = rtcCurTime() - rtc->RCNR_offset;
				break;
			
			case 1:
				val = rtc->RTAR;
				break;
			
			case 2:
				val = rtc->RTSR;
				break;
			
			case 3:
				val = rtc->RTTR;
				break;
		}
		*(UInt32*)buf = val;
	}
	
	return true;
}


Boolean pxa255rtcInit(Pxa255rtc* rtc, ArmMem* physMem, Pxa255ic* ic){
	
	__mem_zero(rtc, sizeof(Pxa255rtc));
	rtc->ic = ic;
	rtc->RCNR_offset = 0;
	rtc->RTTR = 0x7FFF;	//nice default value
	rtc->lastSeenTime = rtcCurTime();
	return memRegionAdd(physMem, PXA255_RTC_BASE, PXA255_RTC_SIZE, pxa255rtcPrvMemAccessF, rtc);
}

void pxa255rtcUpdate(Pxa255rtc* rtc){
	pxa255rtcPrvUpdate(rtc);
}
