; 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
        GET     hdr.I2C

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

        EXPORT  RTC_Init

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

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

; HAL API I2C transfer states
ECOMPLETED *    0
EINPROGRESS *   1
ENOACK  *       2
EBUSY   *       3
EERROR  *       5

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 ; todo - add RTCFormatFlags_BCD_NeedsYearHelp once NVRAM is implemented
        %       2
        DCD     RTCReadTime
        DCD     RTCWriteTime

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

        ALIGN

TPSRead
        ; a1 = IIC address(*2)
        ; a2 = buffer
        ; a3 = count
        ; a4 = start register
        ; v1 = IIC func
        ; v2 = IIC param
        ; out:
        ; a1 = return code
        ; ip corrupted
        ; buffer updated
        ORR     a1, a1, #1 ; read
        Push    "a1-a4,lr" ; Push regs and second iic_transfer block
        EOR     a1, a1, #1+(1:SHL:29) ; write with retry
        ADD     a2, sp, #12
        MOV     a3, #1
        Push    "a1-a3" ; push first iic_transfer block
        MOV     a1, sp
        MOV     a2, #2
        MOV     a3, v2
        MOV     lr, pc
        MOV     pc, v1
        ADD     sp, sp, #16
        Pull    "a2-a4,pc"

TPSWrite
        ; a1 = IIC address(*2)
        ; a2 = buffer
        ; a3 = count
        ; a4 = start register
        ; v1 = IIC func
        ; v2 = IIC param
        ; out:
        ; a1 = return code
        ; ip corrupted
        ORR     a1, a1, #1:SHL:31 ; Write (no start bit)
        Push    "a1-a4,lr" ; Push regs and second iic_transfer block
        EOR     a1, a1, #(1:SHL:29)+(1:SHL:31) ; Write (retries)
        ADD     a2, sp, #12
        MOV     a3, #1
        Push    "a1-a3" ; push first iic_transfer block
        MOV     a1, sp
        MOV     a2, #2
        MOV     a3, v2
        MOV     lr, pc
        MOV     pc, v1
        ADD     sp, sp, #16
        Pull    "a2-a4,pc"
        ; Hack version to get around nostart bug
;        Push    "a2-a4,v3-v5,lr"
;        MOV     v4, sp
;        ; Copy the write data to the stack
;        MOV     v5, a3
;10
;        SUBS    v5, v5, #1
;        LDRB    ip, [a2,v5]
;        STRB    ip, [sp,#-1]!
;        BNE     %BT10
;        ; Add the TPS register address
;        STRB    a4, [sp,#-1]!
;        MOV     a2, sp
;        ADD     a3, a3, #1
;        BIC     sp, sp, #3 ; round sp to word address
;        ORR     a1, a1, #(1:SHL:29) ; Write (retries)
;        Push    "a1-a3" ; push iic_transfer block
;        MOV     a1, sp
;        MOV     a2, #1
;        MOV     a3, v2
;        MOV     lr, pc
;        MOV     pc, v1
;        MOV     sp, v4 ; restore original sp
;        Pull    "a2-a4,v3-v5,pc"



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, #&4b*2
        SUB     sp, sp, #4 ; temp small buffer on stack
        MOV     a3, #1
        MOV     a4, #&2a ; 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, #&4b*2
        MOV     a4, #&29 ; RTC_CTRL_REG
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADD     sp, sp, #4
        Pull    "v1-v3,pc", NE
        MOV     a1, #&4b*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, #&1c ; 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, #&4b*2
        MOV     ip, #0
        STR     ip, [sp,#-4]! ; temp small buffer on stack
        MOV     a3, #1
        MOV     a4, #&29 ; 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, #&4b*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, #&1c ; SECONDS_REG
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADDNE   sp, sp, #4
        Pull    "v1-v3,pc", NE
        MOV     a3, #1
        STR     a3, [sp]
        MOV     a2, sp
        MOV     a4, #&29 ; RTC_CTRL_REG
        MOV     a1, #&4b*2
        BL      TPSWrite
        CMP     a1, #ECOMPLETED
        ASSERT  RTCRetCode_OK = ECOMPLETED
        MOVNE   a1, #RTCRetCode_Error
        ADD     sp, sp, #4
        Pull    "v1-v3,pc"


        END