; Copyright 2010 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:Proc

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

        EXPORT  TPSRead
        EXPORT  TPSWrite
        EXPORT  TPS_Init
        IMPORT  IIC_DoOp_Poll

; A couple of utility functions for reading/writing registers from the TWL4030/TPS65950 IC
; (although the protocol is probably generic enough to work with many other IIC devices)

; For the majority of uses, v1 simply needs to be initialised as follows:
; LDR  v1, OSentries+4*OS_IICOpV
; i.e. the IIC transfer will be performed on IIC bus 0, via RISCOS_IICOpV,
; returning an IICStatus.
; When using OS_IICOpV, v2 can be left uninitialised.
; TODO - Tidy this up - TPSRead/TPSWrite can simply choose for themselves whether to use OS_IICOpV or IIC_DoOp_Poll

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
        BLX     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
        BLX     v1
        ADD     sp, sp, #16
        Pull    "a2-a4,pc"

; TPS power scripts
; These scripts control how the chip subresources respond to on/off/reset events

; At the moment we only set up a script for reset, which is required to make sure VDD1/VDD2 are reset to safe levels (problems seen on some BB-xM boards when SmartReflex is in use, but code is probably needed for non-SmartReflex voltage control as well)

; Resource groups

RES_GRP_RES       * &0
RES_GRP_PP        * &1
RES_GRP_RC        * &2
RES_GRP_PP_RC     * &3
RES_GRP_PR        * &4
RES_GRP_PP_PR     * &5
RES_GRP_RC_PR     * &6
RES_GRP_ALL       * &7

; Types

RES_TYPE_ALL      * &7

; States

RES_STATE_WRST    * &F
RES_STATE_ACTIVE  * &E
RES_STATE_SLEEP   * &8
RES_STATE_OFF     * &0

; Resources

RES_VAUX1         * 1
RES_VAUX2         * 2
RES_VAUX3         * 3
RES_VAUX4         * 4
RES_VMMC1         * 5
RES_VMMC2         * 6
RES_VPLL1         * 7
RES_VPLL2         * 8
RES_VSIM          * 9
RES_VDAC          * 10
RES_VINTANA1      * 11
RES_VINTANA2      * 12
RES_VINTDIG       * 13
RES_VIO           * 14
RES_VDD1          * 15
RES_VDD2          * 16
RES_VUSB_1V5      * 17
RES_VUSB_1V8      * 18
RES_VUSB_3V1      * 19
RES_VUSBCP        * 20
RES_REGEN         * 21
RES_NRES_PWRON    * 22
RES_CLKEN         * 23
RES_SYSEN         * 24
RES_HFCLKOUT      * 25
RES_32KCLKOUT     * 26
RES_RESET         * 27
RES_MAIN_REF      * 28

; IIC addresses

PROTECT_KEY           * &44
P1_SW_EVENTS          * &46
P2_SW_EVENTS          * &47
P3_SW_EVENTS          * &48
PB_CFG                * &4A
PB_WORD_MSB           * &4B
PB_WORD_LSB           * &4C
SEQ_ADD_WARM          * &58
MEMORY_ADDRESS        * &59
MEMORY_DATA           * &5A
VAUX1_DEV_GRP         * &72
VAUX2_DEV_GRP         * &76
VAUX3_DEV_GRP         * &7A
VAUX4_DEV_GRP         * &7E
VMMC1_DEV_GRP         * &82
VMMC2_DEV_GRP         * &86
VPLL1_DEV_GRP         * &8A
VPLL2_DEV_GRP         * &8E
VSIM_DEV_GRP          * &92
VDAC_DEV_GRP          * &96
VINTANA1_DEV_GRP      * &9A
VINTANA2_DEV_GRP      * &9E
VINTDIG_DEV_GRP       * &A2
VIO_DEV_GRP           * &A6
VDD1_DEV_GRP          * &B0
VDD2_DEV_GRP          * &BE
VUSB1V5_DEV_GRP       * &CC
VUSB1V3_DEV_GRP       * &D2
REGEN_DEV_GRP         * &DA
NRESPWRON_DEV_GRP     * &DD
CLKEN_DEV_GRP         * &E0
SYSEN_DEV_GRP         * &E3
HFCLKOUT_DEV_GRP      * &E6
_32KCLKOUT_DEV_GRP    * &E9
TRITON_RESET_DEV_GRP  * &EC
MAINREF_DEV_GRP       * &EF

