; 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
        IMPORT  memcpy

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

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

; RTC_CTRL_REG bitfields
RTC_CTRL_STOP_RTC_M             *       (1 << 0)
RTC_CTRL_ROUND_30S_M            *       (1 << 1)
RTC_CTRL_AUTO_COMP_M            *       (1 << 2)
RTC_CTRL_MODE_12_24_M           *       (1 << 3)
RTC_CTRL_TEST_MODE_M            *       (1 << 4)
RTC_CTRL_SET_32_COUNTER_M       *       (1 << 5)
RTC_CTRL_GET_TIME_M             *       (1 << 6)

; RTC_STATUS_REG bitfields
RTC_STATUS_RUN_M                *       (1 << 1)
RTC_STATUS_1S_EVENT_M           *       (1 << 2)
RTC_STATUS_1M_EVENT_M           *       (1 << 3)
RTC_STATUS_1H_EVENT_M           *       (1 << 4)
RTC_STATUS_1D_EVENT_M           *       (1 << 5)
RTC_STATUS_ALARM_M              *       (1 << 6)
RTC_STATUS_POWER_UP_M           *       (1 << 7)

RTC_Init
        ; Just register our HAL Device with the OS
        ADRL    a1, RTCWS
        ADR     a2, RTCDeviceTemplate
        MOV     a3, #RTCSize
        Push    "lr"
        BL      memcpy
        Pull    "lr"
        STR     sb, [a1, #RTCDeviceHAL_SB]

        MOV     a1, #0
        ADR     a2, RTCDevice
        CallOS  OS_AddDevice, tailcall

RTCDeviceTemplate
        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 + \
                RTCFormatFlags_BCD_NeedsYearHelp
        %       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
        ; Out:
        ; a1 = return code
        ; RTCTimeStruct updated
        Push    "v1,v3,sb,lr"
        LDR     sb, [a1, #RTCDeviceHAL_SB]
        LDR     v1, OSentries+4*OS_IICOpV ; for TPSRead/TPSWrite
        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, #IICStatus_Completed
        LDRB    ip, [a2]
        MOV     a1, #RTCRetCode_InvalidTime
        MOVNE   a1, #RTCRetCode_Error
        EOR     ip, ip, #RTC_STATUS_RUN_M
        TSTEQ   ip, #RTC_STATUS_RUN_M
        ADDNE   sp, sp, #4
        Pull    "v1,v3,sb,pc", NE
        MOV     ip, #(RTC_CTRL_GET_TIME_M + RTC_CTRL_STOP_RTC_M)
        STR     ip, [a2]
        MOV     a1, #TPSRTC_IIC*2
        MOV     a4, #RTC_CTRL_REG
        BL      TPSWrite
        CMP     a1, #IICStatus_Completed
        MOVNE   a1, #RTCRetCode_Error
        ADD     sp, sp, #4
        Pull    "v1,v3,sb,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, #IICStatus_Completed
        MOVNE   a1, #RTCRetCode_Error
        ASSERT  IICStatus_Completed = 0
        STREQB  a1, [v3, #RTCTimeStruct_BCD_Centiseconds] ; No centisecond time
        STREQB  a1, [v3, #RTCTimeStruct_BCD_YearHI] ; Kernel gives year help
        ASSERT  RTCRetCode_OK = 0
        Pull    "v1,v3,sb,pc"

RTCWriteTime
        ; In:
        ; a1 = HALDevice ptr
        ; a2 = RTCTimeStruct ptr
        ; Out:
        ; a1 = return code
        Push    "v1,v3,sb,lr"
        LDR     sb, [a1, #RTCDeviceHAL_SB]
        LDR     v1, OSentries+4*OS_IICOpV ; for TPSRead/TPSWrite
        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, #IICStatus_Completed
        MOVNE   a1, #RTCRetCode_Error
        ADDNE   sp, sp, #4
        Pull    "v1,v3,sb,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, #IICStatus_Completed
        MOVNE   a1, #RTCRetCode_Error
        ADDNE   sp, sp, #4
        Pull    "v1,v3,sb,pc", NE
01
        MOV     a3, #RTC_CTRL_STOP_RTC_M
        STR     a3, [sp]
        MOV     a2, sp
        MOV     a4, #RTC_CTRL_REG
        MOV     a1, #TPSRTC_IIC*2
        BL      TPSWrite
        CMP     a1, #IICStatus_Completed
        ASSERT  RTCRetCode_OK = IICStatus_Completed
        MOVNE   a1, #RTCRetCode_Error
        ADD     sp, sp, #4
        Pull    "v1,v3,sb,pc"

        END