; Copyright 2009 Castle Technology Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
        GET     Hdr:ListOpts
        GET     Hdr:Macros
        GET     Hdr:System
        GET     Hdr:Machine.<Machine>
        GET     Hdr:ImageSize.<ImageSize>
        $GetIO

        GET     Hdr:OSEntries
        GET     Hdr:HALEntries
        GET     Hdr:HALDevice
        GET     Hdr:RTCDevice

        GET     hdr.omap3530
        GET     hdr.StaticWS
        GET     hdr.Timers
        GET     hdr.PRCM

        AREA    |Asm$$Code|, CODE, READONLY, PIC

        EXPORT  RTC_Init
        IMPORT  TPSRead
        IMPORT  TPSWrite

;       Note - debug stuff won't work since we don't get passed a HAL workspace pointer!
;        IMPORT  DebugHALPrint
;        IMPORT  DebugHALPrintReg
;        IMPORT  DebugMemDump
;        IMPORT  DebugHALPrintByte

; TWL/TPS RTC IIC address
TPSRTC_IIC     * &4b

; Some RTC registers
SECONDS_REG       * &1C
RTC_CTRL_REG      * &29
RTC_STATUS_REG    * &2a


        MACRO
        CallOS  $entry, $tailcall
        ASSERT  $entry <= HighestOSEntry
 [ "$tailcall"=""
        MOV     lr, pc
 |
   [ "$tailcall"<>"tailcall"
        ! 0, "Unrecognised parameter to CallOS"
   ]
 ]
        LDR     pc, OSentries + 4*$entry
        MEND

RTC_Init
        ; Just register our HAL Device with the OS
        MOV     a1, #0
        ADR     a2, RTCDevice
        CallOS  OS_AddDevice, tailcall

RTCDevice
        DCW     HALDeviceType_SysPeri + HALDeviceSysPeri_RTC
        DCW     HALDeviceID_RTC_TPS65950
        DCD     HALDeviceBus_Ser + HALDeviceSerBus_IIC
        DCD     0               ; API version
        DCD     RTCDesc
        DCD     0               ; Address - N/A
        %       12              ; Reserved
        DCD     RTCActivate
        DCD     RTCDeactivate
        DCD     RTCReset
        DCD     RTCSleep
        DCD     -1              ; Interrupt N/A
        DCD     0
        %       8
        DCB     RTCTimeFormat_BCD
        DCB     RTCFormatFlags_BCD_1BasedDay+RTCFormatFlags_BCD_1BasedMonth+RTCFormatFlags_BCD_YearLOIsGood ; todo - add RTCFormatFlags_BCD_NeedsYearHelp once NVRAM is implemented
        %       2
        DCD     RTCReadTime
        DCD     RTCWriteTime

RTCDesc
        DCB "TPS65950-compatible real-time clock",0

        ALIGN

RTCActivate
        MOV     a1, #1
RTCDeactivate
RTCReset
        MOV     pc, lr

RTCSleep
        MOV     a1, #0 ; Previously at full power
        MOV     pc, lr