; Macros for constructing a power bus message/script entry:
; first byte is high word of message
; second byte is low word
; third byte is optional delay value (encoded)
; $p123 somewhat useless for script entries - it seems to get stripped out when written to script memory?

        MACRO
        PwrMsgSingle $p123, $res, $state, $delay
        DCB ($p123 :SHL: 5) + ($res :SHR: 4)
        DCB (($res :SHL: 4) + ($state)) :AND: 255
      [ "$delay" <> ""
        DCB $delay
      ]
        MEND

        MACRO
        PwrMsgBroadcast $p123, $grp, $type, $type2, $state, $delay
        DCB ($p123 :SHL: 5) + &10 + ($grp :SHL: 1) + ($type2 :SHR: 1)
        DCB (($type2 :SHL: 7) + ($type :SHL: 4) + ($state)) :AND: 255
      [ "$delay" <> ""
        DCB $delay
      ]
        MEND

; Macros for setting up resource properties
; First byte is IIC address to start at
; Then three bytes for configuration (DEV_GRP, TYPE, REMAP)
        MACRO
        PwrResCfg $res, $p123, $type, $type2, $off, $sleep
        DCB $res._DEV_GRP
        DCB $p123 :SHL: 5
        DCB $type + ($type2 :SHL: 3)
        DCB $sleep + ($off :SHL: 4)
        MEND

; Power resource configuration as per http://omapedia.org/wiki/TWL4030_power_scripts
; This is required for the warmreset script to work
; Note that we're ignoring the erratum 27 workaround (not needed for TWL4030/TPS65950?), and only modifying the resources that need changing from default (to avoid messing up any P1/P2/P3 assignments that have been made to aux LDOs that are used for video, USB, etc.)
powerconfig_start
        PwrResCfg VPLL1,     2_001, 3, 1, RES_STATE_OFF, RES_STATE_OFF
        PwrResCfg VINTANA1,  2_111, 1, 2, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg VINTANA2,  2_111, 0, 2, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg VINTDIG,   2_111, 1, 2, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg VIO,       2_111, 2, 2, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg VDD1,      2_001, 4, 1, RES_STATE_OFF, RES_STATE_OFF
        PwrResCfg VDD2,      2_001, 3, 1, RES_STATE_OFF, RES_STATE_OFF
        PwrResCfg REGEN,     2_111, 2, 1, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg NRESPWRON, 2_111, 0, 1, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg CLKEN,     2_111, 3, 2, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg SYSEN,     2_111, 6, 1, RES_STATE_OFF, RES_STATE_SLEEP
        PwrResCfg HFCLKOUT,  2_001, 0, 2, RES_STATE_OFF, RES_STATE_SLEEP
        DCB 0 ; Terminator

; TI's recommended generic warm reset script
; From http://permalink.gmane.org/gmane.linux.ports.arm.omap/56193
warmreset_script
        PwrMsgSingle    2_000, RES_NRES_PWRON, RES_STATE_OFF, 2
        PwrMsgSingle    2_000, RES_RESET, RES_STATE_OFF, 2
        PwrMsgSingle    2_000, RES_MAIN_REF, RES_STATE_WRST, 2
        PwrMsgBroadcast 2_000, RES_GRP_ALL, 0, 2, RES_STATE_WRST, 2
        PwrMsgSingle    2_000, RES_VUSB_3V1, RES_STATE_WRST, 2
        PwrMsgBroadcast 2_000, RES_GRP_ALL, 0, 1, RES_STATE_WRST, 2
        PwrMsgBroadcast 2_000, RES_GRP_RC, RES_TYPE_ALL, 0, RES_STATE_WRST, 2
        PwrMsgSingle    2_000, RES_RESET, RES_STATE_ACTIVE, 2
        PwrMsgSingle    2_000, RES_NRES_PWRON, RES_STATE_ACTIVE, 2
warmreset_script_end

; Post-warmreset power bus message, as per per swcu050g p240
; I'm not entirely sure why this has to be done manually instead of as part of the warmreset script.
        ASSERT PB_WORD_MSB = PB_CFG + 1
        ASSERT PB_WORD_LSB = PB_WORD_MSB + 1
