; 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

        GET     Hdr:OSEntries
        GET     Hdr:HALEntries

        GET     hdr.omap3530
        GET     hdr.StaticWS
        GET     hdr.GPIO

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

        EXPORT  GPIO_Init
        EXPORT  GPIOx_SetAsOutput
        EXPORT  GPIOx_SetOutput
        EXPORT  GPIOx_SetAndEnableIRQ
        EXPORT  GPIO_InitDevice
        IMPORT  TPSRead
        IMPORT  TPSWrite
        IMPORT  IIC_DoOp_Poll
        IMPORT  DebugHALPrint
        IMPORT  DebugHALPrintReg
        IMPORT  memcpy

GPIO_Init
        ; Don't bother resetting the controllers, it could conflict with settings made by x-loader/u-boot
        Entry   "v1-v3",4
        ADR     a1, L4_GPIO_Table
        MOV     a2, #6
        MOV     a3, #0
10
        LDR     a4, [a1], #4
        SUBS    a2, a2, #1
        STR     a3, [a4, #GPIO_IRQENABLE1]
        BNE     %BT10
        ; Make sure the TPS GPIOs are enabled properly.
        ; Specifically:
        ; - Set the GPIO_ON bit in GPIO_CTRL
        LDR     v1, HALInitialised
        CMP     v1, #0
        ADREQL  v1, IIC_DoOp_Poll
        LDRNE   v1, OSentries+4*OS_IICOpV
        MOV     a1, #TPSGPIO_IIC*2
        MOV     a2, sp
        MOV     a3, #1
        MOV     a4, #TPS_GPIO_CTRL
        BL      TPSRead
        LDRB    ip, [a2]
        TST     ip, #4 ; GPIO_ON
        ORREQ   ip, ip, #4
        STREQB  ip, [a2]
        MOVEQ   a1, #TPSGPIO_IIC*2
        BLEQ    TPSWrite
        ; - For the beagleboard, set up the right pullup/down settings
        ; TODO - Do other platforms, and add to boardconfig
        LDR     a1, [sb, #BoardConfig_MachID]
        LDR     a2, =MachID_BeagleBoard
        CMP     a1, a2
        BNE     %FT10
        MOV     a1, #TPSGPIO_IIC*2
        ADR     a2, Beagle_TPS_PUPD
        MOV     a3, #5
        MOV     a4, #TPS_GPIOPUPCTR1
        BL      TPSWrite
10
        EXIT

Beagle_TPS_PUPD
        DCD     4_1010000111000120 ; 0 = PU/PD off, 1=PD, 2=PU
        DCD     4_0011

        ; a1 = GPIO # (OMAP or TPS or TPS LED)
        ; a2 = initial value (zero or nonzero)
GPIOx_SetAsOutput
        SUBS    a3, a1, #GPIO_PIN_MAX
        BGE     %FT10
        ; OMAP GPIO
        GPIO_PrepareR a3, a4, a1
        GPIO_SetAsOutput a3, a4, a1
        GPIO_SetOutput a2, a3, a4
        MOV     pc, lr
10      ; TPS GPIO
        Entry   "v1-v3", 4
        CMP     a3, #TPS_GPIO_PIN_MAX
        BGE     %FT20
        MOV     a4, a3, LSR #3 ; Register offset
        MOV     v2, a2
        AND     a3, a3, #&7
        MOV     v3, #1
        MOV     v3, v3, LSL a3 ; Mask value
        ; If HAL_Init isn't done yet, we can't use OS_IICOpV
        LDR     v1, HALInitialised
        CMP     v1, #0
        ADREQL  v1, IIC_DoOp_Poll
        LDRNE   v1, OSentries+4*OS_IICOpV
        MOV     a1, #TPSGPIO_IIC*2
        MOV     a2, sp
        MOV     a3, #1
        ADD     a4, a4, #TPS_GPIODATADIR1
        BL      TPSRead
        ; TODO - Handle error!
        LDRB    ip, [a2]
        ORR     ip, ip, v3
        STRB    ip, [a2]
        MOV     a1, #TPSGPIO_IIC*2
        BL      TPSWrite
        STRB    v3, [a2]
        MOV     a1, #TPSGPIO_IIC*2
        CMP     v2, #0
        ADDNE   a4, a4, #TPS_SETGPIODATAOUT1-TPS_GPIODATADIR1
        ADDEQ   a4, a4, #TPS_CLEARGPIODATAOUT1-TPS_GPIODATADIR1
        BL      TPSWrite
        EXIT
20      ; TPS LED
        ; First make sure the relevant PWM is configured correctly
        ; i.e. generate a constant signal, not timed on/off
        Push    "a2,a3"
        MOV     a1, #TPSLED_IIC*2
        ADR     a2, PWM_Value
        MOV     a3, #2
        MOVEQ   a4, #TPS_PWMAON
        MOVNE   a4, #TPS_PWMBON
        LDR     v1, HALInitialised
        CMP     v1, #0
        ADREQL  v1, IIC_DoOp_Poll
        LDRNE   v1, OSentries+4*OS_IICOpV
        BL      TPSWrite
        ; Also make sure the PWM clocks are enabled
        MOV     a1, #&49*2
        ADD     a2, sp, #8
        MOV     a3, #1
        MOV     a4, #&91 ; GPBR1
        BL      TPSRead
        LDR     ip, [sp, #4]
        CMP     ip, #TPS_GPIO_PIN_MAX
        LDRB    ip, [a2]
        ORREQ   ip, ip, #&5
        ORRNE   ip, ip, #&a
        STRB    ip, [a2]
        MOV     a1, #&49*2
        BL      TPSWrite
        Pull    "a2,a3"
        CMP     a3, #TPS_GPIO_PIN_MAX
        ; Match the behaviour of Linux:
        ; a2=0 sets the LED*ON and LED*PWM bits
        ; a2!=0 clears them
        MOVEQ   v3, #&55 ; Mask for LEDA bits
        MOVNE   v3, #&AA ; Mask for LEDB bits
        CMP     a2, #0
        MOVNE   v2, #0 ; a2!=0, don't set any bits
        ANDEQ   v2, v3, #&33 ; a2=0, set appropriate LED*ON and LED*PWM bit
        MOV     a1, #TPSLED_IIC*2
        MOV     a2, sp
        MOV     a3, #1
        MOV     a4, #TPS_LEDEN
        ; If HAL_Init isn't done yet, we can't use OS_IICOpV
        BL      TPSRead
        LDRB    ip, [a2]
        BIC     ip, ip, v3
        ORR     ip, ip, v2
        STRB    ip, [a2]
        MOV     a1, #TPSLED_IIC*2
        BL      TPSWrite
        EXIT

PWM_Value
        DCD     &7f7f

        ; a1 = GPIO # (OMAP or TPS)
        ; a2 = value (zero or nonzero)
GPIOx_SetOutput
        SUBS    a3, a1, #GPIO_PIN_MAX
        BGE     %FT10
        ; OMAP GPIO
        GPIO_PrepareR a3, a4, a1
        GPIO_SetOutput a2, a3, a4
        MOV     pc, lr
10      ; TPS GPIO
        Entry   "v1-v2", 4
        MOV     a4, a3, LSR #3 ; Register offset
        CMP     a2, #0
        AND     a3, a3, #&7
        ADDNE   a4, a4, #TPS_SETGPIODATAOUT1
        MOV     v1, #1
        ADDEQ   a4, a4, #TPS_CLEARGPIODATAOUT1
        MOV     v1, v1, LSL a3 ; Mask value
        MOV     a2, sp
        STRB    v1, [sp]
        MOV     a1, #TPSGPIO_IIC*2
        ; If HAL_Init isn't done yet, we can't use OS_IICOpV
        LDR     v1, HALInitialised
        CMP     v1, #0
        ADREQL  v1, IIC_DoOp_Poll
        LDRNE   v1, OSentries+4*OS_IICOpV
        MOV     a3, #1
        BL      TPSWrite
        EXIT

        ; a1 = GPIO # (OMAP only!)
        ; a2 = IRQ type flags:
        ;      +1 = LEVELDETECT0
        ;      +2 = LEVELDETECT1
        ;      +4 = RISINGDETECT
        ;      +8 = FALLINGDETECT
GPIOx_SetAndEnableIRQ
        GPIO_PrepareR a3, a4, a1
        MRS     ip, CPSR
        ORR     a1, ip, #I32_bit ; interrupts off
        MSR     CPSR_c, a1
        MOV     a2, a2, LSL #28
        MSR     CPSR_f, a2 ; load into NZCV flags (MI EQ CS VS condition codes)
        LDR     a1, [a3, #GPIO_FALLINGDETECT]
        BICPL   a1, a1, a4
        LDR     a2, [a3, #GPIO_RISINGDETECT]
        ORRMI   a1, a1, a4
        STR     a1, [a3, #GPIO_FALLINGDETECT]
        BICNE   a2, a2, a4
        LDR     a1, [a3, #GPIO_LEVELDETECT1]
        ORREQ   a2, a2, a4
        STR     a2, [a3, #GPIO_RISINGDETECT]
        BICCC   a1, a1, a4
        LDR     a2, [a3, #GPIO_LEVELDETECT0]
        ORRCS   a1, a1, a4
        STR     a1, [a3, #GPIO_LEVELDETECT1]
        BICVC   a2, a2, a4
        LDR     a1, [a3, #GPIO_OE]
        ORRVS   a2, a2, a4
        ORR     a1, a1, a4 ; set pin as input
        STR     a2, [a3, #GPIO_LEVELDETECT0]
        STR     a1, [a3, #GPIO_OE]
        STR     a4, [a3, #GPIO_SETIRQENABLE1]
        MSR     CPSR_c, ip ; interrupts restored
        MOV     pc, lr

        ; Init the GPIO HAL device
        ; a1 = GPIOType value
        ; a2 = GPIORevision value
GPIO_InitDevice
        Push    "a1-a2,lr"
        ADRL    a1, GPIOWS
        ADR     a2, GPIOTemplate
        MOV     a3, #HALDeviceSize
        BL      memcpy
        Pull    "a1-a2,lr"
        ADRL    a3, GPIOWS
        STR     a1, [a3, #HALDevice_GPIOType]
        STR     a2, [a3, #HALDevice_GPIORevision]
        MOV     a2, a3
        MOV     a1, #0
        LDR     pc, OSentries + 4*OS_AddDevice ; Tail call

; Generic HAL device used for all board types
GPIOTemplate
        DCW     HALDeviceType_Comms + HALDeviceComms_GPIO
        DCW     HALDeviceID_GPIO_OMAP3
        DCD     HALDeviceBus_Peri + HALDevicePeriBus_Sonics3220
        DCD     0               ; API version
        DCD     GPIODesc        ; Description
        DCD     0               ; Address (none)
        %       12              ; Reserved
        DCD     GPIOActivate
        DCD     GPIODeactivate
        DCD     GPIOReset
        DCD     GPIOSleep
        DCD     -1              ; Device (none)
        DCD     0               ; TestIRQ
        DCD     0               ; ClearIRQ
        %       4
        ASSERT  (.-GPIOTemplate) = HALDeviceSize

GPIODesc
        =       "OMAP3 GPIO interface", 0
        ALIGN

        ; These don't do much
GPIOActivate
        MOV     a1, #1
GPIODeactivate
GPIOReset
        MOV     pc, lr

GPIOSleep
        MOV     a1, #0
        MOV     pc, lr

        END