RTCReadTime
        ; In:
        ; a1 = HALDevice ptr
        ; a2 = RTCTimeStruct ptr
        ; a3 = IICOp func ptr
        ; a4 = kernel workspace ptr
        ; Out:
        ; a1 = return code
        ; RTCTimeStruct updated
        Push    "v1-v3,lr"
        MOV     v1, a3
        MOV     v2, a4
        MOV     v3, a2
        ; Reading the time safely involves several transfers:
        ; 1. Read RTC_STATUS_REG. If bit 1 is clear, the RTC is stopped and we can just assume its contents are invalid.
        ; 2. Set RTC_CTRL_REG=&41 (RTC running, GET_TIME set, 24hr mode). GET_TIME will read the time from the RTC circuitry and latch it into the time registers (the regular time registers, NOT the alarm ones as stated by the manual!)
        ; 3. Read the time regs to read latched time.
        ; There's no need to clear GET_TIME either, as it is cleared automatically by the HW.
        MOV     a1, #TPSRTC_IIC*2
        SUB     sp, sp, #4 ; temp small buffer on stack
        MOV     a3, #1
        MOV     a4, #RTC_STATUS_REG
        MOV     a2, sp
        BL      TPSRead
        CMP     a1, #ECOMPLETED
        LDRB    ip, [a2]
        MOV     a1, #RTCRetCode_InvalidTime
        MOVNE   a1, #RTCRetCode_Error
        EOR     ip, ip, #2
        TSTEQ   ip, #2
        ADDNE   sp, sp, #4
        Pull    "v1-v3,pc", NE
        MOV     ip, #&41
        STR     ip, [a2]
        MOV     a1, #TPSRTC_IIC*2
        MOV     a4, #RTC_CTRL_REG
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADD     sp, sp, #4
        Pull    "v1-v3,pc", NE
        MOV     a1, #TPSRTC_IIC*2
        ; We can read the time directly into the RTCTimeStruct buffer
        ASSERT RTCTimeStruct_BCD_Minutes=RTCTimeStruct_BCD_Seconds+1
        ASSERT RTCTimeStruct_BCD_Hours=RTCTimeStruct_BCD_Seconds+2
        ASSERT RTCTimeStruct_BCD_DayOfMonth=RTCTimeStruct_BCD_Seconds+3
        ASSERT RTCTimeStruct_BCD_Month=RTCTimeStruct_BCD_Seconds+4
        ASSERT RTCTimeStruct_BCD_YearLO=RTCTimeStruct_BCD_Seconds+5
        ADD     a2, v3, #RTCTimeStruct_BCD_Seconds
        MOV     a3, #6
        MOV     a4, #SECONDS_REG
        BL      TPSRead
        CMP     a1, #ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        Pull    "v1-v3,pc", NE
        ASSERT  ECOMPLETED = 0
        STRB    a1, [v3, #RTCTimeStruct_BCD_Centiseconds] ; No centisecond time
        ; Construct a fakey YearHI by looking at YearLO
        ; Anything 70 or above is considered 1970+, else 2000+
        ; This should work OK, since RISC OS clamps the time to 1970 for unix compatability (or it does on boot, at least)
        LDRB    a2, [v3, #RTCTimeStruct_BCD_YearLO]
        CMP     a2, #&70
        MOVGE   a3, #&19
        MOVLT   a3, #&20
        STRB    a3, [v3, #RTCTimeStruct_BCD_YearHI]
        ASSERT  RTCRetCode_OK = 0
        Pull    "v1-v3,pc"

RTCWriteTime
        ; In:
        ; a1 = HALDevice ptr
        ; a2 = RTCTimeStruct ptr
        ; a3 = IICOp func ptr
        ; a4 = kernel workspace ptr
        ; Out:
        ; a1 = return code
        Push    "v1-v3,lr"
        MOV     v1, a3
        MOV     v2, a4
        MOV     v3, a2
        ; Writing the time safely involves several transfers:
        ; 1. Write 0 to RTC_CTRL_REG to stop the clock (just in case there are any issues with the clock updating while it's being written to)
        ; 2. Write the new time values
        ; 3. Write 1 to RTC_CTRL_REG to start the clock
        MOV     a1, #TPSRTC_IIC*2
        MOV     ip, #0
        STR     ip, [sp,#-4]! ; temp small buffer on stack
        MOV     a3, #1
        MOV     a4, #RTC_CTRL_REG
        MOV     a2, sp
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADDNE   sp, sp, #4
        Pull    "v1-v3,pc", NE
        MOV     a1, #TPSRTC_IIC*2
        ; We can write the time directly from the RTCTimeStruct buffer
        ASSERT RTCTimeStruct_BCD_Minutes=RTCTimeStruct_BCD_Seconds+1
        ASSERT RTCTimeStruct_BCD_Hours=RTCTimeStruct_BCD_Seconds+2
        ASSERT RTCTimeStruct_BCD_DayOfMonth=RTCTimeStruct_BCD_Seconds+3
        ASSERT RTCTimeStruct_BCD_Month=RTCTimeStruct_BCD_Seconds+4
        ASSERT RTCTimeStruct_BCD_YearLO=RTCTimeStruct_BCD_Seconds+5
        ADD     a2, v3, #RTCTimeStruct_BCD_Seconds
        MOV     a3, #6
        MOV     a4, #SECONDS_REG
        ; Sometimes we don't write the time, so skip those bytes if necessary
        LDRB    ip, [a2]
        CMP     ip, #255
        ADDEQ   a2, a2, #3
        SUBEQ   a3, a3, #3
        ADDEQ   a4, a4, #3
        ; Sometimes we don't write the date either
        LDRB    ip, [v3, #RTCTimeStruct_BCD_DayOfMonth]
        CMP     ip, #255
        SUBEQS  a3, a3, #3
        BEQ     %FT01 ; Nothing left to write!
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADDNE   sp, sp, #4
        Pull    "v1-v3,pc", NE
01
        MOV     a3, #1
        STR     a3, [sp]
        MOV     a2, sp
        MOV     a4, #RTC_CTRL_REG
        MOV     a1, #TPSRTC_IIC*2
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        ASSERT  RTCRetCode_OK = ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADD     sp, sp, #4
        Pull    "v1-v3,pc"

        EXPORT  ReadTimeForNVRAM
        IMPORT  IIC_DoOp_Poll

; int ReadTimeForNVRAM (struct rtctime*)
; Reads BCD time into given rtctime struct
; Returns zero on success, non-zero on failure
ReadTimeForNVRAM
        MOV     a2, a1
        ADRL    a1, RTCDevice
        LDR     a3, HALInitialised
        CMP     a3, #0
        ADREQL  a3, IIC_DoOp_Poll
        LDRNE   a3, OSentries + 4 * OS_IICOpV
        B       RTCReadTime

        END