post_warmreset_command
        DCB 2 ; PB_CFG: Allow IIC access to power bus
        PwrMsgBroadcast 2_111, RES_GRP_ALL, RES_TYPE_ALL, 0, RES_STATE_ACTIVE ; &FE, &7E from TRM

        ALIGN
TPS_Init ROUT
        Entry   "v1-v3", 4
        ; Issue the post-warmreset power bus message
        MOV     a1, #&4b*2
        ADR     a2, post_warmreset_command
        MOV     a3, #3
        MOV     a4, #PB_CFG
        ADRL    v1, IIC_DoOp_Poll
        BL      TPSWrite
        ; Poll PB_CFG until command completes
        MOV     a2, sp
        MOV     a3, #1
05
        MOV     a1, #&4b*2
        BL      TPSRead
        CMP     a1, #0
        BNE     %BT05
        LDRB    a1, [a2]
        TST     a1, #1
        BNE     %BT05
        ; Set up resource properties
        ADR     a2, powerconfig_start
        MOV     a3, #3
10
        LDRB    a4, [a2], #1
        CMP     a4, #0
        BEQ     %FT20
        MOV     a1, #&4b*2
        BL      TPSWrite
        ADD     a2, a2, #3
        B       %BT10
20
        ; Now load the warmreset script
        ; Must unlock registers via PROTECT_KEY
        MOV     a1, #&4b*2
        MOV     a2, sp
        MOV     ip, #&c0
        STRB    ip, [a2]
        MOV     a3, #1
        MOV     a4, #PROTECT_KEY
        BL      TPSWrite
        MOV     ip, #&0c
        STRB    ip, [a2]
        MOV     a1, #&4b*2
        BL      TPSWrite
        ; Load script
        ; TODO - Make sure we're not overwriting any other scripts
        ADR     a2, warmreset_script
        ADR     v2, warmreset_script_end
        MOV     v3, #&2b
        BL      loadscript
        ; Set script start address
        MOV     a1, #&4b*2
        MOV     a2, sp
        STRB    v3, [sp]
        MOV     a3, #1
        MOV     a4, #SEQ_ADD_WARM
        BL      TPSWrite
        ; Enable warmreset for P1, P2, P3
        ASSERT  P2_SW_EVENTS = P1_SW_EVENTS + 1
        ASSERT  P3_SW_EVENTS = P2_SW_EVENTS + 1
        MOV     a1, #&4b*2
        MOV     a3, #3
        MOV     a4, #P1_SW_EVENTS
        BL      TPSRead
        LDR     ip, [a2]
        LDR     a1, =&101010
        ORR     ip, ip, a1
        STR     ip, [a2]
        MOV     a1, #&4b*2
        BL      TPSWrite
        ; Lock registers
        MOV     a1, #0
        STRB    a1, [a2]
        MOV     a1, #&4b*2
        MOV     a3, #1
        MOV     a4, #PROTECT_KEY
        BL      TPSWrite
        ; Done
        EXIT

loadscript ROUT
        ; a2 = script start
        ; v1 = IIC func ptr
        ; v2 = script end
        ; v3 = address to load to
        Entry   "a2,v3-v4", 4
        ; Set MEMORY_ADDRESS to the address to start at, SHL 2
        MOV     a1, #&4b*2
        MOV     ip, v3, LSL #2
        MOV     a2, sp
        STRB    ip, [a2]
        MOV     a3, #1
        MOV     a4, #MEMORY_ADDRESS
        BL      TPSWrite
        ; Load script
        LDR     a2, [sp, #4]
        MOV     a4, #MEMORY_DATA
10
        ; Three data bytes
        MOV     v4, #3
20
        MOV     a1, #&4b*2
        BL      TPSWrite
        ADD     a2, a2, #1
        SUBS    v4, v4, #1
        BNE     %BT20
        ; Next address byte
        Push    "a2"
        ADD     v3, v3, #1
        CMP     a2, v2
        MOVEQ   v3, #&3f
        ADD     a2, sp, #4
        STRB    v3, [a2]
        MOV     a1, #&4b*2
        BL      TPSWrite
        Pull    "a2"
        CMP     v3, #&3f
        BNE     %BT10
        EXIT

        END